8000
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 }} | |
docs: ${{ steps.filter.outputs.docs }} | |
go: ${{ steps.filter.outputs.go }} | |
ts: ${{ steps.filter.outputs.ts }} | |
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: Checkout | |
uses: actions/checkout@v4 | |
with: | |
fetch-depth: 1 | |
# For pull requests it's not necessary to checkout the code | |
- name: check changed files | |
uses: dorny/paths-filter@v3 | |
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/*" | |
- "provisioner/**" | |
- "provisionerd/**" | |
- "provisionersdk/**" | |
- "pty/**" | |
- "scaletest/**" | |
- "tailnet/**" | |
- "testutil/**" | |
gomod: | |
- "go.mod" | |
- "go.sum" | |
ts: | |
- "site/**" | |
- "Makefile" | |
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 )}}" | |
update-flake: | |
needs: changes | |
if: needs.changes.outputs.gomod == 'true' | |
runs-on: ${{ github.repository_owner == 'coder' && 'buildjet-8vcpu-ubuntu-2204' || 'ubuntu-latest' }} | |
steps: | |
- name: Checkout | |
uses: actions/checkout@v4 | |
with: | |
fetch-depth: 1 | |
- name: Setup Go | |
uses: ./.github/actions/setup-go | |
- name: Update Nix Flake SRI Hash | |
run: ./scripts/update-flake.sh | |
- name: Ensure No Changes | |
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' && 'buildjet-8vcpu-ubuntu-2204' || 'ubuntu-latest' }} | |
steps: | |
- name: Checkout | |
uses: actions/checkout@v4 | |
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/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: buildjet/cache@v4 | |
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.22.7 | |
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@v4 | |
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@v4 | |
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 | |
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 go.uber.org/mock/mockgen@v0.4.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 | |
# no `-j` flag as `make` fails with: | |
# coderd/rbac/object_gen.go:1:1: syntax error: package statement must be first | |
run: "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' && 'buildjet-8vcpu-ubuntu-2204' || 'ubuntu-latest' }} | |
timeout-minutes: 7 | |
steps: | |
- name: Checkout | |
uses: actions/checkout@v4 | |
with: | |
fetch-depth: 1 | |
- name: Setup Node | |
uses: ./.github/actions/setup-node | |
- name: Setup Go | |
uses: buildjet/setup-go@v5 | |
with: | |
# This doesn't need caching. It's super fast anyways! | |
cache: false | |
go-version: 1.21.9 | |
- 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' && 'buildjet-4vcpu-ubuntu-2204' || matrix.os == 'macos-latest' && github.repository_owner == 'coder' && 'macos-latest-xlarge' || matrix.os == 'windows-2022' && github.repository_owner == 'coder' && 'windows-latest-16-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-2022 | |
steps: | |
- name: Checkout | |
uses: actions/checkout@v4 | |
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: | | |
# 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" \ | |
--packages="./..." -- $PARALLEL_FLAG -short -failfast | |
- 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: | |
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@v4 | |
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: 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' && '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@v4 | |
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 | |
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' && 'buildjet-8vcpu-ubuntu-2204' || '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: Checkout | |
uses: actions/checkout@v4 | |
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' && '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@v4 | |
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' && 'buildjet-16vcpu-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 | |
strategy: | |
fail-fast: false | |
matrix: | |
variant: | |
- enterprise: false | |
name: test-e2e | |
- enterprise: true | |
name: test-e2e-enterprise | |
name: ${{ matrix.variant.name }} | |
steps: | |
- name: Checkout | |
uses: actions/checkout@v4 | |
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: pnpm build | |
working-directory: site | |
- run: pnpm playwright:install | |
working-directory: site | |
# Run tests that don't require an enterprise license without an enterprise license | |
- run: pnpm playwright:test --forbid-only --workers 1 | |
if: ${{ !matrix.variant.enterprise }} | |
env: | |
DEBUG: pw:api | |
working-directory: site | |
# Run all of the tests with an enterprise license | |
- run: pnpm playwright:test --forbid-only --workers 1 | |
if: ${{ matrix.variant.enterprise }} | |
env: | |
DEBUG: pw:api | |
CODER_E2E_ENTERPRISE_LICENSE: ${{ secrets.CODER_E2E_ENTERPRISE_LICENSE }} | |
CODER_E2E_REQUIRE_ENTERPRISE_TESTS: "1" | |
working-directory: site | |
# Temporarily allow these to fail so that I can gather data about which | |
# tests are failing. | |
continue-on-error: true | |
- 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@v4 | |
with: | |
name: failed-test-videos${{ matrix.variant.enterprise && '-enterprise' || '-agpl' }} | |
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@v4 | |
with: | |
name: debug-pprof-dumps${{ matrix.variant.enterprise && '-enterprise' || '-agpl' }} | |
path: ./site/test-results/**/debug-pprof-*.txt | |
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@v4 | |
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@v10 | |
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" | |
# 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 | |
# 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@v10 | |
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" | |
# Run TurboSnap to trace file dependencies to related stories | |
# and tell chromatic to only take snapshots of relevent 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' && 'buildjet-8vcpu-ubuntu-2204' || 'ubuntu-latest' }} | |
if: needs.changes.outputs.offlinedocs == 'true' || needs.changes.outputs.ci == 'true' || needs.changes.outputs.docs == 'true' | |
steps: | |
- name: Checkout | |
uses: actions/checkout@v4 | |
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.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: Setup Go | |
uses: ./.github/actions/setup-go | |
- name: Install go 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 go.uber.org/mock/mockgen@v0.4.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 | |
# 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-js | |
- test-e2e | |
- offlinedocs | |
- sqlc-vet | |
- dependency-license-review | |
# 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 "- test-e2e: ${{ needs.test-e2e.result }}" | |
echo "- offlinedocs: ${{ needs.offlinedocs.result }}" | |
echo "- dependency-license-review: ${{ needs.dependency-license-review.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: | |
# This builds and publishes ghcr.io/coder/coder-preview:main for each commit | |
# to main branch. We are only building this for amd64 platform. (>95% pulls | |
# are for amd64) | |
needs: changes | |
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' && 'buildjet-8vcpu-ubuntu-2204' || 'ubuntu-latest' }} | |
env: | |
DOCKER_CLI_EXPERIMENTAL: "enabled" | |
outputs: | |
IMAGE: ghcr.io/coder/coder-preview:${{ steps.build-docker.outputs.tag }} | |
steps: | |
- name: Checkout | |
uses: actions/checkout@v4 | |
with: | |
fetch-depth: 0 | |
- name: GHCR Login | |
uses: docker/login-action@v3 | |
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 | |
- 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: 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} | |
- 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 | |
make -j 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 | |
make -j 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[@ 8000 ]}"; 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: Prune old images | |
if: github.ref == 'refs/heads/main' | |
uses: vlaurin/action-ghcr-prune@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@v4 | |
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: Checkout | |
uses: actions/checkout@v4 | |
with: | |
fetch-depth: 0 | |
- name: Authenticate to Google Cloud | |
uses: google-github-actions/auth@v2 | |
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@v2 | |
- name: Set up Flux CLI | |
uses: fluxcd/flux2/action@main | |
with: | |
# Keep this up to date with the version of flux installed in dogfood cluster | |
version: "2.2.1" | |
- name: Get Cluster Credentials | |
uses: "google-github-actions/get-gke-credentials@v2" | |
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 | |
deploy-wsproxies: | |
runs-on: ubuntu-latest | |
needs: build | |
if: github.ref == 'refs/heads/main' && !github.event.pull_request.head.repo.fork | |
steps: | |
- name: Checkout | |
uses: actions/checkout@v4 | |
with: | |
fetch-depth: 0 | |
- name: Setup flyctl | |
uses: superfly/flyctl-actions/setup-flyctl@master | |
- 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' && 'buildjet-8vcpu-ubuntu-2204' || 'ubuntu-latest' }} | |
needs: changes | |
if: needs.changes.outputs.db == 'true' || needs.changes.outputs.ci == 'true' || github.ref == 'refs/heads/main' | |
steps: | |
- name: Checkout | |
uses: actions/checkout@v4 | |
with: | |
fetch-depth: 1 | |
# We need golang to run the migration main.go | |
- name: Setup Go | |
uses: ./.github/actions/setup-go | |
- name: Setup sqlc | |
uses: ./.github/actions/setup-sqlc | |
- name: Setup and run sqlc vet | |
run: | | |
make sqlc-vet | |
# dependency-license-review checks that no license-incompatible dependencies have been introduced. | |
# This action is not intended to do a vulnerability check since that is handled by a separate action. | |
dependency-license-review: | |
runs-on: ubuntu-latest | |
if: github.ref != 'refs/heads/main' && github.actor != 'dependabot[bot]' | |
steps: | |
- name: "Checkout Repository" | |
uses: actions/checkout@v4 | |
- name: "Dependency Review" | |
id: review | |
uses: actions/dependency-review-action@v4.3.2 | |
with: | |
allow-licenses: Apache-2.0, BSD-2-Clause, BSD-3-Clause, CC0-1.0, ISC, MIT, MIT-0, MPL-2.0 | |
allow-dependencies-licenses: "pkg:golang/github.com/coder/wgtunnel@0.1.13-0.20240522110300-ade90dfb2da0, pkg:npm/pako@1.0.11" | |
license-check: true | |
vulnerability-check: false | |
- name: "Report" | |
# make sure this step runs even if the previous failed | |
if: always() | |
shell: bash | |
env: | |
VULNERABLE_CHANGES: ${{ steps.review.outputs.invalid-license-changes }} | |
run: | | |
fields=( "unlicensed" "unresolved" "forbidden" ) | |
# This is unfortunate that we have to do this but the action does not support failing on | |
# an unknown license. The unknown dependency could easily have a GPL license which | |
# would be problematic for us. | |
# Track https://github.com/actions/dependency-review-action/issues/672 for when | |
# we can remove this brittle workaround. | |
for field in "${fields[@]}"; do | |
# Use jq to check if the array is not empty | |
if [[ $(echo "$VULNERABLE_CHANGES" | jq ".${field} | length") -ne 0 ]]; then | |
echo "Invalid or unknown licenses detected, contact @sreya to ensure your added dependency falls under one of our allowed licenses." | |
echo "$VULNERABLE_CHANGES" | jq | |
exit 1 | |
fi | |
done | |
echo "No incompatible licenses detected" |