From e0c5e8c71b1dc769b0ebb98ffa370a626a469c6e Mon Sep 17 00:00:00 2001 From: Jon Ayers Date: Tue, 12 Aug 2025 19:53:40 +0000 Subject: [PATCH 1/3] chore: restrict who can make releases --- .github/workflows/release.yaml | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index c132fb1de9957..06041e1865d3a 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -32,9 +32,36 @@ env: CODER_RELEASE_NOTES: ${{ inputs.release_notes }} jobs: + # Only allow maintainers/admins to release. + check-perms: + runs-on: ${{ github.repository_owner == 'coder' && 'depot-ubuntu-22.04-8' || 'ubuntu-latest' }} + steps: + - name: Allow only maintainers/admins + uses: actions/github-script@v7.0.1 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const {data} = await github.rest.repos.getCollaboratorPermissionLevel({ + owner: context.repo.owner, + repo: context.repo.repo, + username: context.actor + }); + const role = data.role_name || data.user?.role_name || data.permission; + const perms = data.user?.permissions || {}; + core.info(`Actor ${context.actor} permission=${data.permission}, role_name=${role}`); + + const allowed = + role === 'admin' || + role === 'maintain' || + perms.admin === true || + perms.maintain === true; + + if (!allowed) core.setFailed('Denied: requires maintain or admin'); + # build-dylib is a separate job to build the dylib on macOS. build-dylib: runs-on: ${{ github.repository_owner == 'coder' && 'depot-macos-latest' || 'macos-latest' }} + needs: check-perms steps: # Harden Runner doesn't work on macOS. - name: Checkout @@ -114,7 +141,7 @@ jobs: release: name: Build and publish - needs: build-dylib + needs: [build-dylib, check-perms] runs-on: ${{ github.repository_owner == 'coder' && 'depot-ubuntu-22.04-8' || 'ubuntu-latest' }} permissions: # Required to publish a release From 1add8e779aa18a5a6a75f06d532e838bb4ed437b Mon Sep 17 00:00:00 2001 From: Jon Ayers Date: Tue, 12 Aug 2025 19:55:02 +0000 Subject: [PATCH 2/3] test release --- .github/workflows/release.yaml | 895 +-------------------------------- 1 file changed, 2 insertions(+), 893 deletions(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 06041e1865d3a..dd57fb513a89c 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -63,82 +63,8 @@ jobs: runs-on: ${{ github.repository_owner == 'coder' && 'depot-macos-latest' || 'macos-latest' }} needs: check-perms steps: - # Harden Runner doesn't work on macOS. - - name: Checkout - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 - with: - fetch-depth: 0 - - # If the event that triggered the build was an annotated tag (which our - # tags are supposed to be), actions/checkout has a bug where the tag in - # question is only a lightweight tag and not a full annotated tag. This - # command seems to fix it. - # https://github.com/actions/checkout/issues/290 - name: Fetch git tags - run: git fetch --tags --force - - - name: Setup build tools - run: | - brew install bash gnu-getopt make - echo "$(brew --prefix bash)/bin" >> $GITHUB_PATH - echo "$(brew --prefix gnu-getopt)/bin" >> $GITHUB_PATH - echo "$(brew --prefix make)/libexec/gnubin" >> $GITHUB_PATH - - - name: Switch XCode Version - uses: maxim-lobanov/setup-xcode@60606e260d2fc5762a71e64e74b2174e8ea3c8bd # v1.6.0 - with: - xcode-version: "16.1.0" - - - name: Setup Go - uses: ./.github/actions/setup-go - - - name: Install rcodesign - run: | - set -euo pipefail - wget -O /tmp/rcodesign.tar.gz https://github.com/indygreg/apple-platform-rs/releases/download/apple-codesign%2F0.22.0/apple-codesign-0.22.0-macos-universal.tar.gz - sudo tar -xzf /tmp/rcodesign.tar.gz \ - -C /usr/local/bin \ - --strip-components=1 \ - apple-codesign-0.22.0-macos-universal/rcodesign - rm /tmp/rcodesign.tar.gz - - - name: Setup Apple Developer certificate and API key - run: | - set -euo pipefail - touch /tmp/{apple_cert.p12,apple_cert_password.txt,apple_apikey.p8} - chmod 600 /tmp/{apple_cert.p12,apple_cert_password.txt,apple_apikey.p8} - echo "$AC_CERTIFICATE_P12_BASE64" | base64 -d > /tmp/apple_cert.p12 - echo "$AC_CERTIFICATE_PASSWORD" > /tmp/apple_cert_password.txt - echo "$AC_APIKEY_P8_BASE64" | base64 -d > /tmp/apple_apikey.p8 - env: - AC_CERTIFICATE_P12_BASE64: ${{ secrets.AC_CERTIFICATE_P12_BASE64 }} - AC_CERTIFICATE_PASSWORD: ${{ secrets.AC_CERTIFICATE_PASSWORD }} - AC_APIKEY_P8_BASE64: ${{ secrets.AC_APIKEY_P8_BASE64 }} - - - name: Build dylibs - run: | - set -euxo pipefail - go mod download - - make gen/mark-fresh - make build/coder-dylib - env: - CODER_SIGN_DARWIN: 1 - AC_CERTIFICATE_FILE: /tmp/apple_cert.p12 - AC_CERTIFICATE_PASSWORD_FILE: /tmp/apple_cert_password.txt - - - name: Upload build artifacts - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 - with: - name: dylibs - path: | - ./build/*.h - ./build/*.dylib - retention-days: 7 - - - name: Delete Apple Developer certificate and API key - run: rm -f /tmp/{apple_cert.p12,apple_cert_password.txt,apple_apikey.p8} - + run: echo "Success" release: name: Build and publish needs: [build-dylib, check-perms] @@ -160,822 +86,5 @@ jobs: outputs: version: ${{ steps.version.outputs.version }} steps: - - name: Harden Runner - uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0 - with: - egress-policy: audit - - - name: Checkout - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 - with: - fetch-depth: 0 - - # If the event that triggered the build was an annotated tag (which our - # tags are supposed to be), actions/checkout has a bug where the tag in - # question is only a lightweight tag and not a full annotated tag. This - # command seems to fix it. - # https://github.com/actions/checkout/issues/290 - - name: Fetch git tags - run: git fetch --tags --force - - - name: Print version - id: version - run: | - set -euo pipefail - version="$(./scripts/version.sh)" - echo "version=$version" >> $GITHUB_OUTPUT - # Speed up future version.sh calls. - echo "CODER_FORCE_VERSION=$version" >> $GITHUB_ENV - echo "$version" - - # Verify that all expectations for a release are met. - - name: Verify release input - if: ${{ !inputs.dry_run }} - run: | - set -euo pipefail - - if [[ "${GITHUB_REF}" != "refs/tags/v"* ]]; then - echo "Ref must be a semver tag when creating a release, did you use scripts/release.sh?" - exit 1 - fi - - # 2.10.2 -> release/2.10 - version="$(./scripts/version.sh)" - release_branch=release/${version%.*} - branch_contains_tag=$(git branch --remotes --contains "${GITHUB_REF}" --list "*/${release_branch}" --format='%(refname)') - if [[ -z "${branch_contains_tag}" ]]; then - echo "Ref tag must exist in a branch named ${release_branch} when creating a release, did you use scripts/release.sh?" - exit 1 - fi - - if [[ -z "${CODER_RELEASE_NOTES}" ]]; then - echo "Release notes are required to create a release, did you use scripts/release.sh?" - exit 1 - fi - - echo "Release inputs verified:" - echo - echo "- Ref: ${GITHUB_REF}" - echo "- Version: ${version}" - echo "- Release channel: ${CODER_RELEASE_CHANNEL}" - echo "- Release branch: ${release_branch}" - echo "- Release notes: true" - - - name: Create release notes file - run: | - set -euo pipefail - - release_notes_file="$(mktemp -t release_notes.XXXXXX)" - echo "$CODER_RELEASE_NOTES" > "$release_notes_file" - echo CODER_RELEASE_NOTES_FILE="$release_notes_file" >> $GITHUB_ENV - - - name: Show release notes - run: | - set -euo pipefail - cat "$CODER_RELEASE_NOTES_FILE" - - - name: Docker Login - uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Setup Go - uses: ./.github/actions/setup-go - - - name: Setup Node - uses: ./.github/actions/setup-node - - # Necessary for signing Windows binaries. - - name: Setup Java - uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 - with: - distribution: "zulu" - java-version: "11.0" - - - name: Install go-winres - run: go install github.com/tc-hib/go-winres@d743268d7ea168077ddd443c4240562d4f5e8c3e # v0.3.3 - - - name: Install nsis and zstd - run: sudo apt-get install -y nsis zstd - - - name: Install nfpm - run: | - set -euo pipefail - wget -O /tmp/nfpm.deb https://github.com/goreleaser/nfpm/releases/download/v2.35.1/nfpm_2.35.1_amd64.deb - sudo dpkg -i /tmp/nfpm.deb - rm /tmp/nfpm.deb - - - name: Install rcodesign - run: | - set -euo pipefail - wget -O /tmp/rcodesign.tar.gz https://github.com/indygreg/apple-platform-rs/releases/download/apple-codesign%2F0.22.0/apple-codesign-0.22.0-x86_64-unknown-linux-musl.tar.gz - sudo tar -xzf /tmp/rcodesign.tar.gz \ - -C /usr/bin \ - --strip-components=1 \ - apple-codesign-0.22.0-x86_64-unknown-linux-musl/rcodesign - rm /tmp/rcodesign.tar.gz - - - name: Install cosign - uses: ./.github/actions/install-cosign - - - name: Install syft - uses: ./.github/actions/install-syft - - - name: Setup Apple Developer certificate and API key - run: | - set -euo pipefail - touch /tmp/{apple_cert.p12,apple_cert_password.txt,apple_apikey.p8} - chmod 600 /tmp/{apple_cert.p12,apple_cert_password.txt,apple_apikey.p8} - echo "$AC_CERTIFICATE_P12_BASE64" | base64 -d > /tmp/apple_cert.p12 - echo "$AC_CERTIFICATE_PASSWORD" > /tmp/apple_cert_password.txt - echo "$AC_APIKEY_P8_BASE64" | base64 -d > /tmp/apple_apikey.p8 - env: - AC_CERTIFICATE_P12_BASE64: ${{ secrets.AC_CERTIFICATE_P12_BASE64 }} - AC_CERTIFICATE_PASSWORD: ${{ secrets.AC_CERTIFICATE_PASSWORD }} - AC_APIKEY_P8_BASE64: ${{ secrets.AC_APIKEY_P8_BASE64 }} - - - name: Setup Windows EV Signing Certificate - run: | - set -euo pipefail - touch /tmp/ev_cert.pem - chmod 600 /tmp/ev_cert.pem - echo "$EV_SIGNING_CERT" > /tmp/ev_cert.pem - wget https://github.com/ebourg/jsign/releases/download/6.0/jsign-6.0.jar -O /tmp/jsign-6.0.jar - env: - EV_SIGNING_CERT: ${{ secrets.EV_SIGNING_CERT }} - - - name: Test migrations from current ref to main - run: | - POSTGRES_VERSION=13 make test-migrations - - # Setup GCloud for signing Windows binaries. - - name: Authenticate to Google Cloud - id: gcloud_auth - uses: google-github-actions/auth@b7593ed2efd1c1617e1b0254da33b86225adb2a5 # v2.1.12 - with: - workload_identity_provider: ${{ vars.GCP_CODE_SIGNING_WORKLOAD_ID_PROVIDER }} - service_account: ${{ vars.GCP_CODE_SIGNING_SERVICE_ACCOUNT }} - token_format: "access_token" - - - name: Setup GCloud SDK - uses: google-github-actions/setup-gcloud@cb1e50a9932213ecece00a606661ae9ca44f3397 # v2.2.0 - - - name: Download dylibs - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 - with: - name: dylibs - path: ./build - - - name: Insert dylibs - run: | - mv ./build/*amd64.dylib ./site/out/bin/coder-vpn-darwin-amd64.dylib - mv ./build/*arm64.dylib ./site/out/bin/coder-vpn-darwin-arm64.dylib - mv ./build/*arm64.h ./site/out/bin/coder-vpn-darwin-dylib.h - - - name: Build binaries - run: | - set -euo pipefail - go mod download - - version="$(./scripts/version.sh)" - make gen/mark-fresh - make -j \ - build/coder_"$version"_linux_{amd64,armv7,arm64}.{tar.gz,apk,deb,rpm} \ - build/coder_"$version"_{darwin,windows}_{amd64,arm64}.zip \ - build/coder_"$version"_windows_amd64_installer.exe \ - build/coder_helm_"$version".tgz \ - build/provisioner_helm_"$version".tgz - env: - CODER_SIGN_WINDOWS: "1" - CODER_SIGN_DARWIN: "1" - CODER_SIGN_GPG: "1" - CODER_GPG_RELEASE_KEY_BASE64: ${{ secrets.GPG_RELEASE_KEY_BASE64 }} - CODER_WINDOWS_RESOURCES: "1" - AC_CERTIFICATE_FILE: /tmp/apple_cert.p12 - AC_CERTIFICATE_PASSWORD_FILE: /tmp/apple_cert_password.txt - AC_APIKEY_ISSUER_ID: ${{ secrets.AC_APIKEY_ISSUER_ID }} - AC_APIKEY_ID: ${{ secrets.AC_APIKEY_ID }} - AC_APIKEY_FILE: /tmp/apple_apikey.p8 - EV_KEY: ${{ secrets.EV_KEY }} - EV_KEYSTORE: ${{ secrets.EV_KEYSTORE }} - EV_TSA_URL: ${{ secrets.EV_TSA_URL }} - EV_CERTIFICATE_PATH: /tmp/ev_cert.pem - GCLOUD_ACCESS_TOKEN: ${{ steps.gcloud_auth.outputs.access_token }} - JSIGN_PATH: /tmp/jsign-6.0.jar - - - name: Delete Apple Developer certificate and API key - run: rm -f /tmp/{apple_cert.p12,apple_cert_password.txt,apple_apikey.p8} - - - name: Delete Windows EV Signing Cert - run: rm /tmp/ev_cert.pem - - - name: Determine base image tag - id: image-base-tag - run: | - set -euo pipefail - if [[ "${CODER_RELEASE:-}" != *t* ]] || [[ "${CODER_DRY_RUN:-}" == *t* ]]; then - # Empty value means use the default and avoid building a fresh one. - echo "tag=" >> $GITHUB_OUTPUT - else - echo "tag=$(CODER_IMAGE_BASE=ghcr.io/coder/coder-base ./scripts/image_tag.sh)" >> $GITHUB_OUTPUT - fi - - - name: Create empty base-build-context directory - if: steps.image-base-tag.outputs.tag != '' - run: mkdir base-build-context - - - name: Install depot.dev CLI - if: steps.image-base-tag.outputs.tag != '' - uses: depot/setup-action@b0b1ea4f69e92ebf5dea3f8713a1b0c37b2126a5 # v1.6.0 - - # This uses OIDC authentication, so no auth variables are required. - - name: Build base Docker image via depot.dev - if: steps.image-base-tag.outputs.tag != '' - uses: depot/build-push-action@2583627a84956d07561420dcc1d0eb1f2af3fac0 # v1.15.0 - with: - project: wl5hnrrkns - context: base-build-context - file: scripts/Dockerfile.base - platforms: linux/amd64,linux/arm64,linux/arm/v7 - provenance: true - sbom: true - pull: true - no-cache: true - push: true - tags: | - ${{ steps.image-base-tag.outputs.tag }} - - - name: Verify that images are pushed properly - if: steps.image-base-tag.outputs.tag != '' - run: | - # retry 10 times with a 5 second delay as the images may not be - # available immediately - for i in {1..10}; do - rc=0 - raw_manifests=$(docker buildx imagetools inspect --raw "${{ steps.image-base-tag.outputs.tag }}") || rc=$? - if [[ "$rc" -eq 0 ]]; then - break - fi - if [[ "$i" -eq 10 ]]; then - echo "Failed to pull manifests after 10 retries" - exit 1 - fi - echo "Failed to pull manifests, retrying in 5 seconds" - sleep 5 - done - - manifests=$( - echo "$raw_manifests" | \ - jq -r '.manifests[].platform | .os + "/" + .architecture + (if .variant then "/" + .variant else "" end)' - ) - - # Verify all 3 platforms are present. - set -euxo pipefail - echo "$manifests" | grep -q linux/amd64 - echo "$manifests" | grep -q linux/arm64 - echo "$manifests" | grep -q linux/arm/v7 - - # GitHub attestation provides SLSA provenance for Docker images, establishing a verifiable - # record that these images were built in GitHub Actions with specific inputs and environment. - # This complements our existing cosign attestations (which focus on SBOMs) by adding - # GitHub-specific build provenance to enhance our supply chain security. - # - # TODO: Consider refactoring these attestation steps to use a matrix strategy or composite action - # to reduce duplication while maintaining the required functionality for each distinct image tag. - - name: GitHub Attestation for Base Docker image - id: attest_base - if: ${{ !inputs.dry_run && steps.image-base-tag.outputs.tag != '' }} - continue-on-error: true - uses: actions/attest@ce27ba3b4a9a139d9a20a4a07d69fabb52f1e5bc # v2.4.0 - with: - subject-name: ${{ steps.image-base-tag.outputs.tag }} - predicate-type: "https://slsa.dev/provenance/v1" - predicate: | - { - "buildType": "https://github.com/actions/runner-images/", - "builder": { - "id": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}" - }, - "invocation": { - "configSource": { - "uri": "git+https://github.com/${{ github.repository }}@${{ github.ref }}", - "digest": { - "sha1": "${{ github.sha }}" - }, - "entryPoint": ".github/workflows/release.yaml" - }, - "environment": { - "github_workflow": "${{ github.workflow }}", - "github_run_id": "${{ github.run_id }}" - } - }, - "metadata": { - "buildInvocationID": "${{ github.run_id }}", - "completeness": { - "environment": true, - "materials": true - } - } - } - push-to-registry: true - - - name: Build Linux Docker images - id: build_docker - run: | - set -euxo pipefail - - # we can't build multi-arch if the images aren't pushed, so quit now - # if dry-running - if [[ "$CODER_RELEASE" != *t* ]]; then - echo Skipping multi-arch docker builds due to dry-run. - exit 0 - fi - - # build Docker images for each architecture - version="$(./scripts/version.sh)" - make build/coder_"$version"_linux_{amd64,arm64,armv7}.tag - - # build and push multi-arch manifest, this depends on the other images - # being pushed so will automatically push them. - make push/build/coder_"$version"_linux.tag - - # Save multiarch image tag for attestation - multiarch_image="$(./scripts/image_tag.sh)" - echo "multiarch_image=${multiarch_image}" >> $GITHUB_OUTPUT - - # For debugging, print all docker image tags - docker images - - # if the current version is equal to the highest (according to semver) - # version in the repo, also create a multi-arch image as ":latest" and - # push it - created_latest_tag=false - if [[ "$(git tag | grep '^v' | grep -vE '(rc|dev|-|\+|\/)' | sort -r --version-sort | head -n1)" == "v$(./scripts/version.sh)" ]]; then - ./scripts/build_docker_multiarch.sh \ - --push \ - --target "$(./scripts/image_tag.sh --version latest)" \ - $(cat build/coder_"$version"_linux_{amd64,arm64,armv7}.tag) - created_latest_tag=true - echo "created_latest_tag=true" >> $GITHUB_OUTPUT - else - echo "created_latest_tag=false" >> $GITHUB_OUTPUT - fi - env: - CODER_BASE_IMAGE_TAG: ${{ steps.image-base-tag.outputs.tag }} - - - name: SBOM Generation and Attestation - if: ${{ !inputs.dry_run }} - env: - COSIGN_EXPERIMENTAL: "1" - run: | - set -euxo pipefail - - # Generate SBOM for multi-arch image with version in filename - echo "Generating SBOM for multi-arch image: ${{ steps.build_docker.outputs.multiarch_image }}" - syft "${{ steps.build_docker.outputs.multiarch_image }}" -o spdx-json > coder_${{ steps.version.outputs.version }}_sbom.spdx.json - - # Attest SBOM to multi-arch image - echo "Attesting SBOM to multi-arch image: ${{ steps.build_docker.outputs.multiarch_image }}" - cosign clean --force=true "${{ steps.build_docker.outputs.multiarch_image }}" - cosign attest --type spdxjson \ - --predicate coder_${{ steps.version.outputs.version }}_sbom.spdx.json \ - --yes \ - "${{ steps.build_docker.outputs.multiarch_image }}" - - # If latest tag was created, also attest it - if [[ "${{ steps.build_docker.outputs.created_latest_tag }}" == "true" ]]; then - latest_tag="$(./scripts/image_tag.sh --version latest)" - echo "Generating SBOM for latest image: ${latest_tag}" - syft "${latest_tag}" -o spdx-json > coder_latest_sbom.spdx.json - - echo "Attesting SBOM to latest image: ${latest_tag}" - cosign clean --force=true "${latest_tag}" - cosign attest --type spdxjson \ - --predicate coder_latest_sbom.spdx.json \ - --yes \ - "${latest_tag}" - fi - - - name: GitHub Attestation for Docker image - id: attest_main - if: ${{ !inputs.dry_run }} - continue-on-error: true - uses: actions/attest@ce27ba3b4a9a139d9a20a4a07d69fabb52f1e5bc # v2.4.0 - with: - subject-name: ${{ steps.build_docker.outputs.multiarch_image }} - predicate-type: "https://slsa.dev/provenance/v1" - predicate: | - { - "buildType": "https://github.com/actions/runner-images/", - "builder": { - "id": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}" - }, - "invocation": { - "configSource": { - "uri": "git+https://github.com/${{ github.repository }}@${{ github.ref }}", - "digest": { - "sha1": "${{ github.sha }}" - }, - "entryPoint": ".github/workflows/release.yaml" - }, - "environment": { - "github_workflow": "${{ github.workflow }}", - "github_run_id": "${{ github.run_id }}" - } - }, - "metadata": { - "buildInvocationID": "${{ github.run_id }}", - "completeness": { - "environment": true, - "materials": true - } - } - } - push-to-registry: true - - # Get the latest tag name for attestation - - name: Get latest tag name - id: latest_tag - if: ${{ !inputs.dry_run && steps.build_docker.outputs.created_latest_tag == 'true' }} - run: echo "tag=$(./scripts/image_tag.sh --version latest)" >> $GITHUB_OUTPUT - - # If this is the highest version according to semver, also attest the "latest" tag - - name: GitHub Attestation for "latest" Docker image - id: attest_latest - if: ${{ !inputs.dry_run && steps.build_docker.outputs.created_latest_tag == 'true' }} - continue-on-error: true - uses: actions/attest@ce27ba3b4a9a139d9a20a4a07d69fabb52f1e5bc # v2.4.0 - with: - subject-name: ${{ steps.latest_tag.outputs.tag }} - predicate-type: "https://slsa.dev/provenance/v1" - predicate: | - { - "buildType": "https://github.com/actions/runner-images/", - "builder": { - "id": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}" - }, - "invocation": { - "configSource": { - "uri": "git+https://github.com/${{ github.repository }}@${{ github.ref }}", - "digest": { - "sha1": "${{ github.sha }}" - }, - "entryPoint": ".github/workflows/release.yaml" - }, - "environment": { - "github_workflow": "${{ github.workflow }}", - "github_run_id": "${{ github.run_id }}" - } - }, - "metadata": { - "buildInvocationID": "${{ github.run_id }}", - "completeness": { - "environment": true, - "materials": true - } - } - } - push-to-registry: true - - # Report attestation failures but don't fail the workflow - - name: Check attestation status - if: ${{ !inputs.dry_run }} - run: | - if [[ "${{ steps.attest_base.outcome }}" == "failure" && "${{ steps.attest_base.conclusion }}" != "skipped" ]]; then - echo "::warning::GitHub attestation for base image failed" - fi - if [[ "${{ steps.attest_main.outcome }}" == "failure" ]]; then - echo "::warning::GitHub attestation for main image failed" - fi - if [[ "${{ steps.attest_latest.outcome }}" == "failure" && "${{ steps.attest_latest.conclusion }}" != "skipped" ]]; then - echo "::warning::GitHub attestation for latest image failed" - fi - - - name: Generate offline docs - run: | - version="$(./scripts/version.sh)" - make -j build/coder_docs_"$version".tgz - - - name: ls build - run: ls -lh build - - - name: Publish Coder CLI binaries and detached signatures to GCS - if: ${{ !inputs.dry_run }} - run: | - set -euxo pipefail - - version="$(./scripts/version.sh)" - - # Source array of slim binaries - declare -A binaries - binaries["coder-darwin-amd64"]="coder-slim_${version}_darwin_amd64" - binaries["coder-darwin-arm64"]="coder-slim_${version}_darwin_arm64" - binaries["coder-linux-amd64"]="coder-slim_${version}_linux_amd64" - binaries["coder-linux-arm64"]="coder-slim_${version}_linux_arm64" - binaries["coder-linux-armv7"]="coder-slim_${version}_linux_armv7" - binaries["coder-windows-amd64.exe"]="coder-slim_${version}_windows_amd64.exe" - binaries["coder-windows-arm64.exe"]="coder-slim_${version}_windows_arm64.exe" - - for cli_name in "${!binaries[@]}"; do - slim_binary="${binaries[$cli_name]}" - detached_signature="${slim_binary}.asc" - gcloud storage cp "./build/${slim_binary}" "gs://releases.coder.com/coder-cli/${version}/${cli_name}" - gcloud storage cp "./build/${detached_signature}" "gs://releases.coder.com/coder-cli/${version}/${cli_name}.asc" - done - - - name: Publish release - run: | - set -euo pipefail - - publish_args=() - if [[ $CODER_RELEASE_CHANNEL == "stable" ]]; then - publish_args+=(--stable) - fi - if [[ $CODER_DRY_RUN == *t* ]]; then - publish_args+=(--dry-run) - fi - declare -p publish_args - - # Build the list of files to publish - files=( - ./build/*_installer.exe - ./build/*.zip - ./build/*.tar.gz - ./build/*.tgz - ./build/*.apk - ./build/*.deb - ./build/*.rpm - ./coder_${{ steps.version.outputs.version }}_sbom.spdx.json - ) - - # Only include the latest SBOM file if it was created - if [[ "${{ steps.build_docker.outputs.created_latest_tag }}" == "true" ]]; then - files+=(./coder_latest_sbom.spdx.json) - fi - - ./scripts/release/publish.sh \ - "${publish_args[@]}" \ - --release-notes-file "$CODER_RELEASE_NOTES_FILE" \ - "${files[@]}" - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - CODER_GPG_RELEASE_KEY_BASE64: ${{ secrets.GPG_RELEASE_KEY_BASE64 }} - - - name: Authenticate to Google Cloud - uses: google-github-actions/auth@b7593ed2efd1c1617e1b0254da33b86225adb2a5 # v2.1.12 - with: - workload_identity_provider: ${{ vars.GCP_WORKLOAD_ID_PROVIDER }} - service_account: ${{ vars.GCP_SERVICE_ACCOUNT }} - - - name: Setup GCloud SDK - uses: google-github-actions/setup-gcloud@cb1e50a9932213ecece00a606661ae9ca44f3397 # 2.2.0 - - - name: Publish Helm Chart - if: ${{ !inputs.dry_run }} - run: | - set -euo pipefail - version="$(./scripts/version.sh)" - mkdir -p build/helm - cp "build/coder_helm_${version}.tgz" build/helm - cp "build/provisioner_helm_${version}.tgz" build/helm - gsutil cp gs://helm.coder.com/v2/index.yaml build/helm/index.yaml - helm repo index build/helm --url https://helm.coder.com/v2 --merge build/helm/index.yaml - gsutil -h "Cache-Control:no-cache,max-age=0" cp build/helm/coder_helm_${version}.tgz gs://helm.coder.com/v2 - gsutil -h "Cache-Control:no-cache,max-age=0" cp build/helm/provisioner_helm_${version}.tgz gs://helm.coder.com/v2 - gsutil -h "Cache-Control:no-cache,max-age=0" cp build/helm/index.yaml gs://helm.coder.com/v2 - gsutil -h "Cache-Control:no-cache,max-age=0" cp helm/artifacthub-repo.yml gs://helm.coder.com/v2 - helm push build/coder_helm_${version}.tgz oci://ghcr.io/coder/chart - helm push build/provisioner_helm_${version}.tgz oci://ghcr.io/coder/chart - - - name: Upload artifacts to actions (if dry-run) - if: ${{ inputs.dry_run }} - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 - with: - name: release-artifacts - path: | - ./build/*_installer.exe - ./build/*.zip - ./build/*.tar.gz - ./build/*.tgz - ./build/*.apk - ./build/*.deb - ./build/*.rpm - ./coder_${{ steps.version.outputs.version }}_sbom.spdx.json - retention-days: 7 - - - name: Upload latest sbom artifact to actions (if dry-run) - if: inputs.dry_run && steps.build_docker.outputs.created_latest_tag == 'true' - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 - with: - name: latest-sbom-artifact - path: ./coder_latest_sbom.spdx.json - retention-days: 7 - - - name: Send repository-dispatch event - if: ${{ !inputs.dry_run }} - uses: peter-evans/repository-dispatch@ff45666b9427631e3450c54a1bcbee4d9ff4d7c0 # v3.0.0 - with: - token: ${{ secrets.CDRCI_GITHUB_TOKEN }} - repository: coder/packages - event-type: coder-release - client-payload: '{"coder_version": "${{ steps.version.outputs.version }}", "release_channel": "${{ inputs.release_channel }}"}' - - publish-homebrew: - name: Publish to Homebrew tap - runs-on: ubuntu-latest - needs: release - if: ${{ !inputs.dry_run }} - - steps: - # TODO: skip this if it's not a new release (i.e. a backport). This is - # fine right now because it just makes a PR that we can close. - - name: Harden Runner - uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0 - with: - egress-policy: audit - - - name: Update homebrew - env: - # Variables used by the `gh` command - GH_REPO: coder/homebrew-coder - GH_TOKEN: ${{ secrets.CDRCI_GITHUB_TOKEN }} - run: | - # Keep version number around for reference, removing any potential leading v - coder_version="$(echo "${{ needs.release.outputs.version }}" | tr -d v)" - - set -euxo pipefail - - # Setup Git - git config --global user.email "ci@coder.com" - git config --global user.name "Coder CI" - git config --global credential.helper "store" - - temp_dir="$(mktemp -d)" - cd "$temp_dir" - - # Download checksums - checksums_url="$(gh release view --repo coder/coder "v$coder_version" --json assets \ - | jq -r ".assets | map(.url) | .[]" \ - | grep -e ".checksums.txt\$")" - wget "$checksums_url" -O checksums.txt - - # Get the SHAs - darwin_arm_sha="$(cat checksums.txt | grep "darwin_arm64.zip" | awk '{ print $1 }')" - darwin_intel_sha="$(cat checksums.txt | grep "darwin_amd64.zip" | awk '{ print $1 }')" - linux_sha="$(cat checksums.txt | grep "linux_amd64.tar.gz" | awk '{ print $1 }')" - - echo "macOS arm64: $darwin_arm_sha" - echo "macOS amd64: $darwin_intel_sha" - echo "Linux amd64: $linux_sha" - - # Check out the homebrew repo - git clone "https://github.com/$GH_REPO" homebrew-coder - brew_branch="auto-release/$coder_version" - cd homebrew-coder - - # Check if a PR already exists. - pr_count="$(gh pr list --search "head:$brew_branch" --json id,closed | jq -r ".[] | select(.closed == false) | .id" | wc -l)" - if [[ "$pr_count" > 0 ]]; then - echo "Bailing out as PR already exists" 2>&1 - exit 0 - fi - - # Set up cdrci credentials for pushing to homebrew-coder - echo "https://x-access-token:$GH_TOKEN@github.com" >> ~/.git-credentials - # Update the formulae and push - git checkout -b "$brew_branch" - ./scripts/update-v2.sh "$coder_version" "$darwin_arm_sha" "$darwin_intel_sha" "$linux_sha" - git add . - git commit -m "coder $coder_version" - git push -u origin -f "$brew_branch" - - # Create PR - gh pr create \ - -B master -H "$brew_branch" \ - -t "coder $coder_version" \ - -b "" \ - -r "${{ github.actor }}" \ - -a "${{ github.actor }}" \ - -b "This automatic PR was triggered by the release of Coder v$coder_version" - - publish-winget: - name: Publish to winget-pkgs - runs-on: windows-latest - needs: release - if: ${{ !inputs.dry_run }} - - steps: - - name: Harden Runner - uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0 - with: - egress-policy: audit - - - name: Sync fork - run: gh repo sync cdrci/winget-pkgs -b master - env: - GH_TOKEN: ${{ secrets.CDRCI_GITHUB_TOKEN }} - - - name: Checkout - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 - with: - fetch-depth: 0 - - # If the event that triggered the build was an annotated tag (which our - # tags are supposed to be), actions/checkout has a bug where the tag in - # question is only a lightweight tag and not a full annotated tag. This - # command seems to fix it. - # https://github.com/actions/checkout/issues/290 - name: Fetch git tags - run: git fetch --tags --force - - - name: Install wingetcreate - run: | - Invoke-WebRequest https://aka.ms/wingetcreate/latest -OutFile wingetcreate.exe - - - name: Submit updated manifest to winget-pkgs - run: | - # The package version is the same as the tag minus the leading "v". - # The version in this output already has the leading "v" removed but - # we do it again to be safe. - $version = "${{ needs.release.outputs.version }}".Trim('v') - - $release_assets = gh release view --repo coder/coder "v${version}" --json assets | ` - ConvertFrom-Json - # Get the installer URLs from the release assets. - $amd64_installer_url = $release_assets.assets | ` - Where-Object name -Match ".*_windows_amd64_installer.exe$" | ` - Select -ExpandProperty url - $amd64_zip_url = $release_assets.assets | ` - Where-Object name -Match ".*_windows_amd64.zip$" | ` - Select -ExpandProperty url - $arm64_zip_url = $release_assets.assets | ` - Where-Object name -Match ".*_windows_arm64.zip$" | ` - Select -ExpandProperty url - - echo "amd64 Installer URL: ${amd64_installer_url}" - echo "amd64 zip URL: ${amd64_zip_url}" - echo "arm64 zip URL: ${arm64_zip_url}" - echo "Package version: ${version}" - - .\wingetcreate.exe update Coder.Coder ` - --submit ` - --version "${version}" ` - --urls "${amd64_installer_url}" "${amd64_zip_url}" "${arm64_zip_url}" ` - --token "$env:WINGET_GH_TOKEN" - - env: - # For gh CLI: - GH_TOKEN: ${{ github.token }} - # For wingetcreate. We need a real token since we're pushing a commit - # to GitHub and then making a PR in a different repo. - WINGET_GH_TOKEN: ${{ secrets.CDRCI_GITHUB_TOKEN }} - - - name: Comment on PR - run: | - # wait 30 seconds - Start-Sleep -Seconds 30.0 - # Find the PR that wingetcreate just made. - $version = "${{ needs.release.outputs.version }}".Trim('v') - $pr_list = gh pr list --repo microsoft/winget-pkgs --search "author:cdrci Coder.Coder version ${version}" --limit 1 --json number | ` - ConvertFrom-Json - $pr_number = $pr_list[0].number - - gh pr comment --repo microsoft/winget-pkgs "${pr_number}" --body "🤖 cc: @deansheather @matifali" - - env: - # For gh CLI. We need a real token since we're commenting on a PR in a - # different repo. - GH_TOKEN: ${{ secrets.CDRCI_GITHUB_TOKEN }} - - # publish-sqlc pushes the latest schema to sqlc cloud. - # At present these pushes cannot be tagged, so the last push is always the latest. - publish-sqlc: - name: "Publish to schema sqlc cloud" - runs-on: "ubuntu-latest" - needs: release - if: ${{ !inputs.dry_run }} - steps: - - name: Harden Runner - uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0 - with: - egress-policy: audit - - - name: Checkout - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 - with: - fetch-depth: 1 - - # We need golang to run the migration main.go - - name: Setup Go - uses: ./.github/actions/setup-go - - - name: Setup sqlc - uses: ./.github/actions/setup-sqlc - - - name: Push schema to sqlc cloud - # Don't block a release on this - continue-on-error: true - run: | - make sqlc-push + run: echo "Success" From ffbc1f856b076ca27a5dc6b057e2fd8660ea1ff5 Mon Sep 17 00:00:00 2001 From: Jon Ayers Date: Tue, 12 Aug 2025 21:05:34 +0000 Subject: [PATCH 3/3] Revert "test release" This reverts commit 1add8e779aa18a5a6a75f06d532e838bb4ed437b. --- .github/workflows/release.yaml | 895 ++++++++++++++++++++++++++++++++- 1 file changed, 893 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index dd57fb513a89c..06041e1865d3a 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -63,8 +63,82 @@ jobs: runs-on: ${{ github.repository_owner == 'coder' && 'depot-macos-latest' || 'macos-latest' }} needs: check-perms steps: + # Harden Runner doesn't work on macOS. + - name: Checkout + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + with: + fetch-depth: 0 + + # If the event that triggered the build was an annotated tag (which our + # tags are supposed to be), actions/checkout has a bug where the tag in + # question is only a lightweight tag and not a full annotated tag. This + # command seems to fix it. + # https://github.com/actions/checkout/issues/290 - name: Fetch git tags - run: echo "Success" + run: git fetch --tags --force + + - name: Setup build tools + run: | + brew install bash gnu-getopt make + echo "$(brew --prefix bash)/bin" >> $GITHUB_PATH + echo "$(brew --prefix gnu-getopt)/bin" >> $GITHUB_PATH + echo "$(brew --prefix make)/libexec/gnubin" >> $GITHUB_PATH + + - name: Switch XCode Version + uses: maxim-lobanov/setup-xcode@60606e260d2fc5762a71e64e74b2174e8ea3c8bd # v1.6.0 + with: + xcode-version: "16.1.0" + + - name: Setup Go + uses: ./.github/actions/setup-go + + - name: Install rcodesign + run: | + set -euo pipefail + wget -O /tmp/rcodesign.tar.gz https://github.com/indygreg/apple-platform-rs/releases/download/apple-codesign%2F0.22.0/apple-codesign-0.22.0-macos-universal.tar.gz + sudo tar -xzf /tmp/rcodesign.tar.gz \ + -C /usr/local/bin \ + --strip-components=1 \ + apple-codesign-0.22.0-macos-universal/rcodesign + rm /tmp/rcodesign.tar.gz + + - name: Setup Apple Developer certificate and API key + run: | + set -euo pipefail + touch /tmp/{apple_cert.p12,apple_cert_password.txt,apple_apikey.p8} + chmod 600 /tmp/{apple_cert.p12,apple_cert_password.txt,apple_apikey.p8} + echo "$AC_CERTIFICATE_P12_BASE64" | base64 -d > /tmp/apple_cert.p12 + echo "$AC_CERTIFICATE_PASSWORD" > /tmp/apple_cert_password.txt + echo "$AC_APIKEY_P8_BASE64" | base64 -d > /tmp/apple_apikey.p8 + env: + AC_CERTIFICATE_P12_BASE64: ${{ secrets.AC_CERTIFICATE_P12_BASE64 }} + AC_CERTIFICATE_PASSWORD: ${{ secrets.AC_CERTIFICATE_PASSWORD }} + AC_APIKEY_P8_BASE64: ${{ secrets.AC_APIKEY_P8_BASE64 }} + + - name: Build dylibs + run: | + set -euxo pipefail + go mod download + + make gen/mark-fresh + make build/coder-dylib + env: + CODER_SIGN_DARWIN: 1 + AC_CERTIFICATE_FILE: /tmp/apple_cert.p12 + AC_CERTIFICATE_PASSWORD_FILE: /tmp/apple_cert_password.txt + + - name: Upload build artifacts + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: dylibs + path: | + ./build/*.h + ./build/*.dylib + retention-days: 7 + + - name: Delete Apple Developer certificate and API key + run: rm -f /tmp/{apple_cert.p12,apple_cert_password.txt,apple_apikey.p8} + release: name: Build and publish needs: [build-dylib, check-perms] @@ -86,5 +160,822 @@ jobs: outputs: version: ${{ steps.version.outputs.version }} steps: + - name: Harden Runner + uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0 + with: + egress-policy: audit + + - name: Checkout + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + with: + fetch-depth: 0 + + # If the event that triggered the build was an annotated tag (which our + # tags are supposed to be), actions/checkout has a bug where the tag in + # question is only a lightweight tag and not a full annotated tag. This + # command seems to fix it. + # https://github.com/actions/checkout/issues/290 + - name: Fetch git tags + run: git fetch --tags --force + + - name: Print version + id: version + run: | + set -euo pipefail + version="$(./scripts/version.sh)" + echo "version=$version" >> $GITHUB_OUTPUT + # Speed up future version.sh calls. + echo "CODER_FORCE_VERSION=$version" >> $GITHUB_ENV + echo "$version" + + # Verify that all expectations for a release are met. + - name: Verify release input + if: ${{ !inputs.dry_run }} + run: | + set -euo pipefail + + if [[ "${GITHUB_REF}" != "refs/tags/v"* ]]; then + echo "Ref must be a semver tag when creating a release, did you use scripts/release.sh?" + exit 1 + fi + + # 2.10.2 -> release/2.10 + version="$(./scripts/version.sh)" + release_branch=release/${version%.*} + branch_contains_tag=$(git branch --remotes --contains "${GITHUB_REF}" --list "*/${release_branch}" --format='%(refname)') + if [[ -z "${branch_contains_tag}" ]]; then + echo "Ref tag must exist in a branch named ${release_branch} when creating a release, did you use scripts/release.sh?" + exit 1 + fi + + if [[ -z "${CODER_RELEASE_NOTES}" ]]; then + echo "Release notes are required to create a release, did you use scripts/release.sh?" + exit 1 + fi + + echo "Release inputs verified:" + echo + echo "- Ref: ${GITHUB_REF}" + echo "- Version: ${version}" + echo "- Release channel: ${CODER_RELEASE_CHANNEL}" + echo "- Release branch: ${release_branch}" + echo "- Release notes: true" + + - name: Create release notes file + run: | + set -euo pipefail + + release_notes_file="$(mktemp -t release_notes.XXXXXX)" + echo "$CODER_RELEASE_NOTES" > "$release_notes_file" + echo CODER_RELEASE_NOTES_FILE="$release_notes_file" >> $GITHUB_ENV + + - name: Show release notes + run: | + set -euo pipefail + cat "$CODER_RELEASE_NOTES_FILE" + + - name: Docker Login + uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Setup Go + uses: ./.github/actions/setup-go + + - name: Setup Node + uses: ./.github/actions/setup-node + + # Necessary for signing Windows binaries. + - name: Setup Java + uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 + with: + distribution: "zulu" + java-version: "11.0" + + - name: Install go-winres + run: go install github.com/tc-hib/go-winres@d743268d7ea168077ddd443c4240562d4f5e8c3e # v0.3.3 + + - name: Install nsis and zstd + run: sudo apt-get install -y nsis zstd + + - name: Install nfpm + run: | + set -euo pipefail + wget -O /tmp/nfpm.deb https://github.com/goreleaser/nfpm/releases/download/v2.35.1/nfpm_2.35.1_amd64.deb + sudo dpkg -i /tmp/nfpm.deb + rm /tmp/nfpm.deb + + - name: Install rcodesign + run: | + set -euo pipefail + wget -O /tmp/rcodesign.tar.gz https://github.com/indygreg/apple-platform-rs/releases/download/apple-codesign%2F0.22.0/apple-codesign-0.22.0-x86_64-unknown-linux-musl.tar.gz + sudo tar -xzf /tmp/rcodesign.tar.gz \ + -C /usr/bin \ + --strip-components=1 \ + apple-codesign-0.22.0-x86_64-unknown-linux-musl/rcodesign + rm /tmp/rcodesign.tar.gz + + - name: Install cosign + uses: ./.github/actions/install-cosign + + - name: Install syft + uses: ./.github/actions/install-syft + + - name: Setup Apple Developer certificate and API key + run: | + set -euo pipefail + touch /tmp/{apple_cert.p12,apple_cert_password.txt,apple_apikey.p8} + chmod 600 /tmp/{apple_cert.p12,apple_cert_password.txt,apple_apikey.p8} + echo "$AC_CERTIFICATE_P12_BASE64" | base64 -d > /tmp/apple_cert.p12 + echo "$AC_CERTIFICATE_PASSWORD" > /tmp/apple_cert_password.txt + echo "$AC_APIKEY_P8_BASE64" | base64 -d > /tmp/apple_apikey.p8 + env: + AC_CERTIFICATE_P12_BASE64: ${{ secrets.AC_CERTIFICATE_P12_BASE64 }} + AC_CERTIFICATE_PASSWORD: ${{ secrets.AC_CERTIFICATE_PASSWORD }} + AC_APIKEY_P8_BASE64: ${{ secrets.AC_APIKEY_P8_BASE64 }} + + - name: Setup Windows EV Signing Certificate + run: | + set -euo pipefail + touch /tmp/ev_cert.pem + chmod 600 /tmp/ev_cert.pem + echo "$EV_SIGNING_CERT" > /tmp/ev_cert.pem + wget https://github.com/ebourg/jsign/releases/download/6.0/jsign-6.0.jar -O /tmp/jsign-6.0.jar + env: + EV_SIGNING_CERT: ${{ secrets.EV_SIGNING_CERT }} + + - name: Test migrations from current ref to main + run: | + POSTGRES_VERSION=13 make test-migrations + + # Setup GCloud for signing Windows binaries. + - name: Authenticate to Google Cloud + id: gcloud_auth + uses: google-github-actions/auth@b7593ed2efd1c1617e1b0254da33b86225adb2a5 # v2.1.12 + with: + workload_identity_provider: ${{ vars.GCP_CODE_SIGNING_WORKLOAD_ID_PROVIDER }} + service_account: ${{ vars.GCP_CODE_SIGNING_SERVICE_ACCOUNT }} + token_format: "access_token" + + - name: Setup GCloud SDK + uses: google-github-actions/setup-gcloud@cb1e50a9932213ecece00a606661ae9ca44f3397 # v2.2.0 + + - name: Download dylibs + uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 + with: + name: dylibs + path: ./build + + - name: Insert dylibs + run: | + mv ./build/*amd64.dylib ./site/out/bin/coder-vpn-darwin-amd64.dylib + mv ./build/*arm64.dylib ./site/out/bin/coder-vpn-darwin-arm64.dylib + mv ./build/*arm64.h ./site/out/bin/coder-vpn-darwin-dylib.h + + - name: Build binaries + run: | + set -euo pipefail + go mod download + + version="$(./scripts/version.sh)" + make gen/mark-fresh + make -j \ + build/coder_"$version"_linux_{amd64,armv7,arm64}.{tar.gz,apk,deb,rpm} \ + build/coder_"$version"_{darwin,windows}_{amd64,arm64}.zip \ + build/coder_"$version"_windows_amd64_installer.exe \ + build/coder_helm_"$version".tgz \ + build/provisioner_helm_"$version".tgz + env: + CODER_SIGN_WINDOWS: "1" + CODER_SIGN_DARWIN: "1" + CODER_SIGN_GPG: "1" + CODER_GPG_RELEASE_KEY_BASE64: ${{ secrets.GPG_RELEASE_KEY_BASE64 }} + CODER_WINDOWS_RESOURCES: "1" + AC_CERTIFICATE_FILE: /tmp/apple_cert.p12 + AC_CERTIFICATE_PASSWORD_FILE: /tmp/apple_cert_password.txt + AC_APIKEY_ISSUER_ID: ${{ secrets.AC_APIKEY_ISSUER_ID }} + AC_APIKEY_ID: ${{ secrets.AC_APIKEY_ID }} + AC_APIKEY_FILE: /tmp/apple_apikey.p8 + EV_KEY: ${{ secrets.EV_KEY }} + EV_KEYSTORE: ${{ secrets.EV_KEYSTORE }} + EV_TSA_URL: ${{ secrets.EV_TSA_URL }} + EV_CERTIFICATE_PATH: /tmp/ev_cert.pem + GCLOUD_ACCESS_TOKEN: ${{ steps.gcloud_auth.outputs.access_token }} + JSIGN_PATH: /tmp/jsign-6.0.jar + + - name: Delete Apple Developer certificate and API key + run: rm -f /tmp/{apple_cert.p12,apple_cert_password.txt,apple_apikey.p8} + + - name: Delete Windows EV Signing Cert + run: rm /tmp/ev_cert.pem + + - name: Determine base image tag + id: image-base-tag + run: | + set -euo pipefail + if [[ "${CODER_RELEASE:-}" != *t* ]] || [[ "${CODER_DRY_RUN:-}" == *t* ]]; then + # Empty value means use the default and avoid building a fresh one. + echo "tag=" >> $GITHUB_OUTPUT + else + echo "tag=$(CODER_IMAGE_BASE=ghcr.io/coder/coder-base ./scripts/image_tag.sh)" >> $GITHUB_OUTPUT + fi + + - name: Create empty base-build-context directory + if: steps.image-base-tag.outputs.tag != '' + run: mkdir base-build-context + + - name: Install depot.dev CLI + if: steps.image-base-tag.outputs.tag != '' + uses: depot/setup-action@b0b1ea4f69e92ebf5dea3f8713a1b0c37b2126a5 # v1.6.0 + + # This uses OIDC authentication, so no auth variables are required. + - name: Build base Docker image via depot.dev + if: steps.image-base-tag.outputs.tag != '' + uses: depot/build-push-action@2583627a84956d07561420dcc1d0eb1f2af3fac0 # v1.15.0 + with: + project: wl5hnrrkns + context: base-build-context + file: scripts/Dockerfile.base + platforms: linux/amd64,linux/arm64,linux/arm/v7 + provenance: true + sbom: true + pull: true + no-cache: true + push: true + tags: | + ${{ steps.image-base-tag.outputs.tag }} + + - name: Verify that images are pushed properly + if: steps.image-base-tag.outputs.tag != '' + run: | + # retry 10 times with a 5 second delay as the images may not be + # available immediately + for i in {1..10}; do + rc=0 + raw_manifests=$(docker buildx imagetools inspect --raw "${{ steps.image-base-tag.outputs.tag }}") || rc=$? + if [[ "$rc" -eq 0 ]]; then + break + fi + if [[ "$i" -eq 10 ]]; then + echo "Failed to pull manifests after 10 retries" + exit 1 + fi + echo "Failed to pull manifests, retrying in 5 seconds" + sleep 5 + done + + manifests=$( + echo "$raw_manifests" | \ + jq -r '.manifests[].platform | .os + "/" + .architecture + (if .variant then "/" + .variant else "" end)' + ) + + # Verify all 3 platforms are present. + set -euxo pipefail + echo "$manifests" | grep -q linux/amd64 + echo "$manifests" | grep -q linux/arm64 + echo "$manifests" | grep -q linux/arm/v7 + + # GitHub attestation provides SLSA provenance for Docker images, establishing a verifiable + # record that these images were built in GitHub Actions with specific inputs and environment. + # This complements our existing cosign attestations (which focus on SBOMs) by adding + # GitHub-specific build provenance to enhance our supply chain security. + # + # TODO: Consider refactoring these attestation steps to use a matrix strategy or composite action + # to reduce duplication while maintaining the required functionality for each distinct image tag. + - name: GitHub Attestation for Base Docker image + id: attest_base + if: ${{ !inputs.dry_run && steps.image-base-tag.outputs.tag != '' }} + continue-on-error: true + uses: actions/attest@ce27ba3b4a9a139d9a20a4a07d69fabb52f1e5bc # v2.4.0 + with: + subject-name: ${{ steps.image-base-tag.outputs.tag }} + predicate-type: "https://slsa.dev/provenance/v1" + predicate: | + { + "buildType": "https://github.com/actions/runner-images/", + "builder": { + "id": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}" + }, + "invocation": { + "configSource": { + "uri": "git+https://github.com/${{ github.repository }}@${{ github.ref }}", + "digest": { + "sha1": "${{ github.sha }}" + }, + "entryPoint": ".github/workflows/release.yaml" + }, + "environment": { + "github_workflow": "${{ github.workflow }}", + "github_run_id": "${{ github.run_id }}" + } + }, + "metadata": { + "buildInvocationID": "${{ github.run_id }}", + "completeness": { + "environment": true, + "materials": true + } + } + } + push-to-registry: true + + - name: Build Linux Docker images + id: build_docker + run: | + set -euxo pipefail + + # we can't build multi-arch if the images aren't pushed, so quit now + # if dry-running + if [[ "$CODER_RELEASE" != *t* ]]; then + echo Skipping multi-arch docker builds due to dry-run. + exit 0 + fi + + # build Docker images for each architecture + version="$(./scripts/version.sh)" + make build/coder_"$version"_linux_{amd64,arm64,armv7}.tag + + # build and push multi-arch manifest, this depends on the other images + # being pushed so will automatically push them. + make push/build/coder_"$version"_linux.tag + + # Save multiarch image tag for attestation + multiarch_image="$(./scripts/image_tag.sh)" + echo "multiarch_image=${multiarch_image}" >> $GITHUB_OUTPUT + + # For debugging, print all docker image tags + docker images + + # if the current version is equal to the highest (according to semver) + # version in the repo, also create a multi-arch image as ":latest" and + # push it + created_latest_tag=false + if [[ "$(git tag | grep '^v' | grep -vE '(rc|dev|-|\+|\/)' | sort -r --version-sort | head -n1)" == "v$(./scripts/version.sh)" ]]; then + ./scripts/build_docker_multiarch.sh \ + --push \ + --target "$(./scripts/image_tag.sh --version latest)" \ + $(cat build/coder_"$version"_linux_{amd64,arm64,armv7}.tag) + created_latest_tag=true + echo "created_latest_tag=true" >> $GITHUB_OUTPUT + else + echo "created_latest_tag=false" >> $GITHUB_OUTPUT + fi + env: + CODER_BASE_IMAGE_TAG: ${{ steps.image-base-tag.outputs.tag }} + + - name: SBOM Generation and Attestation + if: ${{ !inputs.dry_run }} + env: + COSIGN_EXPERIMENTAL: "1" + run: | + set -euxo pipefail + + # Generate SBOM for multi-arch image with version in filename + echo "Generating SBOM for multi-arch image: ${{ steps.build_docker.outputs.multiarch_image }}" + syft "${{ steps.build_docker.outputs.multiarch_image }}" -o spdx-json > coder_${{ steps.version.outputs.version }}_sbom.spdx.json + + # Attest SBOM to multi-arch image + echo "Attesting SBOM to multi-arch image: ${{ steps.build_docker.outputs.multiarch_image }}" + cosign clean --force=true "${{ steps.build_docker.outputs.multiarch_image }}" + cosign attest --type spdxjson \ + --predicate coder_${{ steps.version.outputs.version }}_sbom.spdx.json \ + --yes \ + "${{ steps.build_docker.outputs.multiarch_image }}" + + # If latest tag was created, also attest it + if [[ "${{ steps.build_docker.outputs.created_latest_tag }}" == "true" ]]; then + latest_tag="$(./scripts/image_tag.sh --version latest)" + echo "Generating SBOM for latest image: ${latest_tag}" + syft "${latest_tag}" -o spdx-json > coder_latest_sbom.spdx.json + + echo "Attesting SBOM to latest image: ${latest_tag}" + cosign clean --force=true "${latest_tag}" + cosign attest --type spdxjson \ + --predicate coder_latest_sbom.spdx.json \ + --yes \ + "${latest_tag}" + fi + + - name: GitHub Attestation for Docker image + id: attest_main + if: ${{ !inputs.dry_run }} + continue-on-error: true + uses: actions/attest@ce27ba3b4a9a139d9a20a4a07d69fabb52f1e5bc # v2.4.0 + with: + subject-name: ${{ steps.build_docker.outputs.multiarch_image }} + predicate-type: "https://slsa.dev/provenance/v1" + predicate: | + { + "buildType": "https://github.com/actions/runner-images/", + "builder": { + "id": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}" + }, + "invocation": { + "configSource": { + "uri": "git+https://github.com/${{ github.repository }}@${{ github.ref }}", + "digest": { + "sha1": "${{ github.sha }}" + }, + "entryPoint": ".github/workflows/release.yaml" + }, + "environment": { + "github_workflow": "${{ github.workflow }}", + "github_run_id": "${{ github.run_id }}" + } + }, + "metadata": { + "buildInvocationID": "${{ github.run_id }}", + "completeness": { + "environment": true, + "materials": true + } + } + } + push-to-registry: true + + # Get the latest tag name for attestation + - name: Get latest tag name + id: latest_tag + if: ${{ !inputs.dry_run && steps.build_docker.outputs.created_latest_tag == 'true' }} + run: echo "tag=$(./scripts/image_tag.sh --version latest)" >> $GITHUB_OUTPUT + + # If this is the highest version according to semver, also attest the "latest" tag + - name: GitHub Attestation for "latest" Docker image + id: attest_latest + if: ${{ !inputs.dry_run && steps.build_docker.outputs.created_latest_tag == 'true' }} + continue-on-error: true + uses: actions/attest@ce27ba3b4a9a139d9a20a4a07d69fabb52f1e5bc # v2.4.0 + with: + subject-name: ${{ steps.latest_tag.outputs.tag }} + predicate-type: "https://slsa.dev/provenance/v1" + predicate: | + { + "buildType": "https://github.com/actions/runner-images/", + "builder": { + "id": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}" + }, + "invocation": { + "configSource": { + "uri": "git+https://github.com/${{ github.repository }}@${{ github.ref }}", + "digest": { + "sha1": "${{ github.sha }}" + }, + "entryPoint": ".github/workflows/release.yaml" + }, + "environment": { + "github_workflow": "${{ github.workflow }}", + "github_run_id": "${{ github.run_id }}" + } + }, + "metadata": { + "buildInvocationID": "${{ github.run_id }}", + "completeness": { + "environment": true, + "materials": true + } + } + } + push-to-registry: true + + # Report attestation failures but don't fail the workflow + - name: Check attestation status + if: ${{ !inputs.dry_run }} + run: | + if [[ "${{ steps.attest_base.outcome }}" == "failure" && "${{ steps.attest_base.conclusion }}" != "skipped" ]]; then + echo "::warning::GitHub attestation for base image failed" + fi + if [[ "${{ steps.attest_main.outcome }}" == "failure" ]]; then + echo "::warning::GitHub attestation for main image failed" + fi + if [[ "${{ steps.attest_latest.outcome }}" == "failure" && "${{ steps.attest_latest.conclusion }}" != "skipped" ]]; then + echo "::warning::GitHub attestation for latest image failed" + fi + + - name: Generate offline docs + run: | + version="$(./scripts/version.sh)" + make -j build/coder_docs_"$version".tgz + + - name: ls build + run: ls -lh build + + - name: Publish Coder CLI binaries and detached signatures to GCS + if: ${{ !inputs.dry_run }} + run: | + set -euxo pipefail + + version="$(./scripts/version.sh)" + + # Source array of slim binaries + declare -A binaries + binaries["coder-darwin-amd64"]="coder-slim_${version}_darwin_amd64" + binaries["coder-darwin-arm64"]="coder-slim_${version}_darwin_arm64" + binaries["coder-linux-amd64"]="coder-slim_${version}_linux_amd64" + binaries["coder-linux-arm64"]="coder-slim_${version}_linux_arm64" + binaries["coder-linux-armv7"]="coder-slim_${version}_linux_armv7" + binaries["coder-windows-amd64.exe"]="coder-slim_${version}_windows_amd64.exe" + binaries["coder-windows-arm64.exe"]="coder-slim_${version}_windows_arm64.exe" + + for cli_name in "${!binaries[@]}"; do + slim_binary="${binaries[$cli_name]}" + detached_signature="${slim_binary}.asc" + gcloud storage cp "./build/${slim_binary}" "gs://releases.coder.com/coder-cli/${version}/${cli_name}" + gcloud storage cp "./build/${detached_signature}" "gs://releases.coder.com/coder-cli/${version}/${cli_name}.asc" + done + + - name: Publish release + run: | + set -euo pipefail + + publish_args=() + if [[ $CODER_RELEASE_CHANNEL == "stable" ]]; then + publish_args+=(--stable) + fi + if [[ $CODER_DRY_RUN == *t* ]]; then + publish_args+=(--dry-run) + fi + declare -p publish_args + + # Build the list of files to publish + files=( + ./build/*_installer.exe + ./build/*.zip + ./build/*.tar.gz + ./build/*.tgz + ./build/*.apk + ./build/*.deb + ./build/*.rpm + ./coder_${{ steps.version.outputs.version }}_sbom.spdx.json + ) + + # Only include the latest SBOM file if it was created + if [[ "${{ steps.build_docker.outputs.created_latest_tag }}" == "true" ]]; then + files+=(./coder_latest_sbom.spdx.json) + fi + + ./scripts/release/publish.sh \ + "${publish_args[@]}" \ + --release-notes-file "$CODER_RELEASE_NOTES_FILE" \ + "${files[@]}" + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + CODER_GPG_RELEASE_KEY_BASE64: ${{ secrets.GPG_RELEASE_KEY_BASE64 }} + + - name: Authenticate to Google Cloud + uses: google-github-actions/auth@b7593ed2efd1c1617e1b0254da33b86225adb2a5 # v2.1.12 + with: + workload_identity_provider: ${{ vars.GCP_WORKLOAD_ID_PROVIDER }} + service_account: ${{ vars.GCP_SERVICE_ACCOUNT }} + + - name: Setup GCloud SDK + uses: google-github-actions/setup-gcloud@cb1e50a9932213ecece00a606661ae9ca44f3397 # 2.2.0 + + - name: Publish Helm Chart + if: ${{ !inputs.dry_run }} + run: | + set -euo pipefail + version="$(./scripts/version.sh)" + mkdir -p build/helm + cp "build/coder_helm_${version}.tgz" build/helm + cp "build/provisioner_helm_${version}.tgz" build/helm + gsutil cp gs://helm.coder.com/v2/index.yaml build/helm/index.yaml + helm repo index build/helm --url https://helm.coder.com/v2 --merge build/helm/index.yaml + gsutil -h "Cache-Control:no-cache,max-age=0" cp build/helm/coder_helm_${version}.tgz gs://helm.coder.com/v2 + gsutil -h "Cache-Control:no-cache,max-age=0" cp build/helm/provisioner_helm_${version}.tgz gs://helm.coder.com/v2 + gsutil -h "Cache-Control:no-cache,max-age=0" cp build/helm/index.yaml gs://helm.coder.com/v2 + gsutil -h "Cache-Control:no-cache,max-age=0" cp helm/artifacthub-repo.yml gs://helm.coder.com/v2 + helm push build/coder_helm_${version}.tgz oci://ghcr.io/coder/chart + helm push build/provisioner_helm_${version}.tgz oci://ghcr.io/coder/chart + + - name: Upload artifacts to actions (if dry-run) + if: ${{ inputs.dry_run }} + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: release-artifacts + path: | + ./build/*_installer.exe + ./build/*.zip + ./build/*.tar.gz + ./build/*.tgz + ./build/*.apk + ./build/*.deb + ./build/*.rpm + ./coder_${{ steps.version.outputs.version }}_sbom.spdx.json + retention-days: 7 + + - name: Upload latest sbom artifact to actions (if dry-run) + if: inputs.dry_run && steps.build_docker.outputs.created_latest_tag == 'true' + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: latest-sbom-artifact + path: ./coder_latest_sbom.spdx.json + retention-days: 7 + + - name: Send repository-dispatch event + if: ${{ !inputs.dry_run }} + uses: peter-evans/repository-dispatch@ff45666b9427631e3450c54a1bcbee4d9ff4d7c0 # v3.0.0 + with: + token: ${{ secrets.CDRCI_GITHUB_TOKEN }} + repository: coder/packages + event-type: coder-release + client-payload: '{"coder_version": "${{ steps.version.outputs.version }}", "release_channel": "${{ inputs.release_channel }}"}' + + publish-homebrew: + name: Publish to Homebrew tap + runs-on: ubuntu-latest + needs: release + if: ${{ !inputs.dry_run }} + + steps: + # TODO: skip this if it's not a new release (i.e. a backport). This is + # fine right now because it just makes a PR that we can close. + - name: Harden Runner + uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0 + with: + egress-policy: audit + + - name: Update homebrew + env: + # Variables used by the `gh` command + GH_REPO: coder/homebrew-coder + GH_TOKEN: ${{ secrets.CDRCI_GITHUB_TOKEN }} + run: | + # Keep version number around for reference, removing any potential leading v + coder_version="$(echo "${{ needs.release.outputs.version }}" | tr -d v)" + + set -euxo pipefail + + # Setup Git + git config --global user.email "ci@coder.com" + git config --global user.name "Coder CI" + git config --global credential.helper "store" + + temp_dir="$(mktemp -d)" + cd "$temp_dir" + + # Download checksums + checksums_url="$(gh release view --repo coder/coder "v$coder_version" --json assets \ + | jq -r ".assets | map(.url) | .[]" \ + | grep -e ".checksums.txt\$")" + wget "$checksums_url" -O checksums.txt + + # Get the SHAs + darwin_arm_sha="$(cat checksums.txt | grep "darwin_arm64.zip" | awk '{ print $1 }')" + darwin_intel_sha="$(cat checksums.txt | grep "darwin_amd64.zip" | awk '{ print $1 }')" + linux_sha="$(cat checksums.txt | grep "linux_amd64.tar.gz" | awk '{ print $1 }')" + + echo "macOS arm64: $darwin_arm_sha" + echo "macOS amd64: $darwin_intel_sha" + echo "Linux amd64: $linux_sha" + + # Check out the homebrew repo + git clone "https://github.com/$GH_REPO" homebrew-coder + brew_branch="auto-release/$coder_version" + cd homebrew-coder + + # Check if a PR already exists. + pr_count="$(gh pr list --search "head:$brew_branch" --json id,closed | jq -r ".[] | select(.closed == false) | .id" | wc -l)" + if [[ "$pr_count" > 0 ]]; then + echo "Bailing out as PR already exists" 2>&1 + exit 0 + fi + + # Set up cdrci credentials for pushing to homebrew-coder + echo "https://x-access-token:$GH_TOKEN@github.com" >> ~/.git-credentials + # Update the formulae and push + git checkout -b "$brew_branch" + ./scripts/update-v2.sh "$coder_version" "$darwin_arm_sha" "$darwin_intel_sha" "$linux_sha" + git add . + git commit -m "coder $coder_version" + git push -u origin -f "$brew_branch" + + # Create PR + gh pr create \ + -B master -H "$brew_branch" \ + -t "coder $coder_version" \ + -b "" \ + -r "${{ github.actor }}" \ + -a "${{ github.actor }}" \ + -b "This automatic PR was triggered by the release of Coder v$coder_version" + + publish-winget: + name: Publish to winget-pkgs + runs-on: windows-latest + needs: release + if: ${{ !inputs.dry_run }} + + steps: + - name: Harden Runner + uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0 + with: + egress-policy: audit + + - name: Sync fork + run: gh repo sync cdrci/winget-pkgs -b master + env: + GH_TOKEN: ${{ secrets.CDRCI_GITHUB_TOKEN }} + + - name: Checkout + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + with: + fetch-depth: 0 + + # If the event that triggered the build was an annotated tag (which our + # tags are supposed to be), actions/checkout has a bug where the tag in + # question is only a lightweight tag and not a full annotated tag. This + # command seems to fix it. + # https://github.com/actions/checkout/issues/290 - name: Fetch git tags - run: echo "Success" + run: git fetch --tags --force + + - name: Install wingetcreate + run: | + Invoke-WebRequest https://aka.ms/wingetcreate/latest -OutFile wingetcreate.exe + + - name: Submit updated manifest to winget-pkgs + run: | + # The package version is the same as the tag minus the leading "v". + # The version in this output already has the leading "v" removed but + # we do it again to be safe. + $version = "${{ needs.release.outputs.version }}".Trim('v') + + $release_assets = gh release view --repo coder/coder "v${version}" --json assets | ` + ConvertFrom-Json + # Get the installer URLs from the release assets. + $amd64_installer_url = $release_assets.assets | ` + Where-Object name -Match ".*_windows_amd64_installer.exe$" | ` + Select -ExpandProperty url + $amd64_zip_url = $release_assets.assets | ` + Where-Object name -Match ".*_windows_amd64.zip$" | ` + Select -ExpandProperty url + $arm64_zip_url = $release_assets.assets | ` + Where-Object name -Match ".*_windows_arm64.zip$" | ` + Select -ExpandProperty url + + echo "amd64 Installer URL: ${amd64_installer_url}" + echo "amd64 zip URL: ${amd64_zip_url}" + echo "arm64 zip URL: ${arm64_zip_url}" + echo "Package version: ${version}" + + .\wingetcreate.exe update Coder.Coder ` + --submit ` + --version "${version}" ` + --urls "${amd64_installer_url}" "${amd64_zip_url}" "${arm64_zip_url}" ` + --token "$env:WINGET_GH_TOKEN" + + env: + # For gh CLI: + GH_TOKEN: ${{ github.token }} + # For wingetcreate. We need a real token since we're pushing a commit + # to GitHub and then making a PR in a different repo. + WINGET_GH_TOKEN: ${{ secrets.CDRCI_GITHUB_TOKEN }} + + - name: Comment on PR + run: | + # wait 30 seconds + Start-Sleep -Seconds 30.0 + # Find the PR that wingetcreate just made. + $version = "${{ needs.release.outputs.version }}".Trim('v') + $pr_list = gh pr list --repo microsoft/winget-pkgs --search "author:cdrci Coder.Coder version ${version}" --limit 1 --json number | ` + ConvertFrom-Json + $pr_number = $pr_list[0].number + + gh pr comment --repo microsoft/winget-pkgs "${pr_number}" --body "🤖 cc: @deansheather @matifali" + + env: + # For gh CLI. We need a real token since we're commenting on a PR in a + # different repo. + GH_TOKEN: ${{ secrets.CDRCI_GITHUB_TOKEN }} + + # publish-sqlc pushes the latest schema to sqlc cloud. + # At present these pushes cannot be tagged, so the last push is always the latest. + publish-sqlc: + name: "Publish to schema sqlc cloud" + runs-on: "ubuntu-latest" + needs: release + if: ${{ !inputs.dry_run }} + steps: + - name: Harden Runner + uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0 + with: + egress-policy: audit + + - name: Checkout + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + with: + fetch-depth: 1 + + # We need golang to run the migration main.go + - name: Setup Go + uses: ./.github/actions/setup-go + + - name: Setup sqlc + uses: ./.github/actions/setup-sqlc + + - name: Push schema to sqlc cloud + # Don't block a release on this + continue-on-error: true + run: | + make sqlc-push