From 9afb611793219a249a1a3eb4b75c843d5b883a8c Mon Sep 17 00:00:00 2001 From: infinityplusone Date: Fri, 15 May 2026 15:06:57 -0400 Subject: [PATCH 1/4] ci: add docs-dispatch workflow Pings makegov/docs (the editorial composer) via repository_dispatch when docs/, README.md, CHANGELOG.md, or crates/*/src/** changes on main. docs.makegov.com rebuilds the Rust SDK pages from this repo's docs/. Requires the DOCS_DISPATCH_TOKEN repo secret with contents:write on makegov/docs. Co-Authored-By: Claude Opus 4.7 --- .github/workflows/docs-dispatch.yml | 59 +++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 .github/workflows/docs-dispatch.yml diff --git a/.github/workflows/docs-dispatch.yml b/.github/workflows/docs-dispatch.yml new file mode 100644 index 0000000..202fe5f --- /dev/null +++ b/.github/workflows/docs-dispatch.yml @@ -0,0 +1,59 @@ +name: Docs dispatch + +# Fires on push to main when content that affects the published docs site +# changes (docs/, README, CHANGELOG, or the public source of any workspace +# crate). Notifies makegov/docs via repository_dispatch so the docs site +# rebuilds without waiting for someone to push to the composer. +# +# tango-rust is a `coloc-source` repo: its docs/ folder is the authoritative +# source for the Rust SDK pages on docs.makegov.com. +# +# Required secrets: +# DOCS_DISPATCH_TOKEN — GitHub token with contents:write on makegov/docs. +# +# Required variables (optional): +# DOCS_TARGET_REPO — override the dispatch target (default: makegov/docs). + +on: + push: + branches: + - main + paths: + - "docs/**" + - "README.md" + - "CHANGELOG.md" + - "crates/*/src/**" + workflow_dispatch: + +jobs: + dispatch: + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 2 + + - name: Detect changed paths + id: changes + run: | + set -euo pipefail + base=$(git rev-parse HEAD~1 2>/dev/null || git rev-parse HEAD) + external=$(git diff --name-only "$base" HEAD -- 'docs' 'README.md' 'CHANGELOG.md' 'crates' | paste -sd, -) + { + echo "external=$external" + echo "has_external=$([ -n "$external" ] && echo true || echo false)" + } >> "$GITHUB_OUTPUT" + + - name: Dispatch to docs composer (makegov/docs) + if: steps.changes.outputs.has_external == 'true' + env: + GH_TOKEN: ${{ secrets.DOCS_DISPATCH_TOKEN }} + TARGET: ${{ vars.DOCS_TARGET_REPO || 'makegov/docs' }} + run: | + gh api "repos/$TARGET/dispatches" \ + -f event_type=external_updated \ + -f "client_payload[source_repo]=${{ github.repository }}" \ + -f "client_payload[source_ref]=${{ github.sha }}" \ + -f "client_payload[changed_paths]=${{ steps.changes.outputs.external }}" From 8a01201e267de8d0fe4879649837f3d821a73c6f Mon Sep 17 00:00:00 2001 From: "V. David Zvenyach" Date: Wed, 23 Sep 2026 10:02:27 -0500 Subject: [PATCH 2/4] ci(docs-dispatch): match the sibling SDKs' trigger paths and checkout version The docs composer consumes docs/, README.md and CHANGELOG.md, so a source-only change no longer pings it. Bumps actions/checkout to v6 to match tango-python. Co-Authored-By: Claude Opus 5.5 (1M context) --- .github/workflows/docs-dispatch.yml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/.github/workflows/docs-dispatch.yml b/.github/workflows/docs-dispatch.yml index 202fe5f..5708e5c 100644 --- a/.github/workflows/docs-dispatch.yml +++ b/.github/workflows/docs-dispatch.yml @@ -1,9 +1,9 @@ name: Docs dispatch # Fires on push to main when content that affects the published docs site -# changes (docs/, README, CHANGELOG, or the public source of any workspace -# crate). Notifies makegov/docs via repository_dispatch so the docs site -# rebuilds without waiting for someone to push to the composer. +# changes (docs/, README, or CHANGELOG). Notifies makegov/docs via +# repository_dispatch so the docs site rebuilds without waiting for someone +# to push to the composer. # # tango-rust is a `coloc-source` repo: its docs/ folder is the authoritative # source for the Rust SDK pages on docs.makegov.com. @@ -22,7 +22,6 @@ on: - "docs/**" - "README.md" - "CHANGELOG.md" - - "crates/*/src/**" workflow_dispatch: jobs: @@ -31,7 +30,7 @@ jobs: permissions: contents: read steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: fetch-depth: 2 @@ -40,7 +39,7 @@ jobs: run: | set -euo pipefail base=$(git rev-parse HEAD~1 2>/dev/null || git rev-parse HEAD) - external=$(git diff --name-only "$base" HEAD -- 'docs' 'README.md' 'CHANGELOG.md' 'crates' | paste -sd, -) + external=$(git diff --name-only "$base" HEAD -- 'docs' 'README.md' 'CHANGELOG.md' | paste -sd, -) { echo "external=$external" echo "has_external=$([ -n "$external" ] && echo true || echo false)" From 75e02c74494644e4bfe8979d56ce2c4b06869f83 Mon Sep 17 00:00:00 2001 From: "V. David Zvenyach" Date: Mon, 19 Oct 2026 14:31:18 -0500 Subject: [PATCH 3/4] ci(docs-dispatch): pass values through env and diff the whole push Step outputs and event fields now reach the scripts as environment variables instead of being expanded into the script text, so a file name can no longer inject shell. Change detection diffs `before..sha` instead of only the last commit, with full history checked out, so a multi-commit push no longer misses an earlier docs change. A manual run, a new branch or a force-push whose `before` is gone dispatches unconditionally. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/docs-dispatch.yml | 33 +++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/.github/workflows/docs-dispatch.yml b/.github/workflows/docs-dispatch.yml index 5708e5c..f55fd88 100644 --- a/.github/workflows/docs-dispatch.yml +++ b/.github/workflows/docs-dispatch.yml @@ -32,17 +32,33 @@ jobs: steps: - uses: actions/checkout@v6 with: - fetch-depth: 2 + fetch-depth: 0 + # Diffs the whole push (`before..sha`), not just its last commit. + # A manual run, a new branch (all-zero `before`) or a force-push (`before` no longer in the history) dispatches unconditionally, reporting the last commit's paths. - name: Detect changed paths id: changes + env: + EVENT_NAME: ${{ github.event_name }} + BEFORE: ${{ github.event.before }} run: | set -euo pipefail - base=$(git rev-parse HEAD~1 2>/dev/null || git rev-parse HEAD) - external=$(git diff --name-only "$base" HEAD -- 'docs' 'README.md' 'CHANGELOG.md' | paste -sd, -) + paths=(docs README.md CHANGELOG.md) + force=false + if [ "$EVENT_NAME" = "push" ] && [ -n "$BEFORE" ] && ! [[ "$BEFORE" =~ ^0+$ ]] && git cat-file -e "${BEFORE}^{commit}" 2>/dev/null; then + base="$BEFORE" + else + force=true + base=$(git rev-parse --verify -q HEAD~1 || git hash-object -t tree /dev/null) + fi + external=$(git diff --name-only "$base" HEAD -- "${paths[@]}" | paste -sd, -) + has_external=false + if [ "$force" = true ] || [ -n "$external" ]; then + has_external=true + fi { echo "external=$external" - echo "has_external=$([ -n "$external" ] && echo true || echo false)" + echo "has_external=$has_external" } >> "$GITHUB_OUTPUT" - name: Dispatch to docs composer (makegov/docs) @@ -50,9 +66,12 @@ jobs: env: GH_TOKEN: ${{ secrets.DOCS_DISPATCH_TOKEN }} TARGET: ${{ vars.DOCS_TARGET_REPO || 'makegov/docs' }} + SOURCE_REPO: ${{ github.repository }} + SOURCE_REF: ${{ github.sha }} + CHANGED_PATHS: ${{ steps.changes.outputs.external }} run: | gh api "repos/$TARGET/dispatches" \ -f event_type=external_updated \ - -f "client_payload[source_repo]=${{ github.repository }}" \ - -f "client_payload[source_ref]=${{ github.sha }}" \ - -f "client_payload[changed_paths]=${{ steps.changes.outputs.external }}" + -f "client_payload[source_repo]=$SOURCE_REPO" \ + -f "client_payload[source_ref]=$SOURCE_REF" \ + -f "client_payload[changed_paths]=$CHANGED_PATHS" From 433cac7feadc0f0e66bec8cd6bac65f3ef388f34 Mon Sep 17 00:00:00 2001 From: "V. David Zvenyach" Date: Mon, 19 Oct 2026 14:42:28 -0500 Subject: [PATCH 4/4] ci(docs-dispatch): always dispatch when the push range is unknown Without a usable `before` (an all-zero one, one missing from the clone after a force-push, or a manual run), the earlier docs changes in the push can't be seen, so diffing only the tip commit could skip a needed rebuild. That branch now sets `force=true` and always dispatches, reporting the tip commit's paths. The workflow now matches the other SDK repos' copy apart from its header and checkout version. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/docs-dispatch.yml | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/.github/workflows/docs-dispatch.yml b/.github/workflows/docs-dispatch.yml index f55fd88..05cabae 100644 --- a/.github/workflows/docs-dispatch.yml +++ b/.github/workflows/docs-dispatch.yml @@ -34,8 +34,7 @@ jobs: with: fetch-depth: 0 - # Diffs the whole push (`before..sha`), not just its last commit. - # A manual run, a new branch (all-zero `before`) or a force-push (`before` no longer in the history) dispatches unconditionally, reporting the last commit's paths. + # Every `${{ }}` value reaches the scripts through `env:`, never inline, so a file name or event field cannot inject shell. - name: Detect changed paths id: changes env: @@ -43,18 +42,25 @@ jobs: BEFORE: ${{ github.event.before }} run: | set -euo pipefail - paths=(docs README.md CHANGELOG.md) + # The empty-tree object: diffing against it lists every tracked file. + empty_tree=4b825dc642cb6eb9a060e54bf8d69288fbee4904 + # Without a usable `before` (a manual run, a new branch, or a force-push whose old tip is gone) the push range is unknown, so dispatch regardless and report the tip commit's paths. force=false - if [ "$EVENT_NAME" = "push" ] && [ -n "$BEFORE" ] && ! [[ "$BEFORE" =~ ^0+$ ]] && git cat-file -e "${BEFORE}^{commit}" 2>/dev/null; then + if [ "$EVENT_NAME" = "push" ] && [ -n "$BEFORE" ] && ! echo "$BEFORE" | grep -qx '0*' && git cat-file -e "$BEFORE^{commit}" 2>/dev/null; then base="$BEFORE" else force=true - base=$(git rev-parse --verify -q HEAD~1 || git hash-object -t tree /dev/null) + if git rev-parse --verify -q HEAD~1 >/dev/null; then + base=HEAD~1 + else + base="$empty_tree" + fi fi - external=$(git diff --name-only "$base" HEAD -- "${paths[@]}" | paste -sd, -) - has_external=false - if [ "$force" = true ] || [ -n "$external" ]; then + external=$(git diff --name-only "$base" HEAD -- 'docs' 'README.md' 'CHANGELOG.md' | paste -sd, -) + if [ -n "$external" ] || [ "$force" = true ]; then has_external=true + else + has_external=false fi { echo "external=$external"