diff --git a/.claude/docs-guidelines.md b/.claude/docs-guidelines.md index b271196c2..7867e60f0 100644 --- a/.claude/docs-guidelines.md +++ b/.claude/docs-guidelines.md @@ -30,6 +30,32 @@ Documentation follows the Diátaxis framework: - When referencing alternatives in other docs, maintain this order: "Homebrew, shell script, ..." (e.g., "See the Installation Guide for Homebrew, shell script, or other options") - Both `icp-cli` and `ic-wasm` are available as official Homebrew formulas: `brew install icp-cli` and `brew install ic-wasm` +## Docs-Only Fixes for Released Versions + +Versioned docs deployments (e.g. `/0.2/`) are controlled by `docs/vX.Y` tags. To fix or improve docs for an already-released version without cutting a new code release: + +**Rule: always merge the change to `main` first.** The `docs/vX.Y` tag is only for immediate deployment — when the next patch release is tagged, `sync-docs-tag.yml` resets `docs/vX.Y` to the new release commit. Any commit that exists only on the tag (not in `main`) will be silently lost at that point. + +**Workflow:** + +```bash +# 1. Merge the fix to main via a normal PR (always required) + +# 2. To immediately deploy the fix to /X.Y/ without waiting for a release: +git fetch --tags +git checkout -b temp/docs-fix-vX.Y docs/vX.Y # start from current tag state +git cherry-pick # pick the merged commit(s) + +git tag -f docs/vX.Y HEAD +git push origin refs/tags/docs/vX.Y --force # triggers re-deploy of /X.Y/ + +git branch -D temp/docs-fix-vX.Y # local branch no longer needed +``` + +The commits remain reachable via the tag — no remote branch is needed. + +**On the next release:** `sync-docs-tag.yml` resets `docs/vX.Y` to the release commit. Because the fix was already merged to `main`, the release will contain it, and the reset preserves it automatically. + ## Writing Guidelines - Use "canister environment variables" (not just "environment variables") when referring to runtime variables stored in canister settings — this distinguishes them from shell/build environment variables diff --git a/.claude/skills/release/rollback.md b/.claude/skills/release/rollback.md index ab73b0c7a..9aa7b37cc 100644 --- a/.claude/skills/release/rollback.md +++ b/.claude/skills/release/rollback.md @@ -9,8 +9,20 @@ If something fails mid-release, here's how to clean up depending on how far you git tag -d v$ARGUMENTS ``` If a GitHub Release was already created, delete it first via `gh release delete v$ARGUMENTS --yes`, then delete the tag. + Also revert the `docs/vX.Y` tag that `sync-docs-tag.yml` moved automatically. If a previous patch existed (e.g. rolling back v0.2.3 means docs/v0.2 should revert to v0.2.2), force-move it back: + ```bash + MINOR=$(echo "$ARGUMENTS" | sed 's/\.[0-9]*$//') + git tag -f docs/v${MINOR} v${MINOR}.$(( $(echo "$ARGUMENTS" | sed 's/.*\.//') - 1 )) + git push origin refs/tags/docs/v${MINOR} --force + ``` + If this was the first release of the minor (no previous patch), delete the docs tag instead: + ```bash + MINOR=$(echo "$ARGUMENTS" | sed 's/\.[0-9]*$//') + git push origin --delete refs/tags/docs/v${MINOR} + git tag -d docs/v${MINOR} + ``` - **Task 3 failed (Release workflow)**: Investigate the failure. The tag still exists. Once fixed, you can re-run the workflow from the GitHub Actions UI. Do **not** delete and re-push the tag — that creates duplicate runs. - **Task 4 failed (NPM publish)**: NPM publishes are not easily reversible. If the publish partially succeeded, check `npm info @icp-sdk/icp-cli versions` and coordinate with the team. The workflow can be re-triggered from the GitHub Actions UI. - **Task 5 failed (homebrew-tap)**: If the workflow failed, it can be re-triggered. If the PR was created but has issues, close it and delete the branch `update/icp-cli-beta-$ARGUMENTS` on `dfinity/homebrew-tap` via the GitHub UI. No packages were published. -- **Task 6 failed (docs versions)**: Close the PR and delete the branch. The versioned docs are deployed independently and are unaffected. +- **Task 6 failed (docs versions)**: Close the versions.json PR and delete the branch. The versioned docs at `/X.Y/` are deployed independently by the tag push and are unaffected. The `docs/vX.Y` tag was already moved by `sync-docs-tag.yml` as part of Task 2 — no additional cleanup needed for the tag unless you are also rolling back Task 2. - **Task 7 (homebrew-core check)**: This task is read-only — no cleanup needed. If it fails, just check manually. diff --git a/.claude/skills/release/task6-docs.md b/.claude/skills/release/task6-docs.md index 018d1ede7..e41bcad33 100644 --- a/.claude/skills/release/task6-docs.md +++ b/.claude/skills/release/task6-docs.md @@ -2,7 +2,12 @@ *Skip if `$ARGUMENTS` is a beta release. Requires Task 2. Runs concurrently with Task 3.* -The tag push triggers a docs deployment workflow that builds and publishes the versioned docs to `/X.Y/` on the `docs-deployment` branch (served at `https://cli.internetcomputer.org/X.Y/`). The `versions.json` PR must not be merged until that deployment succeeds, otherwise the root redirect will point to a path that does not exist yet. +The tag push triggers two automated workflows: + +1. **`docs.yml` (`publish-versioned-docs` job):** Builds and publishes the versioned docs to `/X.Y/` on the `docs-deployment` branch (served at `https://cli.internetcomputer.org/X.Y/`). The `versions.json` PR must not be merged until that deployment succeeds, otherwise the root redirect will point to a path that does not exist yet. + +2. **`sync-docs-tag.yml`:** Creates or moves the `docs/vX.Y` tag to the new release commit — no manual action required. This keeps the docs-override tag in sync with the latest patch. Because the tag is moved via `GITHUB_TOKEN`, the push does not re-trigger `docs.yml` (GitHub prevents recursive workflow runs from `GITHUB_TOKEN` pushes). + - The reset overwrites any docs-only commits that were on `docs/vX.Y` but not in the release. This is safe as long as the "merge to main first" rule was followed — those commits will be in the release itself. See `.claude/docs-guidelines.md` → "Docs-Only Fixes for Released Versions". Once the `versions.json` PR merges to `main`, the `publish-root-files` CI job runs automatically and copies `og-image.png`, `llms.txt`, `llms-full.txt`, and `feed.xml` from the new version's folder to the deployment root — no manual step needed. diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 149dee7fb..0c772cdaf 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -5,10 +5,10 @@ on: tags: - 'v*' - '!v*-*' # exclude pre-release tags (e.g. v0.2.0-beta.0) + - 'docs/v*' # docs-only overrides for specific minor versions (e.g. docs/v0.2) + - '!docs/v*-*' # exclude pre-release doc-override tags branches: - main - - 'docs/v*' - - '!docs/v*-*' # exclude pre-release doc branches paths: - 'docs/**' - 'docs-site/**' @@ -255,9 +255,9 @@ jobs: destination_dir: main keep_files: true - # Publish versioned docs - runs on tags (v*) or docs branches (docs/v*) + # Publish versioned docs - runs on release tags (v*) or docs-override tags (docs/v*) publish-versioned-docs: - if: github.event_name == 'push' && (startsWith(github.ref, 'refs/tags/v') || startsWith(github.ref, 'refs/heads/docs/v')) + if: github.event_name == 'push' && (startsWith(github.ref, 'refs/tags/v') || startsWith(github.ref, 'refs/tags/docs/v')) needs: build runs-on: ubuntu-latest steps: @@ -277,22 +277,21 @@ jobs: working-directory: ./docs-site run: npm ci - - name: Extract version from tag or branch + - name: Extract version from tag run: | if [[ "${GITHUB_REF}" == refs/tags/v* ]]; then - # Tag: v0.1.0 -> extract major.minor -> 0.1 + # Release tag: v0.1.0 -> extract major.minor -> 0.1 VERSION=${GITHUB_REF#refs/tags/v} - # Strip patch version (0.1.0 -> 0.1) VERSION=${VERSION%.*} echo "DOCS_VERSION=${VERSION}" >> $GITHUB_ENV echo "DOCS_BASE_PATH=/${VERSION}/" >> $GITHUB_ENV - elif [[ "${GITHUB_REF}" == refs/heads/docs/v* ]]; then - # Branch: docs/v0.1 -> extract version -> 0.1 - VERSION=${GITHUB_REF#refs/heads/docs/v} + elif [[ "${GITHUB_REF}" == refs/tags/docs/v* ]]; then + # Docs-override tag: docs/v0.1 -> extract version -> 0.1 + VERSION=${GITHUB_REF#refs/tags/docs/v} echo "DOCS_VERSION=${VERSION}" >> $GITHUB_ENV echo "DOCS_BASE_PATH=/${VERSION}/" >> $GITHUB_ENV else - echo "❌ Docs should only be published for version tags (v*) or docs branches (docs/v*)" + echo "❌ Docs should only be published for release tags (v*) or docs-override tags (docs/v*)" echo "Current ref: ${GITHUB_REF}" exit 1 fi diff --git a/.github/workflows/sync-docs-tag.yml b/.github/workflows/sync-docs-tag.yml new file mode 100644 index 000000000..87e2aaa4a --- /dev/null +++ b/.github/workflows/sync-docs-tag.yml @@ -0,0 +1,59 @@ +name: Sync docs tag after release + +# When a release tag is pushed, create or force-move the docs/vX.Y tag to that +# same commit. This keeps the docs-override tag in sync with the latest patch +# and prevents stale content from being served if someone later moves the tag +# for a docs-only fix on top of an outdated base. +# +# docs/vX.Y tags are the stable mechanism for docs-only updates to an older +# minor version without cutting a new code release. Moving them is intentional +# and requires explicit force-push — unlike branches, they cannot drift by +# accident. +# +# Note: pushes via GITHUB_TOKEN do not trigger other workflow runs, so moving +# the docs/vX.Y tag here will NOT re-trigger publish-versioned-docs in +# docs.yml. Only a human explicitly pushing the tag triggers a re-deploy. + +on: + push: + tags: + - 'v*' + - '!v*-*' # exclude pre-release tags (e.g. v0.2.0-beta.0) + +permissions: + contents: write + +jobs: + sync-docs-tag: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 0 + + - name: Configure git + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + - name: Extract version info + run: | + TAG=${GITHUB_REF_NAME} # e.g. v0.2.3 + PATCH=${TAG#v} # e.g. 0.2.3 + MINOR=${PATCH%.*} # e.g. 0.2 + echo "TAG=${TAG}" >> $GITHUB_ENV + echo "MINOR=${MINOR}" >> $GITHUB_ENV + echo "DOCS_TAG=docs/v${MINOR}" >> $GITHUB_ENV + + - name: Create or move docs tag to release commit + run: | + if git ls-remote --exit-code --tags origin "refs/tags/${DOCS_TAG}" > /dev/null 2>&1; then + git tag -f "${DOCS_TAG}" "${TAG}" + git push origin "refs/tags/${DOCS_TAG}" --force + echo "Moved ${DOCS_TAG} to ${TAG}" + else + git tag "${DOCS_TAG}" "${TAG}" + git push origin "refs/tags/${DOCS_TAG}" + echo "Created ${DOCS_TAG} at ${TAG}" + fi diff --git a/docs-site/README.md b/docs-site/README.md index 2bb855f8e..daf747ca1 100644 --- a/docs-site/README.md +++ b/docs-site/README.md @@ -119,8 +119,13 @@ The site is hosted on an IC asset canister and served at `https://cli.internetco ### Triggers - **Push to `main`**: Rebuilds `/main/` docs and root files (`index.html`, `versions.json`, `robots.txt`, `sitemap.xml`, IC config). Also copies `og-image.png`, `llms.txt`, `llms-full.txt`, and `feed.xml` from the latest versioned deployment to the root. -- **Tags (`v*`)**: Builds versioned docs (e.g., `v0.2.0` → `/0.2/`) -- **Branches (`docs/v*`)**: Updates versioned docs (e.g., `docs/v0.1` → `/0.1/`) +- **Release tags (`v*`)**: Builds and deploys versioned docs (e.g., `v0.2.0` → `/0.2/`). Also triggers `sync-docs-tag.yml` which automatically creates or moves the `docs/v0.2` tag to that same commit — no manual step needed. +- **Docs-override tags (`docs/v*`)**: Redeploys versioned docs for a specific minor version without cutting a new code release (e.g., `docs/v0.1` → `/0.1/`). To trigger a re-deploy, force-move the tag to the desired commit: + ```bash + git tag -f docs/v0.2 + git push origin refs/tags/docs/v0.2 --force + ``` + Note: `sync-docs-tag.yml` uses `GITHUB_TOKEN` to move the tag after each release, which does **not** re-trigger `docs.yml` (GitHub prevents recursive workflow runs from `GITHUB_TOKEN` pushes). Only a human explicitly pushing the tag triggers a re-deploy. ### Root-level files