Skip to content

Commit 7c3b5e3

Browse files
ci: auto-sync tools.json on merge instead of failing PRs (#849)
* ci: auto-sync tools.json on merge instead of failing PRs Move tools.json sync enforcement from a PR-time unit test to a post-merge workflow. The test_docs_in_sync check (added in #839) causes false CI failures on any PR that touches tool source files when another tool PR merges first — and since the repo doesn't require branches to be up-to-date before merging, stale tools.json silently lands on master anyway. New approach: - sync-tool-docs.yml runs on push to master when tool source files or the extract script change, regenerates tools.json + README, and commits if there's a diff (using the same GitHub App token pattern as the existing changelog sync in semver-release.yml) - test_docs_in_sync removed from unit tests (was advisory anyway — not in required status checks) - test_no_legacy_tags_in_annotations kept — still validates tool source conventions at PR time Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: add concurrency group and fix push logic in sync workflow - Add concurrency group to prevent race conditions when multiple tool PRs merge in quick succession - Fix commit/push logic to use if/fi block instead of chained || (the original pattern would never push because staged changes are gone after commit) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: kingpanther13 <kingpanther13@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 4b0be35 commit 7c3b5e3

2 files changed

Lines changed: 63 additions & 25 deletions

File tree

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
name: Sync Tool Documentation
2+
3+
on:
4+
push:
5+
branches: [master]
6+
paths:
7+
- 'src/ha_mcp/tools/**'
8+
- 'scripts/extract_tools.py'
9+
10+
# Restrict permissions by default
11+
permissions:
12+
contents: read
13+
14+
# Prevent overlapping runs if multiple tool PRs merge in quick succession
15+
concurrency:
16+
group: sync-tool-docs
17+
cancel-in-progress: true
18+
19+
jobs:
20+
sync-tools-json:
21+
name: Regenerate tools.json & README
22+
runs-on: ubuntu-latest
23+
permissions:
24+
contents: write
25+
timeout-minutes: 2
26+
27+
steps:
28+
- name: Generate GitHub App token
29+
id: app-token
30+
uses: actions/create-github-app-token@v3
31+
with:
32+
app-id: ${{ secrets.RELEASE_APP_ID }}
33+
private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}
34+
continue-on-error: true
35+
36+
- uses: actions/checkout@v6
37+
with:
38+
# Use app token to bypass branch protection ruleset
39+
token: ${{ steps.app-token.outputs.token || secrets.RELEASE_TOKEN || secrets.GITHUB_TOKEN }}
40+
41+
- name: Set up Python
42+
uses: actions/setup-python@v5
43+
with:
44+
python-version: '3.13'
45+
46+
- name: Regenerate tool documentation
47+
run: python scripts/extract_tools.py
48+
49+
- name: Commit and push if changed
50+
run: |
51+
git config user.name "github-actions[bot]"
52+
git config user.email "github-actions[bot]@users.noreply.github.com"
53+
git add site/src/data/tools.json README.md
54+
if ! git diff --staged --quiet; then
55+
git commit -m "chore(internal): sync tool docs after merge [skip ci]"
56+
git push
57+
fi
Lines changed: 6 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
1-
"""Tests that tool documentation artifacts stay in sync with source code.
1+
"""Tests that tool source code follows documentation conventions.
22
3-
Run `python scripts/extract_tools.py` to regenerate if this fails.
3+
Legacy tag detection ensures tools use native FastMCP tags parameter.
4+
Sync enforcement (tools.json ↔ source) is handled by the post-merge
5+
sync-tool-docs.yml workflow rather than a PR-time unit test, because
6+
PRs that pass CI can go stale when other tool PRs merge first.
47
"""
58

69
import re
7-
import subprocess
8-
import sys
910
from pathlib import Path
1011

1112
REPO_ROOT = Path(__file__).parent.parent.parent.parent
1213

1314

1415
class TestToolDocsSync:
15-
"""README.md and tools.json must stay in sync with tool source code."""
16+
"""Tool source code must follow documentation conventions."""
1617

1718
def test_no_legacy_tags_in_annotations(self):
1819
"""Tags should be native FastMCP parameter, not inside annotations dict."""
@@ -34,23 +35,3 @@ def test_no_legacy_tags_in_annotations(self):
3435
+ "\n".join(f" - {loc}" for loc in legacy)
3536
+ "\n\nUse tags={'Category'} as a direct @mcp.tool() parameter instead."
3637
)
37-
38-
def test_docs_in_sync(self):
39-
"""Verify generated artifacts match current tool definitions.
40-
41-
If this fails, run: python scripts/extract_tools.py
42-
"""
43-
result = subprocess.run(
44-
[sys.executable, "scripts/extract_tools.py", "--check"],
45-
capture_output=True,
46-
text=True,
47-
cwd=str(REPO_ROOT),
48-
timeout=30,
49-
)
50-
51-
assert result.returncode == 0, (
52-
"Tool documentation is out of sync with source code.\n\n"
53-
+ result.stderr
54-
+ "\nRun this command to fix:\n"
55-
+ " python scripts/extract_tools.py\n"
56-
)

0 commit comments

Comments
 (0)