Skip to content

Commit 40ff87e

Browse files
TMHSDigitalclaude
andcommitted
fix(release): count manifests, mcp.json and LICENSE as plugin content
v0.153.0 added the Blender Lab MCP server to both plugin manifests, and its release notes said "No changes to the plugin content". release_notes.py and plugin-content-changed.sh each kept their own list of five directories, while the plugin-dist build also ships LICENSE, both manifests, the Cursor marketplace and logo, and mcp.json. A fix: that only bumps the MCP pin would not have read as a plugin change either. build_plugin_dist.py now owns the list (SHIPPED = DIRS + FILES, printed by --list-paths) and both release scripts read it. Closes #494 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: TMHSDigital <154358121+TMHSDigital@users.noreply.github.com>
1 parent 7228d81 commit 40ff87e

5 files changed

Lines changed: 57 additions & 15 deletions

File tree

‎.github/scripts/plugin-content-changed.sh‎

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,26 @@
33
#
44
# bash .github/scripts/plugin-content-changed.sh v0.143.9
55
#
6-
# Plugin content is what scripts/build_plugin_dist.py ships: skills/, claude/,
7-
# rules/, snippets/, templates/. release.yml cuts a patch release when this
8-
# says yes even if no commit subject is feat:/fix:, so a `docs:` correction to
9-
# a skill still reaches plugin users (#350).
6+
# Plugin content is every path scripts/build_plugin_dist.py ships (its
7+
# --list-paths: skills/, claude/, rules/, snippets/, templates/, the manifests,
8+
# mcp.json, LICENSE), so the list has one owner (#494). release.yml cuts a
9+
# patch release when this says yes even if no commit subject is feat:/fix:, so
10+
# a `docs:` correction to a skill or a pin bump in mcp.json still reaches
11+
# plugin users (#350). Exit 2 if the path list cannot be read. PYTHON
12+
# overrides the interpreter (default python3).
1013
set -euo pipefail
1114
base="${1:?usage: plugin-content-changed.sh BASE}"
12-
if git diff --quiet "$base" HEAD -- skills claude rules snippets templates; then
15+
here="$(cd "$(dirname "$0")" && pwd)"
16+
paths=()
17+
while IFS= read -r p; do
18+
p="${p%$'\r'}"
19+
[ -n "$p" ] && paths+=("$p")
20+
done < <("${PYTHON:-python3}" "$here/../../scripts/build_plugin_dist.py" --list-paths)
21+
if [ "${#paths[@]}" -eq 0 ]; then
22+
echo "::error::plugin-content-changed.sh: build_plugin_dist.py --list-paths printed nothing" >&2
23+
exit 2
24+
fi
25+
if git diff --quiet "$base" HEAD -- "${paths[@]}"; then
1326
exit 1
1427
fi
1528
exit 0

