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
1 change: 1 addition & 0 deletions .github/workflows/native-image.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ on:
- scripts/ci/promote-release-image-rolling-tags.sh
- scripts/ci/check-native-release-tags.sh
- scripts/ci/verify-release-tag-source.sh
- scripts/ci/verify-native-release-dispatch.sh
- .github/workflows/native-image.yml
- .github/workflows/native-release.yml
- .github/workflows/release.yml
Expand Down
33 changes: 27 additions & 6 deletions .github/workflows/native-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,12 @@ name: Native release candidate
# replaces a PHP image. A final release then moves X.Y, X and latest
# forward to its index (operator decision, November 1); a candidate moves
# none, and the PHP Release workflow leaves latest to a newer native
# release. It runs only from main, in the dockerhub-native Environment.
# release. It is dispatched from the release tag's own ref
# (refs/tags/native-<version>), at the tag's commit, which protected main must
# contain; its build-provenance attestation then names the release commit as
# its source. It publishes from the dockerhub-native Environment, whose only
# deployment rule is the tag pattern native-[0-9]*; creating those tags is
# restricted by the repository's tag ruleset.
#
# Every version, release candidates included, publishes to both registries.
# Candidates 1 and 2 went to GHCR only. That was the porting agent's
Expand Down Expand Up @@ -56,13 +61,29 @@ env:
jobs:
plan:
name: Choose the registries for this tag
if: github.ref == 'refs/heads/main'
# No job condition: a dispatch from any other ref fails here, loudly,
# rather than skipping every job and finishing green.
runs-on: ubuntu-latest
timeout-minutes: 5
outputs:
dockerhub: ${{ steps.plan.outputs.dockerhub }}
version: ${{ steps.plan.outputs.version }}
steps:
- name: Check out trusted release tooling
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
ref: ${{ github.sha }}
persist-credentials: false

- name: Require a dispatch from the release tag, at its commit, on main
env:
GH_TOKEN: ${{ github.token }}
RELEASE_TAG: ${{ inputs.tag }}
RELEASE_COMMIT: ${{ inputs.release_commit }}
DISPATCH_REF: ${{ github.ref }}
DISPATCH_SHA: ${{ github.sha }}
run: scripts/ci/verify-native-release-dispatch.sh

