fix(installer): make uninstall atomic for plugins and pass rollback test
This commit is contained in:
@@ -245,25 +245,64 @@ run_uninstall() {
|
|||||||
|
|
||||||
python3 - <<'PY'
|
python3 - <<'PY'
|
||||||
import json, os, subprocess, sys
|
import json, os, subprocess, sys
|
||||||
|
|
||||||
rec=json.load(open(os.environ['REC_FILE'],encoding='utf-8'))
|
rec=json.load(open(os.environ['REC_FILE'],encoding='utf-8'))
|
||||||
paths=rec.get('paths',{})
|
paths=rec.get('paths',{})
|
||||||
|
|
||||||
for path, info in paths.items():
|
PLUGINS_LOAD='plugins.load.paths'
|
||||||
exists=bool(info.get('exists'))
|
PLUGINS_ENTRY='plugins.entries.whispergate'
|
||||||
if exists:
|
PROVIDER_PATHS=[k for k in paths.keys() if k.startswith('models.providers[')]
|
||||||
|
|
||||||
|
# 1) restore plugins atomically to avoid transient schema failures
|
||||||
|
pcur=subprocess.run(['openclaw','config','get','plugins','--json'],capture_output=True,text=True)
|
||||||
|
plugins={}
|
||||||
|
if pcur.returncode==0:
|
||||||
|
plugins=json.loads(pcur.stdout)
|
||||||
|
if not isinstance(plugins,dict):
|
||||||
|
plugins={}
|
||||||
|
|
||||||
|
load=plugins.get('load') if isinstance(plugins.get('load'),dict) else {}
|
||||||
|
entries=plugins.get('entries') if isinstance(plugins.get('entries'),dict) else {}
|
||||||
|
|
||||||
|
# restore plugins.load.paths from record
|
||||||
|
info=paths.get(PLUGINS_LOAD,{'exists':False})
|
||||||
|
if info.get('exists'):
|
||||||
|
load['paths']=info.get('value')
|
||||||
|
else:
|
||||||
|
load.pop('paths',None)
|
||||||
|
|
||||||
|
# restore plugins.entries.whispergate from record
|
||||||
|
info=paths.get(PLUGINS_ENTRY,{'exists':False})
|
||||||
|
if info.get('exists'):
|
||||||
|
entries['whispergate']=info.get('value')
|
||||||
|
else:
|
||||||
|
entries.pop('whispergate',None)
|
||||||
|
|
||||||
|
plugins['load']=load
|
||||||
|
plugins['entries']=entries
|
||||||
|
|
||||||
|
pset=subprocess.run(['openclaw','config','set','plugins',json.dumps(plugins,ensure_ascii=False),'--json'],capture_output=True,text=True)
|
||||||
|
if pset.returncode!=0:
|
||||||
|
sys.stderr.write(pset.stderr or pset.stdout)
|
||||||
|
raise SystemExit(1)
|
||||||
|
|
||||||
|
# 2) restore provider paths (usually custom no-reply provider)
|
||||||
|
for path in PROVIDER_PATHS:
|
||||||
|
info=paths.get(path,{'exists':False})
|
||||||
|
if info.get('exists'):
|
||||||
val=json.dumps(info.get('value'),ensure_ascii=False)
|
val=json.dumps(info.get('value'),ensure_ascii=False)
|
||||||
p=subprocess.run(['openclaw','config','set',path,val,'--json'],capture_output=True,text=True)
|
p=subprocess.run(['openclaw','config','set',path,val,'--json'],capture_output=True,text=True)
|
||||||
if p.returncode!=0:
|
if p.returncode!=0:
|
||||||
sys.stderr.write(p.stderr)
|
sys.stderr.write(p.stderr or p.stdout)
|
||||||
raise SystemExit(1)
|
raise SystemExit(1)
|
||||||
else:
|
else:
|
||||||
p=subprocess.run(['openclaw','config','unset',path],capture_output=True,text=True)
|
p=subprocess.run(['openclaw','config','unset',path],capture_output=True,text=True)
|
||||||
if p.returncode!=0:
|
if p.returncode!=0:
|
||||||
# path may already be absent; tolerate common "not found" style failures
|
|
||||||
txt=(p.stderr or p.stdout or '').lower()
|
txt=(p.stderr or p.stdout or '').lower()
|
||||||
if 'not found' not in txt and 'missing' not in txt and 'does not exist' not in txt:
|
if 'not found' not in txt and 'missing' not in txt and 'does not exist' not in txt:
|
||||||
sys.stderr.write(p.stderr)
|
sys.stderr.write(p.stderr or p.stdout)
|
||||||
raise SystemExit(1)
|
raise SystemExit(1)
|
||||||
|
|
||||||
print('ok')
|
print('ok')
|
||||||
PY
|
PY
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user