‎.github/scripts/release_notes.py‎

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
Splits the commits in two (#455):
88
99
- **For agents and users**: commits that touched what the plugin ships
10-
(skills/, rules/, snippets/, templates/, claude/), grouped Features, Fixes,
10+
(scripts/build_plugin_dist.py DIRS + FILES: skills/, rules/, snippets/,
11+
templates/, claude/, the manifests, mcp.json, LICENSE), grouped Features, Fixes,
1112
Other. This is what a plugin user gets from the update, so it leads.
1213
- **Maintenance**: everything else (CI, tests, the site and gallery,
1314
showcase pieces, examples, docs), in a collapsed <details> block.
@@ -24,18 +25,24 @@
2425
import re
2526
import subprocess
2627
import sys
28+
from pathlib import Path
29+
30+
sys.path.insert(0, str(Path(__file__).resolve().parent.parent.parent / "scripts"))
31+
from build_plugin_dist import DIRS, FILES # noqa: E402 (the one list of shipped paths)
2732

2833
REPO_URL = "https://github.com/TMHSDigital/Blender-Developer-Tools"
2934
GROUPS = (
3035
("Features", re.compile(r"^(feat|feature)(\(.+?\))?!?:", re.I)),
3136
("Fixes", re.compile(r"^fix(\(.+?\))?!?:", re.I)),
3237
)
3338
SKIP = re.compile(r"^chore: bump version to ", re.I)
34-
# What the plugin ships (scripts/build_plugin_dist.py DIRS): a change here is
35-
# a change a plugin user receives.
36-
USER_PATHS = ("skills/", "rules/", "snippets/", "templates/", "claude/")
37-
NO_USER_CHANGES = ("No changes to the plugin content (skills, rules, snippets, templates) "
38-
"in this release.")
39+
# What the plugin ships (scripts/build_plugin_dist.py DIRS + FILES, #494): a
40+
# change here is a change a plugin user receives, including the manifests and
41+
# mcp.json.
42+
USER_DIRS = tuple(d + "/" for d in DIRS)
43+
USER_FILES = frozenset(FILES)
44+
NO_USER_CHANGES = ("No changes to the plugin content (skills, rules, snippets, templates, "
45+
"manifests, MCP config) in this release.")
3946

4047

4148
def commits(since: str | None) -> list[tuple[str, str, list[str]]]:
@@ -55,7 +62,7 @@ def commits(since: str | None) -> list[tuple[str, str, list[str]]]:
5562

5663

5764
def is_user_facing(files: list[str]) -> bool:
58-
return any(f.startswith(USER_PATHS) for f in files)
65+
return any(f.startswith(USER_DIRS) or f in USER_FILES for f in files)
5966

6067

6168
def _line(sha: str, subject: str) -> str:

‎scripts/build_plugin_dist.py‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@
3939
FILES = ("LICENSE", ".claude-plugin/plugin.json",
4040
".cursor-plugin/plugin.json", ".cursor-plugin/marketplace.json",
4141
"assets/favicon.svg", "mcp.json")
42+
# Every repo path the plugin ships. release.yml's plugin-content-changed.sh and
43+
# release_notes.py read this (--list-paths), so a change to any shipped file,
44+
# manifests and mcp.json included, counts as a plugin change (#494).
45+
SHIPPED = DIRS + FILES
4246
MAX_BYTES = 2_000_000
4347
IGNORE = shutil.ignore_patterns("__pycache__", "*.pyc")
4448

@@ -189,7 +193,12 @@ def main(argv=None) -> int:
189193
help="replace a non-empty --out directory")
190194
ap.add_argument("--fingerprint", type=Path, metavar="DIR",
191195
help="print the content fingerprint of a built tree and exit")
196+
ap.add_argument("--list-paths", action="store_true",
197+
help="print every repo path the plugin ships, one per line, and exit")
192198
a = ap.parse_args(argv)
199+
if a.list_paths:
200+
print("\n".join(SHIPPED))
201+
return 0
193202
if a.fingerprint:
194203
print(fingerprint(a.fingerprint))
195204
return 0

‎tests/test_plugin_content_changed.py‎

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44
"""
55
from __future__ import annotations
66

7+
import os
78
import shutil
89
import subprocess
10+
import sys
911
import tempfile
1012
import unittest
1113
from pathlib import Path
@@ -37,7 +39,8 @@ def tearDown(self):
3739
self.tmp.cleanup()
3840

3941
def changed(self):
40-
return subprocess.run([BASH, SCRIPT.as_posix(), "v1"], cwd=self.repo).returncode == 0
42+
env = {**os.environ, "PYTHON": Path(sys.executable).as_posix()}
43+
return subprocess.run([BASH, SCRIPT.as_posix(), "v1"], cwd=self.repo, env=env).returncode == 0
4144

4245
def commit(self, rel, text):
4346
path = self.repo / rel
@@ -58,6 +61,14 @@ def test_skill_edit_is_content(self):
5861
self.commit("skills/a/SKILL.md", "two\n")
5962
self.assertTrue(self.changed())
6063

64+
def test_mcp_config_manifests_and_license_count(self):
65+
# #494: v0.153.0 shipped the MCP registration as "no plugin changes".
66+
for rel in ("mcp.json", ".claude-plugin/plugin.json", "LICENSE"):
67+
with self.subTest(rel=rel):
68+
git(self.repo, "reset", "-q", "--hard", "v1")
69+
self.commit(rel, "{}\n")
70+
self.assertTrue(self.changed())
71+
6172
def test_rule_and_snippet_dirs_count(self):
6273
self.commit("snippets/x.py", "print(1)\n")
6374
self.assertTrue(self.changed())

‎tests/test_release_notes.py‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,12 @@ def test_maintenance_only_release_is_one_line_plus_collapsed_block(self):
6262

6363
def test_plugin_paths(self):
6464
for path in ("skills/a/SKILL.md", "rules/a.mdc", "snippets/a.py", "templates/t/x.py",
65-
"claude/blender-rules.md"):
65+
"claude/blender-rules.md", "mcp.json", ".claude-plugin/plugin.json",
66+
".cursor-plugin/plugin.json", "LICENSE"):
6667
self.assertTrue(rn.is_user_facing([path]), path)
6768
for path in ("examples/a/a.py", "showcase/a/README.md", "docs/gallery/index.html",
68-
"README.md", ".github/scripts/release_notes.py", "skillsfoo/x"):
69+
"README.md", ".github/scripts/release_notes.py", "skillsfoo/x",
70+
"mcp.json.bak", "examples/LICENSE"):
6971
self.assertFalse(rn.is_user_facing([path]), path)
7072

7173
def test_bump_commits_are_skipped(self):

0 commit comments

Comments
 (0)