Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
16210f9
test: add native alias evidence predicates and countercontrols
rmcdaniel Nov 5, 2026
8fba2e0
Gate native alias promotion on exact qualification receipts
rmcdaniel Nov 5, 2026
a27f11f
Persist alias write progress before possible timeout
rmcdaniel Nov 5, 2026
3bbc660
Check persisted progress in partial alias failures
rmcdaniel Nov 5, 2026
86202db
Refuse ambiguous registry absence and foreign cleanup resources
rmcdaniel Nov 5, 2026
378bdcf
Require unconditional always context for strict cleanup model
rmcdaniel Nov 5, 2026
3882eed
Fix promotion dispatch trust and constrain qualification CLI inputs
rmcdaniel Nov 5, 2026
d98738d
Make registry argument and state-file boundaries explicit
rmcdaniel Nov 5, 2026
ec0142b
Refresh protected native promotion onto accepted contract main
rmcdaniel Nov 5, 2026
cc93ae7
Keep CLI child identity reserved until process-group cleanup
rmcdaniel Nov 5, 2026
65bb6e0
Resolve native child-state and lifecycle assertion findings
rmcdaniel Nov 5, 2026
ebd3bf1
Stage reviewed native promotion source without package jobs [skip ci]
rmcdaniel Nov 5, 2026
b5ec73e
Combine protected promotion source with the qualified PHP image corre…
rmcdaniel Nov 5, 2026
11f43a1
Refresh protected promotion source onto accepted main
rmcdaniel Nov 5, 2026
7b82310
Split MariaDB native execution tests across the existing two shards
rmcdaniel Nov 5, 2026
66a2b0b
Retain bounded execution selections and refuse failed listings
rmcdaniel Nov 5, 2026
0cb2b45
Isolate frozen PHP post-prune cleanup from its SQLite maintenance writer
rmcdaniel Nov 5, 2026
016bc78
Carry the native qualification CI pacing correction into the source t…
rmcdaniel Nov 5, 2026
22ef3f8
Keep the second execution shard report in bounded CI evidence
rmcdaniel Nov 5, 2026
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
105 changes: 105 additions & 0 deletions .github/workflows/native-alias-promotion.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
name: Native alias promotion
run-name: Native alias promotion ${{ github.ref_name }}

# Consumes existing exact-index qualification. Never dispatches a qualifier,
# builds/pulls an image, creates a Release, or changes candidate aliases.
on:
workflow_dispatch:
inputs:
tag:
description: 'Exact final native tag used as this dispatch ref and by the evidence manifest'
required: true
type: string
evidence:
description: 'Closed native-alias-promotion-evidence.v1 JSON manifest with exact current run attempts and artifact IDs/digests'
required: true
type: string

permissions: {}

jobs:
plan:
name: Validate exact promotion dispatch
runs-on: ubuntu-latest
timeout-minutes: 5
permissions:
contents: read
steps:
- name: Check out dispatch commit
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
ref: ${{ github.sha }}
persist-credentials: false
- name: Refuse a wrong ref or source before privileged jobs
env:
GH_TOKEN: ${{ github.token }}
PROMOTION_EVIDENCE: ${{ inputs.evidence }}
PROMOTION_TAG: ${{ inputs.tag }}
run: python3 scripts/ci/native_alias_promote.py plan

verify:
name: Verify existing native qualification
needs: plan
if: github.ref == format('refs/tags/{0}', inputs.tag)
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: read
actions: read
attestations: read
packages: read
steps:
- name: Check out exact tag tooling
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
ref: ${{ github.sha }}
persist-credentials: false
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4
- name: Verify provider-owned qualification without writes
env:
GH_TOKEN: ${{ github.token }}
PROMOTION_EVIDENCE: ${{ inputs.evidence }}
PROMOTION_TAG: ${{ inputs.tag }}
run: python3 scripts/ci/native_alias_promote.py verify

