Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,25 @@ jobs:
- run: make docs-content
- run: git diff --exit-code README.md

dockerflags:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version-file: go.mod
# cmd/dockerflagsgen reads the "docker run" flags out of docker/cli itself. A diff here means
# the pinned docker/cli changed one decolint parses "runArgs" with, which decides where in the
# array a rule reads a value from; it should arrive as a reviewable diff rather than as
# findings that quietly stop matching what Docker does.
- run: make dockerflags
- run: git diff --exit-code dockerargs/runflags.go
- run: make dockerflags-test

dogfooding:
runs-on: ubuntu-latest
permissions:
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ profile.cov
# A stray "go build ./cmd/docgen/..." from the repo root lands here; the Makefile always uses
# "go run" for it instead, so this is never an intentional artifact.
/docgen
# The same for cmd/dockerflagsgen, except that it is a module of its own, so a stray "go build"
# runs from inside it and the binary lands beside the source; "make dockerflags" uses "go run".
/cmd/dockerflagsgen/dockerflagsgen
/docs/public/
/docs/resources/
# Rule pages, the README-derived pages, and the generated syntax stylesheet; see cmd/docgen and
Expand Down
47 changes: 47 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,53 @@ context the two snippets alone don't convey.
The existing rules in [`rules/`](rules/) are good references,
including for the table-driven tests each rule ships with.

## The `docker run` flag table

A devcontainer.json's `runArgs` is spliced into a `docker run` command
line, so where a rule finds a value depends on which flags take one:
`["--label", "--cap-drop=ALL"]` drops no capability, because `--label`
consumes the entry after it. [`dockerargs`](dockerargs/) reads a
`runArgs` array the way pflag — the parser docker/cli uses — reads an
argv, and rules ask it for a flag's values instead of matching entries
themselves.

That needs to know every flag `docker run` registers and whether it
takes a value. A table written by hand would go stale the first time
Docker adds a flag, and stale silently: decolint would keep parsing,
just no longer the way Docker does. So
[`cmd/dockerflagsgen`](cmd/dockerflagsgen/) builds the command
docker/cli builds and writes the flags back out as
[`dockerargs/runflags.go`](dockerargs/runflags.go):

```console
make dockerflags # regenerate the table
make dockerflags-test # the generator's tests, incl. the differential test against pflag
```

The generator is a module of its own, with docker/cli pinned in its own
[`go.mod`](cmd/dockerflagsgen/go.mod). decolint itself depends on
neither docker/cli nor pflag, and `go build`, `go test` and `make lint`,
which all work on the module rooted here, never reach it. Renovate bumps
the pin like any other dependency; CI's `dockerflags` job regenerates
the table and fails on a diff, so a docker/cli release that changes a
flag arrives as a diff to review rather than as findings that quietly
stop matching what Docker does.

Two of the generator module's tests are the ones that keep the table
honest, and both are meant to fail loudly:

- `TestParse` runs random argvs through both `dockerargs.Parse` and a
real `pflag.FlagSet` built from the table, and compares the values
each assigns.
- `TestRunFlags`, in `dockerargs` itself, spells the whole table out a
second time, so regenerating it against a newer docker/cli fails a
test instead of changing which entry a rule reads a value from.

`dockerargs` deliberately parts from Docker in two places, both
documented at `Parse`: it reads on past a `--` terminator and past the
image name, where Docker stops. Anything else is a bug in `dockerargs`,
not a rule's problem.

## Where documentation lives

The README and the site divide by what the reader is trying to decide:
Expand Down
8 changes: 8 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,14 @@ coverage: ## Run tests and open an HTML coverage report
lint: ## Run all lint rules
go run github.com/golangci/golangci-lint/v2/cmd/golangci-lint@$(GOLANGCI_LINT_VERSION) run

.PHONY: dockerflags
dockerflags: ## Regenerate the "docker run" flag table in dockerargs
go -C cmd/dockerflagsgen run . -o ../../dockerargs/runflags.go

