mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-01-24 21:42:51 -05:00
391 lines
12 KiB
QML
391 lines
12 KiB
QML
pragma Singleton
|
|
pragma ComponentBehavior: Bound
|
|
|
|
import QtQuick
|
|
import Quickshell
|
|
import Quickshell.Io
|
|
import qs.Common
|
|
|
|
Singleton {
|
|
id: root
|
|
|
|
property int refCount: 0
|
|
property var availableUpdates: []
|
|
property bool isChecking: false
|
|
property bool hasError: false
|
|
property string errorMessage: ""
|
|
property string updChecker: ""
|
|
property string pkgManager: ""
|
|
property string distribution: ""
|
|
property bool distributionSupported: false
|
|
property string shellVersion: ""
|
|
property string shellCodename: ""
|
|
property string semverVersion: ""
|
|
|
|
function getParsedShellVersion() {
|
|
return parseVersion(semverVersion);
|
|
}
|
|
|
|
readonly property var archBasedUCSettings: {
|
|
"listUpdatesSettings": {
|
|
"params": [],
|
|
"correctExitCodes": [0, 2] // Exit code 0 = updates available, 2 = no updates
|
|
},
|
|
"parserSettings": {
|
|
"lineRegex": /^(\S+)\s+([^\s]+)\s+->\s+([^\s]+)$/,
|
|
"entryProducer": function (match) {
|
|
return {
|
|
"name": match[1],
|
|
"currentVersion": match[2],
|
|
"newVersion": match[3],
|
|
"description": `${match[1]} ${match[2]} → ${match[3]}`
|
|
};
|
|
}
|
|
}
|
|
}
|
|
|
|
readonly property var archBasedPMSettings: {
|
|
"listUpdatesSettings": {
|
|
"params": ["-Qu"],
|
|
"correctExitCodes": [0, 1] // Exit code 0 = updates available, 1 = no updates
|
|
},
|
|
"upgradeSettings": {
|
|
"params": ["-Syu"],
|
|
"requiresSudo": false
|
|
},
|
|
"parserSettings": {
|
|
"lineRegex": /^(\S+)\s+([^\s]+)\s+->\s+([^\s]+)$/,
|
|
"entryProducer": function (match) {
|
|
return {
|
|
"name": match[1],
|
|
"currentVersion": match[2],
|
|
"newVersion": match[3],
|
|
"description": `${match[1]} ${match[2]} → ${match[3]}`
|
|
};
|
|
}
|
|
}
|
|
}
|
|
|
|
readonly property var fedoraBasedPMSettings: {
|
|
"listUpdatesSettings": {
|
|
"params": ["list", "--upgrades", "--quiet", "--color=never"],
|
|
"correctExitCodes": [0, 1] // Exit code 0 = updates available, 1 = no updates
|
|
},
|
|
"upgradeSettings": {
|
|
"params": ["upgrade"],
|
|
"requiresSudo": true
|
|
},
|
|
"parserSettings": {
|
|
"lineRegex": /^([^\s]+)\s+([^\s]+)\s+.*$/,
|
|
"entryProducer": function (match) {
|
|
return {
|
|
"name": match[1],
|
|
"currentVersion": "",
|
|
"newVersion": match[2],
|
|
"description": `${match[1]} → ${match[2]}`
|
|
};
|
|
}
|
|
}
|
|
}
|
|
|
|
readonly property var updateCheckerParams: {
|
|
"checkupdates": archBasedUCSettings
|
|
}
|
|
readonly property var packageManagerParams: {
|
|
"yay": archBasedPMSettings,
|
|
"paru": archBasedPMSettings,
|
|
"dnf": fedoraBasedPMSettings
|
|
}
|
|
readonly property list<string> supportedDistributions: ["arch", "artix", "cachyos", "manjaro", "endeavouros", "fedora"]
|
|
readonly property int updateCount: availableUpdates.length
|
|
readonly property bool helperAvailable: pkgManager !== "" && distributionSupported
|
|
|
|
Process {
|
|
id: distributionDetection
|
|
command: ["sh", "-c", "cat /etc/os-release | grep '^ID=' | cut -d'=' -f2 | tr -d '\"'"]
|
|
running: true
|
|
|
|
onExited: exitCode => {
|
|
if (exitCode === 0) {
|
|
distribution = stdout.text.trim().toLowerCase();
|
|
distributionSupported = supportedDistributions.includes(distribution);
|
|
|
|
if (distributionSupported) {
|
|
updateFinderDetection.running = true;
|
|
pkgManagerDetection.running = true;
|
|
checkForUpdates();
|
|
} else {
|
|
console.warn("SystemUpdate: Unsupported distribution:", distribution);
|
|
}
|
|
} else {
|
|
console.warn("SystemUpdate: Failed to detect distribution");
|
|
}
|
|
}
|
|
|
|
stdout: StdioCollector {}
|
|
|
|
Component.onCompleted: {
|
|
versionDetection.running = true;
|
|
}
|
|
}
|
|
|
|
Process {
|
|
id: versionDetection
|
|
command: ["sh", "-c", `cd "${Quickshell.shellDir}" && if [ -d .git ]; then echo "(git) $(git rev-parse --short HEAD)"; elif [ -f VERSION ]; then cat VERSION; fi`]
|
|
|
|
stdout: StdioCollector {
|
|
onStreamFinished: {
|
|
shellVersion = text.trim();
|
|
}
|
|
}
|
|
}
|
|
|
|
Process {
|
|
id: semverDetection
|
|
command: ["sh", "-c", `cd "${Quickshell.shellDir}" && if [ -f VERSION ]; then cat VERSION; fi`]
|
|
running: true
|
|
|
|
stdout: StdioCollector {
|
|
onStreamFinished: {
|
|
semverVersion = text.trim();
|
|
}
|
|
}
|
|
}
|
|
|
|
Process {
|
|
id: codenameDetection
|
|
command: ["sh", "-c", `cd "${Quickshell.shellDir}" && if [ -f CODENAME ]; then cat CODENAME; fi`]
|
|
running: true
|
|
|
|
stdout: StdioCollector {
|
|
onStreamFinished: {
|
|
shellCodename = text.trim();
|
|
}
|
|
}
|
|
}
|
|
|
|
Process {
|
|
id: updateFinderDetection
|
|
command: ["sh", "-c", "which checkupdates"]
|
|
|
|
onExited: exitCode => {
|
|
if (exitCode === 0) {
|
|
const exeFound = stdout.text.trim();
|
|
updChecker = exeFound.split('/').pop();
|
|
} else {
|
|
console.warn("SystemUpdate: No update checker found. Will use package manager.");
|
|
}
|
|
}
|
|
|
|
stdout: StdioCollector {}
|
|
}
|
|
|
|
Process {
|
|
id: pkgManagerDetection
|
|
command: ["sh", "-c", "which paru || which yay || which dnf"]
|
|
|
|
onExited: exitCode => {
|
|
if (exitCode === 0) {
|
|
const exeFound = stdout.text.trim();
|
|
pkgManager = exeFound.split('/').pop();
|
|
} else {
|
|
console.warn("SystemUpdate: No package manager found");
|
|
}
|
|
}
|
|
|
|
stdout: StdioCollector {}
|
|
}
|
|
|
|
Process {
|
|
id: updateChecker
|
|
|
|
onExited: exitCode => {
|
|
isChecking = false;
|
|
const correctExitCodes = updChecker.length > 0 ? [updChecker].concat(updateCheckerParams[updChecker].listUpdatesSettings.correctExitCodes) : [pkgManager].concat(packageManagerParams[pkgManager].listUpdatesSettings.correctExitCodes);
|
|
if (correctExitCodes.includes(exitCode)) {
|
|
parseUpdates(stdout.text);
|
|
hasError = false;
|
|
errorMessage = "";
|
|
} else {
|
|
hasError = true;
|
|
errorMessage = "Failed to check for updates";
|
|
console.warn("SystemUpdate: Update check failed with code:", exitCode);
|
|
}
|
|
}
|
|
|
|
stdout: StdioCollector {}
|
|
}
|
|
|
|
Process {
|
|
id: updater
|
|
onExited: exitCode => {
|
|
checkForUpdates();
|
|
}
|
|
}
|
|
|
|
function checkForUpdates() {
|
|
if (!distributionSupported || (!pkgManager && !updChecker) || isChecking)
|
|
return;
|
|
isChecking = true;
|
|
hasError = false;
|
|
if (updChecker.length > 0) {
|
|
updateChecker.command = [updChecker].concat(updateCheckerParams[updChecker].listUpdatesSettings.params);
|
|
} else {
|
|
updateChecker.command = [pkgManager].concat(packageManagerParams[pkgManager].listUpdatesSettings.params);
|
|
}
|
|
updateChecker.running = true;
|
|
}
|
|
|
|
function parseUpdates(output) {
|
|
const lines = output.trim().split('\n').filter(line => line.trim());
|
|
const updates = [];
|
|
|
|
const regex = packageManagerParams[pkgManager].parserSettings.lineRegex;
|
|
const entryProducer = packageManagerParams[pkgManager].parserSettings.entryProducer;
|
|
|
|
for (const line of lines) {
|
|
const match = line.match(regex);
|
|
if (match) {
|
|
updates.push(entryProducer(match));
|
|
}
|
|
}
|
|
|
|
availableUpdates = updates;
|
|
}
|
|
|
|
function runUpdates() {
|
|
if (!distributionSupported || !pkgManager || updateCount === 0)
|
|
return;
|
|
const terminal = Quickshell.env("TERMINAL") || "xterm";
|
|
|
|
if (SettingsData.updaterUseCustomCommand && SettingsData.updaterCustomCommand.length > 0) {
|
|
const updateCommand = `${SettingsData.updaterCustomCommand} && echo "Updates complete! Press Enter to close..." && read`;
|
|
const termClass = SettingsData.updaterTerminalAdditionalParams;
|
|
|
|
var finalCommand = [terminal];
|
|
if (termClass.length > 0) {
|
|
finalCommand = finalCommand.concat(termClass.split(" "));
|
|
}
|
|
finalCommand.push("-e");
|
|
finalCommand.push("sh");
|
|
finalCommand.push("-c");
|
|
finalCommand.push(updateCommand);
|
|
updater.command = finalCommand;
|
|
} else {
|
|
const params = packageManagerParams[pkgManager].upgradeSettings.params.join(" ");
|
|
const sudo = packageManagerParams[pkgManager].upgradeSettings.requiresSudo ? "sudo" : "";
|
|
const updateCommand = `${sudo} ${pkgManager} ${params} && echo "Updates complete! Press Enter to close..." && read`;
|
|
|
|
updater.command = [terminal, "-e", "sh", "-c", updateCommand];
|
|
}
|
|
updater.running = true;
|
|
}
|
|
|
|
Timer {
|
|
interval: 30 * 60 * 1000
|
|
repeat: true
|
|
running: refCount > 0 && distributionSupported && (pkgManager || updChecker)
|
|
onTriggered: checkForUpdates()
|
|
}
|
|
|
|
IpcHandler {
|
|
target: "systemupdater"
|
|
|
|
function updatestatus(): string {
|
|
if (root.isChecking) {
|
|
return "ERROR: already checking";
|
|
}
|
|
if (!distributionSupported) {
|
|
return "ERROR: distribution not supported";
|
|
}
|
|
if (!pkgManager && !updChecker) {
|
|
return "ERROR: update checker not available";
|
|
}
|
|
root.checkForUpdates();
|
|
return "SUCCESS: Now checking...";
|
|
}
|
|
}
|
|
|
|
function parseVersion(versionStr) {
|
|
if (!versionStr || typeof versionStr !== "string")
|
|
return {
|
|
major: 0,
|
|
minor: 0,
|
|
patch: 0
|
|
};
|
|
|
|
let v = versionStr.trim();
|
|
if (v.startsWith("v"))
|
|
v = v.substring(1);
|
|
|
|
const dashIdx = v.indexOf("-");
|
|
if (dashIdx !== -1)
|
|
v = v.substring(0, dashIdx);
|
|
|
|
const plusIdx = v.indexOf("+");
|
|
if (plusIdx !== -1)
|
|
v = v.substring(0, plusIdx);
|
|
|
|
const parts = v.split(".");
|
|
return {
|
|
major: parseInt(parts[0], 10) || 0,
|
|
minor: parseInt(parts[1], 10) || 0,
|
|
patch: parseInt(parts[2], 10) || 0
|
|
};
|
|
}
|
|
|
|
function compareVersions(v1, v2) {
|
|
if (v1.major !== v2.major)
|
|
return v1.major - v2.major;
|
|
if (v1.minor !== v2.minor)
|
|
return v1.minor - v2.minor;
|
|
return v1.patch - v2.patch;
|
|
}
|
|
|
|
function checkVersionRequirement(requirementStr, currentVersion) {
|
|
if (!requirementStr || typeof requirementStr !== "string")
|
|
return true;
|
|
|
|
const req = requirementStr.trim();
|
|
let operator = "";
|
|
let versionPart = req;
|
|
|
|
if (req.startsWith(">=")) {
|
|
operator = ">=";
|
|
versionPart = req.substring(2);
|
|
} else if (req.startsWith("<=")) {
|
|
operator = "<=";
|
|
versionPart = req.substring(2);
|
|
} else if (req.startsWith(">")) {
|
|
operator = ">";
|
|
versionPart = req.substring(1);
|
|
} else if (req.startsWith("<")) {
|
|
operator = "<";
|
|
versionPart = req.substring(1);
|
|
} else if (req.startsWith("=")) {
|
|
operator = "=";
|
|
versionPart = req.substring(1);
|
|
} else {
|
|
operator = ">=";
|
|
}
|
|
|
|
const reqVersion = parseVersion(versionPart);
|
|
const cmp = compareVersions(currentVersion, reqVersion);
|
|
|
|
switch (operator) {
|
|
case ">=":
|
|
return cmp >= 0;
|
|
case ">":
|
|
return cmp > 0;
|
|
case "<=":
|
|
return cmp <= 0;
|
|
case "<":
|
|
return cmp < 0;
|
|
case "=":
|
|
return cmp === 0;
|
|
default:
|
|
return cmp >= 0;
|
|
}
|
|
}
|
|
}
|