-
Notifications
You must be signed in to change notification settings - Fork 137
ci: notify help-docs hub on docs changes #1084
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
f3242ad
5fc6c28
1ceb638
e897998
cba8741
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,83 @@ | ||||||||
| name: Notify help-docs (docs changed) | ||||||||
|
|
||||||||
| # When a push to the default branch changes published docs, tell the help-docs | ||||||||
| # hub to promote the change up (product -> help-docs) as a review PR. Requires | ||||||||
| # the shared docs-sync GitHub App as repo secrets: DOCS_SYNC_APP_ID and | ||||||||
| # DOCS_SYNC_APP_PRIVATE_KEY — if either is unset the job skips (no red run). | ||||||||
| # The loop guard checks every commit in the push for the [docs-sync] marker, so | ||||||||
| # it holds across squash, rebase, and merge-commit strategies. | ||||||||
|
|
||||||||
| on: | ||||||||
| push: | ||||||||
| branches: [main] | ||||||||
| paths: ["docs/docs/**"] | ||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. MINOR — path filtering is bounded on very large pushes GitHub evaluates Acceptable if very large pushes are operationally prohibited; otherwise trigger on every push to Separately: the
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. MINOR — path filtering is bounded on very large pushes Unchanged from round 1. GitHub evaluates Acceptable if very large pushes are operationally prohibited; otherwise trigger on every push to |
||||||||
| workflow_dispatch: | ||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
workflow=".github/workflows/notify-help-docs.yml"
if rg -q '^ workflow_dispatch:$' "$workflow" &&
! rg -q 'inputs\.(before|after)' "$workflow"; then
echo "::error::workflow_dispatch has no explicit synchronization range"
exit 1
fi
rg -n -C 3 \
'workflow_dispatch|github\.event\.(before|commits)|github\.ref|BEFORE|AFTER|inputs\.(before|after)' \
"$workflow"Repository: AltimateAI/altimate-code Length of output: 227 🏁 Script executed: #!/bin/bash
set -euo pipefail
workflow=".github/workflows/notify-help-docs.yml"
echo "== workflow exists =="
git ls-files "$workflow" || true
echo "== excerpt =="
cat -n "$workflow"Repository: AltimateAI/altimate-code Length of output: 4089 🌐 Web query:
💡 Result: When you manually trigger a workflow using the workflow_dispatch event, you can specify a target branch or tag via the ref parameter [1]. Within the workflow run, the following properties provide information about the execution context: - github.ref: This context variable contains the fully-formed reference of the branch or tag that was used to trigger the workflow (e.g., refs/heads/main) [2]. - github.sha: This contains the commit SHA that the workflow is running against [2]. For a manual workflow_dispatch run, this corresponds to the HEAD commit of the ref you specified [3]. - github.event.ref: When triggered via workflow_dispatch, this property also contains the reference (e.g., refs/heads/main) that was targeted by the manual run [4]. To access the commit SHA specifically chosen for the run, you can rely on the standard github.sha context, which is automatically set to the commit at the tip of the chosen ref at the time of the trigger [3]. If you need to perform actions relative to a specific commit or branch, you can use the github.ref or github.event.ref context to identify the source [4]. If your workflow requires handling a commit other than the current HEAD of a branch (e.g., a specific SHA), the workflow_dispatch event natively supports specifying a ref, but it does not support passing a specific SHA directly as a "ref" to the workflow trigger itself in the same way it handles branches or tags [1]. If you must target a specific arbitrary SHA, a common workaround is to use custom inputs (e.g., input: sha) in your workflow_dispatch configuration and then use those inputs within your workflow to check out or interact with that specific commit [5][6]. Citations:
🏁 Script executed: #!/bin/bash
set -euo pipefail
workflow=".github/workflows/notify-help-docs.yml"
if [ -f "$workflow" ]; then
echo "== workflow excerpt =="
cat -n "$workflow"
else
echo "Workflow file not found: $workflow"
echo "Matching notify-help-docs files:"
git ls-files | grep -F "notify-help-docs" || true
fiRepository: AltimateAI/altimate-code Length of output: 4037 🌐 Web query:
💡 Result: The github.event.before property is not present in workflow runs triggered by the workflow_dispatch event because this event is manually triggered rather than being tied to a specific git commit history or push activity [1][2]. While events like push provide a commit-based history that includes before and after properties to represent changes [3], a workflow_dispatch event is an ad-hoc trigger that does not inherently know about previous states or commit deltas [1]. Consequently, if a GitHub Action (such as a path-filtering or diff-based tool) relies on github.event.before to determine what changed in the repository, it will fail or skip its logic because that information is missing in the manual event payload [1][2]. Common workarounds for this limitation include: - Removing strict dependency on base commit properties in your action configuration [1]. - Using Git commands within your workflow steps to manually calculate the relevant diff or SHA, such as finding the merge-base between the current branch and the default branch [1][2]. - Relying on GitHub-provided environment variables (like GITHUB_SHA) and branch references rather than event payload properties when performing comparisons [1]. Citations:
Provide explicit sync ranges for
🤖 Prompt for AI AgentsSource: MCP tools There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: Manual re-fires dispatch an empty Prompt for AI agents
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. MAJOR — Flagged by all 7 reviewers. This trigger was added to close round-1 nit 16 ("a failed promotion cannot be manually re-fired, and there is no way to smoke-test"). It can do neither. The {"product":"code","before":"","after":"<sha>","source_pr":"","source_author":"","source_url":""}At the hub, if [ -n "${{ steps.p.outputs.before }}" ] && \
git cat-file -e "${{ steps.p.outputs.before }}^{commit}" 2>/dev/null; then
CH="$(git diff --name-only …)"
else
CH=""
fi
…
if [ "${{ github.event_name }}" = "repository_dispatch" ] && [ -z "$CH" ]; then
echo "run=false" >> "$GITHUB_OUTPUT"The empty Fix — make an empty range mean full reconciliation rather than nothing. Either add explicit inputs: workflow_dispatch:
inputs:
before: { description: "Start SHA (blank = full reconcile)", required: false }
after: { description: "End SHA (blank = main HEAD)", required: false }or send a |
||||||||
|
|
||||||||
| permissions: | ||||||||
| contents: read | ||||||||
| pull-requests: read | ||||||||
|
|
||||||||
|
Comment on lines
+16
to
+19
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
rg -n -C 4 'permissions:|create-github-app-token|repositories:|permission-' .github
gh api /orgs/AltimateAI/installation --jq '.permissions'Repository: AltimateAI/altimate-code Length of output: 11267 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== workflow =="
cat -n .github/workflows/notify-help-docs.yml
echo
echo "== create-github-app-token action v1 metadata =="
git ls-files .github
tmpdir="$(mktemp -d)"
cd "$tmpdir"
git init >/dev/null 2>&1
git remote add origin https://github.com/actions/create-github-app-token.git >/dev/null 2>&1
git fetch --no-tags --depth 1 origin 9626c2827b2773687c9ceab62c3cf66ef99169 >/dev/null 2>&1 || true
git checkout --detach FETCH_HEAD >/dev/null 2>&1 || true
find . -maxdepth 3 -type f \( -name 'action.yml' -o -name 'README.md' \) -print
if [ -f action.yml ]; then cat -n action.yml; fi
echo
echo "== repository-dispatch metadata =="
git init >/dev/null 2>&1
git remote add origin https://github.com/peter-evans/repository-dispatch.git >/dev/null 2>&1
git fetch --no-tags --depth 1 origin v3 >/dev/null 2>&1 || true
git checkout --detach FETCH_HEAD >/dev/null 2>&1 || true
if [ -f action.yml ]; then cat -n action.yml; fiRepository: AltimateAI/altimate-code Length of output: 2587 🌐 Web query:
💡 Result: The actions/create-github-app-token action does not have a default value for the permission-contents input [1]. By default, if you do not specify any permission- inputs (including permission-contents), the generated GitHub App installation access token automatically inherits all of the permissions granted to the GitHub App installation itself [2][3][4]. When you choose to use the permission-contents input, you must explicitly set it to either read or write [1]. The action recommends explicitly listing the permissions required for your specific use case to follow the principle of least privilege [2][3]. If you set any permission- input (such as permission-contents), the token's permissions are restricted to only those explicitly defined in your workflow configuration [5][6]. Ensure that any permission you specify is also granted to the GitHub App installation, otherwise the action will return an error [2][3]. Citations:
Restrict the generated App token to the contents permission used by the dispatch.
🤖 Prompt for AI AgentsSources: MCP tools, Linters/SAST tools |
||||||||
| jobs: | ||||||||
| notify: | ||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. MINOR — no Flagged by 6 reviewers.
Suggested change
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. MAJOR — deleting Round-1 M5 was: GitHub holds one pending run per concurrency group, so three rapid docs pushes evict the middle one and its concurrency:
group: docs-sync-${{ github.event_name }}-${{ github.event.client_payload.product || inputs.product || 'push' }}
cancel-in-progress: falseEvery dispatch from this workflow lands on the single group Fix — this has to be fixed at the hub; there is no sender-side workaround:
Restoring |
||||||||
| if: ${{ !contains(toJSON(github.event.commits.*.message), '[docs-sync]') }} | ||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P1: A mixed push permanently drops ordinary docs updates whenever its commit list also contains a Prompt for AI agents
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. MAJOR (latent) —
The hub checks out the product repo at Today this is inert: the empty if: >-
(github.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/main') ||
(github.event_name == 'push' &&
!contains(toJSON(github.event.commits.*.message), '[docs-sync]'))This also makes the trigger/loop-guard interaction explicit instead of incidental. |
||||||||
| runs-on: ubuntu-latest | ||||||||
| timeout-minutes: 5 | ||||||||
| steps: | ||||||||
| - name: Check docs-sync App is configured | ||||||||
| id: config | ||||||||
| env: | ||||||||
| APP_ID: ${{ secrets.DOCS_SYNC_APP_ID }} | ||||||||
| APP_KEY: ${{ secrets.DOCS_SYNC_APP_PRIVATE_KEY }} | ||||||||
| run: | | ||||||||
| if [ -n "$APP_ID" ] && [ -n "$APP_KEY" ]; then | ||||||||
| echo "enabled=true" >> "$GITHUB_OUTPUT" | ||||||||
| else | ||||||||
| echo "enabled=false" >> "$GITHUB_OUTPUT" | ||||||||
| echo "::notice::docs-sync App not configured — skipping dispatch." | ||||||||
| fi | ||||||||
|
|
||||||||
| - name: Resolve merged PR (author for notifications) | ||||||||
| id: src | ||||||||
| if: steps.config.outputs.enabled == 'true' | ||||||||
| env: | ||||||||
| GH_TOKEN: ${{ github.token }} | ||||||||
| run: | | ||||||||
| J="$(gh api repos/${{ github.repository }}/commits/${{ github.sha }}/pulls --jq '.[0] // {}' 2>/dev/null || echo '{}')" | ||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. MINOR — this line collapses three distinct outcomes into one, and picks an arbitrary PR
Also: J="$(gh api "repos/${GITHUB_REPOSITORY}/commits/${GITHUB_SHA}/pulls" \
--jq 'map(select(.merged_at != null and .base.ref == "main")) | sort_by(.merged_at) | last // {}')" \
|| { echo "::warning::could not resolve merged PR for ${GITHUB_SHA}"; J='{}'; }plus a retry with backoff (roughly 4 attempts over ~15s) while
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. MINOR — this line is unchanged in every respect Flagged by 6 reviewers, and byte-identical to the previous revision:
Consequence at the hub: attribution lands on the wrong author, or on nobody. J="$(gh api "repos/${GITHUB_REPOSITORY}/commits/${GITHUB_SHA}/pulls" \
--jq 'map(select(.merged_at != null and .base.ref == "main")) | sort_by(.merged_at) | last // {}')" \
|| { echo "::warning::could not resolve merged PR for ${GITHUB_SHA}"; J='{}'; }plus a short retry with backoff while |
||||||||
| { | ||||||||
| echo "num=$(echo "$J" | jq -r '.number // ""')" | ||||||||
| echo "login=$(echo "$J" | jq -r '.user.login // ""')" | ||||||||
| echo "url=$(echo "$J" | jq -r '.html_url // ""')" | ||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. MINOR — direct pushes to Every push to Fall back to the commit author: [ -z "$NUM" ] && LOGIN="$(gh api "repos/$GITHUB_REPOSITORY/commits/$GITHUB_SHA" --jq '.author.login // ""')"Do not gate the dispatch on
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. MINOR — direct pushes to Flagged by 5 reviewers. Every push to Fall back to the pusher rather than gating the dispatch: [ -z "$NUM" ] && LOGIN="${{ github.event.sender.login }}"Do not gate the dispatch on |
||||||||
| } >> "$GITHUB_OUTPUT" | ||||||||
|
|
||||||||
| - name: Build dispatch payload | ||||||||
| id: payload | ||||||||
| if: steps.config.outputs.enabled == 'true' | ||||||||
| env: | ||||||||
| BEFORE: ${{ github.event.before }} | ||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. MINOR — Flagged by all 7 reviewers. On branch creation This repo already has the guard pattern — if [[ "${{ github.event.before }}" == "0000000000000000000000000000000000000000" ]]; thenNormalize the zero-SHA to
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. MINOR — Flagged by all 7 reviewers. Unchanged from round 1. The hub's Normalize the zero-SHA and forward if [[ "${{ github.event.before }}" == "0000000000000000000000000000000000000000" ]]; then |
||||||||
| AFTER: ${{ github.sha }} | ||||||||
| SRC_PR: ${{ steps.src.outputs.num }} | ||||||||
| SRC_AUTHOR: ${{ steps.src.outputs.login }} | ||||||||
| SRC_URL: ${{ steps.src.outputs.url }} | ||||||||
| run: | | ||||||||
| JSON="$(jq -cn --arg product code \ | ||||||||
| --arg before "$BEFORE" --arg after "$AFTER" \ | ||||||||
| --arg source_pr "$SRC_PR" --arg source_author "$SRC_AUTHOR" --arg source_url "$SRC_URL" \ | ||||||||
| '{product:$product,before:$before,after:$after,source_pr:$source_pr,source_author:$source_author,source_url:$source_url}')" | ||||||||
| echo "json=$JSON" >> "$GITHUB_OUTPUT" | ||||||||
|
|
||||||||
| - uses: actions/create-github-app-token@d72941d797fd3113feb6b93fd0dec494b13a2547 # v1 | ||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. MAJOR — no guard for absent App credentials; the job hard-fails instead of skipping Flagged independently by all 7 reviewers. Even with the credential reference fixed, there is no The repo's own sibling workflow already handles this — if [ -z "$GH_TOKEN" ]; then
echo "AUTOPILOT_DISPATCH_TOKEN not available — skipping centralized dispatch."
exit 0
fi
- name: Check docs-sync configuration
id: config
env:
APP_ID: ${{ secrets.DOCS_SYNC_APP_ID }}
APP_KEY: ${{ secrets.DOCS_SYNC_APP_PRIVATE_KEY }}
run: |
if [[ -n "$APP_ID" && -n "$APP_KEY" ]]; then
echo "enabled=true" >> "$GITHUB_OUTPUT"
else
echo "enabled=false" >> "$GITHUB_OUTPUT"
echo "::notice::docs-sync App not configured; skipping dispatch."
fithen
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. NIT — still pinned to
|
||||||||
| id: tok | ||||||||
| if: steps.config.outputs.enabled == 'true' | ||||||||
| with: | ||||||||
| app-id: ${{ secrets.DOCS_SYNC_APP_ID }} | ||||||||
| private-key: ${{ secrets.DOCS_SYNC_APP_PRIVATE_KEY }} | ||||||||
| owner: AltimateAI | ||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: Prompt for AI agents |
||||||||
| repositories: help-docs | ||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. MINOR — the App token inherits every installation permission Scoping to
Suggested change
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. MINOR — the minted App token still inherits every installation permission Flagged by 6 reviewers. permission-contents: write |
||||||||
|
|
||||||||
| - uses: peter-evans/repository-dispatch@ff45666b9427631e3450c54a1bcbee4d9ff4d7c0 # v3 | ||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. MINOR — no failure signal beyond a red check A failed dispatch surfaces only in the Actions tab. help-docs already ships |
||||||||
| if: steps.config.outputs.enabled == 'true' | ||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. MINOR — no failure signal beyond a red check, and the revision widened the gap Unchanged from round 1, but there are now three separate paths that converge on "green everywhere, docs never promoted":
A failed dispatch surfaces only in the Actions tab, and none of the three above surfaces anywhere at all. help-docs already ships |
||||||||
| with: | ||||||||
| token: ${{ steps.tok.outputs.token }} | ||||||||
| repository: AltimateAI/help-docs | ||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. MAJOR — merge-order prerequisite: the consumer doesn't exist yet
The payload contract itself is correct — Merge help-docs #43 first, or land this behind the credential guard so it's genuinely inert until both ends are live. If the ordering is already coordinated, state it as a merge prerequisite in the PR description.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. MAJOR — the consumer is still not on help-docs' default branch, and the credentials are configured, so this fires into the void Flagged by all 7 reviewers. Carried from round 1, and now sharper rather than softer:
Merging this first produces a workflow that reports success on every docs merge to Fix: merge help-docs #43 first (or simultaneously) and state the ordering in the PR description. A sender-side preflight — |
||||||||
| event-type: promote-from-product | ||||||||
| client-payload: ${{ steps.payload.outputs.json }} | ||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P2: The
pushtrigger onmainfires for any push that touchesdocs/docs/**, including direct pushes to the branch, not just merged pull requests. This is broader than the stated intent of notifying only when a docs PR merges. Consider a merged-only trigger (e.g., a closedpull_requestevent filtered ongithub.event.pull_request.merged == true) if direct pushes to main should not trigger the dispatch.Prompt for AI agents