.PHONY: dockerflags-test
dockerflags-test: ## Run cmd/dockerflagsgen's tests, including the differential test against pflag
go -C cmd/dockerflagsgen test ./...

.PHONY: docs
docs: docs-content docs-syntax ## Build the documentation site into docs/public
$(HUGO) --source docs --minify
Expand Down
104 changes: 104 additions & 0 deletions cmd/dockerflagsgen/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// dockerflagsgen is a module of its own so that decolint does not depend on docker/cli. See
// main.go.
module github.com/bare-devcontainer/decolint/cmd/dockerflagsgen

go 1.26.5

require (
github.com/bare-devcontainer/decolint v0.0.0-00010101000000-000000000000
github.com/docker/cli v29.7.1+incompatible
github.com/spf13/cobra v1.10.2
github.com/spf13/pflag v1.0.10
)

require (
dario.cat/mergo v1.0.2 // indirect
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/clipperhouse/uax29/v2 v2.6.0 // indirect
github.com/containerd/errdefs v1.0.0 // indirect
github.com/containerd/errdefs/pkg v0.3.0 // indirect
github.com/containerd/log v0.1.0 // indirect
github.com/containerd/platforms v1.0.0-rc.4 // indirect
github.com/distribution/reference v0.6.0 // indirect
github.com/docker/cli-docs-tool v0.11.0 // indirect
github.com/docker/distribution v2.8.3+incompatible // indirect
github.com/docker/docker-credential-helpers v0.9.8 // indirect
github.com/docker/go-connections v0.7.0 // indirect
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c // indirect
github.com/docker/go-metrics v0.0.1 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fvbommel/sortorder v1.1.0 // indirect
github.com/go-jose/go-jose/v4 v4.1.4 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-viper/mapstructure/v2 v2.5.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/mux v1.8.1 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/klauspost/compress v1.18.6 // indirect
github.com/mattn/go-runewidth v0.0.19 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect
github.com/moby/go-archive v0.2.0 // indirect
github.com/moby/moby/api v1.55.0 // indirect
github.com/moby/moby/client v0.5.1 // indirect
github.com/moby/patternmatcher v0.6.1 // indirect
github.com/moby/swarmkit/v2 v2.1.2 // indirect
github.com/moby/sys/atomicwriter v0.1.0 // indirect
github.com/moby/sys/capability v0.4.0 // indirect
github.com/moby/sys/sequential v0.6.0 // indirect
github.com/moby/sys/signal v0.7.1 // indirect
github.com/moby/sys/symlink v0.3.0 // indirect
github.com/moby/sys/user v0.4.0 // indirect
github.com/moby/sys/userns v0.1.0 // indirect
github.com/moby/term v0.5.2 // indirect
github.com/morikuni/aec v1.1.0 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.1 // indirect
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
github.com/prometheus/client_golang v1.20.5 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.62.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/sirupsen/logrus v1.9.4 // indirect
github.com/tonistiigi/go-rosetta v0.0.0-20220804170347-3f4430f2d346 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
go.etcd.io/raft/v3 v3.6.0 // indirect
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.69.0 // indirect
go.opentelemetry.io/otel v1.44.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.44.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.44.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.44.0 // indirect
go.opentelemetry.io/otel/metric v1.44.0 // indirect
go.opentelemetry.io/otel/sdk v1.44.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.44.0 // indirect
go.opentelemetry.io/otel/trace v1.44.0 // indirect
go.opentelemetry.io/proto/otlp v1.10.0 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/net v0.56.0 // indirect
golang.org/x/sync v0.22.0 // indirect
golang.org/x/sys v0.47.0 // indirect
golang.org/x/term v0.45.0 // indirect
golang.org/x/text v0.38.0 // indirect
golang.org/x/time v0.15.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa // indirect
google.golang.org/grpc v1.82.1 // indirect
google.golang.org/protobuf v1.36.11 // indirect
tags.cncf.io/container-device-interface v1.1.0 // indirect
)

replace github.com/bare-devcontainer/decolint => ../..
Loading
Loading