Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 8 additions & 9 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ name: Release
run-name: Release ${{ inputs.tag || github.ref_name }} at ${{ inputs.release_commit || github.sha }}

on:
# PHP Server releases are 2.x and earlier; plain tags from 3.0 are native
# releases (native-release.yml).
push:
tags:
- '[0-2].[0-9]+.[0-9]+*'
# Retired: PHP Server 2.11.8 is the final PHP release (server#1002). No tag
# starts this workflow, and its publish job never runs (`if: false`), so it
# never loads the registry logins or holds contents: write. It stays as the
# reviewed record of how 2.x images were published, for its contract tests.
# Native releases are native-release.yml.
workflow_dispatch:
inputs:
tag:
Expand Down Expand Up @@ -46,10 +46,9 @@ jobs:
concurrency:
group: durable-workflow-server-rolling-aliases
cancel-in-progress: false
if: >-
(github.event_name == 'workflow_dispatch' &&
github.ref == 'refs/heads/main') ||
(github.event_name == 'push' && startsWith(github.ref, 'refs/tags/'))
# Retired (server#1002): a skipped job starts no runner, so none of its
# secrets or write permissions are ever loaded.
if: false
permissions:
contents: write
packages: write
Expand Down
45 changes: 14 additions & 31 deletions scripts/ci/check-native-release-tags.sh
Original file line number Diff line number Diff line change
@@ -1,44 +1,27 @@
#!/usr/bin/env bash
# Native release-candidate tags are their own Git tags, native-<version>
# (Server #325). Check that:
# - the PHP Release workflow's push-tag filter matches no native- tag, so a
# native tag starts no PHP release, and still matches plain versions;
# - the retired PHP Release workflow (server#1002) has no push trigger, so
# no tag starts it, and its publish job never runs;
# - the tag verifier accepts native- only when asked for it;
# - the native publisher accepts only native- tags, and lets only a release
# candidate (-rc.N) skip Docker Hub.
# Each script is stopped at its input checks, before any API or registry call.
set -euo pipefail
fail() { printf 'Native release tag check failed: %s\n' "$1" >&2; exit 1; }

# The tag patterns under release.yml's push trigger.
mapfile -t patterns < <(awk '
/^ push:/ { push = 1; next }
push && /^ [a-z_]+:/ { push = 0 }
push && /^ tags:/ { tags = 1; next }
tags && /^ - / { sub(/^ - /, ""); gsub(/\x27|"/, ""); print; next }
tags { tags = 0 }
# The retired PHP Release workflow (server#1002): no push trigger at all, so
# no tag of any kind starts it, and its publish job is `if: false`, so it never
# loads the registry logins or contents: write.
release_triggers=$(awk '
/^on:/ { on = 1; next }
on && /^[^ #]/ { on = 0 }
on && /^ [a-z_]+:/ { sub(/^ /, ""); sub(/:.*/, ""); print }
' .github/workflows/release.yml)
test "${#patterns[@]}" -ge 1 || fail "release.yml has no push tag patterns."

# A GitHub filter pattern as an anchored extended regular expression:
# [..] and + keep their meaning, * matches anything but /, the rest is literal.
matches() {
local pattern=$1 tag=$2 regex
regex=$(printf '%s' "$pattern" | sed -e 's/[.]/\\./g' -e 's#[*]#[^/]*#g')
[[ $tag =~ ^${regex}$ ]]
}
for pattern in "${patterns[@]}"; do
for tag in native-2.12.0-rc.1 native-2.11.4 native-3.0.0 3.0.2 3.1.0-rc.1 10.0.0; do
if matches "$pattern" "$tag"; then fail "release.yml tag pattern $pattern matches $tag."; fi
done
done
for tag in 2.11.4 2.12.0-rc.1; do
matched=0
for pattern in "${patterns[@]}"; do
if matches "$pattern" "$tag"; then matched=1; fi
done
test "$matched" = 1 || fail "no release.yml tag pattern matches the plain version $tag."
done
test "$release_triggers" = workflow_dispatch \
|| fail "the retired release.yml must have only workflow_dispatch, not: $(echo $release_triggers)."
grep -qx ' if: false' <(sed -n '/^ publish:$/,/^ [a-z-]*:$/p' .github/workflows/release.yml) \
|| fail "the retired release.yml publish job must be if: false."

expect() {
local message=$1; shift
Expand Down Expand Up @@ -480,4 +463,4 @@ grep -q 'run: scripts/ci/verify-native-release-dispatch.sh' .github/workflows/na
# the native Release job may write releases.
grep -q 'name: Refuse a native release tag' .github/workflows/release.yml \
|| fail "release.yml must refuse a 3.x or native- tag."
printf 'Native release tags: %d release.yml pattern(s) checked; verifier, publisher and rolling tags behave as intended.\n' "${#patterns[@]}"
printf 'Native release tags: retired release.yml cannot start or publish; verifier, publisher and rolling tags behave as intended.\n'
14 changes: 7 additions & 7 deletions tests/Unit/ReleaseImagePublishWorkflowContractTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,13 @@ public function test_release_publish_job_is_guarded_before_registry_login(): voi

$parsedWorkflow = Yaml::parse($workflow);
$this->assertIsArray($parsedWorkflow);
$publishCondition = $parsedWorkflow['jobs']['publish']['if'] ?? null;
$this->assertIsString($publishCondition);
$this->assertSame(
"(github.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/main') "
."|| (github.event_name == 'push' && startsWith(github.ref, 'refs/tags/'))",
preg_replace('/\s+/', ' ', trim($publishCondition)),
);
// Retired (server#1002): PHP Server 2.11.8 is final. No tag starts the
// workflow, and its publish job never runs, so it never loads the
// registry logins or contents: write.
$this->assertSame(false, $parsedWorkflow['jobs']['publish']['if'] ?? null);
$triggers = $parsedWorkflow['on'] ?? $parsedWorkflow[true] ?? null;
$this->assertIsArray($triggers);
$this->assertSame(['workflow_dispatch'], array_keys($triggers));

$guardOffset = strpos($workflow, 'Validate release publish context and credentials');
$dockerHubLoginOffset = strpos($workflow, 'Log in to Docker Hub');
Expand Down
Loading