name: ci

on:
  push:
    branches:
      - main

  pull_request:
  workflow_dispatch:

permissions:
  actions: none
  checks: none
  contents: read
  deployments: none
  issues: none
  packages: write
  pull-requests: none
  repository-projects: none
  security-events: none
  statuses: none

# Cancel in-progress runs for pull requests when developers push
# additional changes
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}

jobs:
  changes:
    runs-on: ubuntu-latest
    outputs:
      docs-only: ${{ steps.filter.outputs.docs_count == steps.filter.outputs.all_count }}
      go: ${{ steps.filter.outputs.go }}
      ts: ${{ steps.filter.outputs.ts }}
      k8s: ${{ steps.filter.outputs.k8s }}
      ci: ${{ steps.filter.outputs.ci }}
      offlinedocs-only: ${{ steps.filter.outputs.offlinedocs_count == steps.filter.outputs.all_count }}
      offlinedocs: ${{ steps.filter.outputs.offlinedocs }}
    steps:
      - name: Checkout
        uses: actions/checkout@v3
        with:
          fetch-depth: 1
      # For pull requests it's not necessary to checkout the code
      - name: check changed files
        uses: dorny/paths-filter@v2
        id: filter
        with:
          filters: |
            all:
              - "**"
            docs:
              - "docs/**"
              - "README.md"
              - "examples/templates/**"
              - "examples/web-server/**"
              - "examples/monitoring/**"
              - "examples/lima/**"
            go:
              - "**.sql"
              - "**.go"
              - "**.golden"
              - "go.mod"
              - "go.sum"
              # Other non-Go files that may affect Go code:
              - "**.rego"
              - "**.sh"
              - "**.tpl"
              - "**.gotmpl"
              - "**.gotpl"
              - "Makefile"
              - "site/static/error.html"
              # Main repo directories for completeness in case other files are
              # touched:
              - "agent/**"
              - "cli/**"
              - "cmd/**"
              - "coderd/**"
              - "enterprise/**"
              - "examples/*"
              - "provisioner/**"
              - "provisionerd/**"
              - "provisionersdk/**"
              - "pty/**"
              - "scaletest/**"
              - "tailnet/**"
              - "testutil/**"
            ts:
              - "site/**"
              - "Makefile"
            k8s:
              - "helm/**"
              - "scripts/Dockerfile"
              - "scripts/Dockerfile.base"
              - "scripts/helm.sh"
            ci:
              - ".github/actions/**"
              - ".github/workflows/ci.yaml"
            offlinedocs:
              - "offlinedocs/**"

      - id: debug
        run: |
          echo "${{ toJSON(steps.filter )}}"

  lint:
    needs: changes
    if: needs.changes.outputs.offlinedocs-only == 'false' || needs.changes.outputs.ci == 'true' || github.ref == 'refs/heads/main'
    runs-on: ${{ github.repository_owner == 'coder' && 'buildjet-8vcpu-ubuntu-2204' || 'ubuntu-latest' }}
    steps:
      - name: Checkout
        uses: actions/checkout@v3
        with:
          fetch-depth: 1

      - name: Setup Node
        uses: ./.github/actions/setup-node

      - name: Setup Go
        uses: ./.github/actions/setup-go

      - name: Get golangci-lint cache dir
        run: |
          go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.53.2
          dir=$(golangci-lint cache status | awk '/Dir/ { print $2 }')
          echo "LINT_CACHE_DIR=$dir" >> $GITHUB_ENV

      - name: golangci-lint cache
        uses: buildjet/cache@v3
        with:
          path: |
            ${{ env.LINT_CACHE_DIR }}
          key: golangci-lint-${{ runner.os }}-${{ hashFiles('**/*.go') }}
          restore-keys: |
            golangci-lint-${{ runner.os }}-

      # Check for any typos
      - name: Check for typos
        uses: crate-ci/typos@v1.16.6
        with:
          config: .github/workflows/typos.toml

      - name: Fix the typos
        if: ${{ failure() }}
        run: |
          echo "::notice:: you can automatically fix typos from your CLI:
          cargo install typos-cli
          typos -c .github/workflows/typos.toml -w"

      # Needed for helm chart linting
      - name: Install helm
        uses: azure/setup-helm@v3
        with:
          version: v3.9.2

      - name: make lint
        run: |
          make --output-sync=line -j lint

  gen:
    timeout-minutes: 8
    runs-on: ${{ github.repository_owner == 'coder' && 'buildjet-8vcpu-ubuntu-2204' || 'ubuntu-latest' }}
    needs: changes
    if: needs.changes.outputs.docs-only == 'false' || needs.changes.outputs.ci == 'true' || github.ref == 'refs/heads/main'
    steps:
      - name: Checkout
        uses: actions/checkout@v3
        with:
          fetch-depth: 1

      - name: Setup Node
        uses: ./.github/actions/setup-node

      - name: Setup Go
        uses: ./.github/actions/setup-go

      - name: Setup sqlc
        uses: ./.github/actions/setup-sqlc

      - name: go install tools
        run: |
          go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.30
          go install storj.io/drpc/cmd/protoc-gen-go-drpc@v0.0.33
          go install golang.org/x/tools/cmd/goimports@latest
          go install github.com/mikefarah/yq/v4@v4.30.6
          go install github.com/golang/mock/mockgen@v1.6.0

      - name: Install Protoc
        run: |
          mkdir -p /tmp/proto
          pushd /tmp/proto
          curl -L -o protoc.zip https://github.com/protocolbuffers/protobuf/releases/download/v23.3/protoc-23.3-linux-x86_64.zip
          unzip protoc.zip
          cp -r ./bin/* /usr/local/bin
          cp -r ./include /usr/local/bin/include
          popd

      - name: make gen
        run: "make --output-sync -j -B gen"

      - name: Check for unstaged files
        run: ./scripts/check_unstaged.sh

  fmt:
    needs: changes
    if: needs.changes.outputs.offlinedocs-only == 'false' || needs.changes.outputs.ci == 'true' || github.ref == 'refs/heads/main'
    runs-on: ${{ github.repository_owner == 'coder' && 'buildjet-8vcpu-ubuntu-2204' || 'ubuntu-latest' }}
    timeout-minutes: 7
    steps:
      - name: Checkout
        uses: actions/checkout@v3
        with:
          fetch-depth: 1

      - name: Setup Node
        uses: ./.github/actions/setup-node

      - name: Setup Go
        uses: buildjet/setup-go@v4
        with:
          # This doesn't need caching. It's super fast anyways!
          cache: false
          go-version: 1.20.7

      - name: Install shfmt
        run: go install mvdan.cc/sh/v3/cmd/shfmt@v3.5.0

      - name: make fmt
        run: |
          export PATH=${PATH}:$(go env GOPATH)/bin
          make --output-sync -j -B fmt

      - name: Check for unstaged files
        run: ./scripts/check_unstaged.sh

  test-go:
    runs-on: ${{ matrix.os == 'ubuntu-latest' && github.repository_owner == 'coder' && 'buildjet-4vcpu-ubuntu-2204' || matrix.os == 'macos-latest' && github.repository_owner == 'coder' && 'macos-latest-xl' || matrix.os == 'windows-2019' && github.repository_owner == 'coder' && 'windows-latest-8-cores' || matrix.os }}
    needs: changes
    if: needs.changes.outputs.go == 'true' || needs.changes.outputs.ci == 'true' || github.ref == 'refs/heads/main'
    timeout-minutes: 20
    strategy:
      fail-fast: false
      matrix:
        os:
          - ubuntu-latest
          - macos-latest
          - windows-2019
    steps:
      - name: Checkout
        uses: actions/checkout@v3
        with:
          fetch-depth: 1

      - name: Setup Go
        uses: ./.github/actions/setup-go

      - name: Setup Terraform
        uses: ./.github/actions/setup-tf

      - name: Test with Mock Database
        id: test
        shell: bash
        run: |
          # Code coverage is more computationally expensive and also
          # prevents test caching, so we disable it on alternate operating
          # systems.
          if [ "${{ matrix.os }}" == "ubuntu-latest" ]; then
            echo "cover=true" >> $GITHUB_OUTPUT
            export COVERAGE_FLAGS='-covermode=atomic -coverprofile="gotests.coverage" -coverpkg=./...'
          else
            echo "cover=false" >> $GITHUB_OUTPUT
          fi

          # By default Go will use the number of logical CPUs, which
          # is a fine default.
          PARALLEL_FLAG=""

          export TS_DEBUG_DISCO=true
          gotestsum --junitfile="gotests.xml" --jsonfile="gotests.json" \
            --packages="./..." -- $PARALLEL_FLAG -short -failfast $COVERAGE_FLAGS

      - name: Print test stats
        if: success() || failure()
        run: |
          # Artifacts are not available after rerunning a job,
          # so we need to print the test stats to the log.
          go run ./scripts/ci-report/main.go gotests.json | tee gotests_stats.json

      - name: Upload test stats to Datadog
        uses: ./.github/actions/upload-datadog
        if: success() || failure()
        with:
          api-key: ${{ secrets.DATADOG_API_KEY }}

      - name: Check code coverage
        uses: codecov/codecov-action@v3
        # This action has a tendency to error out unexpectedly, it has
        # the `fail_ci_if_error` option that defaults to `false`, but
        # that is no guarantee, see:
        # https://github.com/codecov/codecov-action/issues/788
        continue-on-error: true
        if: steps.test.outputs.cover && github.actor != 'dependabot[bot]' && !github.event.pull_request.head.repo.fork
        with:
          token: ${{ secrets.CODECOV_TOKEN }}
          files: ./gotests.coverage
          flags: unittest-go-${{ matrix.os }}

  test-go-pg:
    runs-on: ${{ github.repository_owner == 'coder' && 'buildjet-8vcpu-ubuntu-2204' || 'ubuntu-latest' }}
    needs: changes
    if: needs.changes.outputs.go == 'true' || needs.changes.outputs.ci == 'true' || github.ref == 'refs/heads/main'
    # This timeout must be greater than the timeout set by `go test` in
    # `make test-postgres` to ensure we receive a trace of running
    # goroutines. Setting this to the timeout +5m should work quite well
    # even if some of the preceding steps are slow.
    timeout-minutes: 25
    steps:
      - name: Checkout
        uses: actions/checkout@v3
        with:
          fetch-depth: 1

      - name: Setup Go
        uses: ./.github/actions/setup-go

      - name: Setup Terraform
        uses: ./.github/actions/setup-tf

      - name: Test with PostgreSQL Database
        run: |
          export TS_DEBUG_DISCO=true
          make test-postgres

      - name: Print test stats
        if: success() || failure()
        run: |
          # Artifacts are not available after rerunning a job,
          # so we need to print the test stats to the log.
          go run ./scripts/ci-report/main.go gotests.json | tee gotests_stats.json

      - name: Upload test stats to Datadog
        uses: ./.github/actions/upload-datadog
        if: success() || failure()
        with:
          api-key: ${{ secrets.DATADOG_API_KEY }}

      - name: Check code coverage
        uses: codecov/codecov-action@v3
        # This action has a tendency to error out unexpectedly, it has
        # the `fail_ci_if_error` option that defaults to `false`, but
        # that is no guarantee, see:
        # https://github.com/codecov/codecov-action/issues/788
        continue-on-error: true
        if: github.actor != 'dependabot[bot]' && !github.event.pull_request.head.repo.fork
        with:
          token: ${{ secrets.CODECOV_TOKEN }}
          files: ./gotests.coverage
          flags: unittest-go-postgres-linux

  test-go-race:
    runs-on: ${{ github.repository_owner == 'coder' && 'buildjet-8vcpu-ubuntu-2204' || 'ubuntu-latest' }}
    needs: changes
    if: needs.changes.outputs.go == 'true' || needs.changes.outputs.ci == 'true' || github.ref == 'refs/heads/main'
    timeout-minutes: 25
    steps:
      - name: Checkout
        uses: actions/checkout@v3
        with:
          fetch-depth: 1

      - name: Setup Go
        uses: ./.github/actions/setup-go

      - name: Setup Terraform
        uses: ./.github/actions/setup-tf

      - name: Run Tests
        run: |
          gotestsum --junitfile="gotests.xml" -- -race ./...

      - name: Upload test stats to Datadog
        uses: ./.github/actions/upload-datadog
        if: always()
        with:
          api-key: ${{ secrets.DATADOG_API_KEY }}

  deploy:
    name: "deploy"
    runs-on: ${{ github.repository_owner == 'coder' && 'buildjet-8vcpu-ubuntu-2204' || 'ubuntu-latest' }}
    timeout-minutes: 30
    needs: changes
    if: |
      github.ref == 'refs/heads/main' && !github.event.pull_request.head.repo.fork
      && needs.changes.outputs.docs-only == 'false'
    permissions:
      contents: read
      id-token: write
    steps:
      - name: Checkout
        uses: actions/checkout@v3
        with:
          fetch-depth: 0

      - name: Authenticate to Google Cloud
        uses: google-github-actions/auth@v1
        with:
          workload_identity_provider: projects/573722524737/locations/global/workloadIdentityPools/github/providers/github
          service_account: coder-ci@coder-dogfood.iam.gserviceaccount.com

      - name: Set up Google Cloud SDK
        uses: google-github-actions/setup-gcloud@v1

      - name: Setup Node
        uses: ./.github/actions/setup-node

      - name: Setup Go
        uses: ./.github/actions/setup-go

      - name: Install goimports
        run: go install golang.org/x/tools/cmd/goimports@latest
      - name: Install nfpm
        run: go install github.com/goreleaser/nfpm/v2/cmd/nfpm@v2.16.0

      - name: Install zstd
        run: sudo apt-get install -y zstd

      - name: Build Release
        run: |
          set -euo pipefail
          go mod download

          version="$(./scripts/version.sh)"
          make gen/mark-fresh
          make -j \
            build/coder_"$version"_windows_amd64.zip \
            build/coder_"$version"_linux_amd64.{tar.gz,deb}

      - name: Install Release
        run: |
          set -euo pipefail

          regions=(
            # gcp-region-id instance-name systemd-service-name
            "us-central1-a coder coder"
            "australia-southeast1-b coder-sydney coder-workspace-proxy"
            "europe-west3-c coder-europe coder-workspace-proxy"
            "southamerica-east1-b coder-brazil coder-workspace-proxy"
          )

          deb_pkg="./build/coder_$(./scripts/version.sh)_linux_amd64.deb"
          if [ ! -f "$deb_pkg" ]; then
            echo "deb package not found: $deb_pkg"
            ls -l ./build
            exit 1
          fi

          gcloud config set project coder-dogfood
          for region in "${regions[@]}"; do
            echo "::group::$region"
            set -- $region

            set -x
            gcloud config set compute/zone "$1"
            gcloud compute scp "$deb_pkg" "${2}:/tmp/coder.deb"
            gcloud compute ssh "$2" -- /bin/sh -c "set -eux; sudo dpkg -i --force-confdef /tmp/coder.deb; sudo systemctl daemon-reload; sudo service '$3' restart"
            set +x

            echo "::endgroup::"
          done

      - name: Upload build artifacts
        uses: actions/upload-artifact@v3
        with:
          name: coder
          path: |
            ./build/*.zip
            ./build/*.tar.gz
            ./build/*.deb
          retention-days: 7

  test-js:
    runs-on: ${{ github.repository_owner == 'coder' && 'buildjet-8vcpu-ubuntu-2204' || 'ubuntu-latest' }}
    needs: changes
    if: needs.changes.outputs.ts == 'true' || needs.changes.outputs.ci == 'true' || github.ref == 'refs/heads/main'
    timeout-minutes: 20
    steps:
      - name: Checkout
        uses: actions/checkout@v3
        with:
          fetch-depth: 1

      - name: Setup Node
        uses: ./.github/actions/setup-node

      - run: pnpm test:ci --max-workers $(nproc)
        working-directory: site

      - name: Check code coverage
        uses: codecov/codecov-action@v3
        # This action has a tendency to error out unexpectedly, it has
        # the `fail_ci_if_error` option that defaults to `false`, but
        # that is no guarantee, see:
        # https://github.com/codecov/codecov-action/issues/788
        continue-on-error: true
        if: github.actor != 'dependabot[bot]' && !github.event.pull_request.head.repo.fork
        with:
          token: ${{ secrets.CODECOV_TOKEN }}
          files: ./site/coverage/lcov.info
          flags: unittest-js

  test-e2e:
    runs-on: ${{ github.repository_owner == 'coder' && 'buildjet-8vcpu-ubuntu-2204' || 'ubuntu-latest' }}
    needs: changes
    if: needs.changes.outputs.go == 'true' || needs.changes.outputs.ts == 'true' || needs.changes.outputs.ci == 'true' || github.ref == 'refs/heads/main'
    timeout-minutes: 20
    steps:
      - name: Checkout
        uses: actions/checkout@v3
        with:
          fetch-depth: 1

      - name: Setup Node
        uses: ./.github/actions/setup-node

      - name: Setup Go
        uses: ./.github/actions/setup-go

      - name: Setup Terraform
        uses: ./.github/actions/setup-tf

      - name: go install tools
        run: |
          go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.30
          go install storj.io/drpc/cmd/protoc-gen-go-drpc@v0.0.33
          go install golang.org/x/tools/cmd/goimports@latest
          go install github.com/mikefarah/yq/v4@v4.30.6
          go install github.com/golang/mock/mockgen@v1.6.0

      - name: Install Protoc
        run: |
          mkdir -p /tmp/proto
          pushd /tmp/proto
          curl -L -o protoc.zip https://github.com/protocolbuffers/protobuf/releases/download/v23.3/protoc-23.3-linux-x86_64.zip
          unzip protoc.zip
          cp -r ./bin/* /usr/local/bin
          cp -r ./include /usr/local/bin/include
          popd

      - name: Build
        run: |
          make -B site/out/index.html

      - run: pnpm playwright:install
        working-directory: site

      - run: pnpm playwright:test
        env:
          DEBUG: pw:api
        working-directory: site

      - name: Upload Playwright Failed Tests
        if: always() && github.actor != 'dependabot[bot]' && runner.os == 'Linux' && !github.event.pull_request.head.repo.fork
        uses: actions/upload-artifact@v3
        with:
          name: failed-test-videos
          path: ./site/test-results/**/*.webm
          retention-days: 7

  chromatic:
    # REMARK: this is only used to build storybook and deploy it to Chromatic.
    runs-on: ubuntu-latest
    needs: changes
    if: needs.changes.outputs.ts == 'true' || needs.changes.outputs.ci == 'true'
    steps:
      - name: Checkout
        uses: actions/checkout@v3
        with:
          # Required by Chromatic for build-over-build history, otherwise we
          # only get 1 commit on shallow checkout.
          fetch-depth: 0

      - name: Setup Node
        uses: ./.github/actions/setup-node

      # This step is not meant for mainline because any detected changes to
      # storybook snapshots will require manual approval/review in order for
      # the check to pass. This is desired in PRs, but not in mainline.
      - name: Publish to Chromatic (non-mainline)
        if: github.ref != 'refs/heads/main' && github.repository_owner == 'coder'
        uses: chromaui/action@v1
        env:
          NODE_OPTIONS: "--max_old_space_size=4096"
          STORYBOOK: true
        with:
          buildScriptName: "storybook:build"
          exitOnceUploaded: true
          # This will prevent CI from failing when Chromatic detects visual changes
          exitZeroOnChanges: true
          # Chromatic states its fine to make this token public. See:
          # https://www.chromatic.com/docs/github-actions#forked-repositories
          projectToken: 695c25b6cb65
          workingDir: "./site"
          storybookBaseDir: "./site"
          # Prevent excessive build runs on minor version changes
          skip: "@(renovate/**|dependabot/**)"
          # Run TurboSnap to trace file dependencies to related stories
          # and tell chromatic to only take snapshots of relevent stories
          onlyChanged: true

      # This is a separate step for mainline only that auto accepts and changes
      # instead of holding CI up. Since we squash/merge, this is defensive to
      # avoid the same changeset from requiring review once squashed into
      # main. Chromatic is supposed to be able to detect that we use squash
      # commits, but it's good to be defensive in case, otherwise CI remains
      # infinitely "in progress" in mainline unless we re-review each build.
      - name: Publish to Chromatic (mainline)
        if: github.ref == 'refs/heads/main' && github.repository_owner == 'coder'
        uses: chromaui/action@v1
        env:
          NODE_OPTIONS: "--max_old_space_size=4096"
          STORYBOOK: true
        with:
          autoAcceptChanges: true
          # This will prevent CI from failing when Chromatic detects visual changes
          exitZeroOnChanges: true
          buildScriptName: "storybook:build"
          projectToken: 695c25b6cb65
          workingDir: "./site"
          storybookBaseDir: "./site"
          # Run TurboSnap to trace file dependencies to related stories
          # and tell chromatic to only take snapshots of relevent stories
          onlyChanged: true

  offlinedocs:
    name: offlinedocs
    needs: changes
    runs-on: ${{ github.repository_owner == 'coder' && 'buildjet-8vcpu-ubuntu-2204' || 'ubuntu-latest' }}
    if: needs.changes.outputs.offlinedocs == 'true' || needs.changes.outputs.ci == 'true'
    steps:
      - name: Checkout
        uses: actions/checkout@v3
        with:
          # 0 is required here for version.sh to work.
          fetch-depth: 0

      - name: Setup Node
        uses: ./.github/actions/setup-node
        with:
          directory: offlinedocs

      - name: Setup Go
        uses: ./.github/actions/setup-go

      - name: Install go tools
        run: |
          go install github.com/golang/mock/mockgen@v1.6.0

      - name: Setup sqlc
        uses: ./.github/actions/setup-sqlc

      - name: Format
        run: |
          cd offlinedocs
          pnpm format:check

      - name: Lint
        run: |
          cd offlinedocs
          pnpm lint

      - name: Build
        run: |
          make -j build/coder_docs_"$(./scripts/version.sh)".tgz

  required:
    runs-on: ubuntu-latest
    needs:
      - fmt
      - lint
      - gen
      - test-go
      - test-go-pg
      - test-go-race
      - test-js
      - test-e2e
      - offlinedocs
    # Allow this job to run even if the needed jobs fail, are skipped or
    # cancelled.
    if: always()
    steps:
      - name: Ensure required checks
        run: |
          echo "Checking required checks"
          echo "- fmt: ${{ needs.fmt.result }}"
          echo "- lint: ${{ needs.lint.result }}"
          echo "- gen: ${{ needs.gen.result }}"
          echo "- test-go: ${{ needs.test-go.result }}"
          echo "- test-go-pg: ${{ needs.test-go-pg.result }}"
          echo "- test-go-race: ${{ needs.test-go-race.result }}"
          echo "- test-js: ${{ needs.test-js.result }}"
          echo

          # We allow skipped jobs to pass, but not failed or cancelled jobs.
          if [[ "${{ contains(needs.*.result, 'failure') }}" == "true" || "${{ contains(needs.*.result, 'cancelled') }}" == "true" ]]; then
            echo "One of the required checks has failed or has been cancelled"
            exit 1
          fi

          echo "Required checks have passed"

  build-main-image:
    # This build and publihes ghcr.io/coder/coder-preview:main for each merge commit to main branch.
    # We are only building this for amd64 plateform. (>95% pulls are for amd64)
    needs: changes
    if: github.ref == 'refs/heads/main' && needs.changes.outputs.docs-only == 'false'
    runs-on: ${{ github.repository_owner == 'coder' && 'buildjet-8vcpu-ubuntu-2204' || 'ubuntu-latest' }}
    env:
      DOCKER_CLI_EXPERIMENTAL: "enabled"
    steps:
      - name: Checkout
        uses: actions/checkout@v3
        with:
          fetch-depth: 0

      - name: Setup Node
        uses: ./.github/actions/setup-node

      - name: Setup Go
        uses: ./.github/actions/setup-go

      - name: Setup sqlc
        uses: ./.github/actions/setup-sqlc

      - name: GHCR Login
        uses: docker/login-action@v2
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Build and push Linux amd64 Docker image
        id: build_and_push
        run: |
          set -euxo pipefail
          go mod download
          make gen/mark-fresh
          export DOCKER_IMAGE_NO_PREREQUISITES=true
          version="$(./scripts/version.sh)"
          export CODER_IMAGE_BUILD_BASE_TAG="$(CODER_IMAGE_BASE=coder-base ./scripts/image_tag.sh --version "$version")"
          make -j build/coder_linux_amd64
          ./scripts/build_docker.sh \
            --arch amd64 \
            --target ghcr.io/coder/coder-preview:main \
            --version $version \
            --push \
            build/coder_linux_amd64

          # Tag image with new package tag and push
          tag=$(echo "$version" | sed 's/+/-/g')
          docker tag ghcr.io/coder/coder-preview:main ghcr.io/coder/coder-preview:main-$tag
          docker push ghcr.io/coder/coder-preview:main-$tag

      - name: Prune old images
        uses: vlaurin/action-ghcr-prune@v0.5.0
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
          organization: coder
          container: coder-preview
          keep-younger-than: 7 # days
          keep-tags-regexes: ^pr
          prune-tags-regexes: ^main-
          prune-untagged: true
