Build and publish preview version #104
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: Build and publish preview version | |
| on: | |
| workflow_dispatch: | |
| # Cancel previous runs for the same branch when a new one is triggered | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| # Least-privilege root (CIS GHA 1.2). Tag-pushing / deploy jobs grant | |
| # `contents: write` per-job below: `publish-sc-preview` (welder deploy), | |
| # `publish-git-tag` (git push tag), and `finalize` (notify/comment). | |
| permissions: | |
| contents: read | |
| jobs: | |
| prepare: | |
| name: Prepare preview build | |
| runs-on: ubuntu-latest | |
| outputs: | |
| version: ${{ steps.version.outputs.version }} | |
| steps: | |
| # Trust-root ref guard. Preview signing uses cosign keyless identity | |
| # `branch-preview.yaml@refs/heads/<branch>` and the documented | |
| # preview verification regex pins `@refs/heads/.+`. workflow_dispatch | |
| # ALSO allows dispatching from a tag (`refs/tags/<tag>`), which | |
| # would sign artifacts under an identity that no documented | |
| # verifier accepts — fail fast in that case. | |
| - name: Refuse to run on non-branch ref | |
| if: "!startsWith(github.ref, 'refs/heads/')" | |
| run: | | |
| echo "::error title=branch-preview.yaml requires a branch ref::Got github.ref=$GITHUB_REF; this workflow signs preview artifacts under the preview trust root (refs/heads/* only). Re-dispatch from a branch." | |
| exit 1 | |
| - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 | |
| with: | |
| persist-credentials: false | |
| - name: Get next version | |
| uses: reecetech/version-increment@a29aa752dc3b8118a2dc2ed93faf0e95a73a9c7e # 2024.10.1 | |
| id: base-version | |
| with: | |
| scheme: "calver" | |
| increment: "patch" | |
| use_api: "false" | |
| - name: Build preview version string | |
| id: version | |
| run: | | |
| SHORT_SHA=$(git rev-parse --short=7 HEAD) | |
| VERSION="${{ steps.base-version.outputs.version }}-preview.${SHORT_SHA}" | |
| echo "version=${VERSION}" >> $GITHUB_OUTPUT | |
| build-setup: | |
| name: Build Setup (clean, tools, schemas, lint, fmt) | |
| runs-on: blacksmith-8vcpu-ubuntu-2204 | |
| needs: prepare | |
| outputs: | |
| cicd-bot-telegram-token: ${{ steps.telegram-secrets.outputs.cicd-bot-telegram-token }} | |
| cicd-bot-telegram-chat-id: ${{ steps.telegram-secrets.outputs.cicd-bot-telegram-chat-id }} | |
| steps: | |
| - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 | |
| with: | |
| persist-credentials: false | |
| - uses: fregante/setup-git-user@024bc0b8e177d7e77203b48dab6fb45666854b35 # v2.0.2 | |
| - name: Set up Go with Blacksmith caching | |
| uses: useblacksmith/setup-go@647ac649bd5b480f2a262e3e3e5f4d150ed452ad # v6.0.1 | |
| with: | |
| go-version: '1.25' | |
| - name: install sc + welder tools | |
| uses: simple-container-com/actions/setup-sc-tooling@0af5a697f24ea484991660619d0ae42d50343b9d # main | |
| - name: prepare secrets for build | |
| run: | | |
| cat << EOF > ./.sc/cfg.default.yaml | |
| ${{ secrets.SC_CONFIG }} | |
| EOF | |
| cat << EOF > ./.sc/cfg.test.yaml | |
| ${{ secrets.SC_CONFIG }} | |
| EOF | |
| sc secrets reveal | |
| - name: get openai key | |
| id: get-openai-key | |
| run: | | |
| echo "openai-key=$(sc stack secret-get -s dist openai-api-key 2>/dev/null || echo '')" >> $GITHUB_OUTPUT | |
| - name: prepare sc tool (rebuild) | |
| shell: bash | |
| env: | |
| OPENAI_API_KEY: ${{ steps.get-openai-key.outputs.openai-key }} | |
| SKIP_EMBEDDINGS: "true" | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: |- | |
| gh auth setup-git | |
| welder run rebuild | |
| - name: clean | |
| run: | | |
| mkdir -p dist | |
| rm -fR dist/* | |
| mkdir -p .sc/stacks/dist/bundle | |
| rm -fR .sc/stacks/dist/bundle/* | |
| mkdir -p docs/site | |
| rm -fR docs/site/* | |
| mkdir -p docs/schemas | |
| rm -fR docs/schemas/* | |
| - name: tools | |
| run: | | |
| cat tools.go | grep _ | awk -F'"' '{print $2}' | xargs -tI % go get % | |
| go mod download | |
| go generate -tags tools | |
| go mod tidy | |
| - name: generate-schemas | |
| run: | | |
| echo "Generating JSON Schema files for Simple Container resources..." | |
| go build -o bin/schema-gen ./cmd/schema-gen | |
| bin/schema-gen docs/schemas | |
| echo "Successfully generated JSON Schema files in docs/schemas/" | |
| - name: fmt | |
| run: | | |
| go mod tidy | |
| bin/gofumpt -l -w ./ | |
| bin/golangci-lint run --fix --timeout 3m -v | |
| - name: get telegram secrets | |
| id: telegram-secrets | |
| run: | | |
| echo "cicd-bot-telegram-token=$(./bin/sc stack secret-get -s dist cicd-bot-telegram-token)" >> $GITHUB_OUTPUT | |
| echo "cicd-bot-telegram-chat-id=$(./bin/sc stack secret-get -s dist cicd-bot-telegram-chat-id)" >> $GITHUB_OUTPUT | |
| - name: upload bin directory artifacts | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: bin-tools | |
| path: bin | |
| retention-days: 1 | |
| build-platforms: | |
| name: Build sc for ${{ matrix.os }}/${{ matrix.arch }} | |
| runs-on: blacksmith-8vcpu-ubuntu-2204 | |
| needs: [prepare, build-setup] | |
| permissions: | |
| contents: read | |
| id-token: write # OIDC for keyless cosign + attest-build-provenance | |
| attestations: write | |
| strategy: | |
| matrix: | |
| include: | |
| - os: linux | |
| arch: amd64 | |
| - os: darwin | |
| arch: arm64 | |
| - os: darwin | |
| arch: amd64 | |
| steps: | |
| - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 | |
| with: | |
| persist-credentials: false | |
| - name: Set up Go with Blacksmith caching | |
| uses: useblacksmith/setup-go@647ac649bd5b480f2a262e3e3e5f4d150ed452ad # v6.0.1 | |
| with: | |
| go-version: '1.25' | |
| - name: create build directories | |
| run: | | |
| mkdir -p dist | |
| mkdir -p .sc/stacks/dist/bundle | |
| - name: build sc for ${{ matrix.os }}/${{ matrix.arch }} | |
| env: | |
| GOOS: ${{ matrix.os }} | |
| GOARCH: ${{ matrix.arch }} | |
| CGO_ENABLED: "0" | |
| VERSION: ${{ needs.prepare.outputs.version }} | |
| run: | | |
| echo "Building for ${GOOS}/${GOARCH}..." | |
| if [ "${GOOS}" = "windows" ]; then export EXT=".exe"; else export EXT=""; fi | |
| go build -ldflags "-s -w -X=github.com/simple-container-com/api/internal/build.Version=${VERSION}" -o dist/${GOOS}-${GOARCH}/sc${EXT} ./cmd/sc | |
| tar -czf .sc/stacks/dist/bundle/sc-${GOOS}-${GOARCH}.tar.gz -C dist/${GOOS}-${GOARCH} sc${EXT} | |
| cp .sc/stacks/dist/bundle/sc-${GOOS}-${GOARCH}.tar.gz .sc/stacks/dist/bundle/sc-${GOOS}-${GOARCH}-v${VERSION}.tar.gz | |
| # Phase 2 attestation (mirrors push.yaml). Soft-fail per step — the | |
| # tarball itself is the publish artifact. Preview builds only publish | |
| # versioned tarballs (no unversioned twin), so sidecars are per-version. | |
| - name: SHA-256 sidecar for ${{ matrix.os }}/${{ matrix.arch }} | |
| env: | |
| GOOS: ${{ matrix.os }} | |
| GOARCH: ${{ matrix.arch }} | |
| VERSION: ${{ needs.prepare.outputs.version }} | |
| run: | | |
| set -euo pipefail | |
| cd .sc/stacks/dist/bundle | |
| sha256sum "sc-${GOOS}-${GOARCH}-v${VERSION}.tar.gz" > "sc-${GOOS}-${GOARCH}-v${VERSION}.tar.gz.sha256" | |
| - name: Install attestation tools | |
| id: install_attest_tools | |
| continue-on-error: true | |
| uses: simple-container-com/actions/install-attest-tools@0af5a697f24ea484991660619d0ae42d50343b9d # main | |
| - name: Generate CycloneDX SBOM for sc-${{ matrix.os }}-${{ matrix.arch }} | |
| id: sbom_tarball | |
| continue-on-error: true | |
| env: | |
| GOOS: ${{ matrix.os }} | |
| GOARCH: ${{ matrix.arch }} | |
| VERSION: ${{ needs.prepare.outputs.version }} | |
| run: | | |
| set -euo pipefail | |
| cd .sc/stacks/dist/bundle | |
| syft scan "dir:../../../../dist/${GOOS}-${GOARCH}" \ | |
| -o "cyclonedx-json=sc-${GOOS}-${GOARCH}-v${VERSION}.tar.gz.sbom.cdx.json" | |
| - name: Cosign sign-blob (keyless, bundle) for sc-${{ matrix.os }}-${{ matrix.arch }} | |
| id: cosign_sign_tarball | |
| continue-on-error: true | |
| env: | |
| COSIGN_EXPERIMENTAL: "1" | |
| GOOS: ${{ matrix.os }} | |
| GOARCH: ${{ matrix.arch }} | |
| VERSION: ${{ needs.prepare.outputs.version }} | |
| run: | | |
| set -euo pipefail | |
| cd .sc/stacks/dist/bundle | |
| tarball="sc-${GOOS}-${GOARCH}-v${VERSION}.tar.gz" | |
| cosign sign-blob --yes --bundle "${tarball}.cosign-bundle" "${tarball}" | |
| - name: SLSA build provenance for sc-${{ matrix.os }}-${{ matrix.arch }} | |
| id: slsa_tarball | |
| continue-on-error: true | |
| uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0 | |
| with: | |
| subject-path: .sc/stacks/dist/bundle/sc-${{ matrix.os }}-${{ matrix.arch }}-v${{ needs.prepare.outputs.version }}.tar.gz | |
| - name: Materialize SLSA provenance bundle next to tarball | |
| if: steps.slsa_tarball.outcome == 'success' | |
| continue-on-error: true | |
| env: | |
| BUNDLE_PATH: ${{ steps.slsa_tarball.outputs.bundle-path }} | |
| GOOS: ${{ matrix.os }} | |
| GOARCH: ${{ matrix.arch }} | |
| VERSION: ${{ needs.prepare.outputs.version }} | |
| run: | | |
| cp "$BUNDLE_PATH" ".sc/stacks/dist/bundle/sc-${GOOS}-${GOARCH}-v${VERSION}.tar.gz.sigstore.json" | |
| - name: upload build artifacts | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: sc-${{ matrix.os }}-${{ matrix.arch }} | |
| path: | | |
| .sc/stacks/dist/bundle/sc-${{ matrix.os }}-${{ matrix.arch }}-v*.tar.gz | |
| .sc/stacks/dist/bundle/sc-${{ matrix.os }}-${{ matrix.arch }}-v*.tar.gz.sha256 | |
| .sc/stacks/dist/bundle/sc-${{ matrix.os }}-${{ matrix.arch }}-v*.tar.gz.cosign-bundle | |
| .sc/stacks/dist/bundle/sc-${{ matrix.os }}-${{ matrix.arch }}-v*.tar.gz.sigstore.json | |
| .sc/stacks/dist/bundle/sc-${{ matrix.os }}-${{ matrix.arch }}-v*.tar.gz.sbom.cdx.json | |
| retention-days: 1 | |
| build-binaries: | |
| name: Build ${{ matrix.target }} binary | |
| runs-on: blacksmith-8vcpu-ubuntu-2204 | |
| needs: [prepare, build-setup] | |
| strategy: | |
| matrix: | |
| include: | |
| - target: github-actions | |
| cmd: github-actions | |
| output: dist/github-actions | |
| - target: cloud-helpers | |
| cmd: cloud-helpers | |
| output: dist/cloud-helpers | |
| steps: | |
| - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 | |
| with: | |
| persist-credentials: false | |
| - name: Set up Go with Blacksmith caching | |
| uses: useblacksmith/setup-go@647ac649bd5b480f2a262e3e3e5f4d150ed452ad # v6.0.1 | |
| with: | |
| go-version: '1.25' | |
| - name: create build directories | |
| run: | | |
| mkdir -p dist | |
| - name: build ${{ matrix.target }} | |
| env: | |
| VERSION: ${{ needs.prepare.outputs.version }} | |
| CGO_ENABLED: "0" | |
| run: | | |
| go build -a -installsuffix cgo -ldflags "-s -w -X=github.com/simple-container-com/api/internal/build.Version=${VERSION}" -o ${{ matrix.output }} ./cmd/${{ matrix.cmd }} | |
| - name: upload ${{ matrix.target }} binary | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: ${{ matrix.target }}-binary | |
| path: ${{ matrix.output }} | |
| retention-days: 1 | |
| test: | |
| name: Run tests | |
| runs-on: blacksmith-8vcpu-ubuntu-2204 | |
| needs: [prepare, build-setup] | |
| steps: | |
| - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 | |
| with: | |
| persist-credentials: false | |
| - name: Set up Go with Blacksmith caching | |
| uses: useblacksmith/setup-go@647ac649bd5b480f2a262e3e3e5f4d150ed452ad # v6.0.1 | |
| with: | |
| go-version: '1.25' | |
| - name: test | |
| run: | | |
| go test ./... | |
| docker-build: | |
| name: Docker build and push preview ${{ matrix.target }} image | |
| runs-on: blacksmith-8vcpu-ubuntu-2204 | |
| needs: [prepare, build-setup, build-binaries, test] | |
| permissions: | |
| contents: read | |
| id-token: write # OIDC token for keyless cosign + attest-build-provenance | |
| attestations: write | |
| strategy: | |
| matrix: | |
| include: | |
| - target: github-actions | |
| binary: github-actions | |
| dockerfile: github-actions.Dockerfile | |
| image_repo: simplecontainer/github-actions | |
| tag_prefix: "simplecontainer/github-actions:" | |
| - target: cloud-helpers | |
| binary: cloud-helpers | |
| dockerfile: cloud-helpers.aws.Dockerfile | |
| image_repo: simplecontainer/cloud-helpers | |
| tag_prefix: "simplecontainer/cloud-helpers:aws-" | |
| steps: | |
| - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 | |
| with: | |
| persist-credentials: false | |
| - name: download ${{ matrix.target }} binary | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| name: ${{ matrix.target }}-binary | |
| path: dist | |
| - name: fix binary permissions | |
| run: chmod +x dist/${{ matrix.binary }} | |
| - name: install sc tool | |
| uses: simple-container-com/actions/install-sc@0af5a697f24ea484991660619d0ae42d50343b9d # main | |
| - name: prepare secrets for build | |
| run: | | |
| cat << EOF > ./.sc/cfg.default.yaml | |
| ${{ secrets.SC_CONFIG }} | |
| EOF | |
| sc secrets reveal | |
| - name: Setup Docker Buildx with advanced caching | |
| uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0 | |
| with: | |
| driver-opts: | | |
| image=moby/buildkit:buildx-stable-1 | |
| - name: Disable IPv6 (Blacksmith runners have broken IPv6 to Docker Hub) | |
| run: sudo sysctl -w net.ipv6.conf.all.disable_ipv6=1 | |
| - name: Docker login using SC secrets | |
| run: | | |
| sc stack secret-get -s dist dockerhub-cicd-token | docker login --username simplecontainer --password-stdin | |
| - name: Build and push preview ${{ matrix.target }} image | |
| id: build_and_push | |
| uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0 | |
| with: | |
| context: . | |
| file: ${{ matrix.dockerfile }} | |
| platforms: linux/amd64 | |
| tags: ${{ matrix.tag_prefix }}${{ needs.prepare.outputs.version }} | |
| push: true | |
| cache-from: type=gha | |
| cache-to: type=gha,mode=max | |
| provenance: false | |
| # Phase 2 attestation (mirrors push.yaml). Preview builds get the SAME | |
| # security guarantees as production releases so consumers pin-testing a | |
| # preview version (per project_sc_preview memory: PAY-SPACE pins workflows | |
| # to versioned preview builds) can verify the same way they verify prod. | |
| - name: Sign + attest ${{ matrix.target }} (SBOM + cosign + SLSA) | |
| id: signattest | |
| if: steps.build_and_push.outcome == 'success' | |
| uses: simple-container-com/actions/sign-and-attest@0af5a697f24ea484991660619d0ae42d50343b9d # main | |
| with: | |
| image-ref: ${{ matrix.image_repo }}@${{ steps.build_and_push.outputs.digest }} | |
| image-name: ${{ matrix.target }} | |
| subject-name: index.docker.io/${{ matrix.image_repo }} | |
| subject-digest: ${{ steps.build_and_push.outputs.digest }} | |
| # Phase 2 bake-in soft-fail. Mirrors push.yaml. | |
| soft-fail: 'true' | |
| - name: Record image digest for verify-attestations | |
| if: steps.build_and_push.outcome == 'success' | |
| env: | |
| DIGEST: ${{ steps.build_and_push.outputs.digest }} | |
| IMAGE_REPO: ${{ matrix.image_repo }} | |
| run: | | |
| set -euo pipefail | |
| mkdir -p digests | |
| printf '%s@%s\n' "$IMAGE_REPO" "$DIGEST" > "digests/${{ matrix.target }}.txt" | |
| - name: Upload digest artifact for ${{ matrix.target }} | |
| if: steps.build_and_push.outcome == 'success' | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: image-digest-${{ matrix.target }} | |
| path: digests/${{ matrix.target }}.txt | |
| retention-days: 1 | |
| - name: Soft-fail aggregator for ${{ matrix.target }} attestation | |
| if: always() && steps.build_and_push.outcome == 'success' | |
| env: | |
| SIGN_OUTCOME: ${{ steps.signattest.outputs.sign-outcome }} | |
| SBOM_OUTCOME: ${{ steps.signattest.outputs.sbom-outcome }} | |
| ATTEST_SBOM_OUTCOME: ${{ steps.signattest.outputs.attest-outcome }} | |
| SLSA_OUTCOME: ${{ steps.signattest.outputs.slsa-outcome }} | |
| run: | | |
| fail=0 | |
| for v in "$SIGN_OUTCOME" "$SBOM_OUTCOME" "$ATTEST_SBOM_OUTCOME" "$SLSA_OUTCOME"; do | |
| if [ "$v" != "success" ]; then fail=1; fi | |
| done | |
| if [ "$fail" -eq 1 ]; then | |
| echo "::warning title=Attestation incomplete::${{ matrix.target }} preview published but attestation steps failed (sign=$SIGN_OUTCOME sbom=$SBOM_OUTCOME attest-sbom=$ATTEST_SBOM_OUTCOME slsa=$SLSA_OUTCOME)." | |
| else | |
| echo "All attestation steps succeeded for ${{ matrix.target }} preview." | |
| fi | |
| publish-sc-preview: | |
| name: Publish preview SC binaries to dist | |
| runs-on: blacksmith-8vcpu-ubuntu-2204 | |
| # Does not need docker-build — SC binary publishing is independent of the Docker image. | |
| # Runs in parallel with publish-git-tag. | |
| needs: [prepare, build-setup, build-platforms, test] | |
| permissions: | |
| contents: write # welder deploy reads release artifacts + updates dist | |
| steps: | |
| - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 | |
| with: | |
| persist-credentials: false | |
| - name: download all sc platform artifacts | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| path: artifacts | |
| pattern: sc-* | |
| - name: download bin tools artifact | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| name: bin-tools | |
| path: bin | |
| - name: fix bin tools permissions | |
| run: chmod +x bin/* | |
| - name: install sc tool | |
| uses: simple-container-com/actions/install-sc@0af5a697f24ea484991660619d0ae42d50343b9d # main | |
| - name: prepare secrets for build | |
| run: | | |
| cat << EOF > ./.sc/cfg.default.yaml | |
| ${{ secrets.SC_CONFIG }} | |
| EOF | |
| cat << EOF > ./.sc/cfg.test.yaml | |
| ${{ secrets.SC_CONFIG }} | |
| EOF | |
| sc secrets reveal | |
| - name: assemble preview dist bundle (versioned tarballs only) | |
| env: | |
| VERSION: ${{ needs.prepare.outputs.version }} | |
| run: | | |
| set -euo pipefail | |
| mkdir -p .sc/stacks/dist/bundle | |
| rm -fR .sc/stacks/dist/bundle/* | |
| # Copy only versioned tarballs + their sidecars (.sha256, | |
| # .cosign-bundle, .sigstore.json, .sbom.cdx.json) — do NOT add sc.sh | |
| # or the version file. Preserves the unversioned-latest pointer for | |
| # users running sc.sh without a version pin. | |
| shopt -s nullglob | |
| for f in artifacts/sc-*/*-v${VERSION}.tar.gz \ | |
| artifacts/sc-*/*-v${VERSION}.tar.gz.sha256 \ | |
| artifacts/sc-*/*-v${VERSION}.tar.gz.cosign-bundle \ | |
| artifacts/sc-*/*-v${VERSION}.tar.gz.sigstore.json \ | |
| artifacts/sc-*/*-v${VERSION}.tar.gz.sbom.cdx.json; do | |
| cp "$f" .sc/stacks/dist/bundle/ | |
| done | |
| echo "Bundle contents:" | |
| ls -la .sc/stacks/dist/bundle/ | |
| - name: publish preview sc binaries | |
| env: | |
| VERSION: ${{ needs.prepare.outputs.version }} | |
| run: | | |
| # Deploy only the dist stack — welder deploy -e prod would also deploy | |
| # the docs stack which requires docs/site/ that we don't build in preview. | |
| ./bin/sc deploy -s dist -e prod --skip-refresh | |
| publish-git-tag: | |
| name: Create release commit and push git tag | |
| runs-on: blacksmith-8vcpu-ubuntu-2204 | |
| # Only needs docker-build — the tag must point to a commit referencing a published Docker image. | |
| # Does not need build-platforms or publish-sc-preview. Runs in parallel with publish-sc-preview. | |
| needs: [prepare, docker-build] | |
| permissions: | |
| contents: write # commits the release branch + pushes the preview tag | |
| # GH_TOKEN must be visible to every step that runs git (checkout, commit, | |
| # push) because `gh auth setup-git` installs `gh auth git-credential` as | |
| # the credential helper — and that helper reads $GH_TOKEN from the | |
| # invoking step's env when git pushes. Setting it once at job level | |
| # avoids the "fatal: could not read Username" failure from per-step env. | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| steps: | |
| - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 | |
| with: | |
| persist-credentials: false | |
| fetch-depth: 0 | |
| - uses: fregante/setup-git-user@024bc0b8e177d7e77203b48dab6fb45666854b35 # v2.0.2 | |
| - name: configure git credential helper | |
| run: gh auth setup-git | |
| - name: create release branch and update action.yml image tags | |
| env: | |
| VERSION: ${{ needs.prepare.outputs.version }} | |
| run: | | |
| RELEASE_BRANCH="release/${VERSION}" | |
| git checkout -b "${RELEASE_BRANCH}" | |
| # Rewrite whatever tag the action.yml files currently pin (latest, | |
| # staging, …) to the preview version. Matching a fixed base tag is | |
| # brittle: #284 switched :staging -> :latest and silently broke every | |
| # preview build, because this sed still looked for :staging — the | |
| # rewrite became a no-op, leaving "nothing to commit" downstream. | |
| find .github/actions -name "action.yml" | while read f; do | |
| sed -i -E "s|(docker://simplecontainer/github-actions):[A-Za-z0-9._-]+|\1:${VERSION}|g" "$f" | |
| done | |
| echo "Updated action.yml files:" | |
| grep -r "docker://simplecontainer/github-actions:" .github/actions/ | |
| # Fail loudly if the rewrite matched nothing (base-tag drift again), | |
| # instead of producing a confusing empty-commit error in the next step. | |
| if ! grep -rq "docker://simplecontainer/github-actions:${VERSION}" .github/actions/; then | |
| echo "::error::No action.yml image tag was rewritten to ${VERSION} — the base image tag pattern drifted. Update the sed above." | |
| exit 1 | |
| fi | |
| - name: commit, tag and push | |
| env: | |
| VERSION: ${{ needs.prepare.outputs.version }} | |
| run: | | |
| RELEASE_BRANCH="release/${VERSION}" | |
| git add .github/actions/*/action.yml | |
| git commit -m "chore: release preview v${VERSION} - update github-actions image tag" | |
| git tag "v${VERSION}" | |
| git push origin "${RELEASE_BRANCH}" | |
| git push origin "v${VERSION}" | |
| finalize: | |
| name: Finalize preview build | |
| runs-on: ubuntu-latest | |
| if: ${{ always() }} | |
| permissions: | |
| contents: write | |
| needs: | |
| - prepare | |
| - build-setup | |
| - publish-sc-preview | |
| - publish-git-tag | |
| - docker-build | |
| steps: | |
| - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 | |
| if: ${{ always() }} | |
| with: | |
| persist-credentials: false | |
| - name: Extract git reference | |
| id: extract_git_ref | |
| if: ${{ always() }} | |
| shell: bash | |
| env: | |
| COMMIT_MESSAGE: ${{ github.event.head_commit.message || github.event.workflow_run.head_commit.message }} | |
| run: |- | |
| message="$(printf '%s' "$COMMIT_MESSAGE" | tr -d '\n')" | |
| if [ ${#message} -gt 200 ]; then | |
| truncated_message="${message:0:80}...${message: -80}" | |
| message="$truncated_message" | |
| fi | |
| echo "branch=$GITHUB_REF_NAME" >> $GITHUB_OUTPUT | |
| echo "message=$message" >> $GITHUB_OUTPUT | |
| echo "author=$GITHUB_ACTOR" >> $GITHUB_OUTPUT | |
| echo "url=$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID" >> $GITHUB_OUTPUT | |
| - name: Write build summary | |
| if: ${{ !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') }} | |
| env: | |
| VERSION: ${{ needs.prepare.outputs.version }} | |
| BRANCH: ${{ github.ref_name }} | |
| # GHA evaluates ${{ '${{' }} to the literal string ${{ so SC_CONFIG_EXPR becomes | |
| # the printable text "${{ secrets.SC_CONFIG }}" for use in markdown examples. | |
| SC_CONFIG_EXPR: "${{ '${{' }} secrets.SC_CONFIG }}" | |
| run: | | |
| cat >> "$GITHUB_STEP_SUMMARY" << ENDSUMMARY | |
| ## Preview build \`v${VERSION}\` | |
| ### Use SC GitHub Actions at this version | |
| Reference any SC action with the \`@v${VERSION}\` tag: | |
| \`\`\`yaml | |
| - uses: simple-container-com/api/.github/actions/deploy-client-stack@v${VERSION} | |
| with: | |
| stack-name: my-stack | |
| sc-config: ${SC_CONFIG_EXPR} | |
| - uses: simple-container-com/api/.github/actions/provision-parent-stack@v${VERSION} | |
| with: | |
| stack-name: my-stack | |
| sc-config: ${SC_CONFIG_EXPR} | |
| - uses: simple-container-com/api/.github/actions/destroy@v${VERSION} | |
| with: | |
| stack-name: my-stack | |
| sc-config: ${SC_CONFIG_EXPR} | |
| \`\`\` | |
| Docker image: \`simplecontainer/github-actions:${VERSION}\` | |
| ### Install this SC version in your CI | |
| \`\`\`yaml | |
| - uses: simple-container-com/actions/install-sc@0af5a697f24ea484991660619d0ae42d50343b9d # main | |
| with: | |
| version: ${VERSION} | |
| \`\`\` | |
| > Preview build from branch \`${BRANCH}\`. The \`install-sc\` action pins the binary to the exact preview version above and verifies it on download. | |
| ENDSUMMARY | |
| - name: Notify Telegram (success) | |
| if: ${{ success() && !contains(needs.*.result, 'failure') }} | |
| continue-on-error: true | |
| uses: simple-container-com/actions/notify-telegram@0af5a697f24ea484991660619d0ae42d50343b9d # main | |
| with: | |
| chat-id: ${{ needs.build-setup.outputs.cicd-bot-telegram-chat-id }} | |
| token: ${{ needs.build-setup.outputs.cicd-bot-telegram-token }} | |
| text: '✅ Preview published: ' | |
| link-url: ${{ format('{0}/{1}/actions/runs/{2}', github.server_url, github.repository, github.run_id) }} | |
| link-text: 'v${{ needs.prepare.outputs.version }}' | |
| suffix: ' on ${{ github.head_ref || github.ref_name }} by ${{ steps.extract_git_ref.outputs.author }}' | |
| - name: Notify Telegram (failure) | |
| if: ${{ failure() || contains(needs.*.result, 'failure') }} | |
| continue-on-error: true | |
| uses: simple-container-com/actions/notify-telegram@0af5a697f24ea484991660619d0ae42d50343b9d # main | |
| with: | |
| chat-id: ${{ needs.build-setup.outputs.cicd-bot-telegram-chat-id }} | |
| token: ${{ needs.build-setup.outputs.cicd-bot-telegram-token }} | |
| text: '❗ Preview failed: ' | |
| link-url: ${{ format('{0}/{1}/actions/runs/{2}', github.server_url, github.repository, github.run_id) }} | |
| link-text: 'v${{ needs.prepare.outputs.version }}' | |
| suffix: ' on ${{ github.head_ref || github.ref_name }} by ${{ steps.extract_git_ref.outputs.author }}' | |
| - name: Build failed due to previously failed steps | |
| id: fail_if_needed | |
| if: ${{ failure() || contains(needs.*.result, 'failure') }} | |
| shell: bash | |
| run: |- | |
| exit 1 |