Add bounded Panama recovery actions
This commit is contained in:
@@ -24,9 +24,10 @@ Singleton {
|
||||
property var lastRepair: ({})
|
||||
property string lastCopyResult: ""
|
||||
property bool startupScanEnabled: true
|
||||
property bool postRepairScanPending: false
|
||||
|
||||
readonly property bool actionable: root.status === "warning" || root.status === "error"
|
||||
readonly property bool busy: scanProcess.running || repairProcess.running
|
||||
readonly property bool busy: scanProcess.running || repairProcess.running || root.postRepairScanPending
|
||||
readonly property string helperPath: Quickshell.env("PANAMA_HEALTH_HELPER")
|
||||
|| Quickshell.shellDir + "/scripts/panama-doctor"
|
||||
readonly property var statuses: ["ok", "warning", "error", "unconfigured"]
|
||||
@@ -68,8 +69,25 @@ Singleton {
|
||||
|
||||
property string checkId: ""
|
||||
property bool external: false
|
||||
property string outputText: ""
|
||||
property int exitCode: -1
|
||||
property bool exited: false
|
||||
property bool streamFinished: false
|
||||
property bool settled: false
|
||||
|
||||
onExited: (exitCode, exitStatus) => root.finishRepair(exitCode, checkId, external)
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
repairProcess.outputText = this.text;
|
||||
repairProcess.streamFinished = true;
|
||||
root.settleRepair();
|
||||
}
|
||||
}
|
||||
|
||||
onExited: (exitCode, exitStatus) => {
|
||||
repairProcess.exitCode = exitCode;
|
||||
repairProcess.exited = true;
|
||||
root.settleRepair();
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
@@ -226,26 +244,92 @@ Singleton {
|
||||
root.lastError = "";
|
||||
repairProcess.checkId = id;
|
||||
repairProcess.external = external;
|
||||
repairProcess.outputText = "";
|
||||
repairProcess.exitCode = -1;
|
||||
repairProcess.exited = false;
|
||||
repairProcess.streamFinished = false;
|
||||
repairProcess.settled = false;
|
||||
repairProcess.exec([root.helperPath, "--repair", id, "--json"]);
|
||||
return true;
|
||||
}
|
||||
|
||||
function finishRepair(exitCode: int, id: string, external: bool): void {
|
||||
const succeeded = exitCode === 0;
|
||||
function settleRepair(): void {
|
||||
if (repairProcess.settled || !repairProcess.exited || !repairProcess.streamFinished)
|
||||
return;
|
||||
repairProcess.settled = true;
|
||||
root.finishRepair(
|
||||
repairProcess.exitCode,
|
||||
repairProcess.checkId,
|
||||
repairProcess.external,
|
||||
repairProcess.outputText
|
||||
);
|
||||
}
|
||||
|
||||
function finishRepair(exitCode: int, id: string, external: bool, text: string): void {
|
||||
let candidate;
|
||||
try {
|
||||
candidate = JSON.parse(text.trim());
|
||||
} catch (error) {
|
||||
candidate = null;
|
||||
}
|
||||
|
||||
const result = root.validRepairResult(candidate, id, exitCode)
|
||||
? {
|
||||
schemaVersion: 1,
|
||||
checkId: candidate.checkId,
|
||||
accepted: candidate.accepted,
|
||||
exitCode: candidate.exitCode,
|
||||
message: candidate.message
|
||||
}
|
||||
: {
|
||||
schemaVersion: 1,
|
||||
checkId: id,
|
||||
accepted: false,
|
||||
exitCode: exitCode,
|
||||
message: "Panama returned an invalid repair response."
|
||||
};
|
||||
const failed = !result.accepted || result.exitCode !== 0;
|
||||
root.repairingId = "";
|
||||
root.lastRepair = ({ id: id, succeeded: succeeded });
|
||||
if (!succeeded) {
|
||||
root.lastError = "Panama could not repair this item. Try refreshing or use the recommended setup steps.";
|
||||
if (external) {
|
||||
root.lastRepair = result;
|
||||
root.lastError = failed ? result.message : "";
|
||||
if (failed && external && !failureNotification.running) {
|
||||
failureNotification.exec([
|
||||
"notify-send", "-a", "Panama", "-i", "dialog-error-symbolic",
|
||||
"Panama action failed", "The requested health repair could not be completed."
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// Any refresh requested while the repair was running is satisfied by
|
||||
// this one observed post-repair scan. Process exit alone never changes
|
||||
// the accepted check rows.
|
||||
root.queuedRefresh = false;
|
||||
root.postRepairScanPending = true;
|
||||
Qt.callLater(root.startPostRepairScan);
|
||||
}
|
||||
|
||||
function startPostRepairScan(): void {
|
||||
root.postRepairScanPending = false;
|
||||
root.queuedRefresh = false;
|
||||
root.refresh();
|
||||
}
|
||||
|
||||
function validRepairResult(candidate: var, id: string, processExitCode: int): bool {
|
||||
if (!root.plainObject(candidate))
|
||||
return false;
|
||||
const keys = Object.keys(candidate).sort();
|
||||
const expectedKeys = ["accepted", "checkId", "exitCode", "message", "schemaVersion"];
|
||||
if (keys.length !== expectedKeys.length
|
||||
|| !keys.every((key, index) => key === expectedKeys[index]))
|
||||
return false;
|
||||
return candidate.schemaVersion === 1
|
||||
&& candidate.checkId === id
|
||||
&& typeof candidate.accepted === "boolean"
|
||||
&& Number.isInteger(candidate.exitCode)
|
||||
&& candidate.exitCode === processExitCode
|
||||
&& typeof candidate.message === "string"
|
||||
&& candidate.message.length > 0;
|
||||
}
|
||||
|
||||
function copyReport(): bool {
|
||||
if (copyProcess.running)
|
||||
return false;
|
||||
@@ -265,6 +349,8 @@ Singleton {
|
||||
generation: root.generation,
|
||||
acceptedGeneration: root.acceptedGeneration,
|
||||
repairingId: root.repairingId,
|
||||
lastRepair: root.lastRepair,
|
||||
lastError: root.lastError,
|
||||
checks: root.checks.map(check => check.id),
|
||||
checkStates: root.checks.map(check => ({ id: check.id, status: check.status }))
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user