promote:
name: Promote qualified native aliases
needs: [plan, verify]
if: github.ref == format('refs/tags/{0}', inputs.tag)
runs-on: ubuntu-latest
timeout-minutes: 15
environment: dockerhub-native
concurrency:
group: durable-workflow-server-rolling-aliases
cancel-in-progress: false
permissions:
contents: read
actions: read
attestations: read
packages: write
steps:
- name: Check out exact tag tooling
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
ref: ${{ github.sha }}
persist-credentials: false
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4
- name: Log in to Docker Hub
uses: docker/login-action@af1e73f918a031802d376d3c8bbc3fe56130a9b0 # v4
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Log in to GHCR
uses: docker/login-action@af1e73f918a031802d376d3c8bbc3fe56130a9b0 # v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Repeat qualification under approval and lock before promotion
env:
GH_TOKEN: ${{ github.token }}
PROMOTION_EVIDENCE: ${{ inputs.evidence }}
PROMOTION_TAG: ${{ inputs.tag }}
run: python3 scripts/ci/native_alias_promote.py promote
11 changes: 11 additions & 0 deletions .github/workflows/native-github-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,15 @@ on:
required: true
type: string

release_run_attempt:
description: 'Exact current attempt of the publication run'
required: true
type: string
promotion_evidence:
description: 'Final releases require the exact qualification manifest used for alias promotion; candidates leave empty'
required: false
type: string

permissions:
contents: read

Expand Down Expand Up @@ -71,4 +80,6 @@ jobs:
GH_TOKEN: ${{ github.token }}
RELEASE_TAG: ${{ inputs.tag }}
RELEASE_RUN_ID: ${{ inputs.release_run_id }}
RELEASE_RUN_ATTEMPT: ${{ inputs.release_run_attempt }}
PROMOTION_EVIDENCE: ${{ inputs.promotion_evidence }}
run: bash scripts/ci/native-release-github-release.sh
36 changes: 32 additions & 4 deletions .github/workflows/native-image-parity.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
name: Native image parity
run-name: Native image parity ${{ inputs.native_image || github.ref }}

# Differential qualification of a native server image (Server #325). It
# records the shared fixture corpus on SQLite against the published PHP
Expand All @@ -18,13 +19,17 @@ on:
required: true
pull_request:
paths:
- scripts/ci/native_qualification.py
- scripts/ci/native_alias_transport.py
- scripts/ci/native_alias_evidence.py
- scripts/conformance/server-parity/native-image-corpus.sh
- scripts/conformance/server-parity/update-validation-sqlite.sh
- scripts/conformance/server-parity/php-maintenance-window.sh
- .github/workflows/native-image-parity.yml

permissions:
contents: read
actions: read

concurrency:
group: native-image-parity-${{ github.event.pull_request.number || github.run_id }}
Expand All @@ -51,7 +56,6 @@ jobs:
composer:2.10.3@sha256:da624c2c705d54e2f5a9bb77a5dc6c7d091711433ebefd7035672e84c2010fee

- name: Set up Docker Buildx
if: github.event_name == 'pull_request'
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4

- name: Build this pull request's native image into the local daemon
Expand All @@ -69,15 +73,39 @@ jobs:
tags: ${{ env.LOCAL_IMAGE }}

- name: Record and compare the corpus against the native image
id: body
env:
NATIVE_IMAGE: ${{ github.event_name == 'pull_request' && env.LOCAL_IMAGE || inputs.native_image }}
run: bash scripts/conformance/server-parity/native-image-corpus.sh "$NATIVE_IMAGE"

- name: Remove this job's containers and network
- name: Verify owned qualification cleanup
id: cleanup
if: always()
run: |
docker rm -f "$RESOURCE_SCOPE-rust" "$RESOURCE_SCOPE-server" "$RESOURCE_SCOPE-php-maintenance" || true
docker network rm "$RESOURCE_SCOPE" || true
python3 scripts/ci/native_qualification.py cleanup --kind corpus \
--scope "$RESOURCE_SCOPE" --output qualification-state/cleanup.json

