mirror of
https://github.com/flectar/mail.git
synced 2026-11-11 17:57:54 +00:00
175 lines
7.8 KiB
Python
175 lines
7.8 KiB
Python
#!/usr/bin/env python3
|
|
"""Exercise release validation and publishing without contacting GitHub."""
|
|
|
|
import importlib.util
|
|
import json
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
import unittest
|
|
from pathlib import Path
|
|
|
|
SCRIPTS = Path(__file__).resolve().parent
|
|
sys.dont_write_bytecode = True
|
|
|
|
|
|
def load_script(name):
|
|
spec = importlib.util.spec_from_file_location(name, SCRIPTS / f"{name}.py")
|
|
module = importlib.util.module_from_spec(spec)
|
|
spec.loader.exec_module(module)
|
|
return module
|
|
|
|
|
|
metadata = load_script("release-metadata").metadata
|
|
release_assets = load_script("prepare-release")
|
|
prepare = release_assets.prepare
|
|
|
|
|
|
class ReleaseTests(unittest.TestCase):
|
|
def test_versions_and_tags(self):
|
|
for version in ("0.1.0", "1.2.3-alpha.1", "1.2.3-beta.2", "1.2.3-rc.3"):
|
|
with self.subTest(version=version):
|
|
result = metadata(version, f"v{version}")
|
|
self.assertEqual(result["prerelease"], str("-" in version).lower())
|
|
self.assertEqual(result["native_version"], version.split("-")[0])
|
|
for version, tag in (
|
|
("0.1.0", "v0.2.0"), ("0.1.0-beta.1", "v0.1.0"),
|
|
("0.1.0-beta.01", None), ("0.1.0-beta.0", None),
|
|
("01.2.3", None), ("1.2.3-nightly", None), ("1.2.3+build", None),
|
|
):
|
|
with self.subTest(version=version, tag=tag), self.assertRaises(ValueError):
|
|
metadata(version, tag)
|
|
|
|
def test_staging_and_checksums(self):
|
|
with tempfile.TemporaryDirectory() as directory:
|
|
root = Path(directory)
|
|
source = root / "assets"
|
|
source.mkdir()
|
|
names = (
|
|
"linux/appimage/flectar-mail.AppImage",
|
|
"linux/deb/flectar-mail_0.1.0~beta.1_amd64.deb",
|
|
"windows/flectar-mail-windows-x64.zip",
|
|
"windows/flectar-mail-windows-x64-setup.exe",
|
|
"macos/flectar-mail-macos-arm64.zip",
|
|
"macos/flectar-mail-macos-arm64.dmg",
|
|
"android/flectar-mail.apk",
|
|
)
|
|
for name in names:
|
|
path = source / name
|
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
path.write_bytes(name.encode())
|
|
(source / "benchmark.json").write_text("{}")
|
|
dist = root / "dist"
|
|
prepare(source, dist, "0.1.0-beta.1")
|
|
self.assertEqual(len(list(dist.iterdir())), 8)
|
|
self.assertTrue((dist / "flectar-mail-0.1.0-beta.1-android-arm64-test.apk").is_file())
|
|
result = subprocess.run(
|
|
["sha256sum", "--check", "SHA256SUMS"], cwd=dist,
|
|
capture_output=True, text=True,
|
|
)
|
|
self.assertEqual(result.returncode, 0, result.stderr)
|
|
missing = source / names[-1]
|
|
missing.unlink()
|
|
with self.assertRaises(ValueError):
|
|
prepare(source, root / "missing", "0.1.0-beta.1")
|
|
self.assertFalse((root / "missing").exists())
|
|
missing.write_bytes(b"")
|
|
with self.assertRaises(ValueError):
|
|
prepare(source, root / "empty", "0.1.0-beta.1")
|
|
missing.write_bytes(b"dmg")
|
|
(source / missing.name).write_bytes(b"duplicate")
|
|
with self.assertRaises(ValueError):
|
|
prepare(source, root / "duplicate", "0.1.0-beta.1")
|
|
|
|
# Stable releases keep the six desktop downloads, even if the
|
|
# input folder happens to contain test APKs from another step.
|
|
(source / "linux/deb/flectar-mail_0.1.0~beta.1_amd64.deb").rename(
|
|
source / "linux/deb/flectar-mail_0.1.0_amd64.deb"
|
|
)
|
|
stable_dist = root / "stable"
|
|
prepare(source, stable_dist, "0.1.0")
|
|
self.assertEqual(len(list(stable_dist.iterdir())), 7)
|
|
self.assertFalse(list(stable_dist.glob("*.apk")))
|
|
|
|
def test_notes_distinguish_android_preview(self):
|
|
self.assertIn("test-signed", release_assets.release_notes("0.1.0-beta.1"))
|
|
self.assertIn("prereleases only", release_assets.release_notes("0.1.0"))
|
|
|
|
def test_metadata_rejects_stale_android_lockfile(self):
|
|
with tempfile.TemporaryDirectory() as directory:
|
|
root = Path(directory)
|
|
(root / "platform/android").mkdir(parents=True)
|
|
(root / "Cargo.toml").write_text('[package]\nversion = "0.2.0-beta.1"\n')
|
|
lock = '[[package]]\nname = "flectar-mail"\nversion = "0.2.0-beta.1"\n'
|
|
(root / "Cargo.lock").write_text(lock)
|
|
android_lock = root / "platform/android/Cargo.lock"
|
|
android_lock.write_text(lock.replace("0.2.0-beta.1", "0.1.0"))
|
|
command = [sys.executable, str(SCRIPTS / "release-metadata.py"), "--tag", "v0.2.0-beta.1"]
|
|
result = subprocess.run(command, cwd=root, capture_output=True, text=True)
|
|
self.assertNotEqual(result.returncode, 0)
|
|
self.assertIn("platform/android/Cargo.lock", result.stderr)
|
|
android_lock.write_text(lock)
|
|
result = subprocess.run(command, cwd=root, capture_output=True, text=True)
|
|
self.assertEqual(result.returncode, 0, result.stderr)
|
|
self.assertIn("prerelease=true", result.stdout)
|
|
|
|
def publish(self, *, existing="missing", prerelease="true", fail_upload=False):
|
|
with tempfile.TemporaryDirectory() as directory:
|
|
root = Path(directory)
|
|
(root / "dist").mkdir()
|
|
(root / "dist" / "package.zip").write_bytes(b"package")
|
|
(root / "release-notes.md").write_text("Notes")
|
|
gh = root / "gh"
|
|
gh.write_text('''#!/usr/bin/env python3
|
|
import json, os, sys
|
|
with open("calls.jsonl", "a") as stream:
|
|
stream.write(json.dumps(sys.argv[1:]) + "\\n")
|
|
command = sys.argv[2]
|
|
if command == "view":
|
|
existing = os.environ["EXISTING"]
|
|
if existing == "missing":
|
|
sys.exit(1)
|
|
print(existing)
|
|
if command == "upload" and os.environ["FAIL_UPLOAD"] == "true":
|
|
sys.exit(1)
|
|
''')
|
|
gh.chmod(0o755)
|
|
result = subprocess.run(
|
|
["bash", str(SCRIPTS / "publish-release.sh")], cwd=root,
|
|
env={**os.environ, "PATH": f"{root}:{os.environ['PATH']}",
|
|
"GITHUB_REF_NAME": "v0.1.0-beta.1" if prerelease == "true" else "v0.1.0",
|
|
"RELEASE_PRERELEASE": prerelease, "EXISTING": existing,
|
|
"FAIL_UPLOAD": str(fail_upload).lower()},
|
|
capture_output=True, text=True,
|
|
)
|
|
calls = [json.loads(line) for line in (root / "calls.jsonl").read_text().splitlines()]
|
|
return result, calls
|
|
|
|
def test_preview_and_stable_publication(self):
|
|
for prerelease in ("true", "false"):
|
|
with self.subTest(prerelease=prerelease):
|
|
result, calls = self.publish(prerelease=prerelease)
|
|
self.assertEqual(result.returncode, 0, result.stderr)
|
|
self.assertEqual([c[1] for c in calls], ["view", "create", "upload", "edit"])
|
|
self.assertIn("--draft", calls[1])
|
|
self.assertIn(f"--prerelease={prerelease}", calls[-1])
|
|
self.assertIn("--latest=" + str(prerelease == "false").lower(), calls[-1])
|
|
|
|
def test_failed_upload_does_not_publish(self):
|
|
result, calls = self.publish(fail_upload=True)
|
|
self.assertNotEqual(result.returncode, 0)
|
|
self.assertNotIn("edit", [c[1] for c in calls])
|
|
|
|
def test_resume_draft_and_reject_published_replacement(self):
|
|
result, calls = self.publish(existing="true")
|
|
self.assertEqual(result.returncode, 0, result.stderr)
|
|
self.assertEqual([c[1] for c in calls], ["view", "upload", "edit"])
|
|
result, calls = self.publish(existing="false")
|
|
self.assertNotEqual(result.returncode, 0)
|
|
self.assertEqual([c[1] for c in calls], ["view"])
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|