--- name: publish-docs description: Publish the docs site to GHCR with a main- tag and bump site/helm/values.yaml so Argo CD picks it up — without cutting a SemVer release. Use when docs/site changes need to go live between releases. user-invocable: true --- # Publish Docs (no new release) Publishes a docs-only image to `ghcr.io/vfarcic/dot-agent-deck-docs` with a `main-` tag and updates `site/helm/values.yaml` so Argo CD picks it up. Does **not** create a release, version bump, binary build, Homebrew formula, Scoop manifest, or GitHub release. ## What a publish builds The site is generated by `cargo xtask site` (`xtask/site`) from `docs/published.toml`: the landing page from `site/landing/`, every manifest page as raw Markdown at `/docs/.md`, `llms.txt` and `llms-full.txt`, the images under `site/static/img/` (`docs/img` is a symlink to it), and the redirects from the old Docusaurus URLs. There is no Node and no npm anywhere in it. `.github/workflows/docs-publish.yml` runs three jobs: 1. **`site-build`** checks out `main` with a read-only token and no secret, records the commit, and runs `cargo xtask site site/build/public --nginx-redirects site/build/nginx-redirects.conf`. `site/build` is uploaded as the `docs-site` artifact, which both deploy paths consume, so they publish the same bytes. 2. **`publish`** checks out that same commit with `RELEASE_TOKEN` (it fails at its first step if the secret is empty), downloads the artifact, and builds `site/Dockerfile`, which has no build stage: it copies `site/build/public` into nginx's root, `site/build/nginx-redirects.conf` to `/etc/nginx/site-redirects.conf` as an nginx config include, outside the served files, and `site/nginx-default.conf` as the server config. It pushes the image, sets `image.tag` in `site/helm/values.yaml` (and `Chart.yaml`'s version and appVersion on the release path), commits that, and pushes the commit straight to `main`. 3. **`netlify-deploy`** deploys the same artifact's `build/public` to Netlify production with `--no-build`, after `publish` succeeds. ## When to Use - Docs / site changes have been merged to `main` and you want them live now. - You don't want to cut a SemVer release just for documentation. ## When NOT to Use - You're cutting a versioned release — use `/tag-release` instead. The release workflow already publishes docs as part of the release via the same underlying `docs-publish.yml` workflow. - You have un-released non-docs (code) changes that should also ship — cut a release. ## Workflow ### Step 1: Sync main locally The workflow dispatches against `origin/main`, so make sure you know what's there: ```bash git fetch origin git checkout main git pull --rebase origin main ``` If the user is in a worktree, fetch is enough — they don't need to switch branches; `gh workflow run --ref main` dispatches against the remote ref regardless of local checkout. ### Step 2: Confirm there are docs/site changes since the last release ```bash LAST_TAG=$(git tag --list 'v*' --sort=-v:refname | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | head -1) echo "Last release: ${LAST_TAG}" git log --oneline "${LAST_TAG}..origin/main" -- docs/ site/ xtask/site/ src/published_docs.rs git diff --stat "${LAST_TAG}..origin/main" -- docs/ site/ xtask/site/ src/published_docs.rs ``` `xtask/site/` and `src/published_docs.rs` are the generator and the manifest parser, so a change there can change the published output even when no page did. A change under `docs/develop/` changes nothing on the site; disregard it. If there are no docs/site changes since the last release, inform the user and stop — there is nothing meaningful to publish. ### Step 2b: Build the site locally (optional) The same command the workflow runs, so a broken link or a manifest error shows up here rather than in the run. It needs only the Rust toolchain, and refuses an output directory that is not empty. Run it in a checkout at `origin/main` — the user's own, or a detached worktree at a disk-backed sibling path (CLAUDE.md rule 14, since it compiles the `xtask` crates), which you ask about before creating: ```bash rm -rf site/build # generated output, gitignored cargo xtask site site/build/public --nginx-redirects site/build/nginx-redirects.conf ``` Skip it when the user has just done this themselves on that commit. ### Step 3: Show the user what will change Present: - **Current chart tag**: read from `site/helm/values.yaml` `image.tag` (e.g. `v0.26.0`). - **New tag**: `main-` where short-sha is `git rev-parse --short=7 origin/main`. - **Commits included**: the list from Step 2. Ask the user to confirm before triggering the workflow. ### Step 4: Trigger the workflow ```bash gh workflow run docs-publish.yml --ref main ``` ### Step 5: Watch the run ```bash sleep 5 RUN_ID=$(gh run list --workflow=docs-publish.yml --branch=main --limit 1 --json databaseId --jq '.[0].databaseId') gh run watch "$RUN_ID" ``` ### Step 6: Report result On success, tell the user: - The image tag that was pushed (`main-`). - That a `chore: publish docs image main- [skip ci]` commit was pushed to `main` — they should `git pull` to pick it up. - Argo CD will detect the `values.yaml` change and sync within a minute or two; the site at https://agent-deck.devopstoolkit.ai will update shortly after. - The chart now points at a `main-` tag. The next `/tag-release` will re-pin it to `v` automatically. - The same run also deploys that commit's build to Netlify production (site `agent-deck-devopstoolkit-ai`, URL in the run summary). This runs alongside the cluster during the migration to Netlify; until DNS is switched, the Netlify copy is not what `agent-deck.devopstoolkit.ai` serves. A failed Netlify step does not undo the image or the chart bump, which run first. ## Notes - **Same sha → no-op**: re-running on a SHA that's already published is harmless — the workflow pushes the same image bytes and the `values.yaml` diff is empty, so no commit happens. - **`:latest` is untouched**: manual runs never push or move the `:latest` tag. That tag follows formal releases only. - **Not for release flows**: do not run this inside `/pr-create`, `/prd-full`, or any release skill — it would interfere with the release path's own docs publish step. - **No changelog fragment**: a docs-only publish is not a release, so no entry in `changelog.d/` is needed. - **The binary's docs do not move**: `dot-agent-deck docs` prints the pages embedded when that binary was built, so a docs publish updates the website and its `llms` files only. Installed binaries pick up doc changes with the next release.