diff --git a/.github/workflows/dogfood.yaml b/.github/workflows/dogfood.yaml index b039ccc37f..2bd0e6d29f 100644 --- a/.github/workflows/dogfood.yaml +++ b/.github/workflows/dogfood.yaml @@ -11,15 +11,19 @@ on: # PRs: `build_image` builds the image variants but never pushes # (each `depot/build-push-action` step's `push:` and the # `Push Nix image` step are gated on `github.ref == - # 'refs/heads/main'`). `deploy_template` runs `terraform init` + - # `validate` only; the apply step and SHA/title gathering are - # gated on main. + # 'refs/heads/main'`). `test_image` rebuilds the Ubuntu images + # from Depot cache with `load: true` and runs `make gen`, `fmt`, + # `lint`, and a Linux build inside each image to validate that + # the baked-in tooling works. `deploy_template` runs + # `terraform init` + `validate` only; the apply step and + # SHA/title gathering are gated on main. # # Pushes to main: `build_image` retags rolling tags on # `codercom/oss-dogfood` (`:latest`, `:22.04`, `:26.04`), # `codercom/oss-dogfood-vscode-coder` (`:latest`), and # `codercom/oss-dogfood-nix` (`:latest`), plus a per-branch tag on - # each. `deploy_template` runs `terraform apply` and creates new + # each. `test_image` validates tooling as above. + # `deploy_template` runs `terraform apply` and creates new # `coderd_template` versions on dev.coder.com whose `name` is the # commit short SHA. Content is unchanged when neither `dogfood/**` # nor the flake files changed, so the new versions are cosmetic. @@ -31,12 +35,16 @@ on: - ".github/workflows/dogfood.yaml" - "flake.lock" - "flake.nix" + - "mise.toml" + - "mise.lock" pull_request: paths: - "dogfood/**" - ".github/workflows/dogfood.yaml" - "flake.lock" - "flake.nix" + - "mise.toml" + - "mise.lock" workflow_dispatch: permissions: @@ -193,8 +201,59 @@ jobs: env: DOCKER_TAG: ${{ steps.docker-tag-name.outputs.tag }} - deploy_template: + # Validate that the Ubuntu dogfood images contain working tooling. + # Failures here block template deployment (deploy_template). + test_image: needs: build_image + strategy: + fail-fast: false + matrix: + image-version: ["22.04", "26.04"] + runs-on: ${{ github.repository_owner == 'coder' && 'depot-ubuntu-22.04-8' || 'ubuntu-latest' }} + steps: + - name: Harden Runner + uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0 + with: + egress-policy: audit + + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 1 + persist-credentials: false + + - name: Set up Depot CLI + uses: depot/setup-action@15c09a5f77a0840ad4bce955686522a257853461 # v1.7.1 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 + + # Near-instant cache hit from build_image; loads into local daemon + # without pushing to a registry. + - name: Load dogfood image from Depot cache + uses: depot/build-push-action@5f3b3c2e5a00f0093de47f657aeaefcedff27d18 # v1.17.0 + with: + project: b4q6ltmpzh + token: ${{ secrets.DEPOT_TOKEN }} + buildx-fallback: true + context: "{{defaultContext}}" + file: dogfood/coder/ubuntu-${{ matrix.image-version }}/Dockerfile + secrets: | + github_token=${{ secrets.GITHUB_TOKEN }} + pull: true + load: true + push: false + tags: "dogfood-test:${{ matrix.image-version }}" + + - name: Test image tooling + run: ./scripts/dogfood_test_image.sh "dogfood-test:${{ matrix.image-version }}" + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + deploy_template: + needs: + - build_image + - test_image runs-on: ubuntu-latest permissions: # Necessary for GCP authentication (https://github.com/google-github-actions/setup-gcloud#usage) diff --git a/Makefile b/Makefile index f07c8ee35a..5b8af71bb2 100644 --- a/Makefile +++ b/Makefile @@ -992,7 +992,10 @@ GEN_FILES := \ $(AIBRIDGED_MOCKS) # all gen targets should be added here and to gen/mark-fresh -gen: gen/db gen/golden-files $(GEN_FILES) +# Set GEN_SKIP_GOLDEN=1 to skip gen/golden-files (which needs Docker to +# start PostgreSQL via testcontainers). +GEN_SKIP_GOLDEN ?= +gen: gen/db $(if $(GEN_SKIP_GOLDEN),,gen/golden-files) $(GEN_FILES) .PHONY: gen gen/db: $(DB_GEN_FILES) diff --git a/scripts/dogfood_test_image.sh b/scripts/dogfood_test_image.sh new file mode 100755 index 0000000000..08360d7511 --- /dev/null +++ b/scripts/dogfood_test_image.sh @@ -0,0 +1,107 @@ +#!/usr/bin/env bash + +# Validates dogfood image tooling by running gen, fmt, lint, and build inside +# the image. Can be run locally or in CI (mirrors the test_image workflow job). +# +# Usage: ./scripts/dogfood_test_image.sh +# +# Arguments: +# image Docker image to test, e.g. dogfood-test:22.04 or +# ghcr.io/coder/dogfood:latest +# +# Environment: +# GITHUB_TOKEN Passed into the container for authenticated API calls +# (optional for local runs). +# GITHUB_BASE_REF Base branch for diff-only lint checks (e.g. emdash). +# Set automatically by GitHub Actions for PRs. +# CI When set, fmt targets run in check-mode and actionlint +# is excluded from make lint (it runs separately in CI). +# STEPS Space-separated list of steps to run. Defaults to all. +# Valid values: gen fmt lint build check-unstaged +# +# Example: +# ./scripts/dogfood_test_image.sh dogfood-test:22.04 +# STEPS="gen fmt" ./scripts/dogfood_test_image.sh dogfood-test:26.04 + +set -euo pipefail +# shellcheck source=scripts/lib.sh +source "$(dirname "${BASH_SOURCE[0]}")/lib.sh" +cdroot + +if [[ $# -lt 1 ]]; then + echo "Usage: $0 " >&2 + exit 1 +fi + +IMAGE="$1" +STEPS="${STEPS:-gen fmt lint build check-unstaged}" + +log() { + echo "==> $*" >&2 +} + +# --- setup ------------------------------------------------------------------- + +if [[ -n "${CI:-}" ]]; then + log "Preparing checkout for container user (UID 1000)" + chmod -R a+rwX . +else + log "NOTE: if the container cannot write to the checkout, run: chmod -R a+rwX ." +fi + +# Helper: run a make target inside the image. +# Caches are persisted in named Docker volumes so that subsequent steps (and +# repeated local runs) reuse downloaded modules and compiled artifacts. +run_make() { + docker run --rm \ + --volume "$(pwd)":/home/coder/coder \ + --env GIT_CONFIG_COUNT=1 \ + --env GIT_CONFIG_KEY_0=safe.directory \ + --env GIT_CONFIG_VALUE_0=/home/coder/coder \ + --volume coder-dogfood-gomod:/home/coder/go/pkg/mod \ + --volume coder-dogfood-gobuild:/home/coder/.cache/go-build \ + --volume coder-dogfood-pnpm:/home/coder/.local/share/pnpm/store \ + --workdir /home/coder/coder \ + --network=host \ + --env GITHUB_TOKEN \ + --env GITHUB_BASE_REF \ + --env CI \ + "$IMAGE" \ + make "$@" +} + +# --- steps ------------------------------------------------------------------- + +for step in $STEPS; do + case "$step" in + gen) + log "make gen (GEN_SKIP_GOLDEN=1, skips tests that need Docker/testcontainers)" + run_make --output-sync=line -j gen GEN_SKIP_GOLDEN=1 + ;; + fmt) + log "make fmt" + run_make --output-sync=line -j fmt + ;; + lint) + log "make lint" + run_make --output-sync=line -j lint + ;; + build) + log "make build (fat binary)" + run_make -j build/coder_linux_amd64 + ;; + check-unstaged) + # Runs on the host: inspects git state after container steps wrote + # generated/formatted files back via the volume mount. + log "Checking for unstaged files" + ./scripts/check_unstaged.sh + ;; + *) + echo "Unknown step: $step" >&2 + echo "Valid steps: gen fmt lint build check-unstaged" >&2 + exit 1 + ;; + esac +done + +log "All steps passed."