mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-08-15 19:24:23 +08:00
- 新增无头运行时及连接、查询、导出、批处理、审计和 MCP 命令 - 复用活动数据根、密文存储与跨进程锁,落实写入安全和取消语义 - 增加六平台 CLI 归档、独立校验和、Docker、npm 与 WinGet 分发 - 隔离 GUI/CLI 更新资产并强化 macOS 签名与公证门禁 - 补充并发、审计、事务及发布契约回归测试 Refs #902
121 lines
4.7 KiB
Python
121 lines
4.7 KiB
Python
#!/usr/bin/env python3
|
|
"""Generate a WinGet manifest for the standalone GoNavi CLI.
|
|
|
|
The input is the independent CLI checksum file published with a stable
|
|
release. No platform hash is accepted from command-line text, which keeps
|
|
the generated manifest tied to the release's signed asset contract.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import re
|
|
from pathlib import Path
|
|
|
|
|
|
CLI_ASSETS = {
|
|
"x64": "gonavi-cli_{version}_windows_amd64.zip",
|
|
"arm64": "gonavi-cli_{version}_windows_arm64.zip",
|
|
}
|
|
CHECKSUM_NAME = "gonavi-cli_{version}_checksums.txt"
|
|
BINARY_NAME = "gonavi.exe"
|
|
VERSION_RE = re.compile(r"^\d+\.\d+\.\d+$")
|
|
CHECKSUM_RE = re.compile(r"^([0-9a-fA-F]{64})\s+\*?(.+)$")
|
|
|
|
|
|
def load_checksums(path: Path, version: str) -> dict[str, str]:
|
|
expected_checksum_name = CHECKSUM_NAME.format(version=version)
|
|
if path.name != expected_checksum_name:
|
|
raise ValueError(f"checksum file must be named {expected_checksum_name}")
|
|
hashes: dict[str, str] = {}
|
|
for raw_line in path.read_text(encoding="utf-8").splitlines():
|
|
line = raw_line.strip()
|
|
if not line:
|
|
continue
|
|
match = CHECKSUM_RE.fullmatch(line)
|
|
if not match:
|
|
raise ValueError(f"invalid checksum line: {raw_line!r}")
|
|
name = Path(match.group(2).strip()).name
|
|
if name in hashes:
|
|
raise ValueError(f"duplicate checksum entry: {name}")
|
|
hashes[name] = match.group(1).lower()
|
|
|
|
required = {pattern.format(version=version) for pattern in CLI_ASSETS.values()}
|
|
if set(hashes) != required | {
|
|
f"gonavi-cli_{version}_darwin_amd64.tar.gz",
|
|
f"gonavi-cli_{version}_darwin_arm64.tar.gz",
|
|
f"gonavi-cli_{version}_linux_amd64.tar.gz",
|
|
f"gonavi-cli_{version}_linux_arm64.tar.gz",
|
|
}:
|
|
raise ValueError("checksum file does not contain exactly the six CLI archives")
|
|
return hashes
|
|
|
|
|
|
def render_manifest(version: str, hashes: dict[str, str], repo: str) -> str:
|
|
lines = [
|
|
"# Generated by tools/generate-winget-cli-manifest.py; do not edit hashes by hand.",
|
|
"PackageIdentifier: Syngnat.GoNavi.CLI",
|
|
f"PackageVersion: {version}",
|
|
"PackageLocale: en-US",
|
|
"Publisher: Syngnat",
|
|
"PublisherUrl: https://github.com/Syngnat",
|
|
"PublisherSupportUrl: https://github.com/Syngnat/GoNavi/issues",
|
|
"PackageName: GoNavi CLI",
|
|
"PackageUrl: https://github.com/Syngnat/GoNavi",
|
|
"License: Apache-2.0",
|
|
"LicenseUrl: https://github.com/Syngnat/GoNavi/blob/main/LICENSE",
|
|
"ShortDescription: Headless GoNavi database CLI",
|
|
"Description: Run GoNavi queries, exports, batches, audit exports, and MCP without the desktop GUI.",
|
|
"ReleaseNotesUrl: https://github.com/Syngnat/GoNavi/releases/tag/v" + version,
|
|
"Installers:",
|
|
]
|
|
for architecture, pattern in CLI_ASSETS.items():
|
|
asset = pattern.format(version=version)
|
|
lines.extend(
|
|
[
|
|
f"- Architecture: {architecture}",
|
|
" InstallerType: zip",
|
|
" NestedInstallerType: portable",
|
|
f" InstallerUrl: https://github.com/{repo}/releases/download/v{version}/{asset}",
|
|
f" InstallerSha256: {hashes[asset]}",
|
|
" NestedInstallerFiles:",
|
|
f" - RelativeFilePath: {BINARY_NAME}",
|
|
" PortableCommandAlias: gonavi",
|
|
]
|
|
)
|
|
lines.extend(
|
|
[
|
|
"ManifestType: singleton",
|
|
"ManifestVersion: 1.9.0",
|
|
"",
|
|
]
|
|
)
|
|
return "\n".join(lines)
|
|
|
|
|
|
def main() -> int:
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument("--version", required=True, help="stable semantic version, for example 0.9.3")
|
|
parser.add_argument("--checksums", required=True, type=Path, help="independent CLI checksum file")
|
|
parser.add_argument("--output", required=True, type=Path)
|
|
parser.add_argument("--repo", default="Syngnat/GoNavi")
|
|
args = parser.parse_args()
|
|
version = args.version.strip().removeprefix("v")
|
|
if not VERSION_RE.fullmatch(version):
|
|
parser.error(f"invalid stable version: {args.version}")
|
|
if not re.fullmatch(r"[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+", args.repo):
|
|
parser.error(f"invalid GitHub repository: {args.repo}")
|
|
try:
|
|
hashes = load_checksums(args.checksums, version)
|
|
payload = render_manifest(version, hashes, args.repo)
|
|
except (OSError, ValueError) as error:
|
|
parser.error(str(error))
|
|
args.output.parent.mkdir(parents=True, exist_ok=True)
|
|
args.output.write_text(payload, encoding="utf-8")
|
|
print(f"wrote {args.output} for GoNavi CLI {version}")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|