- name: Publish to both registries
id: plan
env:
Expand All @@ -77,7 +98,7 @@ jobs:
build-amd64:
name: Build and smoke linux/amd64
needs: plan
if: github.ref == 'refs/heads/main'
if: github.ref == format('refs/tags/{0}', inputs.tag)
runs-on: ubuntu-latest
timeout-minutes: 75
environment: dockerhub-native
Expand Down Expand Up @@ -170,7 +191,7 @@ jobs:
build-arm64:
name: Build and smoke linux/arm64
needs: plan
if: github.ref == 'refs/heads/main'
if: github.ref == format('refs/tags/{0}', inputs.tag)
runs-on: ubuntu-24.04-arm
timeout-minutes: 75
environment: dockerhub-native
Expand Down Expand Up @@ -263,7 +284,7 @@ jobs:
publish:
name: Tag, verify and attest the native index
needs: [plan, build-amd64, build-arm64]
if: github.ref == 'refs/heads/main'
if: github.ref == format('refs/tags/{0}', inputs.tag)
runs-on: ubuntu-latest
timeout-minutes: 20
environment: dockerhub-native
Expand Down Expand Up @@ -331,7 +352,7 @@ jobs:
aliases:
name: Move the rolling tags of a final release
needs: [plan, publish]
if: github.ref == 'refs/heads/main' && needs.plan.outputs.dockerhub == 'true'
if: github.ref == format('refs/tags/{0}', inputs.tag) && needs.plan.outputs.dockerhub == 'true'
runs-on: ubuntu-latest
timeout-minutes: 15
environment: dockerhub-native
Expand Down
15 changes: 11 additions & 4 deletions rust/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -623,10 +623,17 @@ async fn run() -> Result<(), Box<dyn Error>> {
.with_role_tokens(worker_token, operator_token, admin_token)
.with_auth_options(anonymous, runtime_credentials);
let listener = tokio::net::TcpListener::bind(address).await?;
eprintln!(
"Rust development runtime listening on {}",
listener.local_addr()?
);
// A release build names its version; a source build stays a development runtime.
match option_env!("DW_SERVER_VERSION").filter(|version| !version.is_empty()) {
Some(version) => eprintln!(
"Durable Workflow native server {version} listening on {}",
listener.local_addr()?
),
None => eprintln!(
"Rust development runtime listening on {}",
listener.local_addr()?
),
}
axum::serve(
listener,
router(runtime.clone()).into_make_service_with_connect_info::<std::net::SocketAddr>(),
Expand Down
42 changes: 42 additions & 0 deletions scripts/ci/check-native-release-tags.sh
Original file line number Diff line number Diff line change
Expand Up @@ -212,4 +212,46 @@
|| fail "the PHP promoter must use the resolver's inventory."
awk '/- name: Promote rolling image aliases/,/run: scripts\/ci\/promote-release-image-rolling-tags.sh/' .github/workflows/release.yml \
| grep -q RELEASE_IMAGE_TAG_SOURCE_URL && fail "the PHP promoter must not read the tags again."
# The dispatch verifier: the release tag's own ref, at its commit, on main.
dispatch=scripts/ci/verify-native-release-dispatch.sh
dispatch_scratch=$(mktemp -d)
printf '#!/bin/sh\necho "${COMPARE_STATUS:-ahead}"\n' > "$dispatch_scratch/gh"
chmod +x "$dispatch_scratch/gh"
commit=0123456789abcdef0123456789abcdef01234567
other=fedcba9876543210fedcba9876543210fedcba98
ok_dispatch=(RELEASE_TAG=native-3.0.2 RELEASE_COMMIT=$commit DISPATCH_SHA=$commit GITHUB_REPOSITORY=example/server GH_CLI="$dispatch_scratch/gh")
expect "dispatch from refs/tags/native-3.0.2, not refs/heads/main" "${ok_dispatch[@]}" DISPATCH_REF=refs/heads/main bash "$dispatch"
expect "dispatch from refs/tags/native-3.0.2, not refs/tags/native-3.0.1" "${ok_dispatch[@]}" DISPATCH_REF=refs/tags/native-3.0.1 bash "$dispatch"
expect "is not RELEASE_COMMIT" RELEASE_TAG=native-3.0.2 RELEASE_COMMIT=$commit DISPATCH_SHA=$other DISPATCH_REF=refs/tags/native-3.0.2 GITHUB_REPOSITORY=example/server GH_CLI="$dispatch_scratch/gh" bash "$dispatch"
expect "must be native-X.Y.Z" "${ok_dispatch[@]}" RELEASE_TAG=3.0.2 DISPATCH_REF=refs/tags/3.0.2 bash "$dispatch"
for status in behind diverged; do
expect "is not on main (compare status: $status)" "${ok_dispatch[@]}" DISPATCH_REF=refs/tags/native-3.0.2 COMPARE_STATUS=$status bash "$dispatch"
done
for status in ahead identical; do
env -i PATH="$PATH" "${ok_dispatch[@]}" DISPATCH_REF=refs/tags/native-3.0.2 COMPARE_STATUS=$status bash "$dispatch" >/dev/null \
|| fail "the dispatch verifier must accept a release commit main contains ($status)."
done
rm -rf "$dispatch_scratch"
# Each job's own condition, exactly: plan has none, so a wrong ref fails in
# its verifier rather than skipping every job; every publishing job is gated
# on the release tag's ref.
job_conditions=$(awk '
/^jobs:/ { jobs = 1; next }
jobs && /^ [a-z0-9-]+:$/ { if (job != "") print job "=" cond; job = substr($1, 1, length($1) - 1); cond = "none"; next }
jobs && /^ if: / { sub(/^ if: /, ""); cond = $0 }
END { if (job != "") print job "=" cond }
' .github/workflows/native-release.yml)
tag_gate="github.ref == format('refs/tags/{0}', inputs.tag)"
expected_conditions="plan=none
build-amd64=$tag_gate
build-arm64=$tag_gate
publish=$tag_gate
aliases=$tag_gate && needs.plan.outputs.dockerhub == 'true'"
test "$job_conditions" = "$expected_conditions" \

Check failure on line 250 in scripts/ci/check-native-release-tags.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use '[[' instead of 'test' command for conditional tests. The '[[' construct is safer and more feature-rich.

See more on https://sonarcloud.io/project/issues?id=durable-workflow_server&issues=AaGeLgDF0zlpHW4LQczo&open=AaGeLgDF0zlpHW4LQczo&pullRequest=957
|| fail "native-release.yml job conditions changed; expected:
$expected_conditions
got:
$job_conditions"
grep -q 'run: scripts/ci/verify-native-release-dispatch.sh' .github/workflows/native-release.yml \
|| fail "the plan job must run the dispatch verifier."
printf 'Native release tags: %d release.yml pattern(s) checked; verifier, publisher and rolling tags behave as intended.\n' "${#patterns[@]}"
35 changes: 35 additions & 0 deletions scripts/ci/verify-native-release-dispatch.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#!/usr/bin/env bash
# A native release runs only when dispatched from its own release tag ref, at
# that tag's commit, and that commit is on protected main (Server #325).
# Dispatching from the tag makes the build-provenance attestation's source
# digest the release commit itself; the tag ref alone would let tooling run
# from a commit main never accepted, so main must contain it.
set -euo pipefail
fail() {
printf 'Native release dispatch refused: %s\n' "$1" >&2
exit 1
}
release_tag="${RELEASE_TAG:-}"
release_commit="${RELEASE_COMMIT:-}"
dispatch_ref="${DISPATCH_REF:-}"
dispatch_sha="${DISPATCH_SHA:-}"
repository="${GITHUB_REPOSITORY:-}"
gh_cli="${GH_CLI:-gh}"
[[ "$release_tag" =~ ^native-[0-9]+\.[0-9]+\.[0-9]+(-rc\.[0-9]+)?$ ]] \
|| fail "RELEASE_TAG must be native-X.Y.Z or native-X.Y.Z-rc.N."
[[ "$release_commit" =~ ^[0-9a-f]{40}$ ]] \
|| fail "RELEASE_COMMIT must be a full lowercase Git commit SHA."
[ "$dispatch_ref" = "refs/tags/$release_tag" ] \

Check failure on line 22 in scripts/ci/verify-native-release-dispatch.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich.

See more on https://sonarcloud.io/project/issues?id=durable-workflow_server&issues=AaGeKi4kNraQTLYHyFH5&open=AaGeKi4kNraQTLYHyFH5&pullRequest=957
|| fail "dispatch from refs/tags/$release_tag, not ${dispatch_ref:-an empty ref}."
[ "$dispatch_sha" = "$release_commit" ] \

Check failure on line 24 in scripts/ci/verify-native-release-dispatch.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich.

See more on https://sonarcloud.io/project/issues?id=durable-workflow_server&issues=AaGeKi4kNraQTLYHyFH6&open=AaGeKi4kNraQTLYHyFH6&pullRequest=957
|| fail "the dispatched commit ${dispatch_sha:-(none)} is not RELEASE_COMMIT $release_commit."
[[ "$repository" =~ ^[0-9A-Za-z_.-]+/[0-9A-Za-z_.-]+$ ]] \
|| fail "GITHUB_REPOSITORY must identify the release repository."
if ! status="$("$gh_cli" api "repos/$repository/compare/$release_commit...main" --jq .status 2>/dev/null)"; then
fail "cannot compare $release_commit with main."
fi
case "$status" in
ahead | identical) ;;
*) fail "RELEASE_COMMIT $release_commit is not on main (compare status: ${status:-none})." ;;
esac
printf 'Dispatched from %s at %s, which main contains.\n' "$dispatch_ref" "$release_commit"
Loading