Release 2.0.0-beta.13 at 6ee93a90e60d3865ca23a379cf02856465d4c87b for direct #731
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Release | |
| run-name: Release ${{ inputs.tag || github.ref_name }} at ${{ inputs.release_commit || github.sha }} for ${{ inputs.release_plan || 'direct' }} | |
| on: | |
| push: | |
| tags: | |
| - '[0-9]+.[0-9]+.[0-9]+*' | |
| workflow_dispatch: | |
| inputs: | |
| tag: | |
| description: 'Existing immutable release tag (for example 0.0.1-test)' | |
| required: true | |
| release_commit: | |
| description: 'Exact source commit declared by the immutable release plan' | |
| required: true | |
| release_plan: | |
| description: 'Immutable release-plan tag initiating this recovery run' | |
| required: false | |
| default: 'direct' | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: release-${{ inputs.tag || github.ref_name }} | |
| cancel-in-progress: false | |
| env: | |
| DOCKERHUB_IMAGE: durableworkflow/server | |
| GHCR_IMAGE: ghcr.io/durable-workflow/server | |
| RELEASE_IMAGE_PLATFORMS: linux/amd64,linux/arm64 | |
| PHPREDIS_VERSION: 6.3.0 | |
| PHPREDIS_COMMIT: df4fab2de7fc327c54c94a13af2b9542e4fbd720 | |
| WORKFLOW_PACKAGE_SOURCE: https://github.com/durable-workflow/workflow.git | |
| WORKFLOW_PACKAGE_REF: 2.0.0-beta.10 | |
| WORKFLOW_PACKAGE_COMMIT: 0fddbec98b94a5b542480d746759a2c695bba2be | |
| jobs: | |
| publish: | |
| name: Build and push Docker image | |
| runs-on: ubuntu-latest | |
| environment: dockerhub | |
| if: >- | |
| (github.event_name == 'workflow_dispatch' && | |
| github.ref == 'refs/heads/main') || | |
| (github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')) | |
| permissions: | |
| contents: write | |
| packages: write | |
| steps: | |
| - name: Check out trusted release tooling | |
| uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 | |
| with: | |
| fetch-depth: 0 | |
| ref: ${{ github.sha }} | |
| - name: Check out immutable release source | |
| uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 | |
| with: | |
| fetch-depth: 0 | |
| path: release-source | |
| ref: ${{ github.event_name == 'workflow_dispatch' && format('refs/tags/{0}', inputs.tag) || github.ref }} | |
| - name: Resolve exact source identity | |
| id: release_source | |
| env: | |
| REQUESTED_TAG: ${{ github.event_name == 'workflow_dispatch' && inputs.tag || github.ref_name }} | |
| REQUESTED_COMMIT: ${{ github.event_name == 'workflow_dispatch' && inputs.release_commit || '' }} | |
| run: | | |
| set -euo pipefail | |
| head_commit="$(git -C release-source rev-parse HEAD)" | |
| tag_commit="$(git -C release-source rev-list -n 1 "$REQUESTED_TAG")" | |
| if [ "$tag_commit" != "$head_commit" ]; then | |
| printf 'release tag %s points to %s, not checkout commit %s\n' \ | |
| "$REQUESTED_TAG" "$tag_commit" "$head_commit" >&2 | |
| exit 1 | |
| fi | |
| if [ -n "$REQUESTED_COMMIT" ] && [ "$head_commit" != "$REQUESTED_COMMIT" ]; then | |
| printf 'release checkout %s does not match planned commit %s\n' \ | |
| "$head_commit" "$REQUESTED_COMMIT" >&2 | |
| exit 1 | |
| fi | |
| printf 'commit=%s\n' "$head_commit" >> "$GITHUB_OUTPUT" | |
| - name: Validate release publish context and credentials | |
| id: release_publish | |
| env: | |
| INPUT_TAG: ${{ github.event.inputs.tag }} | |
| DOCKERHUB_IMAGE: ${{ env.DOCKERHUB_IMAGE }} | |
| GHCR_IMAGE: ${{ env.GHCR_IMAGE }} | |
| DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} | |
| DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }} | |
| GHCR_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: scripts/ci/validate-release-image-publish.sh | |
| - name: Select compatible workflow package version | |
| id: workflow | |
| env: | |
| SERVER_WORKER_PROTOCOL_FILE: release-source/app/Support/WorkerProtocol.php | |
| run: scripts/ci/select-compatible-workflow-package-ref.sh | |
| - name: Set up QEMU | |
| uses: docker/setup-qemu-action@96fe6ef7f33517b61c61be40b68a1882f3264fb8 # v4 | |
| - 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: Extract exact image metadata | |
| id: meta | |
| uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6 | |
| with: | |
| images: | | |
| ${{ env.DOCKERHUB_IMAGE }} | |
| ${{ env.GHCR_IMAGE }} | |
| # On a release tag like 0.1.0, produces immutable tags: | |
| # durableworkflow/server:0.1.0 | |
| # ghcr.io/durable-workflow/server:0.1.0 | |
| # workflow_dispatch checks out the existing immutable tag before it | |
| # reproduces these exact image identities for recovery. | |
| # yamllint disable rule:line-length | |
| tags: | | |
| type=semver,pattern={{version}},value=${{ steps.release_publish.outputs.tag }},enable=${{ steps.release_publish.outputs.is_semver == 'true' }} | |
| type=raw,value=${{ steps.release_publish.outputs.tag }},enable=${{ steps.release_publish.outputs.is_semver != 'true' }} | |
| # yamllint enable rule:line-length | |
| flavor: | | |
| latest=false | |
| labels: | | |
| org.opencontainers.image.source=https://github.com/${{ github.repository }} | |
| org.opencontainers.image.revision=${{ steps.release_source.outputs.commit }} | |
| dev.durable-workflow.release.tag=${{ steps.release_publish.outputs.tag }} | |
| dev.durable-workflow.release.run-id=${{ github.run_id }} | |
| dev.durable-workflow.release.run-attempt=${{ github.run_attempt }} | |
| dev.durable-workflow.workflow.package=durable-workflow/workflow | |
| dev.durable-workflow.workflow.version=${{ steps.workflow.outputs.tag }} | |
| dev.durable-workflow.workflow.commit=${{ steps.workflow.outputs.commit }} | |
| - name: Verify immutable release tag at publication boundary | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| RELEASE_TAG: ${{ steps.release_publish.outputs.tag }} | |
| RELEASE_COMMIT: >- | |
| ${{ github.event_name == 'workflow_dispatch' | |
| && inputs.release_commit || steps.release_source.outputs.commit }} | |
| run: scripts/ci/verify-release-tag-source.sh | |
| - name: Resolve shared release image cache | |
| id: cache | |
| env: | |
| RELEASE_IMAGE_CACHE_ROOT: release-source | |
| RELEASE_SOURCE_COMMIT: ${{ steps.release_source.outputs.commit }} | |
| WORKFLOW_PACKAGE_REF: ${{ steps.workflow.outputs.tag }} | |
| WORKFLOW_PACKAGE_COMMIT: ${{ steps.workflow.outputs.commit }} | |
| run: node scripts/ci/resolve-release-image-cache.mjs | |
| - name: Start exact image build timer | |
| id: build_started | |
| run: printf 'epoch_seconds=%s\n' "$(date +%s)" >> "$GITHUB_OUTPUT" | |
| - name: Build and push exact image tags | |
| id: build | |
| timeout-minutes: 45 | |
| continue-on-error: true | |
| uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7 | |
| with: | |
| context: release-source | |
| platforms: ${{ env.RELEASE_IMAGE_PLATFORMS }} | |
| push: true | |
| tags: ${{ steps.meta.outputs.tags }} | |
| labels: ${{ steps.meta.outputs.labels }} | |
| pull: true | |
| cache-from: ${{ steps.cache.outputs.cache_from }} | |
| cache-to: ${{ steps.cache.outputs.cache_to }} | |
| provenance: false | |
| build-args: | | |
| PHPREDIS_VERSION=${{ env.PHPREDIS_VERSION }} | |
| PHPREDIS_COMMIT=${{ env.PHPREDIS_COMMIT }} | |
| WORKFLOW_PACKAGE_SOURCE=${{ env.WORKFLOW_PACKAGE_SOURCE }} | |
| WORKFLOW_PACKAGE_REF=${{ steps.workflow.outputs.tag }} | |
| WORKFLOW_PACKAGE_COMMIT=${{ steps.workflow.outputs.commit }} | |
| - name: Record exact image build timing | |
| id: build_timing | |
| if: ${{ always() && steps.build_started.outputs.epoch_seconds != '' }} | |
| env: | |
| BUILD_STARTED_AT: ${{ steps.build_started.outputs.epoch_seconds }} | |
| run: | | |
| finished_at="$(date +%s)" | |
| duration_seconds="$((finished_at - BUILD_STARTED_AT))" | |
| printf 'duration_seconds=%s\n' "$duration_seconds" >> "$GITHUB_OUTPUT" | |
| printf 'Exact multi-platform image build took %s seconds.\n' "$duration_seconds" >> "$GITHUB_STEP_SUMMARY" | |
| - name: Verify exact image publication | |
| id: exact | |
| env: | |
| RELEASE_TAG: ${{ steps.release_publish.outputs.tag }} | |
| DOCKERHUB_IMAGE: ${{ env.DOCKERHUB_IMAGE }} | |
| GHCR_IMAGE: ${{ env.GHCR_IMAGE }} | |
| DOCKER_BUILD_OUTCOME: ${{ steps.build.outcome || 'skipped' }} | |
| BUILT_IMAGE_DIGEST: ${{ steps.build.outputs.digest }} | |
| BUILT_IMAGE_METADATA: ${{ steps.build.outputs.metadata }} | |
| RELEASE_COMMIT: ${{ steps.release_source.outputs.commit }} | |
| RELEASE_RUN_ID: ${{ github.run_id }} | |
| RELEASE_RUN_ATTEMPT: ${{ github.run_attempt }} | |
| WORKFLOW_PACKAGE_REF: ${{ steps.workflow.outputs.tag }} | |
| WORKFLOW_PACKAGE_COMMIT: ${{ steps.workflow.outputs.commit }} | |
| run: scripts/ci/verify-release-exact-images.sh | |
| - name: Verify published protocol catalog convergence | |
| id: protocol_catalog | |
| if: ${{ steps.exact.outputs.exact_publish_outcome == 'success' }} | |
| env: | |
| RELEASE_TAG: ${{ steps.release_publish.outputs.tag }} | |
| SERVER_IMAGE: ${{ env.DOCKERHUB_IMAGE }}:${{ steps.release_publish.outputs.tag }} | |
| WORKFLOW_PACKAGE_REF: ${{ steps.workflow.outputs.tag }} | |
| WORKFLOW_PACKAGE_COMMIT: ${{ steps.workflow.outputs.commit }} | |
| PUBLIC_CATALOG_URL: https://durable-workflow.github.io/platform-protocol-specs.json | |
| PROTOCOL_CATALOG_CONFORMANCE_EVIDENCE: release-protocol-catalog-conformance.json | |
| run: scripts/ci/verify-release-protocol-catalog.sh | |
| - name: Resolve rolling image aliases | |
| id: rolling | |
| if: >- | |
| steps.release_publish.outputs.is_semver == 'true' && | |
| steps.exact.outputs.exact_publish_outcome == 'success' && | |
| steps.protocol_catalog.outputs.protocol_catalog_conformance_outcome == 'success' | |
| env: | |
| RELEASE_TAG: ${{ steps.release_publish.outputs.tag }} | |
| DOCKERHUB_IMAGE: ${{ env.DOCKERHUB_IMAGE }} | |
| GHCR_IMAGE: ${{ env.GHCR_IMAGE }} | |
| RELEASE_IMAGE_TAG_SOURCE_URL: ${{ github.server_url }}/${{ github.repository }}.git | |
| run: scripts/ci/resolve-release-image-rolling-tags.sh | |
| - name: Promote rolling image aliases | |
| id: promote_rolling | |
| if: ${{ steps.rolling.outputs.rolling_should_promote == 'true' }} | |
| env: | |
| RELEASE_TAG: ${{ steps.release_publish.outputs.tag }} | |
| DOCKERHUB_IMAGE: ${{ env.DOCKERHUB_IMAGE }} | |
| GHCR_IMAGE: ${{ env.GHCR_IMAGE }} | |
| ROLLING_SHOULD_PROMOTE: ${{ steps.rolling.outputs.rolling_should_promote }} | |
| RELEASE_IMAGE_TAG_SOURCE_URL: ${{ github.server_url }}/${{ github.repository }}.git | |
| run: scripts/ci/promote-release-image-rolling-tags.sh | |
| - name: Write release image publish evidence | |
| if: ${{ always() }} | |
| env: | |
| RELEASE_TAG: ${{ steps.release_publish.outputs.tag || github.event.inputs.tag || github.ref_name }} | |
| DOCKERHUB_IMAGE: ${{ env.DOCKERHUB_IMAGE }} | |
| GHCR_IMAGE: ${{ env.GHCR_IMAGE }} | |
| VALIDATION_OUTCOME: ${{ steps.release_publish.outcome || 'skipped' }} | |
| EXACT_PUBLISH_OUTCOME: ${{ steps.exact.outputs.exact_publish_outcome || steps.exact.outcome || steps.build.outcome || 'skipped' }} | |
| EXACT_PUBLISH_REASON: ${{ steps.exact.outputs.exact_publish_reason }} | |
| EXACT_VERIFY_OUTCOME: ${{ steps.exact.outcome || 'skipped' }} | |
| DOCKER_BUILD_OUTCOME: ${{ steps.build.outcome || 'skipped' }} | |
| BUILD_CACHE_IDENTITY: ${{ steps.cache.outputs.identity }} | |
| BUILD_CACHE_REF: ${{ steps.cache.outputs.ref }} | |
| BUILD_DURATION_SECONDS: ${{ steps.build_timing.outputs.duration_seconds }} | |
| WARM_CACHE_TARGET_SECONDS: 600 | |
| PROTOCOL_CATALOG_CONFORMANCE_OUTCOME: >- | |
| ${{ steps.protocol_catalog.outputs.protocol_catalog_conformance_outcome | |
| || steps.protocol_catalog.outcome || 'skipped' }} | |
| ROLLING_GUARD_OUTCOME: ${{ steps.rolling.outcome || 'skipped' }} | |
| ROLLING_PROMOTE_OUTCOME: ${{ steps.promote_rolling.outcome || 'skipped' }} | |
| ROLLING_ARTIFACT_STATUS: ${{ steps.promote_rolling.outputs.artifact_status || steps.rolling.outputs.artifact_status }} | |
| ROLLING_SHOULD_PROMOTE: >- | |
| ${{ steps.promote_rolling.outputs.rolling_should_promote | |
| || steps.rolling.outputs.rolling_should_promote || 'false' }} | |
| ROLLING_SUPERSEDED_BY: ${{ steps.promote_rolling.outputs.superseded_by || steps.rolling.outputs.superseded_by }} | |
| IMAGE_DIGEST: ${{ steps.exact.outputs.image_digest || steps.build.outputs.digest }} | |
| RELEASE_COMMIT: ${{ steps.release_source.outputs.commit }} | |
| RELEASE_RUN_ID: ${{ github.run_id }} | |
| RELEASE_RUN_ATTEMPT: ${{ github.run_attempt }} | |
| WORKFLOW_PACKAGE_REF: ${{ steps.workflow.outputs.tag }} | |
| WORKFLOW_PACKAGE_COMMIT: ${{ steps.workflow.outputs.commit }} | |
| run: scripts/ci/write-release-image-publish-evidence.sh | |
| - name: Classify live docs release readiness after public images | |
| if: >- | |
| steps.exact.outputs.exact_publish_outcome == 'success' && | |
| steps.protocol_catalog.outputs.protocol_catalog_conformance_outcome == 'success' | |
| env: | |
| DOCS_RELEASE_AUDIT_ARTIFACT: server | |
| DOCS_RELEASE_AUDIT_VERSION: ${{ steps.release_publish.outputs.tag || github.event.inputs.tag || github.ref_name }} | |
| DOCS_RELEASE_AUDIT_EVIDENCE: docs-release-audit-evidence.json | |
| DOCS_RELEASE_AUDIT_HANDOFF: docs-release-audit-handoff.json | |
| run: scripts/ci/check-docs-release-audit.sh | |
| - name: Create the source GitHub Release | |
| if: >- | |
| steps.exact.outputs.exact_publish_outcome == 'success' && | |
| steps.protocol_catalog.outputs.protocol_catalog_conformance_outcome == 'success' | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| RELEASE_TAG: ${{ steps.release_publish.outputs.tag }} | |
| run: | | |
| if ! gh release view "$RELEASE_TAG" >/dev/null 2>&1; then | |
| arguments=(--verify-tag --generate-notes --title "$RELEASE_TAG") | |
| if [[ "$RELEASE_TAG" == *-* ]]; then | |
| arguments+=(--prerelease) | |
| fi | |
| gh release create "$RELEASE_TAG" "${arguments[@]}" | |
| fi | |
| - name: Upload release image publish evidence | |
| if: ${{ always() }} | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 | |
| with: | |
| name: release-image-publish-evidence | |
| path: | | |
| release-image-publish-evidence.json | |
| release-protocol-catalog-conformance.json | |
| release-protocol-catalog-bootstrap.log | |
| release-protocol-catalog-server.log | |
| docs-release-audit-evidence.json | |
| docs-release-audit-handoff.json | |
| if-no-files-found: error |