fix(plugins): write manifests with a trailing newline

json.dump emits no trailing newline, but every other writer of these same
tracked files — scripts/new-extension.mjs and
scripts/sync-builtin-frontends.mjs — appends one. The two disagreed, so
each plugin install and manifest regeneration rewrote
frontend/src/plugins/**/plugin.json and plugins-manifest.json with the
newline stripped.

The result was a working tree that went dirty on its own during a normal
backend boot, showing five modified files whose diff contained no actual
change. Easy to dismiss as line-ending noise; it is a writer disagreement,
and it would have kept coming back.

111 plugin/extension tests pass.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Juan DenisandClaude Opus 5 committed 2026-07-25 11:48:15 -04:00
1 parent 1c700776f0
commit dddf0906e7
1 file changed
+11 -1
+11 -1
View File
@@ -866,17 +866,24 @@ def _install_from_buffer(buf, source_url, source_type, user_id=None, force=False
f"File saved to {req_out} for manual review."
)
# Also write the manifest into the backend plugin dir for runtime access
# Also write the manifest into the backend plugin dir for runtime access.
# The trailing newline matters: these manifests are tracked files that
# scripts/new-extension.mjs and scripts/sync-builtin-frontends.mjs also
# write, and both append one. json.dump does not, so without it every
# install rewrote the file with the newline stripped and left a dirty
# working tree whose diff contained no actual change.
if has_backend:
manifest_out = os.path.join(backend_dest, 'plugin.json')
with open(manifest_out, 'w') as f:
json.dump(manifest, f, indent=2)
f.write('\n')
# Also write manifest to frontend dir
if has_frontend:
manifest_out = os.path.join(frontend_dest, 'plugin.json')
with open(manifest_out, 'w') as f:
json.dump(manifest, f, indent=2)
f.write('\n')
# Determine blueprint info from manifest
entry_point = manifest.get('entry_point', '')
@@ -1129,8 +1136,11 @@ def _regenerate_frontend_manifest():
entry['styles'] = [f'plugins/{p.slug}/styles/{s}' for s in styles]
entries.append(entry)
# Trailing newline for the same reason as the per-plugin manifests above:
# scripts/sync-builtin-frontends.mjs writes this same tracked file with one.
with open(manifest_path, 'w') as f:
json.dump({'plugins': entries}, f, indent=2)
f.write('\n')
logger.info(f'Frontend plugin manifest regenerated with {len(entries)} plugin(s)')