Move telemetry and tracing to Configuration and troubleshooting to Guides while preserving legacy URLs on both documentation sites. Separate Guides and Community navigation, apply the final resource curation, and fix portable file/shell links for downstream mounts.
gh api refuses to print a response containing terminal escape sequences
unless --allow-escape-sequences is passed, and go test output on the
runner is coloured, so report-main-failures failed again on its second
real trigger (run 34868755147). normalize_log already strips the escapes.
The test mock now rejects a log fetch without the flag.
`gh run view --job <id> --log-failed` refuses to serve logs while the run
is in progress, and report-main-failures runs inside the run it reports
on, so it failed on its first real trigger and no issue was filed.
Fetch GET /repos/{repo}/actions/jobs/{id}/logs instead, available as soon
as the job completes. normalize_log accepts both log formats; the job-level
excerpt shows the tail of the (now full) job log rather than the checkout
step. The test mock serves the API endpoint with fixtures in its format.
Relocate Windows Go caches outside the workspace and exclude hot build
paths from Defender scanning. Version every Go cache namespace
consistently, preserve live test failure output, and publish raw JSON
timing data plus step summaries for Linux and Windows CI.
- `changes` (dorny/paths-filter) marks a PR docs-only when every changed
file is under docs/; Markdown elsewhere is test data so it still counts as
code. Go jobs and image need it; lint always runs; gate treats skipped as
passed. Pushes and merge-queue runs always run everything.
- `merge_group` trigger, with its own concurrency group like main and tags.
- `timeout-minutes` on every job (default was 6 h).
- `test-race` runs `task test-race` (-race -shuffle=on) on main pushes only;
not part of gate so it never sits on the PR critical path.
- test-windows excludes the workspace, Go caches and TEMP from Defender
real-time scanning; best effort (continue-on-error).
- zizmor audits workflows and composite actions in lint (min severity low);
.github/zizmor.yml keeps the deliberate workflow_run trigger of pr-review.
Its findings fixed here: persist-credentials: false on every checkout.
- Local actions referenced as `$/.github/actions/...` (resolved from the
running commit, no checkout needed) — also fixes publish, which called a
./ action without checking out. actionlint 1.7.12 does not know the syntax
yet, so that one message is ignored; workflow-lint skips `$/` refs.
- go-licenses pinned to v2.0.1; gate writes a job/result table to the step
summary; publish no longer creates an unused builder.
docs-lint, docs-a11y and docs-upstream shared the same triggers and path
filters; they become parallel jobs of docs.yml (markdownlint, canonical,
links, llms-txt, a11y, upstream). Hugo is installed by a setup-hugo composite
action so the version is pinned once for docs and docs-deploy. Path filters
also cover the composite action and every scripts/docs-*.sh. deploy-pages
bumped to v5.0.1.
- `gate` is the single job the main ruleset needs to require: it needs
every blocking job (now also test-windows, cross-compile and image) and
passes on success or skip, so adding a check no longer touches the ruleset;
`publish` needs `gate` instead of listing every job.
- `build-image` and `build-and-push-image` duplicated ~120 lines and differed
only by exporter/attestations/build args; one `image` job now decides
publish vs cache-only in a step and feeds both build-push steps.
- OIDC validation, Hub login and builder setup move to `setup-buildx`; Task
install moves into `setup-go`, which also saves the cache itself on main
(actions/cache post step) so jobs lose their Save Go caches step.
- `lint` gains the parts of `task lint` CI never ran (project cops, go mod
tidy) plus shellcheck on every script and the models-delta golden test,
replacing the models-delta-lint workflow.
- Jobs renamed to short nouns: test, test-windows, cross-compile, licenses,
image, publish. Actions bumped to the latest allow-listed versions
(checkout v7.0.1, setup-go v7.0.0, setup-task v2.2.0, docker/* majors on
Node 24, golangci-lint v2.13.2, Task 3.53.1).
- The run-commit sanity checks that only re-verified actions/checkout are
dropped; the expected_sha input and OIDC config checks remain.
Two independent improvements to the weekly models.dev snapshot refresh
(.github/workflows/update-models.yml), which previously opened PRs
with an unsigned commit attributed to whoever happened to trigger the
run, and a static, uninformative PR body.
Signed commits
---------------
Add `sign-commits: true` to the existing `peter-evans/create-pull-request`
step. This builds the commit through GitHub's API instead of a local
`git push`, so it is Verified regardless of which token creates it.
On the default `GITHUB_TOKEN` (used explicitly via `token: github.token`
for clarity) the commit author/committer becomes `github-actions[bot]`
consistently, rather than the human who happened to run the workflow.
`persist-credentials: false` is set on checkout as a minor hardening
measure, since `create-pull-request` is given its token explicitly and
never needs checkout's persisted git credential.
Semantic catalog diff, no AI
-----------------------------
`pkg/modelsdev/snapshot.json` is a single-line, multi-megabyte JSON
file, so its raw git diff is one useless line — this is why the PR
body was static. Instead:
- `.github/scripts/models-delta.jq` computes a structural
added/removed/changed diff between two snapshot.json files.
- `.github/scripts/models-delta-render.jq` renders that diff to
markdown tables, with a `safe()`/`safe_diff()` sanitiser clamping
every third-party (models.dev) string to a short, bounded,
control-character-free charset before it reaches the PR body — that
body is consumed by this repo's automated PR-review pipeline, so
untrusted text needs bounding before it gets there.
- `scripts/models-delta.sh` is the single wrapper around both jq
scripts; a new `task models-delta` target and the workflow's
"Compute catalog delta" step both call it, so the diff logic exists
in exactly one place. With no arguments it diffs HEAD's
pkg/modelsdev/snapshot.json against the working tree — the same
comparison the workflow makes after `task update-models`, so the
local and CI code paths never drift apart.
- The workflow uses the rendered markdown as the PR body/title/commit
subject. The delta step is `continue-on-error: true` with a
static-body fallback step, so a jq/shell bug degrades the PR text
instead of silently skipping the weekly refresh. A refresh still
opens a PR even when only snapshot_date.txt changed (no catalog
content diff), preserving today's behaviour and avoiding any
interaction with the check-models-fresh staleness test.
- `scripts/models-delta-test.sh` is a golden-fixture regression test
for the two jq scripts (fixtures under .github/scripts/testdata/),
since they have no Go-test equivalent and only run for real,
unattended, once a week. `.github/workflows/models-delta-lint.yml`
runs it (plus shellcheck) as its own path-filtered workflow — mirrors
the repo's existing docs-lint.yml pattern — rather than unconditionally
on every PR via ci.yml's blanket lint job.
- A concurrency group and a canonical-repo job guard prevent an
overlapping workflow_dispatch from racing the weekly schedule run.
Phase 3 of the original proposal (an LLM-written PR summary via
docker/docker-agent-action) is deferred to a future change.
Add a spec-compliant (https://llmstxt.org/) /llms.txt to the edge docs
site, generated at Hugo build time from docs/data/nav.yml so it stays
in sync as pages are added — deployed by the existing docs-deploy
workflow with no new CI or cron.
- hugo.yaml: register a plain-text 'llms' output format attached to the
home page (outputs.home) so it is actually rendered.
- layouts/home.llms.txt: template emitting the H1, blockquote summary,
and one H2 per nav section with '- [title](url): note' links
(absolute URLs via absURL; note from each page's front-matter
description). Flattens both items and groups[].items, and hard-fails
the build when a nav page is unresolved or its description is empty.
- layouts/_partials/footer.html: add an llms.txt discoverability link.
- scripts/docs-check-llms-txt.sh: CI-invoked semantic validator that
rebuilds the site and asserts llms.txt matches nav.yml's sections,
titles, order, count and per-entry URL — catching template
regressions a green build would miss.
- docs-lint.yml: wire in the new llms-txt-check job (pinned Hugo).
- STYLE.md: document generation, the description requirement, and CI.
Completes phase 4.1 of #3371. The github.io preview site now renders
with the same engine as docs.docker.com, so the mounted Markdown is
single-sourced byte-for-byte across both surfaces.
- add docs/hugo.yaml with explicit section mounts and ERROR-level
relative link validation, matching the docker/docs build
- port the Jekyll layout, includes and nav data to Hugo templates
- resolve relative .md links with a render hook equivalent to the
docker/docs one; render GFM alerts at build time (replaces the
client-side transform in app.js)
- rewrite the github.io-only homepage and 404 without Liquid
- deploy via GitHub Actions (docs-deploy.yml) instead of the legacy
branch build; local preview and link check now use Hugo
New pages mounted on docs.docker.com must carry the canonical: value
derived from their path, which is easy to forget when scaffolding a
page from an existing one. docs-check-canonical.sh verifies the exact
expected value inside the front matter of every mounted content page,
a canonical-check job in docs-lint runs it on every docs change, and
STYLE.md documents the convention next to the linking rules.
The pr-review-trigger workflow intentionally runs all events to
completion (it's cheap, just saves context). Deduplication happens
downstream in pr-review.yml where the expensive AI review runs.
This updates the linter to skip the concurrency check for
pr-review-trigger.yml, resolving the failing lint check.
See PR #2789 for the rationale behind moving concurrency control
from the trigger to the review workflow.
actionlint is already in CI but its validation stops at workflow
syntax / shellcheck — it does not validate event-payload schemas, so
'github.event.issue.type' on an 'issues: labeled' trigger sails through
even though that field doesn't exist in the labeled payload (the bug
that shipped on PR #2645).
scripts/workflow-lint.sh encodes three project-specific invariants
documented in AGENTS.md § GitHub Actions:
1. PR-triggered workflows declare a 'concurrency:' block.
2. third-party 'uses:' references are pinned by 40-char SHA, not
by tag or branch.
3. workflows do not reference fields on the post-mortem deny-list
(currently 'github.event.issue.type', from PR #2645).
The script runs as a step in the existing 'lint' job, after
actionlint so failures don't shadow each other. It is also runnable
locally:
./scripts/workflow-lint.sh
The first run found one existing finding — pr-review-trigger.yml
had no concurrency block. Added one keyed on the PR number so
overlapping runs get cancelled per-PR.