mirror of
https://github.com/mattermost/mattermost.git
synced 2026-11-03 20:56:57 -06:00
* Replace docs impact review with self-clearing gap detection The old workflow sparse-checked-out mattermost/docs and cited source/**.rst paths that no longer exist, so it was judging the current PR against a docs tree this repo replaced. It also applied Docs/Needed and never removed it, which made the label a permanent record of a gap rather than a statement about the PR as it stands. docs-gap.yml reads both sides of the diff — the code and whatever docs the PR already carries — so a later push that documents the change flips the verdict to none and clears the label. The label follows the verdict, not the presence of a docs diff, or clearing it would be an escape hatch anyone could take by editing an unrelated page. Prior state travels in the sticky comment, so a label a human applied survives a none verdict. The audience personas come from the same frontmatter the review router reads instead of being restated in workflow YAML, which is what keeps the two halves of the pipeline from disagreeing about who reads what. Both are added and removed in one commit: they share a trigger, so running them together would double-comment and race the label. Co-authored-by: Cursor <cursoragent@cursor.com> * Tune docs-gap prompt, CAP limits, and Action pins Correct API docs publish scope, size wrap caps for ~10k/1k LoC and ~100 paths, and bump checkout/setup-node to v7. Co-authored-by: Cursor <cursoragent@cursor.com> * Split docs-ai into review/, gap/, and shared lib/ Make which pipeline owns which scripts obvious; keep one package and shared helpers. Co-authored-by: Cursor <cursoragent@cursor.com> * Trim docs-gap comments to non-obvious rationale Keep the why for label lifecycle, soft-fail, and secrets gating; drop restated narration. Co-authored-by: Cursor <cursoragent@cursor.com> * Fix gap-report footer, label-remove restore, and cancelled report job Address CodeRabbit: hide opt-out on none, restore sticky if removeLabel fails, shell-safe dry-run example, skip report on concurrency cancel. Co-authored-by: Cursor <cursoragent@cursor.com> * Harden sticky author match and pin claude-code-action v1.0.235 Match stickies to github-actions[bot] only, use the job GITHUB_TOKEN instead of Claude App OIDC on analyze, and bump the action pin. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com>
147 lines
5.3 KiB
YAML
147 lines
5.3 KiB
YAML
name: Docs Gap Analysis
|
|
|
|
# Owns Docs/Needed. No paths: filter — must run on docs-only pushes that close a gap.
|
|
# analyze (model, read-only) and report (label/comment writer) are separate jobs.
|
|
|
|
on:
|
|
pull_request:
|
|
types: [opened, synchronize, reopened, ready_for_review]
|
|
branches:
|
|
- master
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
concurrency:
|
|
group: docs-gap-${{ github.event.pull_request.number }}
|
|
cancel-in-progress: true
|
|
|
|
jobs:
|
|
analyze:
|
|
name: Analyze documentation impact
|
|
# Skip forks (no key/token) and human opt-outs.
|
|
if: >-
|
|
github.event.pull_request.draft == false &&
|
|
github.event.pull_request.head.repo.full_name == github.repository &&
|
|
!startsWith(github.event.pull_request.user.login, 'unified-ci-app') &&
|
|
!contains(github.event.pull_request.labels.*.name, 'Docs/Not Needed') &&
|
|
!contains(github.event.pull_request.labels.*.name, 'Docs/Done')
|
|
runs-on: ubuntu-24.04
|
|
permissions:
|
|
contents: read
|
|
pull-requests: read
|
|
outputs:
|
|
analyzable: ${{ steps.diff.outputs.analyzable }}
|
|
result: ${{ steps.gap.outputs.structured_output }}
|
|
outcome: ${{ steps.gap.outcome }}
|
|
env:
|
|
HAS_ANTHROPIC_KEY: ${{ secrets.ANTHROPIC_API_KEY != '' }}
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
with:
|
|
fetch-depth: 2
|
|
persist-credentials: false
|
|
|
|
# Job-level if cannot read secrets; fold the key check into analyzable so
|
|
# report skips when unset.
|
|
- name: Collect both sides of the diff
|
|
id: diff
|
|
run: |
|
|
set -euo pipefail
|
|
mkdir -p .docs-ai/gap
|
|
|
|
DOCS_PATHSPEC=(
|
|
docs/main docs/develop
|
|
':(exclude)docs/main/agents/docs'
|
|
)
|
|
|
|
git diff --name-only HEAD^1 HEAD > .docs-ai/gap/files.raw
|
|
git diff HEAD^1 HEAD -- . ':(exclude)docs' > .docs-ai/gap/code.raw
|
|
git diff HEAD^1 HEAD -- "${DOCS_PATHSPEC[@]}" > .docs-ai/gap/docs.raw
|
|
|
|
COUNT=$(wc -l < .docs-ai/gap/files.raw | tr -d ' ')
|
|
echo "Changed files: ${COUNT}"
|
|
|
|
if [ "${COUNT}" -eq 0 ]; then
|
|
echo "Nothing changed; skipping gap analysis." >> "$GITHUB_STEP_SUMMARY"
|
|
echo "analyzable=false" >> "$GITHUB_OUTPUT"
|
|
elif [ "$HAS_ANTHROPIC_KEY" != "true" ]; then
|
|
echo "ANTHROPIC_API_KEY is not configured; skipping gap analysis." >> "$GITHUB_STEP_SUMMARY"
|
|
echo "analyzable=false" >> "$GITHUB_OUTPUT"
|
|
else
|
|
echo "analyzable=true" >> "$GITHUB_OUTPUT"
|
|
fi
|
|
|
|
- name: Set up Node
|
|
if: steps.diff.outputs.analyzable == 'true'
|
|
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
|
with:
|
|
node-version-file: .nvmrc
|
|
cache: "npm"
|
|
cache-dependency-path: .github/scripts/docs-ai/package-lock.json
|
|
|
|
- name: Install dependencies
|
|
if: steps.diff.outputs.analyzable == 'true'
|
|
working-directory: .github/scripts/docs-ai
|
|
run: npm ci --ignore-scripts
|
|
|
|
- name: Build the prompt
|
|
id: prompt
|
|
if: steps.diff.outputs.analyzable == 'true'
|
|
run: |
|
|
node .github/scripts/docs-ai/gap/prepare.mjs \
|
|
--files .docs-ai/gap/files.raw \
|
|
--code-diff .docs-ai/gap/code.raw \
|
|
--docs-diff .docs-ai/gap/docs.raw \
|
|
--out-dir .docs-ai/gap
|
|
|
|
# Soft-fail so report can leave the label alone on outage.
|
|
- name: Assess the gap
|
|
id: gap
|
|
if: steps.diff.outputs.analyzable == 'true'
|
|
continue-on-error: true
|
|
uses: anthropics/claude-code-action@756cc22e19660d20e8cc9496b4f242475a7f7790 # v1.0.235
|
|
with:
|
|
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
# Without this the action uses Claude App via OIDC (needs id-token: write).
|
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
|
allowed_bots: "cursor,claude"
|
|
prompt: ${{ steps.prompt.outputs.prompt }}
|
|
claude_args: |
|
|
--model ${{ vars.DOCS_AI_GAP_MODEL || 'claude-sonnet-4-6' }}
|
|
--max-turns 30
|
|
--allowedTools "Read,Glob,Grep"
|
|
--json-schema '${{ steps.prompt.outputs.schema }}'
|
|
|
|
report:
|
|
name: Post comment and manage label
|
|
needs: analyze
|
|
# !cancelled (not always): skip superseded concurrency cancels; still run after soft-fail.
|
|
if: ${{ !cancelled() && needs.analyze.outputs.analyzable == 'true' }}
|
|
runs-on: ubuntu-24.04
|
|
permissions:
|
|
contents: read
|
|
pull-requests: write
|
|
issues: write
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
with:
|
|
persist-credentials: false
|
|
|
|
- name: Set up Node
|
|
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
|
with:
|
|
node-version-file: .nvmrc
|
|
|
|
- name: Report
|
|
env:
|
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
PR_NUMBER: ${{ github.event.pull_request.number }}
|
|
PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }}
|
|
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
|
GAP_RESULT: ${{ needs.analyze.outputs.result }}
|
|
GAP_OUTCOME: ${{ needs.analyze.outputs.outcome }}
|
|
run: node .github/scripts/docs-ai/gap/report.mjs
|