mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-08-10 08:43:34 +08:00
🔧 chore(license): 补齐开源许可与发布声明
- 新增项目版权及上游 NOTICE - 让桌面、容器、驱动发布包携带法律文档 - 补充 Wails 和前端许可证元数据 - 增加发布资产回归校验
This commit is contained in:
@@ -34,6 +34,8 @@ REPO = "Syngnat/GoNavi"
|
||||
SCHEMA_VERSION = 1
|
||||
SKIP_NAMES = {
|
||||
"SHA256SUMS",
|
||||
"LICENSE",
|
||||
"NOTICE",
|
||||
"latest.json",
|
||||
"latest-dev.json",
|
||||
".DS_Store",
|
||||
|
||||
@@ -18,6 +18,8 @@ class GenerateUpdateLatestManifestTest(unittest.TestCase):
|
||||
msi = assets / "GoNavi-1.2.3-Windows-Amd64-Installer.msi"
|
||||
exe.write_bytes(b"fake-binary")
|
||||
msi.write_bytes(b"fake-installer")
|
||||
(assets / "LICENSE").write_text("license text\n", encoding="utf-8")
|
||||
(assets / "NOTICE").write_text("notice text\n", encoding="utf-8")
|
||||
(assets / "SHA256SUMS").write_text(
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa GoNavi-1.2.3-Windows-Amd64-Portable.exe\n"
|
||||
"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb GoNavi-1.2.3-Windows-Amd64-Installer.msi\n",
|
||||
@@ -67,6 +69,8 @@ class GenerateUpdateLatestManifestTest(unittest.TestCase):
|
||||
)
|
||||
self.assertEqual(installer_asset["sha256"], "b" * 64)
|
||||
self.assertNotIn("SHA256SUMS", [a["name"] for a in data["assets"]])
|
||||
self.assertNotIn("LICENSE", [a["name"] for a in data["assets"]])
|
||||
self.assertNotIn("NOTICE", [a["name"] for a in data["assets"]])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
69
tools/legal-release-assets.test.py
Normal file
69
tools/legal-release-assets.test.py
Normal file
@@ -0,0 +1,69 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import json
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parent.parent
|
||||
WORKFLOWS = (
|
||||
ROOT / ".github" / "workflows" / "release.yml",
|
||||
ROOT / ".github" / "workflows" / "dev-build.yml",
|
||||
)
|
||||
DOCKERFILES = (
|
||||
ROOT / "Dockerfile.mcp-server",
|
||||
ROOT / "Dockerfile.web-server",
|
||||
ROOT / "Dockerfile.build-env",
|
||||
)
|
||||
|
||||
|
||||
class LegalReleaseAssetsTest(unittest.TestCase):
|
||||
def test_notice_identifies_project_and_upstream_notices(self) -> None:
|
||||
notice = (ROOT / "NOTICE").read_text(encoding="utf-8")
|
||||
|
||||
self.assertTrue(notice.startswith("GoNavi\nCopyright 2026 Syngnat\n"))
|
||||
for component in (
|
||||
"Apache RocketMQ",
|
||||
"Apache IoTDB",
|
||||
"Apache Thrift",
|
||||
"Eclipse Paho MQTT Go",
|
||||
"Vendored HighGo pq-sm3",
|
||||
"Vendored InterSystems IRIS native driver",
|
||||
):
|
||||
with self.subTest(component=component):
|
||||
self.assertIn(component, notice)
|
||||
|
||||
def test_project_metadata_declares_the_license_and_copyright(self) -> None:
|
||||
wails = json.loads((ROOT / "wails.json").read_text(encoding="utf-8"))
|
||||
frontend = json.loads((ROOT / "frontend" / "package.json").read_text(encoding="utf-8"))
|
||||
|
||||
self.assertEqual(wails["author"], {"name": "Syngnat", "email": "yangguofeng919@gmail.com"})
|
||||
self.assertEqual(wails["info"]["copyright"], "Copyright 2026 Syngnat")
|
||||
self.assertEqual(frontend["license"], "Apache-2.0")
|
||||
|
||||
def test_release_workflows_bundle_legal_documents(self) -> None:
|
||||
for workflow in WORKFLOWS:
|
||||
with self.subTest(workflow=workflow.name):
|
||||
source = workflow.read_text(encoding="utf-8")
|
||||
|
||||
self.assertIn('cp ../../LICENSE "$APP_NAME/Contents/Resources/LICENSE"', source)
|
||||
self.assertIn('cp ../../NOTICE "$APP_NAME/Contents/Resources/NOTICE"', source)
|
||||
self.assertIn('tar -czvf "$TAR_NAME" "$TARGET" -C ../.. LICENSE NOTICE', source)
|
||||
self.assertIn("AppDir/usr/share/doc/gonavi", source)
|
||||
self.assertIn("cp ../../LICENSE AppDir/usr/share/doc/gonavi/LICENSE", source)
|
||||
self.assertIn("cp ../../NOTICE AppDir/usr/share/doc/gonavi/NOTICE", source)
|
||||
self.assertIn('run: install -m 0644 LICENSE NOTICE release-assets/', source)
|
||||
self.assertIn('-d "LicenseFile=$licenseFile"', source)
|
||||
self.assertIn('-d "NoticeFile=$noticeFile"', source)
|
||||
|
||||
def test_docker_images_include_legal_documents(self) -> None:
|
||||
for dockerfile in DOCKERFILES:
|
||||
with self.subTest(dockerfile=dockerfile.name):
|
||||
source = dockerfile.read_text(encoding="utf-8")
|
||||
self.assertIn("/usr/share/doc/gonavi", source)
|
||||
self.assertIn("LICENSE", source)
|
||||
self.assertIn("NOTICE", source)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -7,6 +7,10 @@ import zipfile
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parent.parent
|
||||
LEGAL_FILENAMES = ("LICENSE", "NOTICE")
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) != 3:
|
||||
raise SystemExit("usage: package-driver-release-assets.py <drivers-dir> <output-dir>")
|
||||
@@ -17,6 +21,12 @@ def main():
|
||||
if not drivers_dir.is_dir():
|
||||
raise SystemExit(f"drivers dir not found: {drivers_dir}")
|
||||
|
||||
legal_files = [ROOT / filename for filename in LEGAL_FILENAMES]
|
||||
missing_legal_files = [path for path in legal_files if not path.is_file()]
|
||||
if missing_legal_files:
|
||||
missing = ", ".join(str(path) for path in missing_legal_files)
|
||||
raise SystemExit(f"legal notice files not found: {missing}")
|
||||
|
||||
out_name = "GoNavi-DriverAgents.zip"
|
||||
index_name = "GoNavi-DriverAgents-Index.json"
|
||||
manifest_name = "GoNavi-DriverAgents-Manifest.json"
|
||||
@@ -46,6 +56,13 @@ def main():
|
||||
shutil.copy2(asset, standalone_path)
|
||||
standalone_assets.append(standalone_path.name)
|
||||
|
||||
for legal_file in legal_files:
|
||||
zf.write(legal_file, legal_file.name)
|
||||
standalone_path = output_dir / legal_file.name
|
||||
if standalone_path.exists():
|
||||
raise RuntimeError(f"release asset already exists: {standalone_path}")
|
||||
shutil.copy2(legal_file, standalone_path)
|
||||
|
||||
index_path.write_text(
|
||||
json.dumps({"assets": size_index}, ensure_ascii=False, indent=2) + "\n",
|
||||
encoding="utf-8",
|
||||
@@ -54,6 +71,7 @@ def main():
|
||||
print(f"created {out_name} size={out_path.stat().st_size} bytes")
|
||||
print(f"created {index_name} entries={len(size_index)}")
|
||||
print(f"published standalone driver assets={len(standalone_assets)}")
|
||||
print(f"bundled legal files={len(legal_files)}")
|
||||
print(f"reserved manifest output path: {manifest_path}")
|
||||
return 0
|
||||
|
||||
|
||||
@@ -39,6 +39,8 @@ class PackageDriverReleaseAssetsTest(unittest.TestCase):
|
||||
self.assertTrue((output_dir / "GoNavi-DriverAgents.zip").is_file())
|
||||
self.assertTrue((output_dir / windows_asset.name).is_file())
|
||||
self.assertTrue((output_dir / darwin_asset.name).is_file())
|
||||
self.assertEqual((output_dir / "LICENSE").read_bytes(), (ROOT / "LICENSE").read_bytes())
|
||||
self.assertEqual((output_dir / "NOTICE").read_bytes(), (ROOT / "NOTICE").read_bytes())
|
||||
|
||||
index = json.loads((output_dir / "GoNavi-DriverAgents-Index.json").read_text(encoding="utf-8"))
|
||||
self.assertEqual(index["assets"][windows_asset.name], len(b"windows-asset"))
|
||||
@@ -48,10 +50,14 @@ class PackageDriverReleaseAssetsTest(unittest.TestCase):
|
||||
self.assertEqual(
|
||||
sorted(zf.namelist()),
|
||||
[
|
||||
"LICENSE",
|
||||
"MacOS/clickhouse-driver-agent-darwin-arm64",
|
||||
"NOTICE",
|
||||
"Windows/clickhouse-driver-agent-windows-amd64.exe",
|
||||
],
|
||||
)
|
||||
self.assertEqual(zf.read("LICENSE"), (ROOT / "LICENSE").read_bytes())
|
||||
self.assertEqual(zf.read("NOTICE"), (ROOT / "NOTICE").read_bytes())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -40,6 +40,10 @@ class WindowsReleaseArtifactsTest(unittest.TestCase):
|
||||
self.assertIn(f'"{ARM64_COMPONENT_GUID}"', source)
|
||||
self.assertIn('$installMarkerPath = Join-Path $env:RUNNER_TEMP ".gonavi-msi-install"', source)
|
||||
self.assertIn('-d "InstallMarker=$installMarkerPath"', source)
|
||||
self.assertIn('$licenseFile = (Resolve-Path -LiteralPath "..\\\\..\\\\LICENSE").Path', source)
|
||||
self.assertIn('$noticeFile = (Resolve-Path -LiteralPath "..\\\\..\\\\NOTICE").Path', source)
|
||||
self.assertIn('-d "LicenseFile=$licenseFile"', source)
|
||||
self.assertIn('-d "NoticeFile=$noticeFile"', source)
|
||||
self.assertNotIn('-d "ProductName=GoNavi Dev"', source)
|
||||
self.assertNotIn('-d "InstallFolderName=GoNavi Dev"', source)
|
||||
self.assertNotIn('-d "RegistryKeyName=GoNavi Dev"', source)
|
||||
@@ -87,6 +91,16 @@ class WindowsReleaseArtifactsTest(unittest.TestCase):
|
||||
assert file_marker is not None
|
||||
self.assertEqual(file_marker.attrib["Source"], "$(var.InstallMarker)")
|
||||
self.assertEqual(file_marker.attrib["Name"], ".gonavi-msi-install")
|
||||
license_file = package.find(".//wix:File[@Id='GoNaviLicense']", ns)
|
||||
self.assertIsNotNone(license_file)
|
||||
assert license_file is not None
|
||||
self.assertEqual(license_file.attrib["Source"], "$(var.LicenseFile)")
|
||||
self.assertEqual(license_file.attrib["Name"], "LICENSE.txt")
|
||||
notice_file = package.find(".//wix:File[@Id='GoNaviNotice']", ns)
|
||||
self.assertIsNotNone(notice_file)
|
||||
assert notice_file is not None
|
||||
self.assertEqual(notice_file.attrib["Source"], "$(var.NoticeFile)")
|
||||
self.assertEqual(notice_file.attrib["Name"], "NOTICE.txt")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
Reference in New Issue
Block a user