fix(extensions): keep settings-only installs canonical (#5094)
This commit is contained in:
parent
46d91a7c61
commit
aef8e524fd
4 changed files with 77 additions and 3 deletions
|
|
@ -857,7 +857,10 @@ def _gallery_installed_runtime_manifest(
|
|||
for key in ("name", "enabled", "scripts", "stylesheets", "sidecar", "permissions", "settings_schema"):
|
||||
if key in manifest:
|
||||
top_entry[key] = manifest[key]
|
||||
if any(key in top_entry for key in ("scripts", "stylesheets", "sidecar")):
|
||||
if any(
|
||||
key in top_entry
|
||||
for key in ("scripts", "stylesheets", "sidecar", "permissions", "settings_schema")
|
||||
):
|
||||
entries.append(_copy_manifest_entry_with_asset_base(top_entry, asset_base))
|
||||
for _source, _index, entry in _manifest_extension_entries(manifest):
|
||||
copied = _copy_manifest_entry_with_asset_base(entry, asset_base)
|
||||
|
|
|
|||
|
|
@ -210,6 +210,14 @@
|
|||
return overrides;
|
||||
}
|
||||
|
||||
function readSettingsState(schema,key){
|
||||
const stored=safeRead(key);
|
||||
const checked=validate(schema,stored);
|
||||
const overrides=overridesFromValues(schema,checked.values);
|
||||
if(JSON.stringify(stored)!==JSON.stringify(overrides)) safeWrite(key,overrides);
|
||||
return {values:checked.values,overrides};
|
||||
}
|
||||
|
||||
function supportsSettings(meta){
|
||||
return !!(meta&&meta.storage_owned&&Array.isArray(meta.settings_schema)&&meta.settings_schema.length);
|
||||
}
|
||||
|
|
@ -220,7 +228,10 @@
|
|||
const schema=supportsSettings(meta)?meta.settings_schema:[];
|
||||
const key=settingsKey(clean);
|
||||
function current(){
|
||||
return validate(schema,safeRead(key)).values;
|
||||
return supportsSettings(meta)?readSettingsState(schema,key).values:validate(schema,safeRead(key)).values;
|
||||
}
|
||||
function currentOverrides(){
|
||||
return supportsSettings(meta)?readSettingsState(schema,key).overrides:safeRead(key);
|
||||
}
|
||||
function setAll(values){
|
||||
if(!supportsSettings(meta)) return {ok:false,values:current(),errors:{extension:'unsupported'}};
|
||||
|
|
@ -237,7 +248,7 @@
|
|||
schema,
|
||||
defaults:defaultsFor(schema),
|
||||
get values(){return current();},
|
||||
get overrides(){return safeRead(key);},
|
||||
get overrides(){return currentOverrides();},
|
||||
get(name){return current()[name];},
|
||||
validate(values){return validate(schema,values);},
|
||||
set(name,value){
|
||||
|
|
|
|||
|
|
@ -68,6 +68,10 @@ def test_extension_settings_runtime_normalizes_persists_resets_and_clears():
|
|||
assert.strictEqual(window.hermesExt.settings.forExtension('demo.ext').get('mode'), 'compact');
|
||||
assert.deepStrictEqual(settings.setAll({{flag: true, mode: 'compact', count: 2}}).values, {{flag: true, mode: 'compact', count: 2}});
|
||||
assert.deepStrictEqual(JSON.parse(store.get('hermes.ext.settings.demo.ext')), {{flag: true}});
|
||||
store.set('hermes.ext.settings.demo.ext', JSON.stringify({{flag: false, unknown: 'kept', bad: 'x'}}));
|
||||
assert.deepStrictEqual(settings.values, {{flag: false, mode: 'compact', count: 2}});
|
||||
assert.deepStrictEqual(settings.overrides, {{}});
|
||||
assert.strictEqual(store.has('hermes.ext.settings.demo.ext'), false);
|
||||
assert.strictEqual(settings.set('mode', 'invalid').ok, false);
|
||||
assert.deepStrictEqual(settings.reset(), {{flag: false, mode: 'compact', count: 2}});
|
||||
assert.strictEqual(store.has('hermes.ext.settings.demo.ext'), false);
|
||||
|
|
|
|||
|
|
@ -148,6 +148,62 @@ def test_gallery_installed_extension_becomes_runtime_manifest(monkeypatch, tmp_p
|
|||
assert status["extensions"][0]["id"] == "my-ext"
|
||||
|
||||
|
||||
def test_gallery_installed_settings_only_manifest_becomes_runtime_entry(monkeypatch, tmp_path):
|
||||
"""Settings-only gallery installs still surface in status and runtime config."""
|
||||
ext_dir, state_dir = _setup_ext_env(monkeypatch, tmp_path)
|
||||
monkeypatch.delenv("HERMES_WEBUI_EXTENSION_MANIFEST", raising=False)
|
||||
monkeypatch.delenv("HERMES_WEBUI_EXTENSION_SCRIPT_URLS", raising=False)
|
||||
monkeypatch.delenv("HERMES_WEBUI_EXTENSION_STYLESHEET_URLS", raising=False)
|
||||
import api.extensions as ext_mod
|
||||
|
||||
files = {
|
||||
"my-ext/manifest.json": json.dumps(
|
||||
{
|
||||
"name": "Settings Only",
|
||||
"version": "1.0.0",
|
||||
"permissions": {"storage": {"owned": True}},
|
||||
"settings_schema": [
|
||||
{"key": "flag", "type": "boolean", "default": True},
|
||||
],
|
||||
}
|
||||
),
|
||||
}
|
||||
zip_bytes = _make_zip(files)
|
||||
sha = hashlib.sha256(zip_bytes).hexdigest()
|
||||
|
||||
monkeypatch.setattr(ext_mod, "_safe_download", lambda *a, **kw: zip_bytes)
|
||||
|
||||
ext_mod.install_extension(
|
||||
"my-ext",
|
||||
"https://hermes-webui.github.io/exts/my-ext.zip",
|
||||
sha,
|
||||
)
|
||||
|
||||
assert ext_mod.get_extension_config() == {
|
||||
"enabled": True,
|
||||
"script_urls": [],
|
||||
"stylesheet_urls": [],
|
||||
"extensions": [
|
||||
{
|
||||
"id": "my-ext",
|
||||
"name": "Settings Only",
|
||||
"storage_owned": True,
|
||||
"settings_schema": [
|
||||
{"key": "flag", "type": "boolean", "label": "flag", "description": "", "default": True},
|
||||
],
|
||||
}
|
||||
],
|
||||
}
|
||||
status = ext_mod.get_extension_status()
|
||||
assert status["manifest"]["status"] == "gallery_installed"
|
||||
assert status["counts"]["manifest_extensions"] == 1
|
||||
assert status["extensions"][0]["id"] == "my-ext"
|
||||
assert status["extensions"][0]["storage_owned"] is True
|
||||
assert status["extensions"][0]["settings_schema"] == [
|
||||
{"key": "flag", "type": "boolean", "label": "flag", "description": "", "default": True},
|
||||
]
|
||||
|
||||
|
||||
def test_install_bootstraps_managed_default_root_without_env(monkeypatch, tmp_path):
|
||||
"""Plug-and-play (#4933): one-click install works with NO env configured.
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue