coder/.github/workflows/ci.yaml

539 lines
17 KiB
YAML

name: ci
on:
push:
branches:
- main
pull_request:
workflow_dispatch:
permissions:
actions: none
checks: none
contents: read
deployments: none
issues: none
packages: none
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.event_name == 'pull_request' }}
jobs:
lint:
runs-on: ${{ github.repository_owner == 'coder' && 'buildjet-8vcpu-ubuntu-2204' || 'ubuntu-latest' }}
steps:
- name: Checkout
uses: actions/checkout@v3
- uses: ./.github/actions/setup-go
# Check for any typos!
- name: Check for typos
uses: crate-ci/typos@v1.14.11
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"
# Check for Go linting errors!
- name: Lint Go
uses: golangci/golangci-lint-action@v3.3.1
with:
version: v1.52.2
- name: Lint shell scripts
uses: ludeeus/action-shellcheck@2.0.0
env:
SHELLCHECK_OPTS: --external-sources
with:
ignore: node_modules
- uses: ./.github/actions/setup-node
- name: Lint TypeScript
run: yarn lint
working-directory: site
# Make sure the Helm chart is linted!
- name: Install helm
uses: azure/setup-helm@v3
with:
version: v3.9.2
- name: Lint Helm chart
run: |
cd helm
make lint
# Ensure AGPL and Enterprise are separated!
- name: Check for AGPL code importing Enterprise...
run: ./scripts/check_enterprise_imports.sh
changes:
runs-on: ubuntu-latest
outputs:
docs-only: ${{ steps.filter.outputs.docs_count == steps.filter.outputs.all_count }}
sh: ${{ steps.filter.outputs.sh }}
ts: ${{ steps.filter.outputs.ts }}
k8s: ${{ steps.filter.outputs.k8s }}
steps:
- uses: actions/checkout@v3
# For pull requests it's not necessary to checkout the code
- uses: dorny/paths-filter@v2
id: filter
with:
filters: |
all:
- '**'
docs:
- 'docs/**'
# For testing:
# - '.github/**'
sh:
- "**.sh"
ts:
- 'site/**'
k8s:
- 'helm/**'
- scripts/Dockerfile
- scripts/Dockerfile.base
- scripts/helm.sh
- id: debug
run: |
echo "${{ toJSON(steps.filter )}}"
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'
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/setup-node
- uses: ./.github/actions/setup-go
- name: Install sqlc
run: |
curl -sSL https://github.com/kyleconroy/sqlc/releases/download/v1.17.2/sqlc_1.17.2_linux_amd64.tar.gz | sudo tar -C /usr/bin -xz sqlc
- name: Install protoc-gen-go
run: go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.30
- name: Install protoc-gen-go-drpc
run: go install storj.io/drpc/cmd/protoc-gen-go-drpc@v0.0.33
- name: Install goimports
run: go install golang.org/x/tools/cmd/goimports@latest
- name: Install yq
run: go run github.com/mikefarah/yq/v4@v4.30.6
- name: Install mockgen
run: go install github.com/golang/mock/mockgen@v1.6.0
- name: Install Protoc
run: |
# protoc must be in lockstep with our dogfood Dockerfile or the
# version in the comments will differ. This is also defined in
# security.yaml
set -x
cd dogfood
DOCKER_BUILDKIT=1 docker build . --target proto -t protoc
protoc_path=/usr/local/bin/protoc
docker run --rm --entrypoint cat protoc /tmp/bin/protoc > $protoc_path
chmod +x $protoc_path
protoc --version
- name: make gen
run: "make --output-sync -j -B gen"
- name: Check for unstaged files
run: ./scripts/check_unstaged.sh
fmt:
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0
submodules: true
- uses: ./.github/actions/setup-node
- uses: ./.github/actions/setup-go
- 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 == 'windows-2019' && github.repository_owner == 'coder' && 'windows-latest-8-cores'|| matrix.os }}
timeout-minutes: 20
strategy:
matrix:
os:
- ubuntu-latest
- macos-latest
- windows-2019
steps:
- uses: actions/checkout@v3
- uses: buildjet/setup-go@v4
with:
cache: false
go-version: ${{ env.CODER_GO_VERSION }}
- uses: ./.github/actions/setup-go
- name: Install gotestsum
uses: jaxxstorm/action-install-gh-release@v1.10.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
repo: gotestyourself/gotestsum
tag: v1.9.0
- uses: hashicorp/setup-terraform@v2
with:
terraform_version: 1.1.9
terraform_wrapper: false
- 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
export TS_DEBUG_DISCO=true
gotestsum --junitfile="gotests.xml" --jsonfile="gotests.json" --packages="./..." -- -parallel=8 -timeout=7m -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
- uses: actions/upload-artifact@v3
if: success() || failure()
with:
name: gotests-${{ matrix.os }}.xml
path: ./gotests.xml
retention-days: 30
- 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' }}
# 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:
- uses: actions/checkout@v3
- uses: ./.github/actions/setup-go
- name: Install gotestsum
uses: jaxxstorm/action-install-gh-release@v1.10.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
repo: gotestyourself/gotestsum
tag: v1.9.0
- uses: hashicorp/setup-terraform@v2
with:
terraform_version: 1.1.9
terraform_wrapper: false
- 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
- uses: actions/upload-artifact@v3
if: success() || failure()
with:
name: gotests-postgres.xml
path: ./gotests.xml
retention-days: 30
- 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' }}
timeout-minutes: 25
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/setup-go
- uses: hashicorp/setup-terraform@v2
with:
terraform_version: 1.1.9
terraform_wrapper: false
- name: Run Tests
run: |
go test -race ./...
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:
- 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
- uses: buildjet/setup-go@v4
with:
cache: false
go-version: ${{ env.CODER_GO_VERSION }}
- uses: ./.github/actions/setup-go
- uses: ./.github/actions/setup-node
- 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
- 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' }}
timeout-minutes: 20
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/setup-node
- run: yarn test:ci --max-workers ${{ steps.cpu-cores.outputs.count }}
working-directory: site
- 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:
needs:
- changes
if: needs.changes.outputs.docs-only == 'false'
runs-on: ${{ github.repository_owner == 'coder' && 'buildjet-8vcpu-ubuntu-2204' || 'ubuntu-latest' }}
timeout-minutes: 20
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/setup-node
- uses: ./.github/actions/setup-go
- uses: hashicorp/setup-terraform@v2
with:
terraform_version: 1.1.9
terraform_wrapper: false
- uses: buildjet/setup-node@v3
with:
node-version: "16.16.0"
- name: Build
run: |
sudo npm install -g prettier
make -B site/out/index.html
- run: yarn playwright:install
working-directory: site
- run: yarn 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'
steps:
- 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
- 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
# Chromatic states its fine to make this token public. See:
# https://www.chromatic.com/docs/github-actions#forked-repositories
projectToken: 695c25b6cb65
workingDir: "./site"
# 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
buildScriptName: "storybook:build"
projectToken: 695c25b6cb65
workingDir: "./site"