From 3ca5cc4bda6ce882d865bf14cb7396655f6f9d45 Mon Sep 17 00:00:00 2001 From: Karolis Strazdas Date: Tue, 6 Oct 2026 00:07:42 +0300 Subject: [PATCH] chore: start testing releases at 0.0.1 --- .github/workflows/release.yml | 5 +++-- app/pubspec.yaml | 2 +- docs/RELEASING.md | 4 ++-- tools/release_gate.py | 9 +++++++-- tools/tests/test_release.py | 20 ++++++++++++++++++++ 5 files changed, 33 insertions(+), 7 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 16233584..fe9749a4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -60,9 +60,9 @@ jobs: run: working-directory: app steps: + - uses: actions/checkout@v4 - name: Set temporary keystore path run: printf 'ANDROID_KEYSTORE_PATH=%s/upload.jks\n' "$RUNNER_TEMP" >> "$GITHUB_ENV" - - uses: actions/checkout@v4 - uses: actions/setup-java@v4 with: distribution: temurin @@ -101,7 +101,8 @@ jobs: if-no-files-found: error - name: Remove upload key if: always() - run: rm -f "$ANDROID_KEYSTORE_PATH" + working-directory: ${{ runner.temp }} + run: rm -f "$RUNNER_TEMP/upload.jks" build-web: name: Build Web diff --git a/app/pubspec.yaml b/app/pubspec.yaml index d8ece82b..5d529aaa 100644 --- a/app/pubspec.yaml +++ b/app/pubspec.yaml @@ -1,7 +1,7 @@ name: papyrus description: "Digital library management application." publish_to: 'none' -version: 1.0.0+1 +version: 0.0.1+1 environment: sdk: ^3.9.2 diff --git a/docs/RELEASING.md b/docs/RELEASING.md index 8fa5a49f..87fdd8c1 100644 --- a/docs/RELEASING.md +++ b/docs/RELEASING.md @@ -17,7 +17,7 @@ release builds. Normal PR CI continues independently. Manual dispatch on `master is available for the first build or a retry of an unreleased commit. Tags are created after builds, not used as a second build trigger. A released tag cannot be reused for a different commit. Client tags include the build number, e.g. -`v1.0.0+1`. Version changes build Android, web, Linux and Windows as before; the +`v0.0.1+1`. Version changes build Android, web, Linux and Windows as before; the Android artifact is now a signed AAB, which Google Play turns into device APKs. The Android artifact is available even if an unrelated desktop job fails. @@ -84,7 +84,7 @@ an actionable message; they cannot silently use the debug certificate. upload key. Keep the same upload key for later releases. 3. Configure the public API, PowerSync, SMTP and Google OAuth settings using the server deployment runbook. Check the endpoints from outside your local network. -4. Dispatch Release on `master` for the initial `1.0.0+1`, download the +4. Merge the initial `0.0.1+1` version change (or dispatch Release on `master`), download the `android-release` artifact, and upload `app-release.aab` to internal testing. The local validation bundle uses a temporary certificate and placeholder URLs and must **never** be submitted to Play. diff --git a/tools/release_gate.py b/tools/release_gate.py index 037abf3f..53771eae 100644 --- a/tools/release_gate.py +++ b/tools/release_gate.py @@ -26,9 +26,11 @@ def parse(text: str, component: str) -> tuple[str, int]: return version, 0 -def decide(current: tuple[str, int], previous: tuple[str, int], component: str) -> bool: +def decide(current: tuple[str, int], previous: tuple[str, int], component: str, *, published: bool = True) -> bool: if current == previous: return False + if not published: + return True if tuple(map(int, current[0].split("."))) < tuple(map(int, previous[0].split("."))): raise ValueError("Release version cannot decrease") if component == "client" and current[1] <= previous[1]: @@ -78,7 +80,10 @@ def main() -> None: if args.manual: release = True elif args.base and set(args.base) != {"0"}: - release = decide(current, parse(git("show", f"{args.base}:{path}"), args.component), args.component) + published = bool(git("tag", "--list", "v[0-9]*")) + release = decide( + current, parse(git("show", f"{args.base}:{path}"), args.component), args.component, published=published + ) else: raise ValueError("A previous commit is required; use a manual build to bootstrap") version, number = current diff --git a/tools/tests/test_release.py b/tools/tests/test_release.py index ee9757e6..af7d4039 100644 --- a/tools/tests/test_release.py +++ b/tools/tests/test_release.py @@ -35,6 +35,14 @@ def test_server_dependency_edit_does_not_release(self) -> None: with self.assertRaises(ValueError): gate.decide(("0.9.0", 0), first, "server") + def test_initial_version_can_be_reset_before_any_release(self) -> None: + for component, number in (("client", 1), ("server", 0)): + with self.subTest(component=component): + self.assertTrue(gate.decide(("0.0.1", number), ("1.0.0", number), component, published=False)) + self.assertFalse(gate.decide(("0.0.1", number), ("0.0.1", number), component, published=False)) + with self.assertRaises(ValueError): + gate.decide(("0.0.1", number), ("1.0.0", number), component, published=True) + def test_invalid_manifest_versions_are_rejected(self) -> None: for version in ("1.0.0", "1.0.0+0", "1.0.0+2100000001", "latest"): with self.assertRaises(ValueError): @@ -114,6 +122,18 @@ def test_compares_committed_values_and_skips_dependency_edits(self) -> None: self.assertIn("tag=v1.0.0+2", result.stdout) self.assertIn("release=true", result.stdout) + def test_committed_initial_reset_requires_no_release_tags(self) -> None: + self.manifest.write_text("version: 0.0.1+1\n") + self.commit() + result = self.run_gate("--base", self.base) + self.assertEqual(result.returncode, 0, result.stderr) + self.assertIn("tag=v0.0.1+1", result.stdout) + self.assertIn("release=true", result.stdout) + self.command("tag", "v1.0.0+1", self.base) + result = self.run_gate("--base", self.base) + self.assertNotEqual(result.returncode, 0) + self.assertIn("Release version cannot decrease", result.stderr) + def test_released_tag_cannot_be_reused_for_another_commit(self) -> None: self.command("tag", "v1.0.0+1") self.manifest.write_text("version: 1.0.0+1\ndependencies: changed\n")