Some checks failed
Research feed publisher tests / test (push) Failing after 3s
Release / Pre-flight checks (push) Failing after 0s
Release / Research Edition — patch and packaging smoke tests (push) Has been skipped
Release / Build Qt artifacts (false, 22, ubuntu-22.04, 3.9, ${{ github.event_name == 'pull_request' || (github.event_name == 'push' && !startsWith(github.ref, 'refs/tags/')) }}, false, false) (push) Has been skipped
Release / Build Qt artifacts (false, 22, ubuntu-22.04, 3.9, false, false, false) (push) Has been skipped
Release / Build Qt artifacts (manylinux_2_28 — glibc 2.28 ABI floor) (22, 3.9) (push) Has been skipped
Release / Generate release notes (push) Has been skipped
Release / Create dev release tag (push) Has been skipped
CodeQL / Analyze (javascript) (push) Failing after 1s
CodeQL / Analyze (python) (push) Failing after 1s
Release / Build Qt artifacts (false, 22, windows-latest, 3.9, false, false, false) (push) Has been cancelled
Release / Build Tauri artifacts (false, 22, macos-15-intel, 3.9, false, false, false) (push) Has been cancelled
Release / Build Tauri artifacts (false, 22, macos-latest, 3.9, false, false, false) (push) Has been cancelled
Release / Build Tauri artifacts (false, 22, ubuntu-24.04, 3.9, ${{ github.event_name == 'pull_request' || (github.event_name == 'push' && !startsWith(github.ref, 'refs/tags/')) }}, false, false) (push) Has been cancelled
Release / Build Tauri artifacts (false, 22, ubuntu-24.04, 3.9, false, false, false) (push) Has been cancelled
Release / Build Tauri artifacts (false, 22, ubuntu-24.04-arm, 3.9, false, false, false) (push) Has been cancelled
Release / Build Tauri artifacts (false, 22, windows-latest, 3.9, false, false, false) (push) Has been cancelled
Release / Build Qt artifacts (false, 22, macos-15-intel, 3.9, false, false, false) (push) Has been cancelled
Release / Build Qt artifacts (false, 22, macos-latest, 3.9, false, false, false) (push) Has been cancelled
Release / Publish draft release (push) Has been cancelled
109 lines
4.1 KiB
Python
109 lines
4.1 KiB
Python
#!/usr/bin/env python3
|
|
"""Set the compiled updater version and isolate Research Edition update trust."""
|
|
|
|
import argparse
|
|
import base64
|
|
import binascii
|
|
import json
|
|
import os
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
from generate_latest_json import tauri_version
|
|
|
|
RESEARCH_ENDPOINT = (
|
|
"https://raw.githubusercontent.com/ActivityWatch/activitywatch/"
|
|
"research-updates/latest-research.json"
|
|
)
|
|
STANDARD_ENDPOINT = "https://github.com/ActivityWatch/activitywatch/releases/latest/download/latest.json"
|
|
|
|
|
|
def msi_rejects(version: str) -> bool:
|
|
"""True if Tauri's msi (WiX) bundler will reject this SemVer version.
|
|
|
|
The msi target requires the optional pre-release identifier to be
|
|
numeric-only (e.g. "0.14.0-5"), unlike nsis or the other bundle formats.
|
|
AW's own pre-release scheme ("0.14.0-beta.5", "0.14.0-dev.gabc1234")
|
|
fails that check every time.
|
|
"""
|
|
if "-" not in version:
|
|
return False
|
|
prerelease = version.split("-", 1)[1]
|
|
return not all(part.isdigit() for part in prerelease.split("."))
|
|
|
|
|
|
def public_key(encoded: str) -> bytes:
|
|
"""Read Tauri's base64-wrapped minisign public key, ignoring its comment."""
|
|
try:
|
|
lines = base64.b64decode(encoded, validate=True).decode("ascii").splitlines()
|
|
if len(lines) != 2 or not lines[0].startswith("untrusted comment: "):
|
|
raise ValueError("invalid public key envelope")
|
|
packet = base64.b64decode(lines[1], validate=True)
|
|
if len(packet) != 42 or packet[:2] != b"Ed":
|
|
raise ValueError("invalid public key packet")
|
|
return packet[10:]
|
|
except (ValueError, UnicodeError, binascii.Error) as exc:
|
|
raise ValueError("Expected a base64-encoded minisign public key") from exc
|
|
|
|
|
|
def configure(
|
|
path: Path,
|
|
version: str,
|
|
research: bool,
|
|
require_signing_key: bool,
|
|
*,
|
|
platform: str = sys.platform,
|
|
) -> None:
|
|
config = json.loads(path.read_text(encoding="utf-8"))
|
|
tv = tauri_version(version)
|
|
config["version"] = tv
|
|
if platform == "win32" and msi_rejects(tv):
|
|
bundle = config.setdefault("bundle", {})
|
|
if bundle.get("targets") == "all":
|
|
# Windows only ever produces msi+nsis from "all"; drop msi and
|
|
# keep nsis, which accepts the full AW pre-release scheme.
|
|
bundle["targets"] = ["nsis"]
|
|
if research:
|
|
updater = config["plugins"]["updater"]
|
|
if updater["endpoints"] != [STANDARD_ENDPOINT]:
|
|
raise ValueError(
|
|
"Unexpected source updater endpoint; review before patching"
|
|
)
|
|
research_key = os.environ.get("TAURI_UPDATER_PUBLIC_KEY_RESEARCH", "").strip()
|
|
signing_key = os.environ.get("TAURI_SIGNING_PRIVATE_KEY", "").strip()
|
|
if require_signing_key and (not research_key or not signing_key):
|
|
raise ValueError(
|
|
"Research releases require their own public and signing keys"
|
|
)
|
|
if research_key:
|
|
if public_key(research_key) == public_key(updater["pubkey"]):
|
|
raise ValueError(
|
|
"Research updater key must differ from the standard key"
|
|
)
|
|
updater["pubkey"] = research_key
|
|
updater["endpoints"] = [RESEARCH_ENDPOINT]
|
|
else:
|
|
if signing_key:
|
|
raise ValueError("Research signing key supplied without its public key")
|
|
# Secretless PR smoke builds must never retain standard update trust.
|
|
updater["pubkey"] = ""
|
|
updater["endpoints"] = []
|
|
path.write_text(json.dumps(config, indent=2) + "\n", encoding="utf-8")
|
|
|
|
|
|
def main() -> None:
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument("--config", type=Path, required=True)
|
|
parser.add_argument("--version", required=True)
|
|
parser.add_argument("--research", action="store_true")
|
|
parser.add_argument("--require-signing-key", action="store_true")
|
|
args = parser.parse_args()
|
|
try:
|
|
configure(args.config, args.version, args.research, args.require_signing_key)
|
|
except (ValueError, KeyError) as exc:
|
|
parser.exit(1, f"ERROR: {exc}\n")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|