@@ -79,6 +79,13 @@ cargo-binstall = "latest"
7979cmake = " latest"
8080"conda:openssl" = " 3"
8181conftest = " latest"
82+ # Compose CLI for the generated scenario stacks under verification/.
83+ # The aqua backend rather than the registry default (`github:docker/compose`): the github backend records SLSA
84+ # provenance in the lockfile, and a mise older than the one that wrote it refuses the entry outright with
85+ # "Lockfile requires slsa provenance for github:docker/compose@<ver> but verification was not performed".
86+ # aqua records `provenance = "github-attestations"` instead, which is what every other pinned tool here uses.
87+ # This is the compose implementation only -- a docker daemon still has to come from the host.
88+ "aqua:docker/compose" = " latest"
8289"github:nextest-rs/nextest" = { version = " latest" , version_prefix = " cargo-nextest-" }
8390taplo = " latest"
8491watchexec = " latest"
@@ -607,6 +614,11 @@ task_shell_trace = 'bash -xeuo pipefail "{{ config_root }}/.mise/task-shell.sh"'
607614task_shell_plain = ' bash "{{ config_root }}/.mise/task-shell.sh"'
608615# OpenObserve docker image for the openobserve/o2 dev-observability task.
609616o2_image = " openobserve/openobserve:v0.91.5"
617+ # Port publish spec for that task, bound to loopback rather than every interface.
618+ # config/o2.env's credential is committed, so the collector must not be reachable off the machine. It lives here
619+ # as a var because the mise policy rejects an `X.Y.Z` literal inside a task `run` body, and an IPv4 address trips
620+ # that check the same way a version would.
621+ o2_publish = " 127.0.0.1:5080:5080"
610622# OpenTelemetry Collector image for the o2-hostmetrics / o2-macmon / o2-nvidia / o2-winmetrics tasks.
611623otelcol_image = " otel/opentelemetry-collector-contrib:0.156.0"
612624rp_wasm_dir = ''' {% if os() == "windows" and env?.MISE_INSTALLS_DIR -%}
@@ -727,6 +739,7 @@ depends = [
727739 " jscpd-check" ,
728740 " link-check" ,
729741 " ls-lint-check" ,
742+ " mise-lock-check" ,
730743 " readme-mise-version-check" ,
731744 " regal-check" ,
732745 " ryl-check" ,
@@ -808,7 +821,15 @@ run = "semgrep scan --config config/semgrep --autofix --metrics=off ."
808821
809822[tasks .hadolint-check ]
810823description = " Lint the Dockerfiles with hadolint"
811- run = " git ls-files '*Dockerfile' '*Dockerfile.*' | xargs hadolint --config config/hadolint.yaml"
824+ # `*Dockerfile.*` also catches a BuildKit per-Dockerfile ignore file, which is not a Dockerfile.
825+ # BuildKit looks for `<path-to-dockerfile>.dockerignore` beside the Dockerfile it is told to build, so the name
826+ # is fixed and cannot be moved out of the glob's way; hadolint parses it as Dockerfile syntax and stops on the
827+ # first ignore pattern with `unexpected '*' expecting ... a pragma, at least one space, or end of input`.
828+ run = """
829+ git ls-files '*Dockerfile' '*Dockerfile.*' | rg -v '\\ .dockerignore$' |
830+ xargs hadolint --config config/hadolint.yaml
831+ """
832+ shell = " {{ vars.task_shell }}"
812833
813834# Duplication is gated as a fingerprint ratchet, not as a percentage.
814835# config/jscpd-baseline.json records the fingerprint of every clone that already existed, and only a clone absent
@@ -976,8 +997,12 @@ description = "Cross-check Dockerfiles against mise [tools] pins + heredoc body-
976997# Each git-tracked Dockerfile* gets both passes, except that the `ignore` pass only visits Dockerfiles that
977998# actually contain heredocs: conftest 0.68.2's ignore parser panics with `runtime error: slice bounds out of
978999# range` on plain Dockerfiles (no heredocs), and the body-first-line rule has nothing to check there anyway.
1000+ # A BuildKit per-Dockerfile ignore file is dropped from the list first. `<path-to-dockerfile>.dockerignore` is
1001+ # the name BuildKit demands beside the Dockerfile it builds, so it cannot be moved out of the glob's way, and
1002+ # the dockerfile parser reads its first pattern as an instruction:
1003+ # unknown instruction: **/.claude/
9791004run = """
980- files=$(git ls-files '*Dockerfile' '*Dockerfile.*')
1005+ files=$(git ls-files '*Dockerfile' '*Dockerfile.*' | rg -v ' \\ .dockerignore$' )
9811006# $files is a newline-separated path list; word-splitting is intentional (no spaces in paths).
9821007# shellcheck disable=SC2086
9831008conftest test --combine --namespace dockerfile -p config/conftest/policy $files .mise
@@ -1289,6 +1314,11 @@ description = "Regenerate .dockerignore from .gitignore + Docker-only excludes"
12891314# A bare pattern there is root-anchored -- whereas a slash-less .gitignore pattern matches at any depth.
12901315# So mirror .gitignore, add the Docker-only excludes (`.git/`, plus the build recipe + docs so editing
12911316# them doesn't bust the `COPY . .` cache), and `**/`-prefix each non-anchored pattern.
1317+ # `.dockerignore` itself is deliberately NOT excluded any more. Excluding it saved nothing (the file is tiny) and
1318+ # meant it was absent from the build context, so `et-cli` -- which derives each scenario's per-Dockerfile ignore
1319+ # file from it -- could not run inside an image built from this Dockerfile:
1320+ # called `Result::unwrap()` on an `Err` value: Io(... NotFound ... path: "/workspace/.dockerignore")
1321+ # That surfaced only in the docker build.yaml lanes, because every other environment has the file on disk.
12921322# goawk emits CRLF on Windows, so the whole block is piped through `tr -d '\r'` -- .dockerignore must stay
12931323# LF-only (editorconfig enforces it, and CRLF would also make dockerignore-check see a spurious diff).
12941324run = '''
@@ -1299,7 +1329,7 @@ run = '''
12991329 echo
13001330 {
13011331 cat .gitignore
1302- printf '%s\n' '.git/' 'Dockerfile*' '/README.md' '.dockerignore'
1332+ printf '%s\n' '.git/' 'Dockerfile*' '/README.md'
13031333 } | goawk '
13041334 /^[[:space:]]*#/ { print; next }
13051335 /^[[:space:]]*$/ { print; next }
@@ -1514,7 +1544,8 @@ description = "Run the WebSocket server, restarting on .rs/.toml changes"
15141544run = " watchexec --restart --clear --exts rs,toml -i '**/tests/**' --shell=none -- cargo run -p et-ws-server"
15151545
15161546[tasks .ws-server .env ]
1517- OTLP_AUTH_PASSWORD = " 1234"
1547+ # Must match ZO_ROOT_USER_PASSWORD in config/o2.env; the server authenticates its OTLP exports as that root user.
1548+ OTLP_AUTH_PASSWORD = " Complexpass#123" # skipcq: SCT-A000 -- local dev-only credential, committed deliberately
15181549OTLP_AUTH_USERNAME = " root@example.com"
15191550
15201551[tasks .ws-server-no-otel ]
@@ -1753,7 +1784,7 @@ description = "Run both the ws-server and ws-wasm-agent using Chrome"
17531784
17541785[tasks .openobserve ]
17551786alias = " o2"
1756- run = " docker run --rm --name openobserve -p 5080:5080 --env-file config/o2.env {{ vars.o2_image }}"
1787+ run = " docker run --rm --name openobserve -p {{ vars.o2_publish }} --env-file config/o2.env {{ vars.o2_image }}"
17571788
17581789[tasks .demo ]
17591790depends = [" openobserve" , " ws-server" ]
@@ -2059,6 +2090,32 @@ description = "Install every language toolchain (all .mise/config.<lang>.toml)"
20592090run = ' MISE_ENV="$ALL_LANGS" mise install'
20602091shell = " {{ vars.task_shell_plain }}"
20612092
2093+ # Fail when a full install resolves a different tool set or version than the committed lockfiles record.
2094+ # Only the platform-independent fields are compared, because mise fills a lockfile's per-platform rows in lazily on
2095+ # whichever platform runs the install: `checksum`, `url`, `url_api`, `provenance` and `provenance_verified` appear
2096+ # for macos-arm64 on a developer's machine and for linux-x64 on a runner. A whole-file `git diff` therefore can
2097+ # never be clean across the five-platform matrix -- every lane legitimately adds its own rows, and the committed
2098+ # lockfile already carries a checksum for some platforms of a tool and not others. The `[[tools.<name>]]` headers
2099+ # and their `version` / `backend` are identical everywhere, so those are what this gates on: a tool added, removed
2100+ # or bumped without regenerating the lockfiles is caught, while a lane recording its own checksums is not.
2101+ # install-all rather than a bare install, so guest-language tools reach their lockfiles too -- MISE_ENV only
2102+ # loads a subset, and a partial install leaves every unloaded config's lockfile unexamined.
2103+ [tasks .mise-lock-check ]
2104+ depends = [" install-all" ]
2105+ description = " Fail if .mise/mise*.lock records a different tool set or version than a full install resolves"
2106+ # `|| true` because rg exits 1 on no matches, which under pipefail would abort the whole task on the clean path.
2107+ # `^[-+][-+]` drops git's `+++`/`---` file headers; `@@` hunk headers never match the leading `[-+]`.
2108+ run = """
2109+ drift=$(git diff -U0 -- .mise/mise*.lock | rg "^[-+]" | rg -v "^[-+][-+]" |
2110+ rg "^[-+](version|backend) = |^[-+]\\ [\\ [tools\\ ." || true)
2111+ if [ -n "$drift" ]; then
2112+ echo "$drift"
2113+ echo "Lockfile tool set/versions differ from install-all; commit the regenerated .mise/mise*.lock" >&2
2114+ exit 1
2115+ fi
2116+ """
2117+ shell = " {{ vars.task_shell }}"
2118+
20622119[tasks .all ]
20632120# Generic stand-in: run any command with every language env enabled.
20642121# mise appends the task's CLI args after `mise exec --`, e.g. `mise run all zig version`.
0 commit comments