fix(plugins): keep dashboard/ and JS module files revision-owned on no-git carry
A subdirectory install has no .git, so the carry cannot tell removed upstream code from user files and relies on a deny-list. That list missed the dashboard surface (web_server_dashboard loads dashboard/manifest.json) and .mjs/.cjs/.jsx/.tsx, so an upstream that dropped its dashboard or a hook script got the old files back. Add dashboard/ to _NO_GIT_REVISION_DIRS and the four extensions as a carry-local set. They stay out of tools.plugin_guard.CODE_FILE_EXTENSIONS on purpose: that set exempts code files from env-secret scan patterns, so widening it there would weaken scanning rather than broaden it. The docs now list what is actually enforced, and the existing subdir update test pins dashboard/manifest.json + hooks/run.cjs removed upstream are not resurrected.
This commit is contained in:
@@ -289,7 +289,10 @@ _NO_GIT_REVISION_FILES = frozenset({
|
||||
"plugin.yaml", "plugin.yml", "plugin.json", "mcp.json",
|
||||
"pyproject.toml", "package.json", "package-lock.json", "uv.lock",
|
||||
})
|
||||
_NO_GIT_REVISION_DIRS = frozenset({"desktop", "skills", "sidecar", "node_modules"})
|
||||
_NO_GIT_REVISION_DIRS = frozenset({"dashboard", "desktop", "skills", "sidecar", "node_modules"})
|
||||
# JS module/JSX variants the guard does not classify as code. Kept local: adding them to
|
||||
# tools.plugin_guard.CODE_FILE_EXTENSIONS would exempt them from env-secret scan patterns.
|
||||
_NO_GIT_REVISION_EXTENSIONS = frozenset({".mjs", ".cjs", ".jsx", ".tsx"})
|
||||
|
||||
|
||||
def _skip_preserve(name: str) -> bool:
|
||||
@@ -300,8 +303,10 @@ def _skip_preserve(name: str) -> bool:
|
||||
def _revision_owned_without_git(rel: Path) -> bool:
|
||||
"""True for plugin code/control surfaces an update must never resurrect from the old tree."""
|
||||
from tools.plugin_guard import CODE_FILE_EXTENSIONS
|
||||
suffix = rel.suffix.lower()
|
||||
return (
|
||||
rel.suffix.lower() in CODE_FILE_EXTENSIONS
|
||||
suffix in CODE_FILE_EXTENSIONS
|
||||
or suffix in _NO_GIT_REVISION_EXTENSIONS
|
||||
or rel.as_posix() in _NO_GIT_REVISION_FILES
|
||||
or bool(rel.parts and rel.parts[0] in _NO_GIT_REVISION_DIRS)
|
||||
)
|
||||
|
||||
@@ -216,6 +216,10 @@ def test_update_of_a_subdir_install_keeps_files_the_user_created_or_edited(world
|
||||
(src / "desktop").mkdir()
|
||||
(src / "desktop" / "plugin.js").write_text("export default { id: \"v1\" }\n")
|
||||
(src / "server.js").write_text("console.log('v1')\n")
|
||||
(src / "dashboard").mkdir()
|
||||
(src / "dashboard" / "manifest.json").write_text("{}")
|
||||
(src / "hooks").mkdir()
|
||||
(src / "hooks" / "run.cjs").write_text("module.exports = 1\n")
|
||||
sp.run(["git", "init", "-q"], cwd=mono, check=True, env=_GIT_ENV)
|
||||
pin = {"sha": _commit(mono, "v1")}
|
||||
|
||||
@@ -244,6 +248,8 @@ def test_update_of_a_subdir_install_keeps_files_the_user_created_or_edited(world
|
||||
(src / "config.yaml.example").write_text("endpoint: new-default\n")
|
||||
shutil.rmtree(src / "desktop")
|
||||
(src / "server.js").unlink()
|
||||
shutil.rmtree(src / "dashboard")
|
||||
shutil.rmtree(src / "hooks")
|
||||
pin["sha"] = _commit(mono, "v2")
|
||||
assert pc.dashboard_update_user_plugin("sub-plugin")["ok"] is True
|
||||
|
||||
@@ -252,6 +258,7 @@ def test_update_of_a_subdir_install_keeps_files_the_user_created_or_edited(world
|
||||
assert (target / "data" / "state.json").read_text() == "{}"
|
||||
assert not (target / "desktop").exists()
|
||||
assert not (target / "server.js").exists()
|
||||
assert not (target / "dashboard").exists() and not (target / "hooks" / "run.cjs").exists()
|
||||
|
||||
|
||||
def test_repin_keeps_a_wholly_ignored_data_dir_in_a_git_checkout(world):
|
||||
|
||||
@@ -198,8 +198,11 @@ enabled/disabled state is preserved, and so are files the plugin's repo does
|
||||
not track (the `config.yaml` created from its `.example`, data files, `.env`).
|
||||
For monorepo/subdirectory installs, which do not carry a local Git checkout,
|
||||
update preserves user-state files the new revision does not ship. Plugin code and
|
||||
control surfaces (Python, Desktop/skills, manifests/MCP and dependency metadata)
|
||||
remain revision-owned and are not resurrected from the old install. If a user-state
|
||||
control surfaces remain revision-owned and are not resurrected from the old install:
|
||||
source files (Python, JavaScript/TypeScript including `.mjs`/`.cjs`/`.jsx`/`.tsx`,
|
||||
shell, Ruby, Perl, PHP), the top-level `dashboard/`, `desktop/`, `skills/`,
|
||||
`sidecar/` and `node_modules/` directories, the plugin manifest, `mcp.json` and
|
||||
dependency metadata (`pyproject.toml`, `package.json`, lockfiles). If a user-state
|
||||
path conflicts with the new tree's file/directory layout, the update stops before
|
||||
publication so the installed copy — and the user's data — remain intact.
|
||||
Edits you made to *tracked* files are not carried onto the new code; copies are
|
||||
|
||||
Reference in New Issue
Block a user