From d9522d3cc4e954e7a129921b8421b0752f1810aa Mon Sep 17 00:00:00 2001 From: Albrecht Degering Date: Tue, 4 Aug 2026 14:54:54 +0200 Subject: [PATCH] Publish release tags in dependency order --- docs/PACKAGE_REGISTRY_RELEASES.md | 5 +++++ tests/test_release_entrypoint_gates.py | 9 +++++++++ tools/release/push-release-tag.sh | 5 ++++- 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/docs/PACKAGE_REGISTRY_RELEASES.md b/docs/PACKAGE_REGISTRY_RELEASES.md index 854a206..ef05b94 100644 --- a/docs/PACKAGE_REGISTRY_RELEASES.md +++ b/docs/PACKAGE_REGISTRY_RELEASES.md @@ -54,6 +54,11 @@ skips complete registry pairs and does not duplicate an active workflow. Use `--repository govoplan-core` for a bounded dispatch or `--verify-existing` to rebuild and hash-verify versions already present in both registries. +For coordinated lockstep tags, `push-release-tag.sh` pushes module tags first, +Core next, and the meta tag last. This is a dependency guarantee for a +single-capacity Actions runner: the developer package cannot run before its +exact Core and module versions have entered the queue. + It builds one wheel and, where applicable, one npm tarball. The workflow records the source tag, source commit, filename, size, and SHA-256 in `package-artifacts.json` before publishing. Gitea rejects a second upload of the diff --git a/tests/test_release_entrypoint_gates.py b/tests/test_release_entrypoint_gates.py index e727351..b0976f0 100644 --- a/tests/test_release_entrypoint_gates.py +++ b/tests/test_release_entrypoint_gates.py @@ -40,6 +40,15 @@ class ReleaseEntrypointGateTests(unittest.TestCase): self.assertLess(full_gate, first_push) self.assertLess(manifest_gate, confirm) + def test_lockstep_release_pushes_meta_package_after_core(self) -> None: + script = (META_ROOT / "tools" / "release" / "push-release-tag.sh").read_text() + module_push = script.index('for repo in "${MODULE_REPOS[@]}"; do\n run git -C "$repo" push') + core_push = script.index('run git -C "$ROOT" push', module_push) + support_push = script.index('for repo in "${SUPPORT_REPOS[@]}"; do\n run git -C "$repo" push', core_push) + + self.assertLess(module_push, core_push) + self.assertLess(core_push, support_push) + def test_source_catalog_generator_enforces_explicit_repo_versions(self) -> None: script = (META_ROOT / "tools" / "release" / "generate-release-catalog.py").read_text() diff --git a/tools/release/push-release-tag.sh b/tools/release/push-release-tag.sh index 1d9ed63..50c7b71 100644 --- a/tools/release/push-release-tag.sh +++ b/tools/release/push-release-tag.sh @@ -938,10 +938,13 @@ fi run git -C "$ROOT" commit -m "$COMMIT_MESSAGE" run git -C "$ROOT" tag -a "$TAG" -m "$TAG_MESSAGE" -for repo in "${PRE_CORE_REPOS[@]}"; do +for repo in "${MODULE_REPOS[@]}"; do run git -C "$repo" push --atomic "$REMOTE" "HEAD:refs/heads/${BRANCHES[$repo]}" "refs/tags/$TAG" done run git -C "$ROOT" push --atomic "$REMOTE" "HEAD:refs/heads/${BRANCHES[$ROOT]}" "refs/tags/$TAG" +for repo in "${SUPPORT_REPOS[@]}"; do + run git -C "$repo" push --atomic "$REMOTE" "HEAD:refs/heads/${BRANCHES[$repo]}" "refs/tags/$TAG" +done if [[ "$PUBLISH_WEB_CATALOG" -eq 1 ]]; then CATALOG_ARGS=(