diff --git a/.github/workflows/dispatcher-publish.yml b/.github/workflows/dispatcher-publish.yml index f4f181f7d..8419e755c 100644 --- a/.github/workflows/dispatcher-publish.yml +++ b/.github/workflows/dispatcher-publish.yml @@ -252,6 +252,12 @@ jobs: echo "Release tag ${RELEASE_TAG} does not match approved build commit ${GITHUB_SHA}." >&2 exit 1 fi + if [ -z "${tag_sha}" ]; then + # Why the tag is created before the release: with an active tag ruleset the + # workflow token cannot create a release for a not-yet-existing tag, while + # explicit ref creation is permitted and pins the tag to the verified commit. + gh api "repos/${GITHUB_REPOSITORY}/git/refs" -f ref="refs/tags/${RELEASE_TAG}" -f sha="${GITHUB_SHA}" + fi if gh release view "${RELEASE_TAG}" --json isDraft,targetCommitish > release-input/release-state.json 2>/dev/null; then is_draft=$(jq -r '.isDraft' release-input/release-state.json) if [ "${is_draft}" = "false" ]; then diff --git a/.github/workflows/native-cli-publish.yml b/.github/workflows/native-cli-publish.yml index 73c8d2428..08ff8ada5 100644 --- a/.github/workflows/native-cli-publish.yml +++ b/.github/workflows/native-cli-publish.yml @@ -301,6 +301,12 @@ jobs: echo "Release tag ${RELEASE_TAG} does not match approved release commit ${RELEASE_SHA}." >&2 exit 1 fi + if [ -z "${tag_sha}" ]; then + # Why the tag is created before the release: with an active tag ruleset the + # workflow token cannot create a release for a not-yet-existing tag, while + # explicit ref creation is permitted and pins the tag to the verified commit. + gh api "repos/${GITHUB_REPOSITORY}/git/refs" -f ref="refs/tags/${RELEASE_TAG}" -f sha="${RELEASE_SHA}" + fi if gh release view "${RELEASE_TAG}" --json isDraft,targetCommitish > release-input/release-state.json 2>/dev/null; then is_draft=$(jq -r '.isDraft' release-input/release-state.json) if [ "${is_draft}" = "false" ]; then diff --git a/scripts/test-dispatcher-publish-workflow.sh b/scripts/test-dispatcher-publish-workflow.sh index 782798a83..0e4de9edb 100755 --- a/scripts/test-dispatcher-publish-workflow.sh +++ b/scripts/test-dispatcher-publish-workflow.sh @@ -166,6 +166,11 @@ test_publish_rejects_manifest_and_existing_tag_mismatches() { assert_not_contains "release-input/dist/dispatcher-release" } +test_release_tag_is_created_before_the_release() { + assert_contains 'gh api "repos/${GITHUB_REPOSITORY}/git/refs" -f ref="refs/tags/${RELEASE_TAG}" -f sha="${GITHUB_SHA}"' + assert_before 'gh api "repos/${GITHUB_REPOSITORY}/git/refs"' 'gh release create "${RELEASE_TAG}"' +} + test_draft_creation_accepts_only_the_known_missing_tag_responses() { assert_contains '*"HTTP 404"*) tag_sha="" ;;' assert_contains '*"HTTP 422"*"No commit found for SHA"*|*"No commit found for SHA"*"HTTP 422"*) tag_sha="" ;;' @@ -214,6 +219,7 @@ test_checkout_free_publish_has_explicit_repository_context test_dispatcher_assets_are_attested_after_the_manifest_is_verified test_dispatcher_verifies_tagged_installers_after_draft_creation test_publish_rejects_manifest_and_existing_tag_mismatches +test_release_tag_is_created_before_the_release test_draft_creation_accepts_only_the_known_missing_tag_responses test_dispatcher_publish_rechecks_the_tag_before_publishing test_dispatcher_build_preserves_release_checks diff --git a/scripts/test-native-cli-publish-workflow.sh b/scripts/test-native-cli-publish-workflow.sh index 959187e0b..dd4ea064a 100755 --- a/scripts/test-native-cli-publish-workflow.sh +++ b/scripts/test-native-cli-publish-workflow.sh @@ -177,6 +177,11 @@ test_publish_rejects_manifest_and_existing_tag_mismatches() { assert_not_contains "release-input/dist/release" } +test_release_tag_is_created_before_the_release() { + assert_contains 'gh api "repos/${GITHUB_REPOSITORY}/git/refs" -f ref="refs/tags/${RELEASE_TAG}" -f sha="${RELEASE_SHA}"' + assert_before 'gh api "repos/${GITHUB_REPOSITORY}/git/refs"' 'gh release create "${RELEASE_TAG}"' +} + test_draft_creation_accepts_only_the_known_missing_tag_responses() { assert_contains '*"HTTP 404"*) tag_sha="" ;;' assert_contains '*"HTTP 422"*"No commit found for SHA"*|*"No commit found for SHA"*"HTTP 422"*) tag_sha="" ;;' @@ -224,6 +229,7 @@ test_publish_validates_metadata_without_checking_out_source test_checkout_free_publish_has_explicit_repository_context test_assets_are_attested_after_the_manifest_is_verified test_publish_rejects_manifest_and_existing_tag_mismatches +test_release_tag_is_created_before_the_release test_draft_creation_accepts_only_the_known_missing_tag_responses test_publish_rechecks_the_tag_and_uses_least_privilege_post_publish_permissions test_build_verifies_assets_before_writing_the_publish_input