mirror of
https://github.com/coder/coder.git
synced 2026-09-21 12:44:32 +08:00
Wires Vale into docs CI as an advisory (non-blocking) prose-lint step. Closes [DOCS-40](https://linear.app/codercom/issue/DOCS-40). > **Integration update (rebased onto `main`).** Since this branch was opened, `main` consolidated docs linting into the **required** `lint-docs` job in `ci.yaml` and removed the standalone `docs-ci.yaml` ([#25608](https://github.com/coder/coder/pull/25608)). This PR adds Vale to that `lint-docs` job instead of resurrecting `docs-ci.yaml`, and the `docs/.style/` scaffold defers to the merged [#25466](https://github.com/coder/coder/pull/25466) (DOCS-180). Vale stays advisory. > **Post-review refactor.** Following the Coder Agents review, Vale is now invoked through `mise exec "aqua:errata-ai/vale"` (the same pattern as `actionlint`/`zizmor`) instead of a bespoke `curl`/`tar` download. This removed the GNU-only `grep -oP` version extraction and `uname`/arch mapping that broke on macOS BSD grep, and the prose step now skips paths a PR deletes. See the resolved review threads for CRF-17/19/20/21/22. A sample of what this check does is as follows: <img width="1443" height="1293" alt="image" src="https://github.com/user-attachments/assets/cf68dbf9-d9df-49ba-8dbf-200875bc289e" /> ## What changes - `.vale.ini` at the repo root: Google base + Coder (custom, empty in v1) + curated write-good. `alex` rules are pulled in a la carte. Inline comments justify every enable/disable. - `mise.toml`: pin Vale `3.7.1` via aqua. `mise.lock`: lock that pin across all platforms so `mise install --locked` (used by `build_image`) resolves it. - `Makefile`: a `docs/.style/.vale-synced` sentinel that gates `vale sync`, and a `lint/prose` target that runs `vale --no-exit`. Both invoke Vale via `mise exec "aqua:errata-ai/vale" -- vale ...`, so mise owns the version and the OS/arch download (no hand-rolled install path). - `.github/workflows/ci.yaml`: append Vale steps to the existing required `lint-docs` job: `Detect changed Markdown`, `Restore Vale styles`, `Prepare Vale styles` (`make docs/.style/.vale-synced`), `Vale prose lint`, and a default-branch-only `Save Vale styles`. They lint only changed Markdown under `docs/` that still exists on disk, with a problem matcher for inline PR annotations. - `.github/vale-problem-matcher.json`: parses `vale --output=line` so alerts surface as annotations on the Files Changed tab. - `.gitignore` and the workflow cache `path:`: use `docs/.style/styles/*` plus a `!docs/.style/styles/Coder` negation so adding a package does not require parallel edits. - `.markdownlint-cli2.jsonc`: ignore the synced styles so `make lint/markdown` does not lint upstream READMEs. Scaffold prose under `docs/.style/` and `.claude/docs/DOCS_STYLE_GUIDE.md` / `AGENTS.md` come from the merged DOCS-180; this PR no longer touches them. Net diff against `main` is the 8 Vale-wiring files only. ## Severity policy (v1) Rule severity reflects two things together: the rule's false-positive rate against real Coder docs and the gravity of the rule. Low FPs plus high gravity argues for `error`; lower gravity or more judgment calls argue for `warning` or `suggestion`. v1 lands most rules at `warning` and the wordiness rules at `suggestion`. A rule promotes to `error` only when (a) its false-positive rate against real content is effectively zero and (b) the existing-content violation count for that rule is also zero. Vale exits non-zero only on error-level alerts regardless of `MinAlertLevel`; the Makefile and CI invoke Vale with `--no-exit` so the baseline error count from un-overridden Google rules does not fail the build while real failures (bad config, missing files) still propagate. ## CI integration Vale runs as steps appended to the required `lint-docs` job in `ci.yaml`, gated on changed Markdown: 1. **`Detect changed Markdown`** (`tj-actions/changed-files`) scopes to changed `**.md`; the prose step re-filters to `docs/` (the `docs/**.md` glob silently skips dot-prefixed dirs and would miss `docs/.style/style-guide.md`). 2. **`Restore Vale styles`** (`actions/cache/restore`), keyed off `hashFiles('.vale.ini', 'mise.toml', 'docs/.style/styles/Coder/**')`. mise manages the Vale binary, so only the synced styles are cached. 3. **`Prepare Vale styles`** runs `make docs/.style/.vale-synced` (`mise exec ... vale sync`). 4. **`Vale prose lint`** filters the changed set to `docs/` paths still present on disk, then runs `mise exec ... vale --no-exit --output=line`, emitting inline annotations via the problem matcher. 5. **`Save Vale styles`** writes the cache, gated to `refs/heads/main` only so PR runs cannot poison the cache other branches restore from (the zizmor `cache-poisoning` concern). **Every Vale step is `continue-on-error: true`.** This is a deliberate change from the original standalone-workflow design: now that Vale lives inside the *required* `lint-docs` job, a transient `vale sync` network failure (or first-use `mise` install blip) would otherwise block merges. `continue-on-error` keeps Vale advisory, so only the markdownlint / table-formatter checks above (`pnpm check-docs`) remain merge-blocking. `vale --no-exit` additionally keeps the baseline error count from un-overridden Google rules from failing the step. ## Verification - `actionlint` clean on `ci.yaml` (local + `make lint/actions/actionlint`); `zizmor --persona regular` reports no findings. - `make lint/prose` on the full `docs/` corpus: ~406 errors, ~5,346 warnings, ~7,928 suggestions across 461 files, exit 0 (`--no-exit`), Vale `3.7.1` installed by mise. - Net diff vs `main` is the 8 Vale-wiring files only; the `docs/.style/` scaffold already matches `main`. <details> <summary>Implementation plan and decision log</summary> ### Why this rule set The Vale evaluation against the full docs corpus (measured 2026-05-18) produced ~43,940 raw violations across six candidate base styles. The selection here drops Microsoft and RedHat (overlap with Google, and RedHat's Spacing rule hammers technical IDs), and proselint (Annotations rule treats `> [!NOTE]` admonitions as TODO markers). Within the kept styles: - **Google** is the base. Disables: `EmDash` (conflicts with `make lint/emdash`), `Latin` (i.e./e.g. are fine for our audience), `Spacing` (4,500 errors on `codersdk.SomeType` patterns in the auto-generated API reference). Softened: `Parens` to `suggestion`, `WordList` to `warning`. - **write-good** is the base, with `Passive` and `E-Prime` off. `TooWordy` and `ThereIs` are suggestions; `Weasel` is a warning. - **alex** is cherry-picked (not in `BasedOnStyles`): `Ablist`, `Condescending`, `LGBTQ`, `ProfanityLikely`, `Race`, `Suicide` at warning. The `ProfanityMaybe`/`ProfanityUnlikely` rules trip on `execute`, `kill`, `failed`, and `attack`, which read as technical vocabulary in our context. - **Coder** is in `BasedOnStyles` but the directory is empty in v1. Rules land through the per-rule tickets in the [Docs style guide](https://linear.app/codercom/project/docs-style-guide-7828445b9afc) project. ### Why `mise exec` instead of a download block Vale is pinned in `mise.toml` like `actionlint` and `zizmor`, so invoking it via `mise exec "aqua:errata-ai/vale" -- vale ...` makes the pin the single source of truth and lets mise handle the OS/arch-specific download. This replaced an earlier ~30-line `curl`/`tar` block whose GNU-only `grep -oP ...\K` version extraction returned empty on macOS BSD grep. Note: the bare `vale` short name in `mise exec` ignores the pin and resolves to the latest release, so the full aqua key is required. ### Why `vale sync` instead of vendoring The three style packages weigh ~272 KB combined, so vendoring is cheap. But Vale's ecosystem treats `Packages = ` + `vale sync` as canonical, the upstream LICENSE files are not in the package tarballs (would need to be added manually), and the CI cache makes the sync nearly free after the first run. Sticking with the canonical pattern keeps the repo lean and the upgrade path obvious. ### Why `lint/prose` is not in `lint:` or `lint-light:` Vale on the full docs corpus takes ~20s on cold caches. Forcing every pre-commit through that would be aggressive for a feature that ships as warnings. `make lint/typos` follows the same pattern (it is in `lint-light` but not `lint`; CI invokes it directly). v1 keeps Vale opt-in locally and CI-only by default; promote to `lint:` once the rule set stabilizes. ### Exit-code handling Two mechanisms combine, and the choice changed when the step moved into the required `lint-docs` job: - `vale --no-exit` suppresses Vale's non-zero exit on alerts, so the baseline error-level violations from un-overridden Google rules do not fail the step while the cleanup PRs land. Real failures (config invalid, file missing) still exit non-zero. - `continue-on-error: true` on every Vale step. Because the steps now run inside the *required* `lint-docs` job, a `vale sync` download/network blip must not block merges. The original (standalone, non-required) design rejected `continue-on-error` for showing a misleading yellow badge; in a required job that tradeoff flips, and advisory-yellow is strictly preferable to merge-blocking-red on an infrastructure flake. `|| true` in the Makefile was also rejected: it swallows missing-config failures indiscriminately. ### Pre-mortem - **Generated docs noise**: `docs/reference/` is dominated by auto-generated content (clidocgen, apidocgen, auditdocgen, metricsdocgen). The architectural decision is to fix the generators, not exclude paths in Vale. Google.Spacing is the only rule silenced specifically to defer the generator fix; everything else surfaces as warnings. - **First-run cost**: `mise` installs the pinned Vale (a single small binary) and `vale sync` pulls the style packages on a cold run. The Actions cache keyed off `hashFiles('.vale.ini', 'mise.toml', 'docs/.style/styles/Coder/**')` makes subsequent runs near-instant; the `Coder/**` hash is defense-in-depth against [actions/toolkit#713](https://github.com/actions/toolkit/issues/713) so a future cache release that regresses path-negation cannot serve a stale `Coder/` from cache. - **Required-job blast radius**: moving Vale into the required `lint-docs` job means any Vale step failure would gate merges. Mitigated by `continue-on-error` on all Vale steps plus a clean skip when no changed `docs/` Markdown remains on disk, so only `pnpm check-docs` stays blocking. - **Cross-platform install**: handled by mise (aqua backend) rather than a hand-rolled `uname`/arch map, which removes the macOS BSD-grep break the review flagged. - **Deleted files**: `all_changed_files` is ACMRD and lists paths a PR removes; the prose step filters to files still present on disk so Vale does not error on a missing file. - **Local-vs-CI parity**: CI lints changed files only; local `make lint/prose` lints the full tree. This mirrors `make lint/markdown` (full tree) vs the changed-files CI step. Acceptable for v1. </details> --- *Filed via [Coder Agents](https://coder.com/docs/ai-coder/agents) on Nick's behalf.*
1694 lines
61 KiB
Makefile
1694 lines
61 KiB
Makefile
# This is the Coder Makefile. The build directory for most tasks is `build/`.
|
|
#
|
|
# These are the targets you're probably looking for:
|
|
# - clean
|
|
# - build-fat: builds all "fat" binaries for all architectures
|
|
# - build-slim: builds all "slim" binaries (no frontend or slim binaries
|
|
# embedded) for all architectures
|
|
# - release: simulate a release (mostly, does not push images)
|
|
# - build/coder(-slim)?_${os}_${arch}(.exe)?: build a single fat binary
|
|
# - build/coder_${os}_${arch}.(zip|tar.gz): build a release archive
|
|
# - build/coder_linux_${arch}.(apk|deb|rpm): build a release Linux package
|
|
# - build/coder_${version}_linux_${arch}.tag: build a release Linux Docker image
|
|
# - build/coder_helm.tgz: build a release Helm chart
|
|
|
|
.DEFAULT_GOAL := build-fat
|
|
|
|
# Use a single bash shell for each job, and immediately exit on failure
|
|
SHELL := bash
|
|
.SHELLFLAGS := -ceu
|
|
.ONESHELL:
|
|
|
|
# When MAKE_TIMED=1, replace SHELL with a wrapper that prints
|
|
# elapsed wall-clock time for each recipe. pre-commit and pre-push
|
|
# set this on their sub-makes so every parallel job reports its
|
|
# duration. Ad-hoc usage: make MAKE_TIMED=1 test
|
|
ifdef MAKE_TIMED
|
|
SHELL := $(CURDIR)/scripts/lib/timed-shell.sh
|
|
.SHELLFLAGS = $@ -ceu
|
|
export MAKE_TIMED
|
|
export MAKE_LOGDIR
|
|
endif
|
|
|
|
# This doesn't work on directories.
|
|
# See https://stackoverflow.com/questions/25752543/make-delete-on-error-for-directory-targets
|
|
.DELETE_ON_ERROR:
|
|
|
|
# Protect git-tracked generated files from deletion on interrupt.
|
|
# .DELETE_ON_ERROR is desirable for most targets but for files that
|
|
# are committed to git and serve as inputs to other rules, deletion
|
|
# is worse than a stale file — `git restore` is the recovery path.
|
|
.PRECIOUS: \
|
|
coderd/database/dump.sql \
|
|
coderd/database/querier.go \
|
|
coderd/database/unique_constraint.go \
|
|
coderd/database/dbmetrics/querymetrics.go \
|
|
coderd/database/dbauthz/dbauthz.go \
|
|
coderd/database/dbmock/dbmock.go \
|
|
coderd/database/pubsub/psmock/psmock.go \
|
|
agent/agentcontainers/acmock/acmock.go \
|
|
coderd/httpmw/loggermw/loggermock/loggermock.go \
|
|
codersdk/workspacesdk/agentconnmock/agentconnmock.go \
|
|
tailnet/tailnettest/coordinatormock.go \
|
|
tailnet/tailnettest/coordinateemock.go \
|
|
tailnet/tailnettest/workspaceupdatesprovidermock.go \
|
|
tailnet/tailnettest/subscriptionmock.go \
|
|
coderd/aibridged/aibridgedmock/clientmock.go \
|
|
coderd/aibridged/aibridgedmock/poolmock.go \
|
|
tailnet/proto/tailnet.pb.go \
|
|
agent/proto/agent.pb.go \
|
|
agent/agentsocket/proto/agentsocket.pb.go \
|
|
agent/boundarylogproxy/codec/boundary.pb.go \
|
|
provisionersdk/proto/provisioner.pb.go \
|
|
provisionerd/proto/provisionerd.pb.go \
|
|
vpn/vpn.pb.go \
|
|
coderd/aibridged/proto/aibridged.pb.go \
|
|
site/src/api/typesGenerated.ts \
|
|
site/e2e/provisionerGenerated.ts \
|
|
site/src/api/chatModelOptionsGenerated.json \
|
|
site/src/api/rbacresourcesGenerated.ts \
|
|
site/src/api/countriesGenerated.ts \
|
|
site/src/theme/icons.json \
|
|
examples/examples.gen.json \
|
|
docs/manifest.json \
|
|
docs/admin/integrations/prometheus.md \
|
|
docs/admin/security/audit-logs.md \
|
|
docs/reference/cli/index.md \
|
|
coderd/apidoc/swagger.json \
|
|
coderd/rbac/object_gen.go \
|
|
coderd/rbac/scopes_constants_gen.go \
|
|
codersdk/rbacresources_gen.go \
|
|
codersdk/apikey_scopes_gen.go
|
|
|
|
# atomic_write runs a command, captures stdout into a temp file, and
|
|
# atomically replaces $@. An optional second argument is a formatting
|
|
# command that receives the temp file path as its argument.
|
|
# Usage: $(call atomic_write,GENERATE_CMD[,FORMAT_CMD])
|
|
define atomic_write
|
|
tmpdir=$$(mktemp -d -p _gen) && tmpfile=$$(realpath "$$tmpdir")/$(notdir $@) && \
|
|
$(1) > "$$tmpfile" && \
|
|
$(if $(2),$(2) "$$tmpfile" &&) \
|
|
mv "$$tmpfile" "$@" && rm -rf "$$tmpdir"
|
|
endef
|
|
|
|
# CLI doc generation reflects over the assembled CLI tree. Track command
|
|
# definitions plus the top-level SDK types they expose in help text and flag
|
|
# values, without pulling in unrelated generated sources.
|
|
CLIDOC_SRC_FILES := \
|
|
$(shell find ./cli ./enterprise/cli -type f -name '*.go' -not -name '*_test.go') \
|
|
$(wildcard codersdk/*.go) \
|
|
$(wildcard buildinfo/*.go)
|
|
|
|
CLIDOCGEN_INPUTS := \
|
|
$(wildcard scripts/clidocgen/*.go) \
|
|
scripts/clidocgen/command.tpl \
|
|
$(CLIDOC_SRC_FILES)
|
|
|
|
# Helper binaries that import repo packages need their compile-time inputs on
|
|
# the binary target. Most generated outputs keep these binaries as order-only
|
|
# prereqs, so stale binaries otherwise survive source changes.
|
|
RBAC_GO_FILES := \
|
|
$(wildcard coderd/rbac/*.go) \
|
|
$(wildcard coderd/rbac/policy/*.go)
|
|
|
|
DBDUMP_INPUTS := \
|
|
$(wildcard coderd/database/migrations/*.go) \
|
|
$(wildcard coderd/database/migrations/*.sql)
|
|
|
|
# Exclude generated RBAC files to avoid cycles with typegen outputs. The
|
|
# output rules still order generated RBAC prerequisites where needed.
|
|
TYPEGEN_RBAC_GO_FILES := \
|
|
$(filter-out coderd/rbac/%_gen.go,$(wildcard coderd/rbac/*.go)) \
|
|
$(wildcard coderd/rbac/policy/*.go)
|
|
|
|
TYPEGEN_INPUTS := \
|
|
$(wildcard scripts/typegen/*.go) \
|
|
$(wildcard scripts/typegen/*.gotmpl) \
|
|
$(wildcard scripts/typegen/*.tstmpl) \
|
|
$(TYPEGEN_RBAC_GO_FILES) \
|
|
$(wildcard coderd/util/strings/*.go) \
|
|
codersdk/countries.go
|
|
|
|
# Helper binary targets. Built with go build -o to avoid caching
|
|
# link-stage executables in GOCACHE. Each binary is a real Make
|
|
# target so parallel -j builds serialize correctly instead of
|
|
# racing on the same output path.
|
|
|
|
_gen/bin/apitypings: $(wildcard scripts/apitypings/*.go) $(wildcard codersdk/*.go) | _gen
|
|
@mkdir -p _gen/bin
|
|
go build -o $@ ./scripts/apitypings
|
|
|
|
_gen/bin/auditdocgen: $(wildcard scripts/auditdocgen/*.go) $(wildcard enterprise/audit/*.go) | _gen
|
|
@mkdir -p _gen/bin
|
|
go build -o $@ ./scripts/auditdocgen
|
|
|
|
_gen/bin/check-scopes: $(wildcard scripts/check-scopes/*.go) $(RBAC_GO_FILES) | _gen
|
|
@mkdir -p _gen/bin
|
|
go build -o $@ ./scripts/check-scopes
|
|
|
|
# clidocgen reflects over the full CLI tree, so it must rebuild when its
|
|
# command definitions, flag types, or embedded template change.
|
|
_gen/bin/clidocgen: $(CLIDOCGEN_INPUTS) | _gen
|
|
@mkdir -p _gen/bin
|
|
go build -o $@ ./scripts/clidocgen
|
|
|
|
_gen/bin/dbdump: $(wildcard coderd/database/gen/dump/*.go) $(DBDUMP_INPUTS) | _gen
|
|
@mkdir -p _gen/bin
|
|
go build -o $@ ./coderd/database/gen/dump
|
|
|
|
_gen/bin/examplegen: $(wildcard scripts/examplegen/*.go) | _gen
|
|
@mkdir -p _gen/bin
|
|
go build -o $@ ./scripts/examplegen
|
|
|
|
_gen/bin/gensite: $(wildcard scripts/gensite/*.go) | _gen
|
|
@mkdir -p _gen/bin
|
|
go build -o $@ ./scripts/gensite
|
|
|
|
_gen/bin/apikeyscopesgen: $(wildcard scripts/apikeyscopesgen/*.go) $(RBAC_GO_FILES) | _gen
|
|
@mkdir -p _gen/bin
|
|
go build -o $@ ./scripts/apikeyscopesgen
|
|
|
|
_gen/bin/aibridgepricesgen: $(wildcard scripts/aibridgepricesgen/*.go) | _gen
|
|
@mkdir -p _gen/bin
|
|
go build -o $@ ./scripts/aibridgepricesgen
|
|
|
|
_gen/bin/metricsdocgen: $(wildcard scripts/metricsdocgen/*.go) | _gen
|
|
@mkdir -p _gen/bin
|
|
go build -o $@ ./scripts/metricsdocgen
|
|
|
|
_gen/bin/metricsdocgen-scanner: $(wildcard scripts/metricsdocgen/scanner/*.go) | _gen
|
|
@mkdir -p _gen/bin
|
|
go build -o $@ ./scripts/metricsdocgen/scanner
|
|
|
|
_gen/bin/modeloptionsgen: $(wildcard scripts/modeloptionsgen/*.go) $(wildcard codersdk/*.go) | _gen
|
|
@mkdir -p _gen/bin
|
|
go build -o $@ ./scripts/modeloptionsgen
|
|
|
|
_gen/bin/typegen: $(TYPEGEN_INPUTS) | _gen
|
|
@mkdir -p _gen/bin
|
|
go build -o $@ ./scripts/typegen
|
|
|
|
# Shared temp directory for atomic writes. Lives at the project root
|
|
# so all targets share the same filesystem, and is gitignored.
|
|
# Order-only prerequisite: recipes that need it depend on | _gen
|
|
_gen:
|
|
mkdir -p _gen
|
|
|
|
# Don't print the commands in the file unless you specify VERBOSE. This is
|
|
# essentially the same as putting "@" at the start of each line.
|
|
ifndef VERBOSE
|
|
.SILENT:
|
|
endif
|
|
|
|
# Create the output directories if they do not exist.
|
|
$(shell mkdir -p build site/out/bin)
|
|
|
|
GOOS := $(shell go env GOOS)
|
|
GOARCH := $(shell go env GOARCH)
|
|
GOOS_BIN_EXT := $(if $(filter windows, $(GOOS)),.exe,)
|
|
VERSION := $(shell ./scripts/version.sh)
|
|
|
|
POSTGRES_VERSION ?= 17
|
|
POSTGRES_IMAGE ?= us-docker.pkg.dev/coder-v2-images-public/public/postgres:$(POSTGRES_VERSION)
|
|
|
|
# Limit parallel Make jobs in pre-commit/pre-push. Defaults to
|
|
# nproc/4 (min 2) since test, lint, and build targets have internal
|
|
# parallelism. Override: make pre-push PARALLEL_JOBS=8
|
|
PARALLEL_JOBS ?= $(shell n=$$(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 8); echo $$(( n / 4 > 2 ? n / 4 : 2 )))
|
|
|
|
# Use the highest ZSTD compression level in release builds to
|
|
# minimize artifact size. For non-release CI builds (e.g. main
|
|
# branch preview), use multithreaded level 6 which is ~99% faster
|
|
# at the cost of ~30% larger archives.
|
|
ifeq ($(CODER_RELEASE),true)
|
|
ZSTDFLAGS := -22 --ultra
|
|
else
|
|
ZSTDFLAGS := -6 -T0
|
|
endif
|
|
|
|
# Common paths to exclude from find commands, this rule is written so
|
|
# that it can be it can be used in a chain of AND statements (meaning
|
|
# you can simply write `find . $(FIND_EXCLUSIONS) -name thing-i-want`).
|
|
# Note, all find statements should be written with `.` or `./path` as
|
|
# the search path so that these exclusions match.
|
|
FIND_EXCLUSIONS= \
|
|
-not \( \( -path '*/.git/*' -o -path './build/*' -o -path './vendor/*' -o -path './.coderv2/*' -o -path '*/node_modules/*' -o -path '*/out/*' -o -path './coderd/apidoc/*' -o -path '*/.next/*' -o -path '*/.terraform/*' -o -path './_gen/*' \) -prune \)
|
|
|
|
# Source files used for make targets, evaluated on use.
|
|
GO_SRC_FILES := $(shell find . $(FIND_EXCLUSIONS) -type f -name '*.go' -not -name '*_test.go')
|
|
|
|
# All the shell files in the repo, excluding ignored files.
|
|
SHELL_SRC_FILES := $(shell find . $(FIND_EXCLUSIONS) -type f -name '*.sh')
|
|
|
|
MIGRATION_FILES := $(shell find ./coderd/database/migrations/ -maxdepth 1 $(FIND_EXCLUSIONS) -type f -name '*.sql')
|
|
FIXTURE_FILES := $(shell find ./coderd/database/migrations/testdata/fixtures/ $(FIND_EXCLUSIONS) -type f -name '*.sql')
|
|
|
|
# Ensure we don't use the user's git configs which might cause side-effects
|
|
GIT_FLAGS = GIT_CONFIG_GLOBAL=/dev/null GIT_CONFIG_SYSTEM=/dev/null
|
|
|
|
# All ${OS}_${ARCH} combos we build for. Windows binaries have the .exe suffix.
|
|
OS_ARCHES := \
|
|
linux_amd64 linux_arm64 linux_armv7 \
|
|
darwin_amd64 darwin_arm64 \
|
|
windows_amd64.exe windows_arm64.exe
|
|
|
|
# Archive formats and their corresponding ${OS}_${ARCH} combos.
|
|
ARCHIVE_TAR_GZ := linux_amd64 linux_arm64 linux_armv7
|
|
ARCHIVE_ZIP := \
|
|
darwin_amd64 darwin_arm64 \
|
|
windows_amd64 windows_arm64
|
|
|
|
# All package formats we build and the ${OS}_${ARCH} combos we build them for.
|
|
PACKAGE_FORMATS := apk deb rpm
|
|
PACKAGE_OS_ARCHES := linux_amd64 linux_armv7 linux_arm64
|
|
|
|
# All architectures we build Docker images for (Linux only).
|
|
DOCKER_ARCHES := amd64 arm64 armv7
|
|
|
|
# Computed variables based on the above.
|
|
CODER_SLIM_BINARIES := $(addprefix build/coder-slim_$(VERSION)_,$(OS_ARCHES))
|
|
CODER_FAT_BINARIES := $(addprefix build/coder_$(VERSION)_,$(OS_ARCHES))
|
|
CODER_ALL_BINARIES := $(CODER_SLIM_BINARIES) $(CODER_FAT_BINARIES)
|
|
CODER_TAR_GZ_ARCHIVES := $(foreach os_arch, $(ARCHIVE_TAR_GZ), build/coder_$(VERSION)_$(os_arch).tar.gz)
|
|
CODER_ZIP_ARCHIVES := $(foreach os_arch, $(ARCHIVE_ZIP), build/coder_$(VERSION)_$(os_arch).zip)
|
|
CODER_ALL_ARCHIVES := $(CODER_TAR_GZ_ARCHIVES) $(CODER_ZIP_ARCHIVES)
|
|
CODER_ALL_PACKAGES := $(foreach os_arch, $(PACKAGE_OS_ARCHES), $(addprefix build/coder_$(VERSION)_$(os_arch).,$(PACKAGE_FORMATS)))
|
|
CODER_ARCH_IMAGES := $(foreach arch, $(DOCKER_ARCHES), build/coder_$(VERSION)_linux_$(arch).tag)
|
|
CODER_ARCH_IMAGES_PUSHED := $(addprefix push/, $(CODER_ARCH_IMAGES))
|
|
CODER_MAIN_IMAGE := build/coder_$(VERSION)_linux.tag
|
|
|
|
CODER_SLIM_NOVERSION_BINARIES := $(addprefix build/coder-slim_,$(OS_ARCHES))
|
|
CODER_FAT_NOVERSION_BINARIES := $(addprefix build/coder_,$(OS_ARCHES))
|
|
CODER_ALL_NOVERSION_IMAGES := $(foreach arch, $(DOCKER_ARCHES), build/coder_linux_$(arch).tag) build/coder_linux.tag
|
|
CODER_ALL_NOVERSION_IMAGES_PUSHED := $(addprefix push/, $(CODER_ALL_NOVERSION_IMAGES))
|
|
|
|
# If callers are only building Docker images and not the packages and archives,
|
|
# we can skip those prerequisites as they are not actually required and only
|
|
# specified to avoid concurrent write failures.
|
|
ifdef DOCKER_IMAGE_NO_PREREQUISITES
|
|
CODER_ARCH_IMAGE_PREREQUISITES :=
|
|
else
|
|
CODER_ARCH_IMAGE_PREREQUISITES := \
|
|
build/coder_$(VERSION)_%.apk \
|
|
build/coder_$(VERSION)_%.deb \
|
|
build/coder_$(VERSION)_%.rpm \
|
|
build/coder_$(VERSION)_%.tar.gz
|
|
endif
|
|
|
|
|
|
clean:
|
|
rm -rf build/ site/build/ site/out/
|
|
rm -rf _gen/bin
|
|
mkdir -p build/
|
|
git restore site/out/
|
|
.PHONY: clean
|
|
|
|
build-slim: $(CODER_SLIM_BINARIES)
|
|
.PHONY: build-slim
|
|
|
|
build-fat build-full build: $(CODER_FAT_BINARIES)
|
|
.PHONY: build-fat build-full build
|
|
|
|
release: $(CODER_FAT_BINARIES) $(CODER_ALL_ARCHIVES) $(CODER_ALL_PACKAGES) $(CODER_ARCH_IMAGES) build/coder_helm_$(VERSION).tgz
|
|
.PHONY: release
|
|
|
|
build/coder-slim_$(VERSION)_checksums.sha1: site/out/bin/coder.sha1
|
|
cp "$<" "$@"
|
|
|
|
site/out/bin/coder.sha1: $(CODER_SLIM_BINARIES)
|
|
pushd ./site/out/bin
|
|
openssl dgst -r -sha1 coder-* | tee coder.sha1
|
|
popd
|
|
|
|
build/coder-slim_$(VERSION).tar: build/coder-slim_$(VERSION)_checksums.sha1 $(CODER_SLIM_BINARIES)
|
|
pushd ./site/out/bin
|
|
tar cf "../../../build/$(@F)" coder-*
|
|
popd
|
|
|
|
# delete the uncompressed binaries from the embedded dir
|
|
rm -f site/out/bin/coder-*
|
|
|
|
site/out/bin/coder.tar.zst: build/coder-slim_$(VERSION).tar.zst
|
|
cp "$<" "$@"
|
|
|
|
build/coder-slim_$(VERSION).tar.zst: build/coder-slim_$(VERSION).tar
|
|
zstd $(ZSTDFLAGS) \
|
|
--force \
|
|
--long \
|
|
--no-progress \
|
|
-o "build/coder-slim_$(VERSION).tar.zst" \
|
|
"build/coder-slim_$(VERSION).tar"
|
|
|
|
# Redirect from version-less targets to the versioned ones. There is a similar
|
|
# target for slim binaries below.
|
|
#
|
|
# Called like this:
|
|
# make build/coder_linux_amd64
|
|
# make build/coder_windows_amd64.exe
|
|
$(CODER_FAT_NOVERSION_BINARIES): build/coder_%: build/coder_$(VERSION)_%
|
|
rm -f "$@"
|
|
ln "$<" "$@"
|
|
|
|
# Same as above, but for slim binaries.
|
|
#
|
|
# Called like this:
|
|
# make build/coder-slim_linux_amd64
|
|
# make build/coder-slim_windows_amd64.exe
|
|
$(CODER_SLIM_NOVERSION_BINARIES): build/coder-slim_%: build/coder-slim_$(VERSION)_%
|
|
rm -f "$@"
|
|
ln "$<" "$@"
|
|
|
|
# "fat" binaries always depend on the site and the compressed slim binaries.
|
|
$(CODER_FAT_BINARIES): \
|
|
site/out/index.html \
|
|
site/out/bin/coder.sha1 \
|
|
site/out/bin/coder.tar.zst
|
|
|
|
# This is a handy block that parses the target to determine whether it's "slim"
|
|
# or "fat", which OS was specified and which architecture was specified.
|
|
#
|
|
# It populates the following variables: mode, os, arch_ext, arch, ext (without
|
|
# dot).
|
|
define get-mode-os-arch-ext =
|
|
mode="$$([[ "$@" = build/coder-slim* ]] && echo "slim" || echo "fat")"
|
|
os="$$(echo $@ | cut -d_ -f3)"
|
|
arch_ext="$$(echo $@ | cut -d_ -f4)"
|
|
if [[ "$$arch_ext" == *.* ]]; then
|
|
arch="$$(echo $$arch_ext | cut -d. -f1)"
|
|
ext="$${arch_ext#*.}"
|
|
else
|
|
arch="$$arch_ext"
|
|
ext=""
|
|
fi
|
|
endef
|
|
|
|
# This task handles all builds, for both "fat" and "slim" binaries. It parses
|
|
# the target name to get the metadata for the build, so it must be specified in
|
|
# this format:
|
|
# build/coder(-slim)?_${version}_${os}_${arch}(.exe)?
|
|
#
|
|
# You should probably use the non-version targets above instead if you're
|
|
# calling this manually.
|
|
$(CODER_ALL_BINARIES): go.mod go.sum \
|
|
$(GO_SRC_FILES) \
|
|
$(shell find ./examples/templates) \
|
|
site/static/error.html
|
|
|
|
$(get-mode-os-arch-ext)
|
|
if [[ "$$os" != "windows" ]] && [[ "$$ext" != "" ]]; then
|
|
echo "ERROR: Invalid build binary extension" 1>&2
|
|
exit 1
|
|
fi
|
|
if [[ "$$os" == "windows" ]] && [[ "$$ext" != exe ]]; then
|
|
echo "ERROR: Windows binaries must have an .exe extension." 1>&2
|
|
exit 1
|
|
fi
|
|
|
|
build_args=( \
|
|
--os "$$os" \
|
|
--arch "$$arch" \
|
|
--version "$(VERSION)" \
|
|
--output "$@" \
|
|
)
|
|
if [ "$$mode" == "slim" ]; then
|
|
build_args+=(--slim)
|
|
fi
|
|
|
|
./scripts/build_go.sh "$${build_args[@]}"
|
|
|
|
if [[ "$$mode" == "slim" ]]; then
|
|
dot_ext=""
|
|
if [[ "$$ext" != "" ]]; then
|
|
dot_ext=".$$ext"
|
|
fi
|
|
|
|
cp "$@" "./site/out/bin/coder-$$os-$$arch$$dot_ext"
|
|
|
|
if [[ "$${CODER_SIGN_GPG:-0}" == "1" ]]; then
|
|
cp "$@.asc" "./site/out/bin/coder-$$os-$$arch$$dot_ext.asc"
|
|
fi
|
|
fi
|
|
|
|
# This task builds all archives. It parses the target name to get the metadata
|
|
# for the build, so it must be specified in this format:
|
|
# build/coder_${version}_${os}_${arch}.${format}
|
|
#
|
|
# The following OS/arch/format combinations are supported:
|
|
# .tar.gz: linux_amd64, linux_arm64, linux_armv7
|
|
# .zip: darwin_amd64, darwin_arm64, windows_amd64, windows_arm64
|
|
#
|
|
# This depends on all fat binaries because it's difficult to do dynamic
|
|
# dependencies due to the .exe requirement on Windows. These targets are
|
|
# typically only used during release anyways.
|
|
$(CODER_ALL_ARCHIVES): $(CODER_FAT_BINARIES)
|
|
$(get-mode-os-arch-ext)
|
|
bin_ext=""
|
|
if [[ "$$os" == "windows" ]]; then
|
|
bin_ext=".exe"
|
|
fi
|
|
|
|
./scripts/archive.sh \
|
|
--format "$$ext" \
|
|
--os "$$os" \
|
|
--output "$@" \
|
|
"build/coder_$(VERSION)_$${os}_$${arch}$${bin_ext}"
|
|
|
|
# This task builds all packages. It parses the target name to get the metadata
|
|
# for the build, so it must be specified in this format:
|
|
# build/coder_${version}_linux_${arch}.${format}
|
|
#
|
|
# Supports apk, deb, rpm for all linux targets.
|
|
#
|
|
# This depends on all Linux fat binaries and archives because it's difficult to
|
|
# do dynamic dependencies due to the extensions in the filenames. These targets
|
|
# are typically only used during release anyways.
|
|
#
|
|
# Packages need to run after the archives are built, otherwise they cause tar
|
|
# errors like "file changed as we read it".
|
|
CODER_PACKAGE_DEPS := $(foreach os_arch, $(PACKAGE_OS_ARCHES), build/coder_$(VERSION)_$(os_arch) build/coder_$(VERSION)_$(os_arch).tar.gz)
|
|
$(CODER_ALL_PACKAGES): $(CODER_PACKAGE_DEPS)
|
|
$(get-mode-os-arch-ext)
|
|
|
|
./scripts/package.sh \
|
|
--arch "$$arch" \
|
|
--format "$$ext" \
|
|
--version "$(VERSION)" \
|
|
--output "$@" \
|
|
"build/coder_$(VERSION)_$${os}_$${arch}"
|
|
|
|
# This task builds a Windows amd64 installer. Depends on makensis.
|
|
build/coder_$(VERSION)_windows_amd64_installer.exe: build/coder_$(VERSION)_windows_amd64.exe
|
|
./scripts/build_windows_installer.sh \
|
|
--version "$(VERSION)" \
|
|
--output "$@" \
|
|
"$<"
|
|
|
|
# Redirect from version-less Docker image targets to the versioned ones.
|
|
#
|
|
# Called like this:
|
|
# make build/coder_linux_amd64.tag
|
|
$(CODER_ALL_NOVERSION_IMAGES): build/coder_%: build/coder_$(VERSION)_%
|
|
.PHONY: $(CODER_ALL_NOVERSION_IMAGES)
|
|
|
|
# Redirect from version-less push Docker image targets to the versioned ones.
|
|
#
|
|
# Called like this:
|
|
# make push/build/coder_linux_amd64.tag
|
|
$(CODER_ALL_NOVERSION_IMAGES_PUSHED): push/build/coder_%: push/build/coder_$(VERSION)_%
|
|
.PHONY: $(CODER_ALL_NOVERSION_IMAGES_PUSHED)
|
|
|
|
# This task builds all Docker images. It parses the target name to get the
|
|
# metadata for the build, so it must be specified in this format:
|
|
# build/coder_${version}_${os}_${arch}.tag
|
|
#
|
|
# Supports linux_amd64, linux_arm64, linux_armv7.
|
|
#
|
|
# Images need to run after the archives and packages are built, otherwise they
|
|
# cause errors like "file changed as we read it".
|
|
$(CODER_ARCH_IMAGES): build/coder_$(VERSION)_%.tag: build/coder_$(VERSION)_% $(CODER_ARCH_IMAGE_PREREQUISITES)
|
|
$(get-mode-os-arch-ext)
|
|
|
|
image_tag="$$(./scripts/image_tag.sh --arch "$$arch" --version "$(VERSION)")"
|
|
./scripts/build_docker.sh \
|
|
--arch "$$arch" \
|
|
--target "$$image_tag" \
|
|
--version "$(VERSION)" \
|
|
"build/coder_$(VERSION)_$${os}_$${arch}"
|
|
|
|
echo "$$image_tag" > "$@"
|
|
|
|
# Multi-arch Docker image. This requires all architecture-specific images to be
|
|
# built AND pushed.
|
|
$(CODER_MAIN_IMAGE): $(CODER_ARCH_IMAGES_PUSHED)
|
|
image_tag="$$(./scripts/image_tag.sh --version "$(VERSION)")"
|
|
./scripts/build_docker_multiarch.sh \
|
|
--target "$$image_tag" \
|
|
--version "$(VERSION)" \
|
|
$(foreach img, $^, "$$(cat "$(img:push/%=%)")")
|
|
|
|
echo "$$image_tag" > "$@"
|
|
|
|
# Push a Docker image.
|
|
$(CODER_ARCH_IMAGES_PUSHED): push/%: %
|
|
image_tag="$$(cat "$<")"
|
|
docker push "$$image_tag"
|
|
.PHONY: $(CODER_ARCH_IMAGES_PUSHED)
|
|
|
|
# Push the multi-arch Docker manifest.
|
|
push/$(CODER_MAIN_IMAGE): $(CODER_MAIN_IMAGE)
|
|
image_tag="$$(cat "$<")"
|
|
docker manifest push "$$image_tag"
|
|
.PHONY: push/$(CODER_MAIN_IMAGE)
|
|
|
|
# Helm charts that are available
|
|
charts = coder provisioner
|
|
|
|
# Shortcut for Helm chart package.
|
|
$(foreach chart,$(charts),build/$(chart)_helm.tgz): build/%_helm.tgz: build/%_helm_$(VERSION).tgz
|
|
rm -f "$@"
|
|
ln "$<" "$@"
|
|
|
|
# Helm chart package.
|
|
$(foreach chart,$(charts),build/$(chart)_helm_$(VERSION).tgz): build/%_helm_$(VERSION).tgz:
|
|
./scripts/helm.sh \
|
|
--version "$(VERSION)" \
|
|
--chart $* \
|
|
--output "$@"
|
|
|
|
node_modules/.installed: package.json pnpm-lock.yaml
|
|
./scripts/pnpm_install.sh
|
|
touch "$@"
|
|
|
|
offlinedocs/node_modules/.installed: offlinedocs/package.json offlinedocs/pnpm-lock.yaml
|
|
(cd offlinedocs/ && ../scripts/pnpm_install.sh)
|
|
touch "$@"
|
|
|
|
site/node_modules/.installed: site/package.json site/pnpm-lock.yaml
|
|
(cd site/ && ../scripts/pnpm_install.sh)
|
|
touch "$@"
|
|
|
|
scripts/apidocgen/node_modules/.installed: scripts/apidocgen/package.json scripts/apidocgen/pnpm-lock.yaml
|
|
(cd scripts/apidocgen && ../../scripts/pnpm_install.sh)
|
|
touch "$@"
|
|
|
|
SITE_GEN_FILES := \
|
|
site/src/api/typesGenerated.ts \
|
|
site/src/api/rbacresourcesGenerated.ts \
|
|
site/src/api/countriesGenerated.ts \
|
|
site/src/api/chatModelOptionsGenerated.json \
|
|
site/src/theme/icons.json
|
|
|
|
site/out/index.html: \
|
|
site/node_modules/.installed \
|
|
site/static/install.sh \
|
|
$(SITE_GEN_FILES) \
|
|
$(shell find ./site $(FIND_EXCLUSIONS) -type f \( -name '*.ts' -o -name '*.tsx' \))
|
|
cd site/
|
|
# prevents this directory from getting to big, and causing "too much data" errors
|
|
rm -rf out/assets/
|
|
pnpm build
|
|
|
|
offlinedocs/out/index.html: offlinedocs/node_modules/.installed $(shell find ./offlinedocs $(FIND_EXCLUSIONS) -type f) $(shell find ./docs $(FIND_EXCLUSIONS) -type f | sed 's: :\\ :g')
|
|
cd offlinedocs/
|
|
../scripts/pnpm_install.sh
|
|
pnpm export
|
|
|
|
build/coder_docs_$(VERSION).tgz: offlinedocs/out/index.html
|
|
tar -czf "$@" -C offlinedocs/out .
|
|
|
|
install: build/coder_$(VERSION)_$(GOOS)_$(GOARCH)$(GOOS_BIN_EXT)
|
|
install_dir="$$(go env GOPATH)/bin"
|
|
output_file="$${install_dir}/coder$(GOOS_BIN_EXT)"
|
|
|
|
mkdir -p "$$install_dir"
|
|
cp "$<" "$$output_file"
|
|
.PHONY: install
|
|
|
|
# Only wildcard the go files in the develop directory to avoid rebuilds
|
|
# when project files are changd. Technically changes to some imports may
|
|
# not be detected, but it's unlikely to cause any issues.
|
|
build/.bin/develop: go.mod go.sum $(wildcard scripts/develop/*.go)
|
|
CGO_ENABLED=0 go build -o $@ ./scripts/develop
|
|
|
|
BOLD := $(shell tput bold 2>/dev/null)
|
|
GREEN := $(shell tput setaf 2 2>/dev/null)
|
|
RED := $(shell tput setaf 1 2>/dev/null)
|
|
YELLOW := $(shell tput setaf 3 2>/dev/null)
|
|
DIM := $(shell tput dim 2>/dev/null || tput setaf 8 2>/dev/null)
|
|
RESET := $(shell tput sgr0 2>/dev/null)
|
|
|
|
fmt: fmt/ts fmt/go fmt/terraform fmt/shfmt fmt/biome fmt/markdown
|
|
.PHONY: fmt
|
|
|
|
# Subset of fmt that does not require Go or Node toolchains.
|
|
fmt-light: fmt/shfmt fmt/terraform fmt/markdown
|
|
.PHONY: fmt-light
|
|
|
|
fmt/go:
|
|
ifdef FILE
|
|
# Format single file
|
|
if [[ -f "$(FILE)" ]] && [[ "$(FILE)" == *.go ]] && ! grep -q "DO NOT EDIT" "$(FILE)"; then \
|
|
echo "$(GREEN)==>$(RESET) $(BOLD)fmt/go$(RESET) $(FILE)"; \
|
|
./scripts/format_go_file.sh "$(FILE)"; \
|
|
fi
|
|
else
|
|
go mod tidy
|
|
echo "$(GREEN)==>$(RESET) $(BOLD)fmt/go$(RESET)"
|
|
# VS Code users should check out
|
|
# https://github.com/mvdan/gofumpt#visual-studio-code
|
|
find . $(FIND_EXCLUSIONS) -type f -name '*.go' -print0 | \
|
|
xargs -0 grep -E --null -L '^// Code generated .* DO NOT EDIT\.$$' | \
|
|
xargs -0 ./scripts/format_go_file.sh
|
|
endif
|
|
.PHONY: fmt/go
|
|
|
|
fmt/ts: site/node_modules/.installed
|
|
ifdef FILE
|
|
# Format single TypeScript/JavaScript file
|
|
if [[ -f "$(FILE)" ]] && [[ "$(FILE)" == *.ts ]] || [[ "$(FILE)" == *.tsx ]] || [[ "$(FILE)" == *.js ]] || [[ "$(FILE)" == *.jsx ]]; then \
|
|
echo "$(GREEN)==>$(RESET) $(BOLD)fmt/ts$(RESET) $(FILE)"; \
|
|
(cd site/ && pnpm exec biome format --write "../$(FILE)"); \
|
|
fi
|
|
else
|
|
echo "$(GREEN)==>$(RESET) $(BOLD)fmt/ts$(RESET)"
|
|
cd site
|
|
# Avoid writing files in CI to reduce file write activity
|
|
ifdef CI
|
|
pnpm run check --linter-enabled=false
|
|
else
|
|
pnpm run check:fix
|
|
endif
|
|
endif
|
|
.PHONY: fmt/ts
|
|
|
|
fmt/biome: site/node_modules/.installed
|
|
ifdef FILE
|
|
# Format single file with biome
|
|
if [[ -f "$(FILE)" ]] && [[ "$(FILE)" == *.ts ]] || [[ "$(FILE)" == *.tsx ]] || [[ "$(FILE)" == *.js ]] || [[ "$(FILE)" == *.jsx ]]; then \
|
|
echo "$(GREEN)==>$(RESET) $(BOLD)fmt/biome$(RESET) $(FILE)"; \
|
|
(cd site/ && pnpm exec biome format --write "../$(FILE)"); \
|
|
fi
|
|
else
|
|
echo "$(GREEN)==>$(RESET) $(BOLD)fmt/biome$(RESET)"
|
|
cd site/
|
|
# Avoid writing files in CI to reduce file write activity
|
|
ifdef CI
|
|
pnpm run format:check
|
|
else
|
|
pnpm run format
|
|
endif
|
|
endif
|
|
.PHONY: fmt/biome
|
|
|
|
fmt/terraform: $(wildcard *.tf)
|
|
ifdef FILE
|
|
# Format single Terraform file
|
|
if [[ -f "$(FILE)" ]] && [[ "$(FILE)" == *.tf ]] || [[ "$(FILE)" == *.tfvars ]]; then \
|
|
echo "$(GREEN)==>$(RESET) $(BOLD)fmt/terraform$(RESET) $(FILE)"; \
|
|
terraform fmt "$(FILE)"; \
|
|
fi
|
|
else
|
|
echo "$(GREEN)==>$(RESET) $(BOLD)fmt/terraform$(RESET)"
|
|
terraform fmt -recursive
|
|
endif
|
|
.PHONY: fmt/terraform
|
|
|
|
fmt/shfmt: $(SHELL_SRC_FILES)
|
|
ifdef FILE
|
|
# Format single shell script
|
|
if [[ -f "$(FILE)" ]] && [[ "$(FILE)" == *.sh ]]; then \
|
|
echo "$(GREEN)==>$(RESET) $(BOLD)fmt/shfmt$(RESET) $(FILE)"; \
|
|
shfmt -w "$(FILE)"; \
|
|
fi
|
|
else
|
|
echo "$(GREEN)==>$(RESET) $(BOLD)fmt/shfmt$(RESET)"
|
|
# Only do diff check in CI, errors on diff.
|
|
ifdef CI
|
|
shfmt -d $(SHELL_SRC_FILES)
|
|
else
|
|
shfmt -w $(SHELL_SRC_FILES)
|
|
endif
|
|
endif
|
|
.PHONY: fmt/shfmt
|
|
|
|
fmt/markdown: node_modules/.installed
|
|
ifdef FILE
|
|
# Format single markdown file
|
|
if [[ -f "$(FILE)" ]] && [[ "$(FILE)" == *.md ]]; then \
|
|
echo "$(GREEN)==>$(RESET) $(BOLD)fmt/markdown$(RESET) $(FILE)"; \
|
|
pnpm exec markdown-table-formatter "$(FILE)"; \
|
|
fi
|
|
else
|
|
echo "$(GREEN)==>$(RESET) $(BOLD)fmt/markdown$(RESET)"
|
|
pnpm format-docs
|
|
endif
|
|
.PHONY: fmt/markdown
|
|
|
|
# Note: we don't run zizmor in the lint target because it takes a while.
|
|
# GitHub Actions linters are run in a separate CI job (lint-actions) that only
|
|
# triggers when workflow files change, so we skip them here when CI=true.
|
|
LINT_ACTIONS_TARGETS := $(if $(CI),,lint/actions/actionlint)
|
|
lint: lint/shellcheck lint/go lint/ts lint/examples lint/helm lint/site-icons lint/markdown lint/check-scopes lint/migrations lint/bootstrap lint/architecture lint/emdash lint/agents lint/mise-versions $(LINT_ACTIONS_TARGETS)
|
|
.PHONY: lint
|
|
|
|
# Fast lint subset for lightweight hooks. Some targets use mise-managed tools.
|
|
lint-light: lint/shellcheck lint/markdown lint/helm lint/bootstrap lint/migrations lint/actions/actionlint lint/typos lint/emdash lint/mise-versions
|
|
.PHONY: lint-light
|
|
|
|
lint/site-icons:
|
|
./scripts/check_site_icons.sh
|
|
.PHONY: lint/site-icons
|
|
|
|
lint/ts: site/node_modules/.installed
|
|
cd site/
|
|
pnpm lint
|
|
.PHONY: lint/ts
|
|
|
|
lint/go:
|
|
golangci-lint run
|
|
paralleltestctx -custom-funcs="testutil.Context,chatdTestContext" ./...
|
|
go run ./scripts/intxcheck ./...
|
|
.PHONY: lint/go
|
|
|
|
lint/examples: | _gen/bin/examplegen
|
|
_gen/bin/examplegen -lint
|
|
.PHONY: lint/examples
|
|
|
|
# Use shfmt to determine the shell files, takes editorconfig into consideration.
|
|
lint/shellcheck: $(SHELL_SRC_FILES)
|
|
echo "--- shellcheck"
|
|
shellcheck --external-sources $(SHELL_SRC_FILES)
|
|
.PHONY: lint/shellcheck
|
|
|
|
lint/bootstrap:
|
|
bash scripts/check_bootstrap_quotes.sh
|
|
.PHONY: lint/bootstrap
|
|
|
|
lint/emdash:
|
|
bash scripts/check_emdash.sh
|
|
.PHONY: lint/emdash
|
|
|
|
lint/architecture:
|
|
./scripts/check_architecture.sh
|
|
.PHONY: lint/architecture
|
|
|
|
lint/agents:
|
|
./scripts/check_agents_structure.sh
|
|
.PHONY: lint/agents
|
|
|
|
lint/helm:
|
|
cd helm/
|
|
make lint
|
|
.PHONY: lint/helm
|
|
|
|
lint/markdown: node_modules/.installed
|
|
pnpm lint-docs
|
|
.PHONY: lint/markdown
|
|
|
|
lint/actions: lint/actions/actionlint lint/actions/zizmor
|
|
.PHONY: lint/actions
|
|
|
|
lint/actions/actionlint:
|
|
mise exec actionlint -- actionlint
|
|
.PHONY: lint/actions/actionlint
|
|
|
|
# zizmor uses GH_TOKEN to fetch imported workflows from GitHub; without it,
|
|
# external action references are skipped silently.
|
|
lint/actions/zizmor:
|
|
@set -euo pipefail; \
|
|
if [ -z "$${GH_TOKEN:-}" ] && command -v gh >/dev/null 2>&1; then \
|
|
GH_TOKEN="$$(gh auth token 2>/dev/null || true)"; \
|
|
export GH_TOKEN; \
|
|
fi; \
|
|
mise exec zizmor -- zizmor \
|
|
--strict-collection \
|
|
--persona=regular \
|
|
.
|
|
.PHONY: lint/actions/zizmor
|
|
|
|
lint/mise-versions:
|
|
./scripts/check_mise_versions.sh
|
|
.PHONY: lint/mise-versions
|
|
|
|
# Verify api_key_scope enum contains all RBAC <resource>:<action> values.
|
|
lint/check-scopes: coderd/database/dump.sql | _gen/bin/check-scopes
|
|
_gen/bin/check-scopes
|
|
.PHONY: lint/check-scopes
|
|
|
|
# Verify migrations do not hardcode the public schema.
|
|
lint/migrations:
|
|
./scripts/check_pg_schema.sh "Migrations" $(MIGRATION_FILES)
|
|
./scripts/check_pg_schema.sh "Fixtures" $(FIXTURE_FILES)
|
|
.PHONY: lint/migrations
|
|
|
|
lint/typos:
|
|
typos --config .github/workflows/typos.toml
|
|
.PHONY: lint/typos
|
|
|
|
# Vale (prose linter).
|
|
#
|
|
# Invoked through `mise exec` like actionlint and zizmor above, so the
|
|
# version pinned in mise.toml ("aqua:errata-ai/vale") is the single source
|
|
# of truth and mise downloads the right OS/arch build. Always pass the full
|
|
# aqua key: the bare `vale` short name ignores the pin and resolves to the
|
|
# latest release.
|
|
|
|
# `vale sync` pulls the packages listed in .vale.ini's Packages directive
|
|
# into StylesPath (docs/.style/styles/). The .vale-synced sentinel makes
|
|
# sync idempotent across `make lint/prose` calls and lets warm checkouts
|
|
# skip the re-sync entirely. Make rebuilds this target when `.vale.ini`
|
|
# changes.
|
|
docs/.style/.vale-synced: .vale.ini
|
|
@echo "$(GREEN)==>$(RESET) $(BOLD)vale sync$(RESET)"
|
|
mise exec "aqua:errata-ai/vale" -- vale sync
|
|
@touch $@
|
|
|
|
# Vale exits non-zero only on error-level alerts. `--no-exit` keeps the
|
|
# target green while the un-overridden Google error-level rules still
|
|
# produce a baseline error count; real failures (bad config, missing
|
|
# files) still propagate. Once the baseline error count reaches zero, drop
|
|
# `--no-exit` and surface error-level violations as real failures. See
|
|
# DOCS-40.
|
|
lint/prose: docs/.style/.vale-synced
|
|
@echo "$(GREEN)==>$(RESET) $(BOLD)lint/prose$(RESET)"
|
|
mise exec "aqua:errata-ai/vale" -- vale --no-exit docs/
|
|
.PHONY: lint/prose
|
|
|
|
# pre-commit and pre-push mirror CI checks locally.
|
|
#
|
|
# pre-commit runs checks that don't need external services (Docker,
|
|
# Playwright). This is the git pre-commit hook default since Docker
|
|
# and browser issues in the local environment would otherwise block
|
|
# all commits.
|
|
#
|
|
# pre-push adds heavier checks: Go tests, JS tests, and site build.
|
|
# The pre-push hook is allowlisted, see scripts/githooks/pre-push.
|
|
#
|
|
# pre-commit uses two phases: gen+fmt first, then lint+build. This
|
|
# avoids races where gen creates temporary .go files that lint's
|
|
# find-based checks pick up. Within each phase, targets run in
|
|
# parallel via -j. It fails if any tracked files have unstaged
|
|
# changes afterward.
|
|
|
|
define check-unstaged
|
|
unstaged="$$(git diff --name-only)"
|
|
if [[ -n $$unstaged ]]; then
|
|
echo "$(RED)✗ check unstaged changes$(RESET)"
|
|
echo "$$unstaged" | sed 's/^/ - /'
|
|
echo ""
|
|
echo "$(DIM) Verify generated changes are correct before staging:$(RESET)"
|
|
echo "$(DIM) git diff$(RESET)"
|
|
echo "$(DIM) git add -u && git commit$(RESET)"
|
|
exit 1
|
|
fi
|
|
endef
|
|
define check-untracked
|
|
untracked=$$(git ls-files --other --exclude-standard)
|
|
if [[ -n $$untracked ]]; then
|
|
echo "$(YELLOW)? check untracked files$(RESET)"
|
|
echo "$$untracked" | sed 's/^/ - /'
|
|
echo ""
|
|
echo "$(DIM) Review if these should be committed or added to .gitignore.$(RESET)"
|
|
fi
|
|
endef
|
|
|
|
pre-commit:
|
|
start=$$(date +%s)
|
|
logdir=$$(mktemp -d "$${TMPDIR:-/tmp}/coder-pre-commit.XXXXXX")
|
|
echo "$(BOLD)pre-commit$(RESET) ($$logdir)"
|
|
echo "gen + fmt:"
|
|
$(MAKE) --no-print-directory -j$(PARALLEL_JOBS) MAKE_TIMED=1 MAKE_LOGDIR=$$logdir gen fmt
|
|
$(check-unstaged)
|
|
echo "lint + build:"
|
|
$(MAKE) --no-print-directory -j$(PARALLEL_JOBS) MAKE_TIMED=1 MAKE_LOGDIR=$$logdir \
|
|
lint \
|
|
lint/typos \
|
|
build/coder-slim_$(GOOS)_$(GOARCH)$(GOOS_BIN_EXT)
|
|
$(check-unstaged)
|
|
$(check-untracked)
|
|
rm -rf $$logdir
|
|
echo "$(GREEN)✓ pre-commit passed$(RESET) ($$(( $$(date +%s) - $$start ))s)"
|
|
.PHONY: pre-commit
|
|
|
|
# Lightweight pre-commit for changes that don't touch Go or
|
|
# TypeScript. Skips gen, lint/go, lint/ts, fmt/go, fmt/ts, and
|
|
# the binary build. Used by the pre-commit hook when only docs,
|
|
# shell, terraform, helm, or other fast-to-check files changed.
|
|
pre-commit-light:
|
|
start=$$(date +%s)
|
|
logdir=$$(mktemp -d "$${TMPDIR:-/tmp}/coder-pre-commit-light.XXXXXX")
|
|
echo "$(BOLD)pre-commit-light$(RESET) ($$logdir)"
|
|
echo "fmt:"
|
|
$(MAKE) --no-print-directory -j$(PARALLEL_JOBS) MAKE_TIMED=1 MAKE_LOGDIR=$$logdir fmt-light
|
|
$(check-unstaged)
|
|
echo "lint:"
|
|
$(MAKE) --no-print-directory -j$(PARALLEL_JOBS) MAKE_TIMED=1 MAKE_LOGDIR=$$logdir lint-light
|
|
$(check-unstaged)
|
|
$(check-untracked)
|
|
rm -rf $$logdir
|
|
echo "$(GREEN)✓ pre-commit-light passed$(RESET) ($$(( $$(date +%s) - $$start ))s)"
|
|
.PHONY: pre-commit-light
|
|
|
|
pre-push:
|
|
start=$$(date +%s)
|
|
logdir=$$(mktemp -d "$${TMPDIR:-/tmp}/coder-pre-push.XXXXXX")
|
|
echo "$(BOLD)pre-push$(RESET) ($$logdir)"
|
|
test -d site/node_modules/.cache/storybook || (cd site/ && pnpm exec node scripts/warmup-storybook-cache.mjs)
|
|
echo "test + build site:"
|
|
$(MAKE) --no-print-directory -j$(PARALLEL_JOBS) MAKE_TIMED=1 MAKE_LOGDIR=$$logdir \
|
|
test \
|
|
test-js \
|
|
site/out/index.html
|
|
# Storybook tests run after Go tests and the site build to avoid
|
|
# CPU starvation. Rolldown's tokio workers in Vite's transform
|
|
# pipeline stall when competing with Go compilation and the
|
|
# production build, causing browser import() calls to hang
|
|
# indefinitely (vitest has no import-phase timeout).
|
|
echo "test storybook:"
|
|
$(MAKE) --no-print-directory MAKE_TIMED=1 MAKE_LOGDIR=$$logdir \
|
|
test-storybook
|
|
rm -rf $$logdir
|
|
echo "$(GREEN)✓ pre-push passed$(RESET) ($$(( $$(date +%s) - $$start ))s)"
|
|
.PHONY: pre-push
|
|
|
|
offlinedocs/check: offlinedocs/node_modules/.installed
|
|
cd offlinedocs/
|
|
pnpm format:check
|
|
pnpm lint
|
|
pnpm export
|
|
.PHONY: offlinedocs/check
|
|
|
|
# All files generated by the database should be added here, and this can be used
|
|
# as a target for jobs that need to run after the database is generated.
|
|
DB_GEN_FILES := \
|
|
coderd/database/dump.sql \
|
|
coderd/database/querier.go \
|
|
coderd/database/unique_constraint.go \
|
|
coderd/database/dbmetrics/querymetrics.go \
|
|
coderd/database/dbauthz/dbauthz.go \
|
|
coderd/database/dbmock/dbmock.go
|
|
|
|
TAILNETTEST_MOCKS := \
|
|
tailnet/tailnettest/coordinatormock.go \
|
|
tailnet/tailnettest/coordinateemock.go \
|
|
tailnet/tailnettest/workspaceupdatesprovidermock.go \
|
|
tailnet/tailnettest/subscriptionmock.go
|
|
|
|
AIBRIDGED_MOCKS := \
|
|
coderd/aibridged/aibridgedmock/clientmock.go \
|
|
coderd/aibridged/aibridgedmock/poolmock.go
|
|
|
|
GEN_FILES := \
|
|
tailnet/proto/tailnet.pb.go \
|
|
agent/proto/agent.pb.go \
|
|
agent/agentsocket/proto/agentsocket.pb.go \
|
|
agent/boundarylogproxy/codec/boundary.pb.go \
|
|
provisionersdk/proto/provisioner.pb.go \
|
|
provisionerd/proto/provisionerd.pb.go \
|
|
vpn/vpn.pb.go \
|
|
coderd/aibridged/proto/aibridged.pb.go \
|
|
$(DB_GEN_FILES) \
|
|
$(SITE_GEN_FILES) \
|
|
coderd/rbac/object_gen.go \
|
|
codersdk/rbacresources_gen.go \
|
|
coderd/rbac/scopes_constants_gen.go \
|
|
codersdk/apikey_scopes_gen.go \
|
|
docs/admin/integrations/prometheus.md \
|
|
docs/reference/cli/index.md \
|
|
docs/admin/security/audit-logs.md \
|
|
docs/install/releases/feature-stages.md \
|
|
coderd/apidoc/swagger.json \
|
|
docs/manifest.json \
|
|
provisioner/terraform/testdata/version \
|
|
scripts/metricsdocgen/generated_metrics \
|
|
site/e2e/provisionerGenerated.ts \
|
|
examples/examples.gen.json \
|
|
$(TAILNETTEST_MOCKS) \
|
|
coderd/database/pubsub/psmock/psmock.go \
|
|
agent/agentcontainers/acmock/acmock.go \
|
|
agent/agentcontainers/dcspec/dcspec_gen.go \
|
|
coderd/httpmw/loggermw/loggermock/loggermock.go \
|
|
codersdk/workspacesdk/agentconnmock/agentconnmock.go \
|
|
$(AIBRIDGED_MOCKS)
|
|
|
|
# all gen targets should be added here and to gen/mark-fresh
|
|
# 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)
|
|
.PHONY: gen/db
|
|
|
|
# Refresh the AI Bridge pricing seed file from models.dev. Kept out of
|
|
# `make gen`. Phony so each invocation regenerates.
|
|
coderd/aibridge/prices/data/prices.json: _gen/bin/aibridgepricesgen | _gen
|
|
@mkdir -p $(dir $@)
|
|
$(call atomic_write,_gen/bin/aibridgepricesgen)
|
|
.PHONY: coderd/aibridge/prices/data/prices.json
|
|
|
|
gen/aibridge-prices: coderd/aibridge/prices/data/prices.json
|
|
.PHONY: gen/aibridge-prices
|
|
|
|
gen/golden-files: \
|
|
agent/unit/testdata/.gen-golden \
|
|
cli/testdata/.gen-golden \
|
|
coderd/.gen-golden \
|
|
coderd/notifications/.gen-golden \
|
|
enterprise/cli/testdata/.gen-golden \
|
|
enterprise/tailnet/testdata/.gen-golden \
|
|
helm/coder/tests/testdata/.gen-golden \
|
|
helm/provisioner/tests/testdata/.gen-golden \
|
|
provisioner/terraform/testdata/.gen-golden \
|
|
tailnet/testdata/.gen-golden
|
|
.PHONY: gen/golden-files
|
|
|
|
# Mark all generated files as fresh so make thinks they're up-to-date. This is
|
|
# used during releases so we don't run generation scripts.
|
|
gen/mark-fresh:
|
|
files="\
|
|
tailnet/proto/tailnet.pb.go \
|
|
agent/proto/agent.pb.go \
|
|
provisionersdk/proto/provisioner.pb.go \
|
|
provisionerd/proto/provisionerd.pb.go \
|
|
agent/agentsocket/proto/agentsocket.pb.go \
|
|
agent/boundarylogproxy/codec/boundary.pb.go \
|
|
vpn/vpn.pb.go \
|
|
coderd/aibridged/proto/aibridged.pb.go \
|
|
coderd/database/dump.sql \
|
|
coderd/database/querier.go \
|
|
coderd/database/unique_constraint.go \
|
|
coderd/database/dbmetrics/querymetrics.go \
|
|
coderd/database/dbauthz/dbauthz.go \
|
|
coderd/database/dbmock/dbmock.go \
|
|
coderd/database/pubsub/psmock/psmock.go \
|
|
site/src/api/typesGenerated.ts \
|
|
coderd/rbac/object_gen.go \
|
|
codersdk/rbacresources_gen.go \
|
|
coderd/rbac/scopes_constants_gen.go \
|
|
codersdk/apikey_scopes_gen.go \
|
|
site/src/api/rbacresourcesGenerated.ts \
|
|
site/src/api/countriesGenerated.ts \
|
|
site/src/api/chatModelOptionsGenerated.json \
|
|
docs/admin/integrations/prometheus.md \
|
|
docs/reference/cli/index.md \
|
|
docs/admin/security/audit-logs.md \
|
|
docs/install/releases/feature-stages.md \
|
|
coderd/apidoc/swagger.json \
|
|
docs/manifest.json \
|
|
site/e2e/provisionerGenerated.ts \
|
|
site/src/theme/icons.json \
|
|
examples/examples.gen.json \
|
|
scripts/metricsdocgen/generated_metrics \
|
|
$(TAILNETTEST_MOCKS) \
|
|
agent/agentcontainers/acmock/acmock.go \
|
|
agent/agentcontainers/dcspec/dcspec_gen.go \
|
|
coderd/httpmw/loggermw/loggermock/loggermock.go \
|
|
codersdk/workspacesdk/agentconnmock/agentconnmock.go \
|
|
$(AIBRIDGED_MOCKS) \
|
|
"
|
|
|
|
for file in $$files; do
|
|
echo "$$file"
|
|
if [ ! -f "$$file" ]; then
|
|
echo "File '$$file' does not exist"
|
|
exit 1
|
|
fi
|
|
|
|
# touch sets the mtime of the file to the current time
|
|
touch "$$file"
|
|
done
|
|
.PHONY: gen/mark-fresh
|
|
|
|
# Runs migrations to output a dump of the database schema after migrations are
|
|
# applied.
|
|
coderd/database/dump.sql: coderd/database/gen/dump/main.go $(DBDUMP_INPUTS) | _gen/bin/dbdump
|
|
_gen/bin/dbdump
|
|
touch "$@"
|
|
|
|
# Generates Go code for querying the database.
|
|
# coderd/database/queries.sql.go
|
|
# coderd/database/models.go
|
|
#
|
|
# NOTE: grouped target (&:) ensures generate.sh runs only once even
|
|
# with -j and all outputs are considered produced together. These
|
|
# files are all written by generate.sh (via sqlc and scripts/dbgen).
|
|
coderd/database/querier.go \
|
|
coderd/database/unique_constraint.go \
|
|
coderd/database/dbmetrics/querymetrics.go \
|
|
coderd/database/dbauthz/dbauthz.go &: \
|
|
coderd/database/sqlc.yaml \
|
|
coderd/database/dump.sql \
|
|
$(wildcard coderd/database/queries/*.sql)
|
|
SKIP_DUMP_SQL=1 ./coderd/database/generate.sh
|
|
touch coderd/database/querier.go coderd/database/unique_constraint.go coderd/database/dbmetrics/querymetrics.go coderd/database/dbauthz/dbauthz.go
|
|
|
|
coderd/database/dbmock/dbmock.go: coderd/database/db.go coderd/database/querier.go
|
|
go generate ./coderd/database/dbmock/
|
|
touch "$@"
|
|
|
|
coderd/database/pubsub/psmock/psmock.go: coderd/database/pubsub/pubsub.go
|
|
go generate ./coderd/database/pubsub/psmock
|
|
touch "$@"
|
|
|
|
agent/agentcontainers/acmock/acmock.go: agent/agentcontainers/containers.go
|
|
go generate ./agent/agentcontainers/acmock/
|
|
touch "$@"
|
|
|
|
coderd/httpmw/loggermw/loggermock/loggermock.go: coderd/httpmw/loggermw/logger.go
|
|
go generate ./coderd/httpmw/loggermw/loggermock/
|
|
touch "$@"
|
|
|
|
codersdk/workspacesdk/agentconnmock/agentconnmock.go: codersdk/workspacesdk/agentconn.go
|
|
go generate ./codersdk/workspacesdk/agentconnmock/
|
|
./scripts/format_go_file.sh "$@"
|
|
touch "$@"
|
|
|
|
$(AIBRIDGED_MOCKS): coderd/aibridged/client.go coderd/aibridged/pool.go
|
|
go generate ./coderd/aibridged/aibridgedmock/
|
|
touch "$@"
|
|
|
|
agent/agentcontainers/dcspec/dcspec_gen.go: \
|
|
node_modules/.installed \
|
|
agent/agentcontainers/dcspec/devContainer.base.schema.json \
|
|
agent/agentcontainers/dcspec/gen.sh \
|
|
agent/agentcontainers/dcspec/doc.go
|
|
DCSPEC_QUIET=true go generate ./agent/agentcontainers/dcspec/
|
|
touch "$@"
|
|
|
|
$(TAILNETTEST_MOCKS): tailnet/coordinator.go tailnet/service.go
|
|
go generate ./tailnet/tailnettest/
|
|
touch "$@"
|
|
|
|
tailnet/proto/tailnet.pb.go: tailnet/proto/tailnet.proto
|
|
./scripts/atomic_protoc.sh \
|
|
--go_out=. \
|
|
--go_opt=paths=source_relative \
|
|
--go-drpc_out=. \
|
|
--go-drpc_opt=paths=source_relative \
|
|
./tailnet/proto/tailnet.proto
|
|
|
|
agent/proto/agent.pb.go: agent/proto/agent.proto
|
|
./scripts/atomic_protoc.sh \
|
|
--go_out=. \
|
|
--go_opt=paths=source_relative \
|
|
--go-drpc_out=. \
|
|
--go-drpc_opt=paths=source_relative \
|
|
./agent/proto/agent.proto
|
|
|
|
agent/agentsocket/proto/agentsocket.pb.go: agent/agentsocket/proto/agentsocket.proto agent/proto/agent.proto
|
|
./scripts/atomic_protoc.sh \
|
|
--go_out=. \
|
|
--go_opt=paths=source_relative \
|
|
--go-drpc_out=. \
|
|
--go-drpc_opt=paths=source_relative \
|
|
./agent/agentsocket/proto/agentsocket.proto
|
|
|
|
provisionersdk/proto/provisioner.pb.go: provisionersdk/proto/provisioner.proto
|
|
./scripts/atomic_protoc.sh \
|
|
--go_out=. \
|
|
--go_opt=paths=source_relative \
|
|
--go-drpc_out=. \
|
|
--go-drpc_opt=paths=source_relative \
|
|
./provisionersdk/proto/provisioner.proto
|
|
|
|
provisionerd/proto/provisionerd.pb.go: provisionerd/proto/provisionerd.proto
|
|
./scripts/atomic_protoc.sh \
|
|
--go_out=. \
|
|
--go_opt=paths=source_relative \
|
|
--go-drpc_out=. \
|
|
--go-drpc_opt=paths=source_relative \
|
|
./provisionerd/proto/provisionerd.proto
|
|
|
|
vpn/vpn.pb.go: vpn/vpn.proto
|
|
./scripts/atomic_protoc.sh \
|
|
--go_out=. \
|
|
--go_opt=paths=source_relative \
|
|
./vpn/vpn.proto
|
|
|
|
agent/boundarylogproxy/codec/boundary.pb.go: agent/boundarylogproxy/codec/boundary.proto agent/proto/agent.proto
|
|
./scripts/atomic_protoc.sh \
|
|
--go_out=. \
|
|
--go_opt=paths=source_relative \
|
|
./agent/boundarylogproxy/codec/boundary.proto
|
|
|
|
coderd/aibridged/proto/aibridged.pb.go: coderd/aibridged/proto/aibridged.proto
|
|
./scripts/atomic_protoc.sh \
|
|
--go_out=. \
|
|
--go_opt=paths=source_relative \
|
|
--go-drpc_out=. \
|
|
--go-drpc_opt=paths=source_relative \
|
|
./coderd/aibridged/proto/aibridged.proto
|
|
|
|
site/src/api/typesGenerated.ts: site/node_modules/.installed $(wildcard scripts/apitypings/*) \
|
|
$(shell find ./codersdk $(FIND_EXCLUSIONS) -type f -name '*.go') \
|
|
$(wildcard coderd/healthcheck/health/*.go) \
|
|
$(wildcard codersdk/healthsdk/*.go) | _gen _gen/bin/apitypings
|
|
$(call atomic_write,_gen/bin/apitypings,./scripts/biome_format.sh)
|
|
|
|
site/e2e/provisionerGenerated.ts: site/node_modules/.installed provisionerd/proto/provisionerd.pb.go provisionersdk/proto/provisioner.pb.go
|
|
(cd site/ && pnpm run gen:provisioner)
|
|
touch "$@"
|
|
|
|
site/src/theme/icons.json: site/node_modules/.installed $(wildcard scripts/gensite/*) $(wildcard site/static/icon/*) | _gen _gen/bin/gensite
|
|
tmpdir=$$(mktemp -d -p _gen) && tmpfile=$$(realpath "$$tmpdir")/$(notdir $@) && \
|
|
_gen/bin/gensite -icons "$$tmpfile" && \
|
|
./scripts/biome_format.sh "$$tmpfile" && \
|
|
mv "$$tmpfile" "$@" && rm -rf "$$tmpdir"
|
|
|
|
examples/examples.gen.json: scripts/examplegen/main.go examples/examples.go $(shell find ./examples/templates) | _gen _gen/bin/examplegen
|
|
$(call atomic_write,_gen/bin/examplegen)
|
|
|
|
coderd/rbac/object_gen.go: scripts/typegen/rbacobject.gotmpl scripts/typegen/main.go coderd/rbac/object.go coderd/rbac/policy/policy.go | _gen _gen/bin/typegen
|
|
$(call atomic_write,_gen/bin/typegen rbac object)
|
|
touch "$@"
|
|
|
|
# NOTE: depends on object_gen.go because the generator build
|
|
# compiles coderd/rbac which includes it.
|
|
coderd/rbac/scopes_constants_gen.go: scripts/typegen/scopenames.gotmpl scripts/typegen/main.go coderd/rbac/policy/policy.go \
|
|
coderd/rbac/object_gen.go | _gen _gen/bin/typegen
|
|
# Write to a temp file first to avoid truncating the package
|
|
# during build since the generator imports the rbac package.
|
|
$(call atomic_write,_gen/bin/typegen rbac scopenames)
|
|
touch "$@"
|
|
|
|
# NOTE: depends on object_gen.go and scopes_constants_gen.go because
|
|
# the generator build compiles coderd/rbac which includes both.
|
|
codersdk/rbacresources_gen.go: scripts/typegen/codersdk.gotmpl scripts/typegen/main.go coderd/rbac/object.go coderd/rbac/policy/policy.go \
|
|
coderd/rbac/object_gen.go coderd/rbac/scopes_constants_gen.go | _gen _gen/bin/typegen
|
|
# Write to a temp file to avoid truncating the target, which
|
|
# would break the codersdk package and any parallel build targets.
|
|
$(call atomic_write,_gen/bin/typegen rbac codersdk)
|
|
touch "$@"
|
|
|
|
# NOTE: depends on object_gen.go and scopes_constants_gen.go because
|
|
# the generator build compiles coderd/rbac which includes both.
|
|
codersdk/apikey_scopes_gen.go: scripts/apikeyscopesgen/main.go coderd/rbac/scopes_catalog.go coderd/rbac/scopes.go \
|
|
coderd/rbac/object_gen.go coderd/rbac/scopes_constants_gen.go | _gen _gen/bin/apikeyscopesgen
|
|
# Generate SDK constants for external API key scopes.
|
|
$(call atomic_write,_gen/bin/apikeyscopesgen)
|
|
touch "$@"
|
|
|
|
# NOTE: depends on object_gen.go and scopes_constants_gen.go because
|
|
# the generator build compiles coderd/rbac which includes both.
|
|
site/src/api/rbacresourcesGenerated.ts: site/node_modules/.installed scripts/typegen/codersdk.gotmpl scripts/typegen/main.go coderd/rbac/object.go coderd/rbac/policy/policy.go \
|
|
coderd/rbac/object_gen.go coderd/rbac/scopes_constants_gen.go | _gen _gen/bin/typegen
|
|
$(call atomic_write,_gen/bin/typegen rbac typescript,./scripts/biome_format.sh)
|
|
|
|
site/src/api/countriesGenerated.ts: site/node_modules/.installed scripts/typegen/countries.tstmpl scripts/typegen/main.go codersdk/countries.go | _gen _gen/bin/typegen
|
|
$(call atomic_write,_gen/bin/typegen countries,./scripts/biome_format.sh)
|
|
|
|
site/src/api/chatModelOptionsGenerated.json: scripts/modeloptionsgen/main.go codersdk/chats.go | _gen _gen/bin/modeloptionsgen
|
|
$(call atomic_write,_gen/bin/modeloptionsgen | tail -n +2,./scripts/biome_format.sh)
|
|
|
|
scripts/metricsdocgen/generated_metrics: $(GO_SRC_FILES) | _gen _gen/bin/metricsdocgen-scanner
|
|
$(call atomic_write,_gen/bin/metricsdocgen-scanner)
|
|
|
|
docs/admin/integrations/prometheus.md: node_modules/.installed scripts/metricsdocgen/main.go scripts/metricsdocgen/metrics scripts/metricsdocgen/generated_metrics | _gen _gen/bin/metricsdocgen
|
|
tmpdir=$$(mktemp -d -p _gen) && tmpfile=$$(realpath "$$tmpdir")/$(notdir $@) && cp "$@" "$$tmpfile" && \
|
|
_gen/bin/metricsdocgen --prometheus-doc-file="$$tmpfile" && \
|
|
pnpm exec markdownlint-cli2 --fix "$$tmpfile" && \
|
|
pnpm exec markdown-table-formatter "$$tmpfile" && \
|
|
mv "$$tmpfile" "$@" && rm -rf "$$tmpdir"
|
|
|
|
docs/reference/cli/index.md: node_modules/.installed examples/examples.gen.json _gen/bin/clidocgen | _gen
|
|
tmpdir=$$(mktemp -d -p _gen) && \
|
|
tmpdir=$$(realpath "$$tmpdir") && \
|
|
mkdir -p "$$tmpdir/docs/reference/cli" && \
|
|
cp docs/manifest.json "$$tmpdir/docs/manifest.json" && \
|
|
CI=true DOCS_DIR="$$tmpdir/docs" _gen/bin/clidocgen && \
|
|
pnpm exec markdownlint-cli2 --fix "$$tmpdir/docs/reference/cli/*.md" && \
|
|
pnpm exec markdown-table-formatter "$$tmpdir/docs/reference/cli/*.md" && \
|
|
for f in "$$tmpdir/docs/reference/cli/"*.md; do mv "$$f" "docs/reference/cli/$$(basename "$$f")"; done && \
|
|
rm -rf "$$tmpdir"
|
|
|
|
docs/admin/security/audit-logs.md: node_modules/.installed coderd/database/querier.go scripts/auditdocgen/main.go enterprise/audit/table.go coderd/rbac/object_gen.go | _gen _gen/bin/auditdocgen
|
|
tmpdir=$$(mktemp -d -p _gen) && tmpfile=$$(realpath "$$tmpdir")/$(notdir $@) && cp "$@" "$$tmpfile" && \
|
|
_gen/bin/auditdocgen --audit-doc-file="$$tmpfile" && \
|
|
pnpm exec markdownlint-cli2 --fix "$$tmpfile" && \
|
|
pnpm exec markdown-table-formatter "$$tmpfile" && \
|
|
mv "$$tmpfile" "$@" && rm -rf "$$tmpdir"
|
|
|
|
docs/install/releases/feature-stages.md: \
|
|
node_modules/.installed \
|
|
scripts/release/docs_update_feature_stages.sh \
|
|
codersdk/deployment.go \
|
|
docs/manifest.json | _gen
|
|
tmpdir=$$(mktemp -d -p _gen) && tmpfile=$$(realpath "$$tmpdir")/$(notdir $@) && cp "$@" "$$tmpfile" && \
|
|
./scripts/release/docs_update_feature_stages.sh "$$tmpfile" && \
|
|
pnpm exec markdownlint-cli2 --fix "$$tmpfile" && \
|
|
pnpm exec markdown-table-formatter "$$tmpfile" && \
|
|
mv "$$tmpfile" "$@" && rm -rf "$$tmpdir"
|
|
|
|
coderd/apidoc/.gen: \
|
|
node_modules/.installed \
|
|
scripts/apidocgen/node_modules/.installed \
|
|
$(wildcard coderd/*.go) \
|
|
$(wildcard enterprise/coderd/*.go) \
|
|
$(wildcard codersdk/*.go) \
|
|
$(wildcard enterprise/wsproxy/wsproxysdk/*.go) \
|
|
$(wildcard coderd/workspaceconnwatcher/*.go) \
|
|
$(DB_GEN_FILES) \
|
|
coderd/rbac/object_gen.go \
|
|
.swaggo \
|
|
scripts/apidocgen/generate.sh \
|
|
scripts/apidocgen/swaginit/main.go \
|
|
$(wildcard scripts/apidocgen/postprocess/*) \
|
|
$(wildcard scripts/apidocgen/markdown-template/*) | _gen
|
|
tmpdir=$$(mktemp -d -p _gen) && swagtmp=$$(mktemp -d -p _gen) && \
|
|
tmpdir=$$(realpath "$$tmpdir") && swagtmp=$$(realpath "$$swagtmp") && \
|
|
mkdir -p "$$tmpdir/reference/api" && \
|
|
cp docs/manifest.json "$$tmpdir/manifest.json" && \
|
|
SWAG_OUTPUT_DIR="$$swagtmp" APIDOCGEN_DOCS_DIR="$$tmpdir" ./scripts/apidocgen/generate.sh && \
|
|
pnpm exec markdownlint-cli2 --fix "$$tmpdir/reference/api/*.md" && \
|
|
pnpm exec markdown-table-formatter "$$tmpdir/reference/api/*.md" && \
|
|
./scripts/biome_format.sh "$$swagtmp/swagger.json" && \
|
|
for f in "$$tmpdir/reference/api/"*.md; do mv "$$f" "docs/reference/api/$$(basename "$$f")"; done && \
|
|
mv "$$tmpdir/manifest.json" _gen/manifest-staging.json && \
|
|
mv "$$swagtmp/docs.go" coderd/apidoc/docs.go && \
|
|
mv "$$swagtmp/swagger.json" coderd/apidoc/swagger.json && \
|
|
rm -rf "$$tmpdir" "$$swagtmp"
|
|
touch "$@"
|
|
|
|
docs/manifest.json: site/node_modules/.installed coderd/apidoc/.gen docs/reference/cli/index.md | _gen
|
|
tmpdir=$$(mktemp -d -p _gen) && tmpfile=$$(realpath "$$tmpdir")/$(notdir $@) && \
|
|
cp _gen/manifest-staging.json "$$tmpfile" && \
|
|
./scripts/biome_format.sh "$$tmpfile" && \
|
|
mv "$$tmpfile" "$@" && rm -rf "$$tmpdir"
|
|
|
|
coderd/apidoc/swagger.json: site/node_modules/.installed coderd/apidoc/.gen
|
|
touch "$@"
|
|
|
|
update-golden-files:
|
|
echo 'WARNING: This target is deprecated. Use "make gen/golden-files" instead.' >&2
|
|
echo 'Running "make gen/golden-files"' >&2
|
|
make gen/golden-files
|
|
.PHONY: update-golden-files
|
|
|
|
clean/golden-files:
|
|
find . -type f -name '.gen-golden' -delete
|
|
find \
|
|
cli/testdata \
|
|
coderd/notifications/testdata \
|
|
coderd/testdata \
|
|
enterprise/cli/testdata \
|
|
enterprise/tailnet/testdata \
|
|
helm/coder/tests/testdata \
|
|
helm/provisioner/tests/testdata \
|
|
provisioner/terraform/testdata \
|
|
tailnet/testdata \
|
|
-type f -name '*.golden' -delete
|
|
.PHONY: clean/golden-files
|
|
|
|
agent/unit/testdata/.gen-golden: $(wildcard agent/unit/testdata/*.golden) $(GO_SRC_FILES) $(wildcard agent/unit/*_test.go)
|
|
TZ=UTC go test ./agent/unit -run="TestGraph" -update
|
|
touch "$@"
|
|
|
|
cli/testdata/.gen-golden: $(wildcard cli/testdata/*.golden) $(wildcard cli/*.tpl) $(GO_SRC_FILES) $(wildcard cli/*_test.go)
|
|
TZ=UTC go test ./cli -run="Test(CommandHelp|ServerYAML|ErrorExamples|.*Golden)" -update
|
|
touch "$@"
|
|
|
|
enterprise/cli/testdata/.gen-golden: $(wildcard enterprise/cli/testdata/*.golden) $(wildcard cli/*.tpl) $(GO_SRC_FILES) $(wildcard enterprise/cli/*_test.go)
|
|
TZ=UTC go test ./enterprise/cli -run="TestEnterpriseCommandHelp" -update
|
|
touch "$@"
|
|
|
|
tailnet/testdata/.gen-golden: $(wildcard tailnet/testdata/*.golden.html) $(GO_SRC_FILES) $(wildcard tailnet/*_test.go)
|
|
TZ=UTC go test ./tailnet -run="TestDebugTemplate" -update
|
|
touch "$@"
|
|
|
|
enterprise/tailnet/testdata/.gen-golden: $(wildcard enterprise/tailnet/testdata/*.golden.html) $(GO_SRC_FILES) $(wildcard enterprise/tailnet/*_test.go)
|
|
TZ=UTC go test ./enterprise/tailnet -run="TestDebugTemplate" -update
|
|
touch "$@"
|
|
|
|
helm/coder/tests/testdata/.gen-golden: $(wildcard helm/coder/tests/testdata/*.yaml) $(wildcard helm/coder/tests/testdata/*.golden) $(GO_SRC_FILES) $(wildcard helm/coder/tests/*_test.go)
|
|
if command -v helm >/dev/null 2>&1; then
|
|
TZ=UTC go test ./helm/coder/tests -run=TestUpdateGoldenFiles -update
|
|
else
|
|
echo "WARNING: helm not found; skipping helm/coder golden generation" >&2
|
|
fi
|
|
touch "$@"
|
|
|
|
helm/provisioner/tests/testdata/.gen-golden: $(wildcard helm/provisioner/tests/testdata/*.yaml) $(wildcard helm/provisioner/tests/testdata/*.golden) $(GO_SRC_FILES) $(wildcard helm/provisioner/tests/*_test.go)
|
|
if command -v helm >/dev/null 2>&1; then
|
|
TZ=UTC go test ./helm/provisioner/tests -run=TestUpdateGoldenFiles -update
|
|
else
|
|
echo "WARNING: helm not found; skipping helm/provisioner golden generation" >&2
|
|
fi
|
|
touch "$@"
|
|
|
|
coderd/.gen-golden: $(wildcard coderd/testdata/*/*.golden) $(GO_SRC_FILES) $(wildcard coderd/*_test.go)
|
|
TZ=UTC go test ./coderd -run="Test.*Golden$$" -update
|
|
touch "$@"
|
|
|
|
coderd/notifications/.gen-golden: $(wildcard coderd/notifications/testdata/*/*.golden) $(GO_SRC_FILES) $(wildcard coderd/notifications/*_test.go)
|
|
TZ=UTC go test ./coderd/notifications -run="Test.*Golden$$" -update
|
|
touch "$@"
|
|
|
|
provisioner/terraform/testdata/.gen-golden: $(wildcard provisioner/terraform/testdata/*/*.golden) $(wildcard provisioner/terraform/testdata/*/*/*.golden) $(GO_SRC_FILES) $(wildcard provisioner/terraform/*_test.go)
|
|
TZ=UTC go test ./provisioner/terraform -run="Test.*Golden$$" -update
|
|
touch "$@"
|
|
|
|
provisioner/terraform/testdata/version:
|
|
@tf_match=true; \
|
|
if [[ "$$(cat provisioner/terraform/testdata/version.txt)" != \
|
|
"$$(terraform version -json | jq -r '.terraform_version')" ]]; then \
|
|
tf_match=false; \
|
|
fi; \
|
|
if ! $$tf_match || \
|
|
! ./provisioner/terraform/testdata/generate.sh --check; then \
|
|
./provisioner/terraform/testdata/generate.sh; \
|
|
fi
|
|
.PHONY: provisioner/terraform/testdata/version
|
|
|
|
update-terraform-testdata:
|
|
./provisioner/terraform/testdata/generate.sh --upgrade
|
|
.PHONY: update-terraform-testdata
|
|
|
|
# Set the retry flags if TEST_RETRIES is set
|
|
ifdef TEST_RETRIES
|
|
GOTESTSUM_RETRY_FLAGS := --rerun-fails=$(TEST_RETRIES)
|
|
else
|
|
GOTESTSUM_RETRY_FLAGS :=
|
|
endif
|
|
|
|
# Default to 8x8 parallelism to avoid overwhelming our workspaces.
|
|
# Race detection defaults to 4x4 because the detector adds significant
|
|
# CPU overhead. Override via TEST_NUM_PARALLEL_PACKAGES /
|
|
# TEST_NUM_PARALLEL_TESTS.
|
|
TEST_PARALLEL_PACKAGES := $(or $(TEST_NUM_PARALLEL_PACKAGES),8)
|
|
TEST_PARALLEL_TESTS := $(or $(TEST_NUM_PARALLEL_TESTS),8)
|
|
RACE_PARALLEL_PACKAGES := $(or $(TEST_NUM_PARALLEL_PACKAGES),4)
|
|
RACE_PARALLEL_TESTS := $(or $(TEST_NUM_PARALLEL_TESTS),4)
|
|
|
|
# Use testsmallbatch tag to reduce wireguard memory allocation in tests
|
|
# (from ~18GB to negligible). Recursively expanded so target-specific
|
|
# overrides of TEST_PARALLEL_* take effect (e.g. test-race lowers
|
|
# parallelism). CI job timeout is 25m (see test-go-pg in ci.yaml),
|
|
# keep the Go timeout 5m shorter so tests produce goroutine dumps
|
|
# instead of the CI runner killing the process with no output.
|
|
GOTEST_FLAGS = -tags=testsmallbatch -v -timeout 20m -p $(TEST_PARALLEL_PACKAGES) -parallel=$(TEST_PARALLEL_TESTS)
|
|
|
|
# The most common use is to set TEST_COUNT=1 to avoid Go's test cache.
|
|
ifdef TEST_COUNT
|
|
GOTEST_FLAGS += -count=$(TEST_COUNT)
|
|
endif
|
|
|
|
ifdef TEST_SHORT
|
|
GOTEST_FLAGS += -short
|
|
endif
|
|
|
|
# RUN is single-quoted for the shell so regex metacharacters survive make.
|
|
# Embedded single quotes are not supported; whichtests only emits RUN values
|
|
# built from ASCII test names so generated regexes stay within this contract.
|
|
ifdef RUN
|
|
GOTEST_FLAGS += -run '$(RUN)'
|
|
endif
|
|
|
|
# TEST_SHUFFLE values must be off, on, or an integer seed.
|
|
ifdef TEST_SHUFFLE
|
|
GOTEST_FLAGS += -shuffle=$(TEST_SHUFFLE)
|
|
endif
|
|
|
|
ifdef TEST_CPUPROFILE
|
|
GOTEST_FLAGS += -cpuprofile=$(TEST_CPUPROFILE)
|
|
endif
|
|
|
|
ifdef TEST_MEMPROFILE
|
|
GOTEST_FLAGS += -memprofile=$(TEST_MEMPROFILE)
|
|
endif
|
|
|
|
TEST_PACKAGES ?= ./...
|
|
|
|
test:
|
|
$(GIT_FLAGS) gotestsum --format standard-quiet \
|
|
$(GOTESTSUM_RETRY_FLAGS) \
|
|
--packages="$(TEST_PACKAGES)" \
|
|
-- \
|
|
$(GOTEST_FLAGS)
|
|
.PHONY: test
|
|
|
|
test-race: TEST_PARALLEL_PACKAGES := $(RACE_PARALLEL_PACKAGES)
|
|
test-race: TEST_PARALLEL_TESTS := $(RACE_PARALLEL_TESTS)
|
|
test-race:
|
|
$(GIT_FLAGS) gotestsum --format standard-quiet \
|
|
--junitfile="gotests.xml" \
|
|
$(GOTESTSUM_RETRY_FLAGS) \
|
|
--packages="$(TEST_PACKAGES)" \
|
|
-- \
|
|
-race \
|
|
$(GOTEST_FLAGS)
|
|
.PHONY: test-race
|
|
|
|
test-cli:
|
|
$(MAKE) test TEST_PACKAGES="./cli..."
|
|
.PHONY: test-cli
|
|
|
|
test-js: site/node_modules/.installed
|
|
cd site/
|
|
pnpm test:ci
|
|
.PHONY: test-js
|
|
|
|
test-storybook: site/node_modules/.installed
|
|
cd site/
|
|
pnpm playwright:install
|
|
pnpm exec vitest run --project=storybook
|
|
.PHONY: test-storybook
|
|
|
|
# sqlc-cloud-is-setup will fail if no SQLc auth token is set. Use this as a
|
|
# dependency for any sqlc-cloud related targets.
|
|
sqlc-cloud-is-setup:
|
|
if [[ "$(SQLC_AUTH_TOKEN)" == "" ]]; then
|
|
echo "ERROR: 'SQLC_AUTH_TOKEN' must be set to auth with sqlc cloud before running verify." 1>&2
|
|
exit 1
|
|
fi
|
|
.PHONY: sqlc-cloud-is-setup
|
|
|
|
sqlc-push: sqlc-cloud-is-setup test-postgres-docker
|
|
echo "--- sqlc push"
|
|
SQLC_DATABASE_URL="postgresql://postgres:postgres@localhost:5432/$$(go run scripts/migrate-ci/main.go)" \
|
|
sqlc push -f coderd/database/sqlc.yaml && echo "Passed sqlc push"
|
|
.PHONY: sqlc-push
|
|
|
|
sqlc-verify: sqlc-cloud-is-setup test-postgres-docker
|
|
echo "--- sqlc verify"
|
|
SQLC_DATABASE_URL="postgresql://postgres:postgres@localhost:5432/$$(go run scripts/migrate-ci/main.go)" \
|
|
sqlc verify -f coderd/database/sqlc.yaml && echo "Passed sqlc verify"
|
|
.PHONY: sqlc-verify
|
|
|
|
sqlc-vet: test-postgres-docker
|
|
echo "--- sqlc vet"
|
|
SQLC_DATABASE_URL="postgresql://postgres:postgres@localhost:5432/$$(go run scripts/migrate-ci/main.go)" \
|
|
sqlc vet -f coderd/database/sqlc.yaml && echo "Passed sqlc vet"
|
|
.PHONY: sqlc-vet
|
|
|
|
|
|
test-migrations: test-postgres-docker
|
|
echo "--- test migrations"
|
|
set -euo pipefail
|
|
COMMIT_FROM=$(shell git log -1 --format='%h' HEAD)
|
|
echo "COMMIT_FROM=$${COMMIT_FROM}"
|
|
COMMIT_TO=$(shell git log -1 --format='%h' origin/main)
|
|
echo "COMMIT_TO=$${COMMIT_TO}"
|
|
if [[ "$${COMMIT_FROM}" == "$${COMMIT_TO}" ]]; then echo "Nothing to do!"; exit 0; fi
|
|
echo "DROP DATABASE IF EXISTS migrate_test_$${COMMIT_FROM}; CREATE DATABASE migrate_test_$${COMMIT_FROM};" | psql 'postgresql://postgres:postgres@localhost:5432/postgres?sslmode=disable'
|
|
go run ./scripts/migrate-test/main.go --from="$$COMMIT_FROM" --to="$$COMMIT_TO" --postgres-url="postgresql://postgres:postgres@localhost:5432/migrate_test_$${COMMIT_FROM}?sslmode=disable"
|
|
.PHONY: test-migrations
|
|
|
|
# NOTE: we set --memory to the same size as a GitHub runner.
|
|
test-postgres-docker:
|
|
# If our container is already running, nothing to do.
|
|
if docker ps --filter "name=test-postgres-docker-${POSTGRES_VERSION}" --format '{{.Names}}' | grep -q .; then \
|
|
echo "test-postgres-docker-${POSTGRES_VERSION} is already running."; \
|
|
exit 0; \
|
|
fi
|
|
# If something else is on 5432, warn but don't fail.
|
|
if pg_isready -h 127.0.0.1 -q 2>/dev/null; then \
|
|
echo "WARNING: PostgreSQL is already running on 127.0.0.1:5432 (not our container)."; \
|
|
echo "Tests will use this instance. To use the Makefile's container, stop it first."; \
|
|
exit 0; \
|
|
fi
|
|
docker rm -f test-postgres-docker-${POSTGRES_VERSION} || true
|
|
|
|
# Try pulling up to three times to avoid CI flakes.
|
|
docker pull ${POSTGRES_IMAGE} || {
|
|
retries=2
|
|
for try in $$(seq 1 $${retries}); do
|
|
echo "Failed to pull image, retrying ($${try}/$${retries})..."
|
|
sleep 1
|
|
if docker pull ${POSTGRES_IMAGE}; then
|
|
break
|
|
fi
|
|
done
|
|
}
|
|
|
|
# Make sure to not overallocate work_mem and max_connections as each
|
|
# connection will be allowed to use this much memory. Try adjusting
|
|
# shared_buffers instead, if needed.
|
|
#
|
|
# - work_mem=8MB * max_connections=1000 = 8GB
|
|
# - shared_buffers=2GB + effective_cache_size=1GB = 3GB
|
|
#
|
|
# This leaves 5GB for the rest of the system _and_ storing the
|
|
# database in memory (--tmpfs).
|
|
#
|
|
# https://www.postgresql.org/docs/current/runtime-config-resource.html#GUC-WORK-MEM
|
|
docker run \
|
|
--env POSTGRES_PASSWORD=postgres \
|
|
--env POSTGRES_USER=postgres \
|
|
--env POSTGRES_DB=postgres \
|
|
--env PGDATA=/tmp \
|
|
--tmpfs /tmp \
|
|
--publish 5432:5432 \
|
|
--name test-postgres-docker-${POSTGRES_VERSION} \
|
|
--restart no \
|
|
--detach \
|
|
--memory 16GB \
|
|
${POSTGRES_IMAGE} \
|
|
-c shared_buffers=2GB \
|
|
-c effective_cache_size=1GB \
|
|
-c work_mem=8MB \
|
|
-c max_connections=1000 \
|
|
-c fsync=off \
|
|
-c synchronous_commit=off \
|
|
-c full_page_writes=off \
|
|
-c log_statement=all
|
|
while ! pg_isready -h 127.0.0.1
|
|
do
|
|
echo "$$(date) - waiting for database to start"
|
|
sleep 0.5
|
|
done
|
|
.PHONY: test-postgres-docker
|
|
|
|
# test-postgres-docker-logs prints the PostgreSQL container's logs. The
|
|
# postgres image logs to stderr (no logging_collector), which Docker captures,
|
|
# so combined with log_statement=all in test-postgres-docker these logs include
|
|
# every executed statement. Redirect to a file to save them, e.g.
|
|
# `make test-postgres-docker-logs > postgres.log`.
|
|
test-postgres-docker-logs:
|
|
docker logs test-postgres-docker-${POSTGRES_VERSION}
|
|
.PHONY: test-postgres-docker-logs
|
|
|
|
test-tailnet-integration:
|
|
env \
|
|
CODER_TAILNET_TESTS=true \
|
|
CODER_MAGICSOCK_DEBUG_LOGGING=true \
|
|
TS_DEBUG_NETCHECK=true \
|
|
GOTRACEBACK=single \
|
|
go test \
|
|
-tags=testsmallbatch \
|
|
-exec "sudo -E" \
|
|
-timeout=5m \
|
|
-count=1 \
|
|
./tailnet/test/integration
|
|
.PHONY: test-tailnet-integration
|
|
|
|
# Note: we used to add this to the test target, but it's not necessary and we can
|
|
# achieve the desired result by specifying -count=1 in the go test invocation
|
|
# instead. Keeping it here for convenience.
|
|
test-clean:
|
|
go clean -testcache
|
|
.PHONY: test-clean
|
|
|
|
site/e2e/bin/coder: go.mod go.sum $(GO_SRC_FILES)
|
|
go build -o $@ \
|
|
-tags ts_omit_aws,ts_omit_bird,ts_omit_tap,ts_omit_kube \
|
|
./enterprise/cmd/coder
|
|
|
|
test-e2e: site/e2e/bin/coder site/node_modules/.installed site/out/index.html
|
|
cd site/
|
|
pnpm playwright:install
|
|
ifdef CI
|
|
DEBUG=pw:api pnpm playwright:test --forbid-only --workers 1
|
|
else
|
|
pnpm playwright:test
|
|
endif
|
|
.PHONY: test-e2e
|
|
|
|
# Count the number of test databases created per test package.
|
|
count-test-databases:
|
|
PGPASSWORD=postgres psql -h localhost -U postgres -d coder_testing -P pager=off -c 'SELECT test_package, count(*) as count from test_databases GROUP BY test_package ORDER BY count DESC'
|
|
.PHONY: count-test-databases
|
|
|
|
.PHONY: count-test-databases
|