- name: Finalize promotion receipt
if: always()
env:
GH_TOKEN: ${{ github.token }}
NATIVE_IMAGE: ${{ github.event_name == 'pull_request' && env.LOCAL_IMAGE || inputs.native_image }}
QUALIFICATION_BODY_OUTCOME: ${{ steps.body.outcome }}
QUALIFICATION_CLEANUP_OUTCOME: ${{ steps.cleanup.outcome }}
run: >-
python3 scripts/ci/native_qualification.py finalize --kind corpus --image "$NATIVE_IMAGE"
--identity qualification-state/image.json
--observations parity-evidence/native-image-corpus.json
--cleanup qualification-state/cleanup.json --output promotion-receipt/receipt.json

- name: Keep promotion receipt
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: native-corpus-promotion-receipt
path: promotion-receipt/receipt.json
retention-days: 14
if-no-files-found: error

- name: Keep the comparison evidence
if: always()
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/native-image.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ on:
- Dockerfile.native
- Dockerfile.native.dockerignore
- scripts/ci/native-image-smoke.sh
- scripts/ci/native-registry-absence.sh
- scripts/ci/native-release-publish.sh
- scripts/ci/native-release-rolling-tags.sh
- scripts/ci/native-release-identity.sh
Expand Down
29 changes: 29 additions & 0 deletions .github/workflows/native-promotion-contracts.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: Native promotion contracts
on:
pull_request:
paths:
- scripts/ci/native_alias_*.py
- scripts/ci/native_qualification.py
- scripts/ci/native-registry-absence.sh
- scripts/ci/native-release-*.sh
- scripts/ci/check-native-release-tags.sh
- scripts/ci/select-published-compose-image.sh
- scripts/smoke-published-compose.sh
- scripts/conformance/server-parity/native-image-corpus.sh
- tests/Unit/Support/native_*test.py
- .github/workflows/native-*.yml
- .github/workflows/published-compose.yml
permissions:
contents: read
jobs:
contracts:
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
persist-credentials: false
- name: Countercheck provider receipts, cleanup and fail-before writes
run: python3 -m unittest discover -s tests/Unit/Support -p 'native_*test.py' -v
- name: Preserve release, candidate and monotonic alias contracts
run: bash scripts/ci/check-native-release-tags.sh
60 changes: 7 additions & 53 deletions .github/workflows/native-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,14 @@ name: Native release candidate
# decision, November 1), with the repository's Docker Hub credentials. A
# native version is a major version above every PHP release, and the
# publish job refuses a tag either registry already has, so it never
# 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
# replaces a PHP image. Future final aliases move only in the separate
# protected native-alias-promotion.yml phase after exact-index corpus and
# four-cell Compose qualification (Server #1020); a candidate moves none, and the PHP Release workflow leaves latest to a newer native
# 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
# its source. It publishes from the dockerhub-native Environment, whose
# deployment rules permit native-[0-9]* and 3.[0-9]*.[0-9]*; creating those tags is
# restricted by the repository's tag ruleset.
#
# Every version, release candidates included, publishes to both registries.
Expand Down Expand Up @@ -353,51 +353,5 @@ jobs:
subject-digest: ${{ steps.index.outputs.digest }}
push-to-registry: true

# A final release (never a candidate) moves X.Y, X and latest forward to the
# attested index. This is its own job so a failed alias write can be rerun
# alone ("Re-run failed jobs") from the already published index; it first
# re-verifies that both exact tags name that index. One repository-wide
# group serializes alias writes across native releases, and the script
# rechecks each alias's current version in the registry right before
# writing, so an older release never moves an alias backward.
aliases:
name: Move the rolling tags of a final release
needs: [plan, publish]
if: github.ref == format('refs/tags/{0}', inputs.tag) && needs.plan.outputs.dockerhub == 'true'
runs-on: ubuntu-latest
timeout-minutes: 15
environment: dockerhub-native
concurrency:
group: durable-workflow-server-rolling-aliases
cancel-in-progress: false
permissions:
contents: read
packages: write
steps:
- name: Check out trusted release tooling
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
ref: ${{ github.sha }}

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4

