# GitHub release workflow. name: Release on: push: tags: - "v*" workflow_dispatch: inputs: dry_run: description: Perform a dry-run release (devel). Note that ref must be an annotated tag when run without dry-run. type: boolean required: true default: false permissions: # Required to publish a release contents: write # Necessary to push docker images to ghcr.io. packages: write # Necessary for GCP authentication (https://github.com/google-github-actions/setup-gcloud#usage) id-token: write concurrency: ${{ github.workflow }}-${{ github.ref }} env: # Use `inputs` (vs `github.event.inputs`) to ensure that booleans are actual # booleans, not strings. # https://github.blog/changelog/2022-06-10-github-actions-inputs-unified-across-manual-and-reusable-workflows/ CODER_RELEASE: ${{ !inputs.dry_run }} CODER_DRY_RUN: ${{ inputs.dry_run }} jobs: release: name: Build and publish runs-on: ${{ github.repository_owner == 'coder' && 'ubuntu-latest-8-cores' || 'ubuntu-latest' }} env: # Necessary for Docker manifest DOCKER_CLI_EXPERIMENTAL: "enabled" outputs: version: ${{ steps.version.outputs.version }} steps: - uses: actions/checkout@v3 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" - name: Create release notes env: # We always have to set this since there might be commits on # main that didn't have a PR. CODER_IGNORE_MISSING_COMMIT_METADATA: "1" run: | set -euo pipefail ref=HEAD old_version="$(git describe --abbrev=0 "$ref^1")" version="$(./scripts/version.sh)" # Generate notes. release_notes_file="$(mktemp -t release_notes.XXXXXX)" ./scripts/release/generate_release_notes.sh --old-version "$old_version" --new-version "$version" --ref "$ref" >> "$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@v2 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - uses: actions/setup-go@v3 with: go-version: "~1.20" - name: Cache Node id: cache-node uses: actions/cache@v3 with: path: | **/node_modules .eslintcache key: js-${{ runner.os }}-test-${{ hashFiles('**/yarn.lock') }} restore-keys: | js-${{ runner.os }}- - 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.18.1/nfpm_amd64.deb sudo dpkg -i /tmp/nfpm.deb - name: Install rcodesign run: | set -euo pipefail # Install a prebuilt binary of rcodesign for linux amd64. Once the # following PR is merged and released upstream, we can download # directly from GitHub releases instead: # https://github.com/indygreg/PyOxidizer/pull/635 wget -O /tmp/rcodesign https://cdn.discordapp.com/attachments/283356472258199552/1016767245717872700/rcodesign sudo install --mode 755 /tmp/rcodesign /usr/local/bin/rcodesign - 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 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 env: CODER_SIGN_DARWIN: "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 - name: Delete Apple Developer certificate and API key run: rm -f /tmp/{apple_cert.p12,apple_cert_password.txt,apple_apikey.p8} - name: Build Linux Docker images run: | set -euxo pipefail # build Docker images for each architecture version="$(./scripts/version.sh)" make -j build/coder_"$version"_linux_{amd64,arm64,armv7}.tag # 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 and push multi-arch manifest, this depends on the other images # being pushed so will automatically push them. make -j push/build/coder_"$version"_linux.tag # 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 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) fi - name: ls build run: ls -lh build - name: Publish release run: | set -euo pipefail publish_args=() if [[ $CODER_DRY_RUN == *t* ]]; then publish_args+=(--dry-run) fi declare -p publish_args ./scripts/release/publish.sh \ "${publish_args[@]}" \ --release-notes-file "$CODER_RELEASE_NOTES_FILE" \ ./build/*_installer.exe \ ./build/*.zip \ ./build/*.tar.gz \ ./build/*.tgz \ ./build/*.apk \ ./build/*.deb \ ./build/*.rpm 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@v1 with: workload_identity_provider: ${{ secrets.GCP_WORKLOAD_ID_PROVIDER }} service_account: ${{ secrets.GCP_SERVICE_ACCOUNT }} - name: Setup GCloud SDK uses: "google-github-actions/setup-gcloud@v1" - 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 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/index.yaml gs://helm.coder.com/v2 - name: Upload artifacts to actions (if dry-run) if: ${{ inputs.dry_run }} uses: actions/upload-artifact@v2 with: name: release-artifacts path: | ./build/*_installer.exe ./build/*.zip ./build/*.tar.gz ./build/*.tgz ./build/*.apk ./build/*.deb ./build/*.rpm retention-days: 7 publish-winget: name: Publish to winget-pkgs runs-on: windows-latest needs: release steps: - uses: actions/checkout@v3 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 URL from the release assets. $installer_url = $release_assets.assets | ` Where-Object name -Match ".*_windows_amd64_installer.exe$" | ` Select -ExpandProperty url echo "Installer URL: ${installer_url}" echo "Package version: ${version}" # Bail if dry-run. if ($env:CODER_DRY_RUN -match "t") { echo "Skipping submission due to dry-run." exit 0 } # The URL "|X64" suffix forces the architecture as it cannot be # sniffed properly from the URL. wingetcreate checks both the URL and # binary magic bytes for the architecture and they need to both match, # but they only check for `x64`, `win64` and `_64` in the URL. Our URL # contains `amd64` which doesn't match sadly. # # wingetcreate will still do the binary magic bytes check, so if we # accidentally change the architecture of the installer, it will fail # submission. .\wingetcreate.exe update Coder.Coder ` --submit ` --version "${version}" ` --urls "${installer_url}|X64" ` --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 if: ${{ !inputs.dry_run }} run: | # 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 }}