mirror of
https://github.com/saphid/frame-control.git
synced 2026-10-18 03:01:34 +02:00
- publish-release.sh: the fallback takes only an untagged-... draft titled
exactly "Frame Control X.Y.Z" (optionally ": subtitle"); a pre-release's
draft ("9.8.7-rc.1") or one bound to another tag is refused.
- updater.js: ignore the page in update.json/the API entirely and always
link to releases/tag/v<version> built from the validated version, so a
path like tag/..\..\other/repo can't reach shell.openExternal.
- Tests for both, including backslash and %2e%2e traversal.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
111 lines
5.9 KiB
Python
111 lines
5.9 KiB
Python
"""scripts/publish-release.sh against a stand-in gh (tests/fakegh/gh): finds the draft
|
|
through the REST API even when its tag_name still says untagged-..., refuses
|
|
missing installers or digests, writes update.json's page from the tag, and
|
|
publishes with one PATCH. Nothing here talks to GitHub.
|
|
|
|
Run: python3 -m unittest discover -s tests
|
|
"""
|
|
import sandbox # noqa: F401 (first: keeps tests off real data and services)
|
|
import json
|
|
import os
|
|
import subprocess
|
|
import tempfile
|
|
import unittest
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).resolve().parent.parent
|
|
SCRIPT = ROOT / "scripts" / "publish-release.sh"
|
|
FAKEGH = ROOT / "tests" / "fakegh"
|
|
INSTALLERS = ["Frame-Control-mac-arm64.dmg", "Frame-Control-mac-arm64.zip", "Frame-Control-Setup-x64.exe",
|
|
"Frame-Control-win-x64.zip", "Frame-Control-linux-x86_64.AppImage",
|
|
"Frame-Control-linux-arm64.AppImage", "Frame-Control-linux-amd64.deb",
|
|
"Frame-Control-linux-arm64.deb"]
|
|
|
|
|
|
def draft(tag_name="untagged-c6ddfed7f75d67db2e99", name="Frame Control 9.8.7", rid=42, assets=None):
|
|
if assets is None:
|
|
assets = [{"id": 100 + i, "name": n, "size": 1000 + i, "digest": "sha256:" + "%064x" % i}
|
|
for i, n in enumerate(INSTALLERS)]
|
|
return {"id": rid, "tag_name": tag_name, "name": name, "draft": True, "prerelease": False,
|
|
"body": "notes", "html_url": "https://github.com/saphid/frame-control/releases/tag/" + tag_name,
|
|
"assets": assets}
|
|
|
|
|
|
class PublishRelease(unittest.TestCase):
|
|
def run_script(self, releases, *args, tags="v9.8.7"):
|
|
d = Path(tempfile.mkdtemp())
|
|
(d / "releases.json").write_text(json.dumps(releases))
|
|
env = dict(os.environ, PATH="%s:%s" % (FAKEGH, os.environ["PATH"]),
|
|
FAKEGH_RELEASES=str(d / "releases.json"), FAKEGH_TAGS=tags,
|
|
FAKEGH_LOG=str(d / "log"), FAKEGH_UPLOAD=str(d / "upload.json"))
|
|
p = subprocess.run(["sh", str(SCRIPT), *args], env=env, capture_output=True, text=True, timeout=30)
|
|
log = [json.loads(line) for line in (d / "log").read_text().splitlines()] if (d / "log").exists() else []
|
|
upload = json.loads((d / "upload.json").read_text()) if (d / "upload.json").exists() else None
|
|
return p, log, upload
|
|
|
|
def test_publishes_an_untagged_draft_by_title_with_the_tag_page(self):
|
|
old = {"id": 7, "name": "update.json", "size": 1, "digest": None}
|
|
rel = draft(assets=draft()["assets"] + [old])
|
|
p, log, upload = self.run_script([rel], "v9.8.7")
|
|
self.assertEqual(p.returncode, 0, p.stderr + p.stdout)
|
|
self.assertEqual(upload["page"], "https://github.com/saphid/frame-control/releases/tag/v9.8.7")
|
|
self.assertEqual(upload["version"], "9.8.7")
|
|
self.assertEqual(sorted(a["name"] for a in upload["assets"]), sorted(INSTALLERS))
|
|
self.assertIn(["api", "-X", "DELETE", "repos/saphid/frame-control/releases/assets/7"], log)
|
|
patch = next(c for c in log if "PATCH" in c)
|
|
self.assertEqual(patch[3], "repos/saphid/frame-control/releases/42")
|
|
for f in ("tag_name=v9.8.7", "draft=false", "prerelease=false", "make_latest=true"):
|
|
self.assertIn(f, patch)
|
|
self.assertTrue(all(c[0] == "api" for c in log)) # never `gh release ...` (GraphQL)
|
|
|
|
def test_an_untagged_draft_may_carry_a_subtitle(self):
|
|
p, log, upload = self.run_script([draft(name="Frame Control 9.8.7: faster")], "v9.8.7")
|
|
self.assertEqual(p.returncode, 0, p.stderr + p.stdout)
|
|
self.assertEqual(upload["version"], "9.8.7")
|
|
|
|
def test_prefers_the_release_whose_tag_name_matches(self):
|
|
p, log, upload = self.run_script([draft(name="Frame Control 9.8.7 old", rid=1),
|
|
draft(tag_name="v9.8.7", name="Renamed", rid=2)], "v9.8.7")
|
|
self.assertEqual(p.returncode, 0, p.stderr + p.stdout)
|
|
self.assertEqual(next(c for c in log if "PATCH" in c)[3], "repos/saphid/frame-control/releases/2")
|
|
|
|
def test_refuses_missing_or_unhashed_installers(self):
|
|
assets = draft()["assets"][1:]
|
|
assets[0] = dict(assets[0], digest=None)
|
|
p, log, upload = self.run_script([draft(assets=assets)], "v9.8.7")
|
|
self.assertNotEqual(p.returncode, 0)
|
|
self.assertIn("MISSING Frame-Control-mac-arm64.dmg", p.stdout)
|
|
self.assertIn("NO HASH Frame-Control-mac-arm64.zip", p.stdout)
|
|
self.assertIsNone(upload)
|
|
self.assertFalse(any(c[1:3] == ["-X", "PATCH"] for c in log))
|
|
|
|
def test_refuses_ambiguous_or_absent_drafts_and_missing_tags(self):
|
|
p, _, _ = self.run_script([draft(rid=1), draft(rid=2)], "v9.8.7")
|
|
self.assertNotEqual(p.returncode, 0)
|
|
self.assertIn("2 releases", p.stderr)
|
|
p, _, _ = self.run_script([draft(name="Frame Control 9.8.70")], "v9.8.7")
|
|
self.assertNotEqual(p.returncode, 0)
|
|
self.assertIn("no release", p.stderr)
|
|
# A pre-release's draft, or one bound to an unrelated tag, is never taken for v9.8.7.
|
|
for rel in (draft(name="Frame Control 9.8.7-rc.1"), draft(name="Frame Control 9.8.7.1"),
|
|
draft(tag_name="kdeconnect-frame-1"), draft(tag_name="")):
|
|
p, log, upload = self.run_script([rel], "v9.8.7")
|
|
self.assertNotEqual(p.returncode, 0, rel)
|
|
self.assertIn("no release", p.stderr)
|
|
self.assertIsNone(upload)
|
|
self.assertFalse(any("PATCH" in c for c in log))
|
|
p, log, _ = self.run_script([draft()], "v9.8.7", tags="")
|
|
self.assertNotEqual(p.returncode, 0)
|
|
self.assertIn("push it first", p.stderr)
|
|
|
|
def test_dry_run_changes_nothing(self):
|
|
p, log, upload = self.run_script([draft()], "--dry-run", "v9.8.7")
|
|
self.assertEqual(p.returncode, 0, p.stderr + p.stdout)
|
|
self.assertIn('"page": "https://github.com/saphid/frame-control/releases/tag/v9.8.7"', p.stdout)
|
|
self.assertIsNone(upload)
|
|
self.assertTrue(all("-X" not in c for c in log))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|