-
Notifications
You must be signed in to change notification settings - Fork 928
8000
name: ci | |
on: | |
push: | |
branches: | |
- main | |
pull_request: | |
workflow_dispatch: | |
permissions: | |
contents: read | |
# 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 }} | |
docs: ${{ steps.filter.outputs.docs }} | |
go: ${{ steps.filter.outputs.go }} | |
site: ${{ steps.filter.outputs.site }} | |
k8s: ${{ steps.filter.outputs.k8s }} | |
ci: ${{ steps.filter.outputs.ci }} | |
db: ${{ steps.filter.outputs.db }} | |
gomod: ${{ steps.filter.outputs.gomod }} | |
offlinedocs-only: ${{ steps.filter.outputs.offlinedocs_count == steps.filter.outputs.all_count }} | |
offlinedocs: ${{ steps.filter.outputs.offlinedocs }} | |
tailnet-integration: ${{ steps.filter.outputs.tailnet-integration }} | |
steps: | |
- name: Harden Runner | |
uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1 | |
with: | |
egress-policy: audit | |
- name: Checkout | |
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | |
with: | |
fetch-depth: 1 | |
# For pull requests it's not necessary to checkout the code | |
- name: check changed files | |
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 | |
id: filter | |
with: | |
filters: | | |
all: | |
- "**" | |
docs: | |
- "docs/**" | |
- "README.md" | |
- "examples/web-server/**" | |
- "examples/monitoring/**" | |
- "examples/lima/**" | |
db: | |
- "**.sql" | |
- "coderd/database/**" | |
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/**" | |
- "helm/**" | |
- "provisioner/**" | |
- "provisionerd/**" | |
- "provisionersdk/**" | |
- "pty/**" | |
- "scaletest/**" | |
- "tailnet/**" | |
- "testutil/**" | |
gomod: | |
- "go.mod" | |
- "go.sum" | |
site: | |
- "site/**" | |
k8s: | |
- "helm/**" | |
- "scripts/Dockerfile" | |
- "scripts/Dockerfile.base" | |
- "scripts/helm.sh" | |
ci: | |
- ".github/actions/**" | |
- ".github/workflows/ci.yaml" | |
offlinedocs: | |
- "offlinedocs/**" | |
tailnet-integration: | |
- "tailnet/**" | |
- "go.mod" | |
- "go.sum" | |
- id: debug | |
run: | | |
echo "${{ toJSON(steps.filter )}}" | |
# Disabled due to instability. See: https://github.com/coder/coder/issues/14553 | |
# Re-enable once the flake hash calculation is stable. | |
# update-flake: | |
# needs: changes | |
# if: needs.changes.outputs.gomod == 'true' | |
# runs-on: ${{ github.repository_owner == 'coder' && 'depot-ubuntu-22.04-8' || 'ubuntu-latest' }} | |
# steps: | |
# - name: Checkout | |
# uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | |
# with: | |
# fetch-depth: 1 | |
# # See: https://github.com/stefanzweifel/git-auto-commit-action?tab=readme-ov-file#commits-made-by-this-action-do-not-trigger-new-workflow-runs | |
# token: ${{ secrets.CDRCI_GITHUB_TOKEN }} | |
# - name: Setup Go | |
# uses: ./.github/actions/setup-go | |
# - name: Update Nix Flake SRI Hash | |
# run: ./scripts/update-flake.sh | |
# # auto update flake for dependabot | |
# - uses: stefanzweifel/git-auto-commit-action@8621497c8c39c72f3e2a999a26b4ca1b5058a842 # v5.0.1 | |
# if: github.actor == 'dependabot[bot]' | |
# with: | |
# # Allows dependabot to still rebase! | |
# commit_message: "[dependabot skip] Update Nix Flake SRI Hash" | |
# commit_user_name: "dependabot[bot]" | |
# commit_user_email: "49699333+dependabot[bot]@users.noreply.github.com>" | |
# commit_author: "dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>" | |
# # require everyone else to update it themselves | |
# - name: Ensure No Changes | |
# if: github.actor != 'dependabot[bot]' | |
# run: git diff --exit-code | |
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' && 'depot-ubuntu-22.04-8' || 'ubuntu-latest' }} | |
steps: | |
- name: Harden Runner | |
uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1 | |
with: | |
egress-policy: audit | |
- name: Checkout | |
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | |
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: | | |
linter_ver=$(egrep -o 'GOLANGCI_LINT_VERSION=\S+' dogfood/coder/Dockerfile | cut -d '=' -f 2) | |
go install github.com/golangci/golangci-lint/cmd/golangci-lint@v$linter_ver | |
dir=$(golangci-lint cache status | awk '/Dir/ { print $2 }') | |
echo "LINT_CACHE_DIR=$dir" >> $GITHUB_ENV | |
- name: golangci-lint cache | |
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 | |
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@b1ae8d918b6e85bd611117d3d9a3be4f903ee5e4 # v1.33.1 | |
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@b9e51907a09c216f16ebe8536097933489208112 # v4.3.0 | |
with: | |
version: v3.9.2 | |
- name: make lint | |
run: | | |
make --output-sync=line -j lint | |
- name: Check workflow files | |
run: | | |
bash <(curl https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash) 1.7.4 | |
./actionlint -color -shellcheck= -ignore "set-output" | |
shell: bash | |
- name: Check for unstaged files | |
run: | | |
rm -f ./actionlint ./typos | |
./scripts/check_unstaged.sh | |
shell: bash | |
gen: | |
timeout-minutes: 8 | |
runs-on: ${{ github.repository_owner == 'coder' && 'depot-ubuntu-22.04-8' || 'ubuntu-latest' }} | |
if: ${{ !cancelled() }} | |
steps: | |
- name: Harden Runner | |
uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1 | |
with: | |
egress-policy: audit | |
- name: Checkout | |
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | |
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: Setup Terraform | |
uses: ./.github/actions/setup-tf | |
- name: go install tools | |
uses: ./.github/actions/setup-go-tools | |
- name: Install Protoc | |
run: | | |
mkdir -p /tmp/proto | |
pushd /tmp/proto | |
curl -L -o protoc.zip https://github.com/protocolbuffers/protobuf/releases/download/v23.4/protoc-23.4-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: | | |
# Remove golden files to detect discrepancy in generated files. | |
make clean/golden-files | |
# Notifications require DB, we could start a DB instance here but | |
# let's just restore for now. | |
git checkout -- coderd/notifications/testdata/rendered-templates | |
# no `-j` flag as `make` fails with: | |
# coderd/rbac/object_gen.go:1:1: syntax error: package statement must be first | |
make --output-sync -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' && 'depot-ubuntu-22.04-8' || 'ubuntu-latest' }} | |
timeout-minutes: 7 | |
steps: | |
- name: Harden Runner | |
uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1 | |
with: | |
egress-policy: audit | |
- name: Checkout | |
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | |
with: | |
fetch-depth: 1 | |
- name: Setup Node | |
uses: ./.github/actions/setup-node | |
- name: Check Go version | |
run: IGNORE_NIX=true ./scripts/check_go_versions.sh | |
# Use default Go version | |
- name: Setup Go | |
uses: ./.github/actions/setup-go | |
- name: Install shfmt | |
run: go install mvdan.cc/sh/v3/cmd/shfmt@v3.7.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' && 'depot-ubuntu-22.04-4' || matrix.os == 'macos-latest' && github.repository_owner == 'coder' && 'depot-macos-latest' || matrix.os == 'windows-2022' && github.repository_owner == 'coder' && 'depot-windows-2022-16' || matrix.os }} | |
needs: changes | |
if: needs.changes.outputs.go == 'true' || needs.changes.outputs.ci == 'tr 8000 ue' || github.ref == 'refs/heads/main' | |
timeout-minutes: 20 | |
strategy: | |
fail-fast: false | |
matrix: | |
os: | |
- ubuntu-latest | |
- macos-latest | |
- windows-2022 | |
steps: | |
- name: Harden Runner | |
# Harden Runner is only supported on Ubuntu runners. | |
if: runner.os == 'Linux' | |
uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1 | |
with: | |
egress-policy: audit | |
# Set up RAM disks to speed up the rest of the job. This action is in | |
# a separate repository to allow its use before actions/checkout. | |
- name: Setup RAM Disks | |
if: runner.os == 'Windows' | |
uses: coder/setup-ramdisk-action@e1100847ab2d7bcd9d14bcda8f2d1b0f07b36f1b | |
- name: Checkout | |
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | |
with: | |
fetch-depth: 1 | |
- name: Setup Go Paths | |
uses: ./.github/actions/setup-go-paths | |
- name: Setup Go | |
uses: ./.github/actions/setup-go | |
with: | |
# Runners have Go baked-in and Go will automatically | |
# download the toolchain configured in go.mod, so we don't | |
# need to reinstall it. It's faster on Windows runners. | |
use-preinstalled-go: ${{ runner.os == 'Windows' }} | |
- name: Setup Terraform | |
uses: ./.github/actions/setup-tf | |
- name: Download Test Cache | |
id: download-cache | |
uses: ./.github/actions/test-cache/download | |
with: | |
key-prefix: test-go-${{ runner.os }}-${{ runner.arch }} | |
- name: Test with Mock Database | |
id: test | |
shell: bash | |
run: | | |
# if macOS, install google-chrome for scaletests. As another concern, | |
# should we really have this kind of external dependency requirement | |
# on standard CI? | |
if [ "${{ matrix.os }}" == "macos-latest" ]; then | |
brew install google-chrome | |
fi | |
# By default Go will use the number of logical CPUs, which | |
# is a fine default. | |
PARALLEL_FLAG="" | |
# macOS will output "The default interactive shell is now zsh" | |
# intermittently in CI... | |
if [ "${{ matrix.os }}" == "macos-latest" ]; then | |
touch ~/.bash_profile && echo "export BASH_SILENCE_DEPRECATION_WARNING=1" >> ~/.bash_profile | |
fi | |
export TS_DEBUG_DISCO=true | |
gotestsum --junitfile="gotests.xml" --jsonfile="gotests.json" --rerun-fails=2 \ | |
--packages="./..." -- $PARALLEL_FLAG -short | |
- name: Upload Test Cache | |
uses: ./.github/actions/test-cache/upload | |
with: | |
cache-key: ${{ steps.download-cache.outputs.cache-key }} | |
- name: Upload test stats to Datadog | |
timeout-minutes: 1 | |
continue-on-error: true | |
uses: ./.github/actions/upload-datadog | |
if: success() || failure() | |
with: | |
api-key: ${{ secrets.DATADOG_API_KEY }} | |
test-go-pg: | |
# make sure to adjust NUM_PARALLEL_PACKAGES and NUM_PARALLEL_TESTS below | |
# when changing runner sizes | |
runs-on: ${{ matrix.os == 'ubuntu-latest' && github.repository_owner == 'coder' && 'depot-ubuntu-22.04-8' || matrix.os && matrix.os == 'macos-latest' && github.repository_owner == 'coder' && 'depot-macos-latest' || matrix.os == 'windows-2022' && github.repository_owner == 'coder' && 'depot-windows-2022-16' || matrix.os }} | |
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 | |
strategy: | |
matrix: | |
os: | |
- ubuntu-latest | |
- macos-latest | |
- windows-2022 | |
steps: | |
- name: Harden Runner | |
uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1 | |
with: | |
egress-policy: audit | |
# macOS indexes all new files in the background. Our Postgres tests | |
# create and destroy thousands of databases on disk, and Spotlight | |
# tries to index all of them, seriously slowing down the tests. | |
- name: Disable Spotlight Indexing | |
8000 if: runner.os == 'macOS' | |
run: | | |
sudo mdutil -a -i off | |
sudo mdutil -X / | |
sudo launchctl bootout system /System/Library/LaunchDaemons/com.apple.metadata.mds.plist | |
# Set up RAM disks to speed up the rest of the job. This action is in | |
# a separate repository to allow its use before actions/checkout. | |
- name: Setup RAM Disks | |
if: runner.os == 'Windows' | |
uses: coder/setup-ramdisk-action@e1100847ab2d7bcd9d14bcda8f2d1b0f07b36f1b | |
- name: Checkout | |
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | |
with: | |
fetch-depth: 1 | |
- name: Setup Go Paths | |
id: go-paths | |
uses: ./.github/actions/setup-go-paths | |
- name: Download Go Build Cache | |
id: download-go-build-cache | |
uses: ./.github/actions/test-cache/download | |
with: | |
key-prefix: test-go-build-${{ runner.os }}-${{ runner.arch }} | |
cache-path: ${{ steps.go-paths.outputs.cached-dirs }} | |
- name: Setup Go | |
uses: ./.github/actions/setup-go | |
with: | |
# Runners have Go baked-in and Go will automatically | |
# download the toolchain configured in go.mod, so we don't | |
# need to reinstall it. It's faster on Windows runners. | |
use-preinstalled-go: ${{ runner.os == 'Windows' }} | |
# Cache is already downloaded above | |
use-cache: false | |
- name: Setup Terraform | |
uses: ./.github/actions/setup-tf | |
- name: Download Test Cache | |
id: download-cache | |
uses: ./.github/actions/test-cache/download | |
with: | |
key-prefix: test-go-pg-${{ runner.os }}-${{ runner.arch }} | |
- name: Setup Embedded Postgres Cache Paths | |
id: embedded-pg-cache | |
uses: ./.github/actions/setup-embedded-pg-cache-paths | |
- name: Download Embedded Postgres Cache | |
id: download-embedded-pg-cache | |
uses: ./.github/actions/embedded-pg-cache/download | |
with: | |
key-prefix: embedded-pg-${{ runner.os }}-${{ runner.arch }} | |
cache-path: ${{ steps.embedded-pg-cache.outputs.cached-dirs }} | |
- name: Normalize File and Directory Timestamps | |
shell: bash | |
# Normalize file modification timestamps so that go test can use the | |
# cache from the previous CI run. See https://github.com/golang/go/issues/58571 | |
# for more details. | |
run: | | |
find . -type f ! -path ./.git/\*\* | mtimehash | |
find . -type d ! -path ./.git/\*\* -exec touch -t 200601010000 {} + | |
- name: Test with PostgreSQL Database | |
env: | |
POSTGRES_VERSION: "13" | |
TS_DEBUG_DISCO: "true" | |
LC_CTYPE: "en_US.UTF-8" | |
LC_ALL: "en_US.UTF-8" | |
shell: bash | |
run: | | |
set -o errexit | |
set -o pipefail | |
if [ "${{ runner.os }}" == "Windows" ]; then | |
# Create a temp dir on the R: ramdisk drive for Windows. The default | |
# C: drive is extremely slow: https://github.com/actions/runner-images/issues/8755 | |
mkdir -p "R:/temp/embedded-pg" | |
go run scripts/embedded-pg/main.go -path "R:/temp/embedded-pg" -cache "${EMBEDDED_PG_CACHE_DIR}" | |
elif [ "${{ runner.os }}" == "macOS" ]; then | |
# Postgres runs faster on a ramdisk on macOS too | |
mkdir -p /tmp/tmpfs | |
sudo mount_tmpfs -o noowners -s 8g /tmp/tmpfs | |
go run scripts/embedded-pg/main.go -path /tmp/tmpfs/embedded-pg -cache "${EMBEDDED_PG_CACHE_DIR}" | |
elif [ "${{ runner.os }}" == "Linux" ]; then | |
make test-postgres-docker | |
fi | |
# if macOS, install google-chrome for scaletests | |
# As another concern, should we really have this kind of external dependency | |
# requirement on standard CI? | |
if [ "${{ matrix.os }}" == "macos-latest" ]; then | |
brew install google-chrome | |
fi | |
# macOS will output "The default interactive shell is now zsh" | |
# intermittently in CI... | |
if [ "${{ matrix.os }}" == "macos-latest" ]; then | |
touch ~/.bash_profile && echo "export BASH_SILENCE_DEPRECATION_WARNING=1" >> ~/.bash_profile | |
fi | |
if [ "${{ runner.os }}" == "Windows" ]; then | |
# Our Windows runners have 16 cores. | |
# On Windows Postgres chokes up when we have 16x16=256 tests | |
# running in parallel, and dbtestutil.NewDB starts to take more than | |
# 10s to complete sometimes causing test timeouts. With 16x8=128 tests | |
# Postgres tends not to choke. | |
NUM_PARALLEL_PACKAGES=8 | |
NUM_PARALLEL_TESTS=16 | |
elif [ "${{ runner.os }}" == "macOS" ]; then | |
# Our macOS runners have 8 cores. We set NUM_PARALLEL_TESTS to 16 | |
# because the tests complete faster and Postgres doesn't choke. It seems | |
# that macOS's tmpfs is faster than the one on Windows. | |
NUM_PARALLEL_PACKAGES=8 | |
NUM_PARALLEL_TESTS=16 | |
elif [ "${{ runner.os }}" == "Linux" ]; then | |
# Our Linux runners have 8 cores. | |
NUM_PARALLEL_PACKAGES=8 | |
NUM_PARALLEL_TESTS=8 | |
fi | |
# by default, run tests with cache | |
TESTCOUNT="" | |
if [ "${{ github.ref }}" == "refs/heads/main" ]; then | |
# on main, run tests without cache | |
TESTCOUNT="-count=1" | |
fi | |
mkdir -p "$RUNNER_TEMP/sym" | |
source scripts/normalize_path.sh | |
# terraform gets installed in a random directory, so we need to normalize | |
# the path to the terraform binary or a bunch of cached tests will be | |
# invalidated. See scripts/normalize_path.sh for more details. | |
normalize_path_with_symlinks "$RUNNER_TEMP/sym" "$(dirname $(which terraform))" | |
# We rerun failing tests to counteract flakiness coming from Postgres | |
# choking on macOS and Windows sometimes. | |
DB=ci gotestsum --rerun-fails=2 --rerun-fails-max-failures=50 \ | |
--format standard-quiet --packages "./..." \ | |
-- -timeout=20m -v -p $NUM_PARALLEL_PACKAGES -parallel=$NUM_PARALLEL_TESTS $TESTCOUNT | |
- name: Upload Go Build Cache | |
uses: ./.github/actions/test-cache/upload | |
with: | |
cache-key: ${{ steps.download-go-build-cache.outputs.cache-key }} | |
cache-path: ${{ steps.go-paths.outputs.cached-dirs }} | |
- name: Upload Test Cache | |
uses: ./.github/actions/test-cache/upload | |
with: | |
cache-key: ${{ steps.download-cache.outputs.cache-key }} | |
- name: Upload Embedded Postgres Cache | |
uses: ./.github/actions/embedded-pg-cache/upload | |
# We only use the embedded Postgres cache on macOS and Windows runners. | |
if: runner.OS == 'macOS' || runner.OS == 'Windows' | |
with: | |
cache-key: ${{ steps.download-embedded-pg-cache.outputs.cache-key }} | |
cache-path: "${{ steps.embedded-pg-cache.outputs.embedded-pg-cache }}" | |
- name: Upload test stats to Datadog | |
timeout-minutes: 1 | |
continue-on-error: true | |
uses: ./.github/actions/upload-datadog | |
if: success() || failure() | |
with: | |
api-key: ${{ secrets.DATADOG_API_KEY }} | |
# NOTE: this could instead be defined as a matrix strategy, but we want to | |
# only block merging if tests on postgres 13 fail. Using a matrix strategy | |
# here makes the check in the above `required` job rather complicated. | |
test-go-pg-17: | |
runs-on: ${{ github.repository_owner == 'coder' && 'depot-ubuntu-22.04-8' || '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: Harden Runner | |
uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1 | |
with: | |
egress-policy: audit | |
- name: Checkout | |
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | |
with: | |
fetch-depth: 1 | |
- name: Setup Go | |
uses: ./.github/actions/setup-go | |
- name: Setup Terraform | |
uses: ./.github/actions/setup-tf | |
- name: Download Test Cache | |
id: download-cache | |
uses: ./.github/actions/test-cache/download | |
with: | |
key-prefix: test-go-pg-17-${{ runner.os }}-${{ runner.arch }} | |
- name: Test with PostgreSQL Database | |
env: | |
POSTGRES_VERSION: "17" | |
TS_DEBUG_DISCO: "true" | |
TEST_RETRIES: 2 | |
run: | | |
make test-postgres | |
- name: Upload Test Cache | |
uses: ./.github/actions/test-cache/upload | |
with: | |
cache-key: ${{ steps.download-cache.outputs.cache-key }} | |
- name: Upload test stats to Datadog | |
timeout-minutes: 1 | |
continue-on-error: true | |
uses: ./.github/actions/upload-datadog | |
if: success() || failure() | |
with: | |
api-key: ${{ secrets.DATADOG_API_KEY }} | |
test-go-race: | |
runs-on: ${{ github.repository_owner == 'coder' && 'depot-ubuntu-22.04-16' || '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: Harden Runner | |
uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1 | |
with: | |
egress-policy: audit | |
- name: Checkout | |
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | |
with: | |
fetch-depth: 1 | |
- name: Setup Go | |
uses: ./.github/actions/setup-go | |
- name: Setup Terraform | |
uses: ./.github/actions/setup-tf | |
- name: Download Test Cache | |
id: download-cache | |
uses: ./.github/actions/test-cache/download | |
with: | |
key-prefix: test-go-race-${{ runner.os }}-${{ runner.arch }} | |
# We run race tests with reduced parallelism because they use more CPU and we were finding | |
# instances where tests appear to hang for multiple seconds, resulting in flaky tests when | |
# short timeouts are used. | |
# c.f. discussion on https://github.com/coder/coder/pull/15106 | |
- name: Run Tests | |
run: | | |
gotestsum --junitfile="gotests.xml" --packages="./..." --rerun-fails=2 --rerun-fails-abort-on-data-race -- -race -parallel 4 -p 4 | |
- name: Upload Test Cache | |
uses: ./.github/actions/test-cache/upload | |
with: | |
cache-key: ${{ steps.download-cache.outputs.cache-key }} | |
- name: Upload test stats to Datadog | |
timeout-minutes: 1 | |
continue-on-error: true | |
uses: ./.github/actions/upload-datadog | |
if: always() | |
with: | |
api-key: ${{ secrets.DATADOG_API_KEY }} | |
test-go-race-pg: | |
runs-on: ${{ github.repository_owner == 'coder' && 'depot-ubuntu-22.04-16' || '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: Harden Runner | |
uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1 | |
with: | |
egress-policy: audit | |
- name: Checkout | |
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | |
with: | |
fetch-depth: 1 | |
- name: Setup Go | |
uses: ./.github/actions/setup-go | |
- name: Setup Terraform | |
uses: ./.github/actions/setup-tf | |
- name: Download Test Cache | |
id: download-cache | |
uses: ./.github/actions/test-cache/download | |
with: | |
key-prefix: test-go-race-pg-${{ runner.os }}-${{ runner.arch }} | |
# We run race tests with reduced parallelism because they use more CPU and we were finding | |
# instances where tests appear to hang for multiple seconds, resulting in flaky tests when | |
# short timeouts are used. | |
# c.f. discussion on https://github.com/coder/coder/pull/15106 | |
- name: Run Tests | |
env: | |
POSTGRES_VERSION: "17" | |
run: | | |
make test-postgres-docker | |
DB=ci gotestsum --junitfile="gotests.xml" --packages="./..." --rerun-fails=2 --rerun-fails-abort-on-data-race -- -race -parallel 4 -p 4 | |
- name: Upload Test Cache | |
uses: ./.github/actions/test-cache/upload | |
with: | |
cache-key: ${{ steps.download-cache.outputs.cache-key }} | |
- name: Upload test stats to Datadog | |
timeout-minutes: 1 | |
continue-on-error: true | |
uses: ./.github/actions/upload-datadog | |
if: always() | |
with: | |
api-key: ${{ secrets.DATADOG_API_KEY }} | |
# Tailnet integration tests only run when the `tailnet` directory or `go.sum` | |
# and `go.mod` are changed. These tests are to ensure we don't add regressions | |
# to tailnet, either due to our code or due to updating dependencies. | |
# | |
# These tests are skipped in the main go test jobs because they require root | |
# and mess with networking. | |
test-go-tailnet-integration: | |
runs-on: ${{ github.repository_owner == 'coder' && 'depot-ubuntu-22.04-8' || 'ubuntu-latest' }} | |
needs: changes | |
# Unnecessary to run on main for now | |
if: needs.changes.outputs.tailnet-integration == 'true' || needs.changes.outputs.ci == 'true' | |
timeout-minutes: 20 | |
steps: | |
- name: Harden Runner | |
uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1 | |
with: | |
egress-policy: audit | |
- name: Checkout | |
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | |
with: | |
fetch-depth: 1 | |
- name: Setup Go | |
uses: ./.github/actions/setup-go | |
# Used by some integration tests. | |
- name: Install Nginx | |
run: sudo apt-get update && sudo apt-get install -y nginx | |
- name: Run Tests | |
run: make test-tailnet-integration | |
test-js: | |
runs-on: ${{ github.repository_owner == 'coder' && 'depot-ubuntu-22.04-8' || 'ubuntu-latest' }} | |
needs: changes | |
if: needs.changes.outputs.site == 'true' || needs.changes.outputs.ci == 'true' || github.ref == 'refs/heads/main' | |
timeout-minutes: 20 | |
steps: | |
- name: Harden Runner | |
uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1 | |
with: | |
egress-policy: audit | |
- name: Checkout | |
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | |
with: | |
fetch-depth: 1 | |
- name: Setup Node | |
uses: ./.github/actions/setup-node | |
- run: pnpm test:ci --max-workers $(nproc) | |
working-directory: site | |
test-e2e: | |
runs-on: ${{ github.repository_owner == 'coder' && 'depot-ubuntu-22.04-4' || 'ubuntu-latest' }} | |
needs: changes | |
strategy: | |
fail-fast: false | |
matrix: | |
variant: | |
- premium: false | |
name: test-e2e | |
#- premium: true | |
# name: test-e2e-premium | |
# Skip test-e2e on forks as they don't have access to CI secrets | |
if: (needs.changes.outputs.go == 'true' || needs.changes.outputs.site == 'true' || needs.changes.outputs.ci == 'true' || github.ref == 'refs/heads/main') && !(github.event.pull_request.head.repo.fork) | |
timeout-minutes: 20 | |
name: ${{ matrix.variant.name }} | |
steps: | |
- name: Harden Runner | |
uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1 | |
with: | |
egress-policy: audit | |
- name: Checkout | |
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | |
with: | |
fetch-depth: 1 | |
- name: Setup Node | |
uses: ./.github/actions/setup-node | |
- name: Setup Go | |
uses: ./.github/actions/setup-go | |
# Assume that the checked-in versions are up-to-date | |
- run: make gen/mark-fresh | |
name: make gen | |
- run: make site/e2e/bin/coder | |
name: make coder | |
- run: pnpm build | |
env: | |
NODE_OPTIONS: ${{ github.repository_owner == 'coder' && '--max_old_space_size=8192' || '' }} | |
working-directory: site | |
- run: pnpm playwright:install | |
working-directory: site | |
# Run tests that don't require a premium license without a premium license | |
- run: pnpm playwright:test --forbid-only --workers 1 | |
if: ${{ !matrix.variant.premium }} | |
env: | |
DEBUG: pw:api | |
CODER_E2E_TEST_RETRIES: 2 | |
working-directory: site | |
# Run all of the tests with a premium license | |
- run: pnpm playwright:test --forbid-only --workers 1 | |
if: ${{ matrix.variant.premium }} | |
env: | |
DEBUG: pw:api | |
CODER_E2E_LICENSE: ${{ secrets.CODER_E2E_LICENSE }} | |
CODER_E2E_REQUIRE_PREMIUM_TESTS: "1" | |
CODER_E2E_TEST_RETRIES: 2 | |
8000 | 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@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 | |
with: | |
name: failed-test-videos${{ matrix.variant.premium && '-premium' || '' }} | |
path: ./site/test-results/**/*.webm | |
retention-days: 7 | |
- name: Upload pprof dumps | |
if: always() && github.actor != 'dependabot[bot]' && runner.os == 'Linux' && !github.event.pull_request.head.repo.fork | |
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 | |
with: | |
name: debug-pprof-dumps${{ matrix.variant.premium && '-premium' || '' }} | |
path: ./site/test-results/**/debug-pprof-*.txt | |
retention-days: 7 | |
# Reference guide: | |
# https://www.chromatic.com/docs/turbosnap-best-practices/#run-with-caution-when-using-the-pull_request-event | |
chromatic: | |
# REMARK: this is only used to build storybook and deploy it to Chromatic. | |
runs-on: ubuntu-latest | |
needs: changes | |
if: needs.changes.outputs.site == 'true' || needs.changes.outputs.ci == 'true' | |
steps: | |
- name: Harden Runner | |
uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1 | |
with: | |
egress-policy: audit | |
- name: Checkout | |
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | |
with: | |
# 👇 Ensures Chromatic can read your full git history | |
fetch-depth: 0 | |
# 👇 Tells the checkout which commit hash to reference | |
ref: ${{ github.event.pull_request.head.ref }} | |
- 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@b5848056bb67ce5f1cccca8e62a37cbd9dd42871 # v13.0.1 | |
env: | |
NODE_OPTIONS: "--max_old_space_size=4096" | |
STORYBOOK: true | |
with: | |
# Do a fast, testing build for change previews | |
buildScriptName: "storybook:ci" | |
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" | |
storybookConfigDir: "./site/.storybook" | |
# 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 relevant stories | |
onlyChanged: true | |
# Avoid uploading single files, because that's very slow | |
zip: 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@b5848056bb67ce5f1cccca8e62a37cbd9dd42871 # v13.0.1 | |
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 | |
# Do a full build with documentation for mainline builds | |
buildScriptName: "storybook:build" | |
projectToken: 695c25b6cb65 | |
workingDir: "./site" | |
storybookBaseDir: "./site" | |
storybookConfigDir: "./site/.storybook" | |
# Run TurboSnap to trace file dependencies to related stories | |
# and tell chromatic to only take snapshots of relevant stories | |
onlyChanged: true | |
# Avoid uploading single files, because that's very slow | |
zip: true | |
offlinedocs: | |
name: offlinedocs | |
needs: changes | |
runs-on: ${{ github.repository_owner == 'coder' && 'depot-ubuntu-22.04-8' || 'ubuntu-latest' }} | |
if: needs.cha 8000 nges.outputs.offlinedocs == 'true' || needs.changes.outputs.ci == 'true' || needs.changes.outputs.docs == 'true' | |
steps: | |
- name: Harden Runner | |
uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1 | |
with: | |
egress-policy: audit | |
- name: Checkout | |
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | |
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: Install Protoc | |
run: | | |
mkdir -p /tmp/proto | |
pushd /tmp/proto | |
curl -L -o protoc.zip https://github.com/protocolbuffers/protobuf/releases/download/v23.4/protoc-23.4-linux-x86_64.zip | |
unzip protoc.zip | |
cp -r ./bin/* /usr/local/bin | |
cp -r ./include /usr/local/bin/include | |
popd | |
- name: Setup Go | |
uses: ./.github/actions/setup-go | |
- name: Install go tools | |
uses: ./.github/actions/setup-go-tools | |
- 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 | |
# no `-j` flag as `make` fails with: | |
# coderd/rbac/object_gen.go:1:1: syntax error: package statement must be first | |
run: | | |
make 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-go-race-pg | |
- test-js | |
- test-e2e | |
- offlinedocs | |
- sqlc-vet | |
# Allow this job to run even if the needed jobs fail, are skipped or | |
# cancelled. | |
if: always() | |
steps: | |
- name: Harden Runner | |
uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1 | |
with: | |
egress-policy: audit | |
- 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-go-race-pg: ${{ needs.test-go-race-pg.result }}" | |
echo "- test-js: ${{ needs.test-js.result }}" | |
echo "- test-e2e: ${{ needs.test-e2e.result }}" | |
echo "- offlinedocs: ${{ needs.offlinedocs.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" | |
# Builds the dylibs and upload it as an artifact so it can be embedded in the main build | |
build-dylib: | |
needs: changes | |
# We always build the dylibs on Go changes to verify we're not merging unbuildable code, | |
# but they need only be signed and uploaded on coder/coder main. | |
if: needs.changes.outputs.go == 'true' || needs.changes.outputs.ci == 'true' || github.ref == 'refs/heads/main' | |
runs-on: ${{ github.repository_owner == 'coder' && 'depot-macos-latest' || 'macos-latest' }} | |
steps: | |
# Harden Runner doesn't work on macOS | |
- name: Checkout | |
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | |
with: | |
fetch-depth: 0 | |
- name: Setup b 8000 uild 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.0.0" | |
- name: Setup Go | |
uses: ./.github/actions/setup-go | |
- name: Install rcodesign | |
if: ${{ github.repository_owner == 'coder' && github.ref == 'refs/heads/main' }} | |
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 | |
if: ${{ github.repository_owner == 'coder' && github.ref == 'refs/heads/main' }} | |
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: ${{ github.ref == 'refs/heads/main' && '1' || '0' }} | |
AC_CERTIFICATE_FILE: /tmp/apple_cert.p12 | |
AC_CERTIFICATE_PASSWORD_FILE: /tmp/apple_cert_password.txt | |
- name: Upload build artifacts | |
if: ${{ github.repository_owner == 'coder' && github.ref == 'refs/heads/main' }} | |
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 | |
if: ${{ github.repository_owner == 'coder' && github.ref == 'refs/heads/main' }} | |
run: rm -f /tmp/{apple_cert.p12,apple_cert_password.txt,apple_apikey.p8} | |
build: | |
# This builds and publishes ghcr.io/coder/coder-preview:main for each commit | |
# to main branch. | |
needs: | |
- changes | |
- build-dylib | |
if: github.ref == 'refs/heads/main' && needs.changes.outputs.docs-only == 'false' && !github.event.pull_request.head.repo.fork | |
runs-on: ${{ github.repository_owner == 'coder' && 'depot-ubuntu-22.04-8' || 'ubuntu-22.04' }} | |
permissions: | |
# Necessary to push docker images to ghcr.io. | |
packages: write | |
# Necessary for GCP authentication (https://github.com/google-github-actions/setup-gcloud#usage) | |
# Also necessary for keyless cosign (https://docs.sigstore.dev/cosign/signing/overview/) | |
# And for GitHub Actions attestation | |
id-token: write | |
# Required for GitHub Actions attestation | |
attestations: write | |
env: | |
DOCKER_CLI_EXPERIMENTAL: "enabled" | |
outputs: | |
IMAGE: ghcr.io/coder/coder-preview:${{ steps.build-docker.outputs.tag }} | |
steps: | |
- name: Harden Runner | |
uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1 | |
with: | |
egress-policy: audit | |
- name: Checkout | |
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | |
with: | |
fetch-depth: 0 | |
- name: GHCR Login | |
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0 | |
with: | |
registry: ghcr.io | |
username: ${{ github.actor }} | |
password: ${{ secrets.GITHUB_TOKEN }} | |
- name: Setup Node | |
uses: ./.github/actions/setup-node | |
- name: Setup Go | |
uses: ./.github/actions/setup-go | |
# 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 nfpm | |
run: go install github.com/goreleaser/nfpm/v2/cmd/nfpm@v2.35.1 | |
- name: Install zstd | |
run: sudo apt-get install -y zstd | |
- name: Install cosign | |
uses: ./.github/actions/install-cosign | |
- name: Install syft | |
uses: ./.github/actions/install-syft | |
- 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 }} | |
# Setup GCloud for signing Windows binaries. | |
- name: Authenticate to Google Cloud | |
id: gcloud_auth | |
uses: google-github-actions/auth@ba79af03959ebeac9769e648f473a284504d9193 # v2.1.10 | |
with: | |
workload_identity_provider: ${{ secrets.GCP_CODE_SIGNING_WORKLOAD_ID_PROVIDER }} | |
service_account: ${{ secrets.GCP_CODE_SIGNING_SERVICE_ACCOUNT }} | |
token_format: "access_token" | |
- name: Setup GCloud SDK | |
uses: google-github-actions/setup-gcloud@77e7a554d41e2ee56fc945c52dfd3f33d12def9a # v2.1.4 | |
- name: Download dylibs | |
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.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 | |
run: | | |
set -euxo pipefail | |
go mod download | |
version="$(./scripts/version.sh)" | |
tag="main-$(echo "$version" | sed 's/+/-/g')" | |
echo "tag=$tag" >> $GITHUB_OUTPUT | |
make gen/mark-fresh | |
make -j \ | |
build/coder_linux_{amd64,arm64,armv7} \ | |
build/coder_"$version"_windows_amd64.zip \ | |
build/coder_"$version"_linux_amd64.{tar.gz,deb} | |
env: | |
# The Windows slim binary must be signed for Coder Desktop to accept | |
# it. The darwin executables don't need to be signed, but the dylibs | |
# do (see above). | |
CODER_SIGN_WINDOWS: "1" | |
CODER_WINDOWS_RESOURCES: "1" | |
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: Build Linux Docker images | |
id: build-docker | |
env: | |
CODER_IMAGE_BASE: ghcr.io/coder/coder-preview | |
CODER_IMAGE_TAG_PREFIX: main | |
DOCKER_CLI_EXPERIMENTAL: "enabled" | |
run: | | |
set -euxo pipefail | |
# build Docker images for each architecture | |
version="$(./scripts/version.sh)" | |
tag="main-$(echo "$version" | sed 's/+/-/g')" | |
echo "tag=$tag" >> $GITHUB_OUTPUT | |
# build images for each architecture | |
# note: omitting the -j argument to avoid race conditions when pushing | |
make build/coder_"$version"_linux_{amd64,arm64,armv7}.tag | |
# only push if we are on main branch | |
if [ "${{ github.ref }}" == "refs/heads/main" ]; then | |
# build and push multi-arch manifest, this depends on the other images | |
# being pushed so will automatically push them | |
# note: omitting the -j argument to avoid race conditions when pushing | |
make push/build/coder_"$version"_linux_{amd64,arm64,armv7}.tag | |
# Define specific tags | |
tags=("$tag" "main" "latest") | |
# Create and push a multi-arch manifest for each tag | |
# we are adding `latest` tag and keeping `main` for backward | |
# compatibality | |
for t in "${tags[@]}"; do | |
./scripts/build_docker_multiarch.sh \ | |
--push \ | |
--target "ghcr.io/coder/coder-preview:$t" \ | |
--version $version \ | |
$(cat build/coder_"$version"_linux_{amd64,arm64,armv7}.tag) | |
done | |
fi | |
- name: SBOM Generation and Attestation | |
if: github.ref == 'refs/heads/main' | |
continue-on-error: true | |
env: | |
COSIGN_EXPERIMENTAL: 1 | |
run: | | |
set -euxo pipefail | |
# Define image base and tags | |
IMAGE_BASE="ghcr.io/coder/coder-preview" | |
TAGS=("${{ steps.build-docker.outputs.tag }}" "main" "latest") | |
# Generate and attest SBOM for each tag | |
for tag in "${TAGS[@]}"; do | |
IMAGE="${IMAGE_BASE}:${tag}" | |
SBOM_FILE="coder_sbom_${tag//[:\/]/_}.spdx.json" | |
echo "Generating SBOM for image: ${IMAGE}" | |
syft "${IMAGE}" -o spdx-json > "${SBOM_FILE}" | |
echo "Attesting SBOM to image: ${IMAGE}" | |
cosign clean --force=true "${IMAGE}" | |
cosign attest --type spdxjson \ | |
--predicate "${SBOM_FILE}" \ | |
--yes \ | |
"${IMAGE}" | |
done | |
# GitHub attestation provides SLSA provenance for the 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. | |
# | |
# We attest each tag separately to ensure all tags have proper provenance records. | |
# TODO: Consider refactoring these steps to use a matrix strategy or composite action to reduce duplication | |
# while maintaining the required functionality for each tag. | |
- name: GitHub Attestation for Docker image | |
id: attest_main | |
if: github.ref == 'refs/heads/main' | |
continue-on-error: true | |
uses: actions/attest@ce27ba3b4a9a139d9a20a4a07d69fabb52f1e5bc # v2.4.0 | |
with: | |
subject-name: "ghcr.io/coder/coder-preview:main" | |
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/ci.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: GitHub Attestation for Docker image (latest tag) | |
id: attest_latest | |
if: github.ref == 'refs/heads/main' | |
continue-on-error: true | |
uses: actions/attest@ce27ba3b4a9a139d9a20a4a07d69fabb52f1e5bc # v2.4.0 | |
with: | |
subject-name: "ghcr.io/coder/coder-preview:latest" | |
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/ci.yaml" | |
}, | |
"environment": { | |
"github_workflow": "${{ github.workflow }}", | |
"github_ 8000 run_id": "${{ github.run_id }}" | |
} | |
}, | |
"metadata": { | |
"buildInvocationID": "${{ github.run_id }}", | |
"completeness": { | |
"environment": true, | |
"materials": true | |
} | |
} | |
} | |
push-to-registry: true | |
- name: GitHub Attestation for version-specific Docker image | |
id: attest_version | |
if: github.ref == 'refs/heads/main' | |
continue-on-error: true | |
uses: actions/attest@ce27ba3b4a9a139d9a20a4a07d69fabb52f1e5bc # v2.4.0 | |
with: | |
subject-name: "ghcr.io/coder/coder-preview:${{ steps.build-docker.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/ci.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: github.ref == 'refs/heads/main' | |
run: | | |
if [[ "${{ steps.attest_main.outcome }}" == "failure" ]]; then | |
echo "::warning::GitHub attestation for main tag failed" | |
fi | |
if [[ "${{ steps.attest_latest.outcome }}" == "failure" ]]; then | |
echo "::warning::GitHub attestation for latest tag failed" | |
fi | |
if [[ "${{ steps.attest_version.outcome }}" == "failure" ]]; then | |
echo "::warning::GitHub attestation for version-specific tag failed" | |
fi | |
- name: Prune old images | |
if: github.ref == 'refs/heads/main' | |
uses: vlaurin/action-ghcr-prune@0cf7d39f88546edd31965acba78cdcb0be14d641 # v0.6.0 | |
with: | |
token: ${{ secrets.GITHUB_TOKEN }} | |
organization: coder | |
container: coder-preview | |
keep-younger-than: 7 # days | |
keep-tags: latest | |
keep-tags-regexes: ^pr | |
prune-tags-regexes: | | |
^main- | |
^v | |
prune-untagged: true | |
- name: Upload build artifacts | |
if: github.ref == 'refs/heads/main' | |
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 | |
with: | |
name: coder | |
path: | | |
./build/*.zip | |
./build/*.tar.gz | |
./build/*.deb | |
retention-days: 7 | |
deploy: | |
name: "deploy" | |
runs-on: ubuntu-latest | |
timeout-minutes: 30 | |
needs: | |
- changes | |
- build | |
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: Harden Runner | |
uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1 | |
with: | |
egress-policy: audit | |
- name: Checkout | |
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | |
with: | |
fetch-depth: 0 | |
- name: Authenticate to Google Cloud | |
uses: google-github-actions/auth@ba79af03959ebeac9769e648f473a284504d9193 # v2.1.10 | |
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@77e7a554d41e2ee56fc945c52dfd3f33d12def9a # v2.1.4 | |
- name: Set up Flux CLI | |
uses: fluxcd/flux2/action@a48f81a66c4ca9fbd993233ab99dd03a7cfbe09a # v2.6.2 | |
with: | |
# Keep this and the github action up to date with the version of flux installed in dogfood cluster | |
version: "2.5.1" | |
- name: Get Cluster Credentials | |
uses: google-github-actions/get-gke-credentials@d0cee45012069b163a631894b98904a9e6723729 # v2.3.3 | |
with: | |
cluster_name: dogfood-v2 | |
location: us-central1-a | |
project_id: coder-dogfood-v2 | |
- name: Reconcile Flux | |
run: | | |
set -euxo pipefail | |
flux --namespace flux-system reconcile source git flux-system | |
flux --namespace flux-system reconcile source git coder-main | |
flux --namespace flux-system reconcile kustomization flux-system | |
flux --namespace flux-system reconcile kustomization coder | |
flux --namespace flux-system reconcile source chart coder-coder | |
flux --namespace flux-system reconcile source chart coder-coder-provisioner | |
flux --namespace coder reconcile helmrelease coder | |
flux --namespace coder reconcile helmrelease coder-provisioner | |
# Just updating Flux is usually not enough. The Helm release may get | |
# redeployed, but unless something causes the Deployment to update the | |
# pods won't be recreated. It's important that the pods get recreated, | |
# since we use `imagePullPolicy: Always` to ensure we're running the | |
# latest image. | |
- name: Rollout Deployment | |
run: | | |
set -euxo pipefail | |
kubectl --namespace coder rollout restart deployment/coder | |
kubectl --namespace coder rollout status deployment/coder | |
kubectl --namespace coder rollout restart deployment/coder-provisioner | |
kubectl --namespace coder rollout status deployment/coder-provisioner | |
kubectl --namespace coder rollout restart deployment/coder-provisioner-tagged | |
kubectl --namespace coder rollout status deployment/coder-provisioner-tagged | |
deploy-wsproxies: | |
runs-on: ubuntu-latest | |
needs: build | |
if: github.ref == 'refs/heads/main' && !github.event.pull_request.head.repo.fork | |
steps: | |
- name: Harden Runner | |
uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1 | |
with: | |
egress-policy: audit | |
- name: Checkout | |
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | |
with: | |
fetch-depth: 0 | |
- name: Setup flyctl | |
uses: superfly/flyctl-actions/setup-flyctl@fc53c09e1bc3be6f54706524e3b82c4f462f77be # v1.5 | |
- name: Deploy workspace proxies | |
run: | | |
flyctl deploy --image "$IMAGE" --app paris-coder --config ./.github/fly-wsproxies/paris-coder.toml --env "CODER_PROXY_SESSION_TOKEN=$TOKEN_PARIS" --yes | |
flyctl deploy --image "$IMAGE" --app sydney-coder --config ./.github/fly-wsproxies/sydney-coder.toml --env "CODER_PROXY_SESSION_TOKEN=$TOKEN_SYDNEY" --yes | |
flyctl deploy --image "$IMAGE" --app sao-paulo-coder --config ./.github/fly-wsproxies/sao-paulo-coder.toml --env "CODER_PROXY_SESSION_TOKEN=$TOKEN_SAO_PAULO" --yes | |
flyctl deploy --image "$IMAGE" --app jnb-coder --config ./.github/fly-wsproxies/jnb-coder.toml --env "CODER_PROXY_SESSION_TOKEN=$TOKEN_JNB" --yes | |
env: | |
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }} | |
IMAGE: ${{ needs.build.outputs.IMAGE }} | |
TOKEN_PARIS: ${{ secrets.FLY_PARIS_CODER_PROXY_SESSION_TOKEN }} | |
TOKEN_SYDNEY: ${{ secrets.FLY_SYDNEY_CODER_PROXY_SESSION_TOKEN }} | |
TOKEN_SAO_PAULO: ${{ secrets.FLY_SAO_PAULO_CODER_PROXY_SESSION_TOKEN }} | |
TOKEN_JNB: ${{ secrets.FLY_JNB_CODER_PROXY_SESSION_TOKEN }} | |
# sqlc-vet runs a postgres docker container, runs Coder migrations, and then | |
# runs sqlc-vet to ensure all queries are valid. This catches any mistakes | |
# in migrations or sqlc queries that makes a query unable to be prepared. | |
sqlc-vet: | |
runs-on: ${{ github.repository_owner == 'coder' && 'depot-ubuntu-22.04-8' || 'ubuntu-latest' }} | |
needs: changes | |
if: needs.changes.outputs.db == 'true' || needs.changes.outputs.ci == 'true' || github.ref == 'refs/heads/main' | |
steps: | |
- name: Harden Runner | |
uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1 | |
with: | |
egress-policy: audit | |
- name: Checkout | |
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | |
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/a 4FFA ctions/setup-sqlc | |
- name: Setup and run sqlc vet | |
run: | | |
make sqlc-vet | |
notify-slack-on-failure: | |
needs: | |
- required | |
runs-on: ubuntu-latest | |
if: failure() && github.ref == 'refs/heads/main' | |
steps: | |
- name: Send Slack notification | |
run: | | |
curl -X POST -H 'Content-type: application/json' \ | |
--data '{ | |
"blocks": [ | |
{ | |
"type": "header", | |
"text": { | |
"type": "plain_text", | |
"text": "❌ CI Failure in main", | |
"emoji": true | |
} | |
}, | |
{ | |
"type": "section", | |
"fields": [ | |
{ | |
"type": "mrkdwn", | |
"text": "*Workflow:*\n${{ github.workflow }}" | |
}, | |
{ | |
"type": "mrkdwn", | |
"text": "*Committer:*\n${{ github.actor }}" | |
}, | |
{ | |
"type": "mrkdwn", | |
"text": "*Commit:*\n${{ github.sha }}" | |
} | |
] | |
}, | |
{ | |
"type": "section", | |
"text": { | |
"type": "mrkdwn", | |
"text": "*View failure:* <${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|Click here>" | |
} | |
} | |
] | |
}' ${{ secrets.CI_FAILURE_SLACK_WEBHOOK }} |