- name: Log in to Docker Hub
uses: docker/login-action@af1e73f918a031802d376d3c8bbc3fe56130a9b0 # v4
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Log in to GHCR
uses: docker/login-action@af1e73f918a031802d376d3c8bbc3fe56130a9b0 # v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Reconcile the rolling tags with the attested index
env:
RELEASE_TAG: ${{ inputs.tag }}
INDEX_DIGEST: ${{ needs.publish.outputs.digest }}
RELEASE_IMAGE_TAG_SOURCE_URL: ${{ github.server_url }}/${{ github.repository }}.git
run: bash scripts/ci/native-release-rolling-tags.sh
# Rolling aliases are deliberately absent. Future final tags containing
# this source use native-alias-promotion.yml after existing qualification.
43 changes: 37 additions & 6 deletions .github/workflows/published-compose.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
name: Published Compose Smoke
run-name: Published Compose ${{ inputs.server_image || github.ref }}

on:
pull_request:
paths:
- docker-compose.published.yml
- scripts/ci/native_qualification.py
- scripts/ci/native_alias_transport.py
- scripts/ci/native_alias_evidence.py
- scripts/smoke-published-compose.sh
- scripts/ci/select-published-compose-image.sh
- .github/workflows/published-compose.yml
Expand All @@ -16,6 +20,7 @@ on:

permissions:
contents: read
actions: read

concurrency:
group: published-compose-${{ github.event_name }}-${{ github.ref }}
Expand All @@ -26,6 +31,8 @@ jobs:
name: ${{ matrix.platform }} ${{ matrix.profile }} published image
runs-on: ubuntu-latest
timeout-minutes: 35
env:
DW_PUBLISHED_COMPOSE_PROJECT: published-compose-${{ github.run_id }}-${{ github.run_attempt }}-${{ github.job }}-${{ matrix.shard }}

strategy:
fail-fast: false
Expand Down Expand Up @@ -58,20 +65,44 @@ jobs:
INPUT_SERVER_IMAGE: ${{ github.event_name == 'workflow_dispatch' && inputs.server_image || '' }}
run: scripts/ci/select-published-compose-image.sh

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4

- name: Smoke published Compose stack
id: body
env:
DW_SERVER_PLATFORM: ${{ matrix.platform }}
DW_PUBLISHED_COMPOSE_PROFILE: ${{ matrix.profile }}
DW_PUBLISHED_COMPOSE_PROJECT: published-compose-${{ github.run_id }}-${{ github.run_attempt }}-${{ github.job }}-${{ matrix.shard }}
SERVER_PORT: "0"
DW_AUTH_TOKEN: published-compose-token
run: scripts/smoke-published-compose.sh

- name: Clean up published Compose stack
- name: Verify owned qualification cleanup
id: cleanup
if: always()
run: |
python3 scripts/ci/native_qualification.py cleanup --kind compose \
--scope "$DW_PUBLISHED_COMPOSE_PROJECT" --output qualification-state/cleanup.json

- name: Finalize promotion receipt
if: always()
env:
DW_PUBLISHED_COMPOSE_PROJECT: published-compose-${{ github.run_id }}-${{ github.run_attempt }}-${{ github.job }}-${{ matrix.shard }}
GH_TOKEN: ${{ github.token }}
QUALIFICATION_BODY_OUTCOME: ${{ steps.body.outcome }}
QUALIFICATION_CLEANUP_OUTCOME: ${{ steps.cleanup.outcome }}
PLATFORM: ${{ matrix.platform }}
PROFILE: ${{ matrix.profile }}
run: >-
docker compose -p "$DW_PUBLISHED_COMPOSE_PROJECT"
-f docker-compose.published.yml down -v --remove-orphans
>/dev/null 2>&1 || true
python3 scripts/ci/native_qualification.py finalize --kind compose --image "${DW_SERVER_IMAGE:-}"
--platform "$PLATFORM" --profile "$PROFILE"
--identity qualification-state/image.json --observations qualification-state/observations.json
--cleanup qualification-state/cleanup.json --output promotion-receipt/receipt.json

- name: Keep promotion receipt
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: native-compose-promotion-receipt-${{ matrix.shard }}
path: promotion-receipt/receipt.json
retention-days: 14
if-no-files-found: error
Loading
Loading