From 5f173b47b1ba00f02a882e92ad0926f1e0ae4a62 Mon Sep 17 00:00:00 2001 From: Agents Agent Date: Mon, 29 Jun 2026 11:22:45 +0000 Subject: [PATCH 1/2] fix: make reusable publishes idempotent --- .github/workflows/deploy-bundle.yml | 2 +- actions/api-client-publish/run.sh | 46 +++++++++++++++++++++++++++-- tests/test_crac_train_workflow.py | 12 +++++++- 3 files changed, 56 insertions(+), 4 deletions(-) diff --git a/.github/workflows/deploy-bundle.yml b/.github/workflows/deploy-bundle.yml index 32f327d..decd3c3 100644 --- a/.github/workflows/deploy-bundle.yml +++ b/.github/workflows/deploy-bundle.yml @@ -118,4 +118,4 @@ name: 'Deploy Bundle' 'run': | set -euo pipefail - oras push "$BUNDLE_REF" "$BUNDLE_PATH:application/vnd.jorisjonkers.deployment.bundle.v1+tar" + oras push --disable-path-validation "$BUNDLE_REF" "$BUNDLE_PATH:application/vnd.jorisjonkers.deployment.bundle.v1+tar" diff --git a/actions/api-client-publish/run.sh b/actions/api-client-publish/run.sh index 48dd458..dd5ce3e 100755 --- a/actions/api-client-publish/run.sh +++ b/actions/api-client-publish/run.sh @@ -39,6 +39,43 @@ validate_identifier() { fi } +maven_publish_failed_because_version_exists() { + local log_path="$1" + + grep -Eiq '(Received status code 409|HTTP 409|409 Conflict)' "$log_path" +} + +run_maven_publish_task() { + local project_dir="$1" + local task_name="$2" + local log_path + + log_path="$(mktemp "${RUNNER_TEMP:-/tmp}/maven-publish.XXXXXX.log")" + set +e + gradle --no-daemon -p "$project_dir" "$task_name" > >(tee "$log_path") 2> >(tee -a "$log_path" >&2) + local status=$? + set -e + + if [[ "$status" -eq 0 ]]; then + return 0 + fi + + if maven_publish_failed_because_version_exists "$log_path"; then + echo "::notice::Maven package for $task_name already exists; treating HTTP 409 as an idempotent publish success." + return 0 + fi + + return "$status" +} + +npm_package_version_exists() { + local package_name="$1" + local version="$2" + local registry="$3" + + npm view "${package_name}@${version}" version --registry "$registry" >/dev/null 2>&1 +} + derive_jvm_package_base() { local maven_group="$1" local api_name="$2" @@ -478,10 +515,15 @@ main() { if [[ -z "${NODE_AUTH_TOKEN:-}" ]]; then die "NODE_AUTH_TOKEN is required for npm publishing" fi - gradle --no-daemon -p "$jvm_dir" :java:publish :kotlin:publish + run_maven_publish_task "$jvm_dir" :java:publish + run_maven_publish_task "$jvm_dir" :kotlin:publish ( cd "$ts_dir" - npm publish --access public --provenance + if npm_package_version_exists "$ts_package" "$version" "$npm_registry_url"; then + echo "::notice::npm package ${ts_package}@${version} already exists; skipping publish." + else + npm publish --access public --provenance + fi ) ;; esac diff --git a/tests/test_crac_train_workflow.py b/tests/test_crac_train_workflow.py index 8dc4c11..aa5de17 100644 --- a/tests/test_crac_train_workflow.py +++ b/tests/test_crac_train_workflow.py @@ -25,6 +25,7 @@ DEPLOY_SOURCES_RENDER_WORKFLOW = ROOT / ".github/workflows/deploy-sources-render.yml" ADD_TO_PROJECT_WORKFLOW = ROOT / ".github/workflows/add-to-project.yml" HYGIENE_GUARD_WORKFLOW = ROOT / ".github/workflows/repository-hygiene-guard.yml" +API_CLIENT_PUBLISH_RUNNER = ROOT / "actions/api-client-publish/run.sh" COMPOSE_ACTION = ROOT / "actions/compose-system-test-stack/action.yml" COMPOSE_RUNNER = ROOT / "actions/compose-system-test-stack/run.sh" COMPOSE_ROUTES_FIXTURE = ROOT / "actions/compose-system-test-stack/fixtures/routes.example.txt" @@ -529,6 +530,7 @@ def setUpClass(cls) -> None: cls.deploy_sources_render = DEPLOY_SOURCES_RENDER_WORKFLOW.read_text(encoding="utf-8") cls.add_to_project = ADD_TO_PROJECT_WORKFLOW.read_text(encoding="utf-8") cls.hygiene_guard = HYGIENE_GUARD_WORKFLOW.read_text(encoding="utf-8") + cls.api_client_publish_runner = API_CLIENT_PUBLISH_RUNNER.read_text(encoding="utf-8") cls.setup_node = SETUP_NODE_ACTION.read_text(encoding="utf-8") cls.readme = README.read_text(encoding="utf-8") @@ -583,7 +585,7 @@ def test_container_publish_tags_version_and_sha_only(self) -> None: def test_deploy_v2_workflows_expose_bundle_render_and_project_surfaces(self) -> None: self.assertIn("'uses': './.github-workflows/actions/deploy-bundle'", self.deploy_bundle) - self.assertIn("oras push", self.deploy_bundle) + self.assertIn("oras push --disable-path-validation", self.deploy_bundle) self.assertIn("application/vnd.jorisjonkers.deployment.bundle.v1+tar", self.deploy_bundle) self.assertIn("'uses': './.github-workflows/actions/deploy-sources-render'", self.deploy_sources_render) self.assertIn("'image-tags':", self.deploy_sources_render) @@ -591,6 +593,14 @@ def test_deploy_v2_workflows_expose_bundle_render_and_project_surfaces(self) -> self.assertIn("actions/add-to-project@v2.0.0", self.add_to_project) self.assertIn(".specify/**", self.hygiene_guard) + def test_api_client_maven_publish_is_idempotent_on_existing_versions(self) -> None: + self.assertIn("maven_publish_failed_because_version_exists", self.api_client_publish_runner) + self.assertIn("Received status code 409", self.api_client_publish_runner) + self.assertIn("run_maven_publish_task \"$jvm_dir\" :java:publish", self.api_client_publish_runner) + self.assertIn("run_maven_publish_task \"$jvm_dir\" :kotlin:publish", self.api_client_publish_runner) + self.assertIn("npm_package_version_exists", self.api_client_publish_runner) + self.assertIn("npm package ${ts_package}@${version} already exists", self.api_client_publish_runner) + def test_gitops_ci_runs_required_and_optional_validation_steps(self) -> None: self.assertIn("uses: ./.github-workflows/actions/platform-config-validate", self.gitops_ci) self.assertIn("uses: ./.github-workflows/actions/flux-render-validate", self.gitops_ci) From 5e2341944c23690c6d7a00e5bb8d527c52cbc5a4 Mon Sep 17 00:00:00 2001 From: Agents Agent Date: Mon, 29 Jun 2026 11:23:04 +0000 Subject: [PATCH 2/2] chore(main): release 0.8.1 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 64f3cdd..02f17d9 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.8.0" + ".": "0.8.1" } diff --git a/CHANGELOG.md b/CHANGELOG.md index 21b688d..95e88ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [0.8.1](https://github.com/JorisJonkers-dev/github-workflows/compare/v0.8.0...v0.8.1) (2026-06-29) + + +### Bug Fixes + +* make reusable publishes idempotent ([5f173b4](https://github.com/JorisJonkers-dev/github-workflows/commit/5f173b4189f38d9e683a4c51af2f0fc3a1e45f9d)) + ## [0.8.0](https://github.com/JorisJonkers-dev/github-workflows/compare/v0.7.3...v0.8.0) (2026-06-29)