From 5c151f93c5edadcbdd19c89603a88eab74dd482a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 22 Dec 2025 14:37:23 +0000 Subject: [PATCH 1/5] Initial plan From 8dc98a6c0efa64b877f8850e8f7534e5030d68a9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 22 Dec 2025 14:43:15 +0000 Subject: [PATCH 2/5] Add Debian/Armbian support to Linux mirror update functionality Co-authored-by: whyour <22700758+whyour@users.noreply.github.com> --- back/services/system.ts | 93 +++++++++++++++++++++++++++++++++-------- 1 file changed, 76 insertions(+), 17 deletions(-) diff --git a/back/services/system.ts b/back/services/system.ts index ecc2a732..6036dc74 100644 --- a/back/services/system.ts +++ b/back/services/system.ts @@ -218,28 +218,87 @@ export default class SystemService { ...oDoc, info: { ...oDoc.info, ...info }, }); - let defaultDomain = 'https://dl-cdn.alpinelinux.org'; - let targetDomain = 'https://dl-cdn.alpinelinux.org'; + if (os.platform() !== 'linux') { return; } - const content = await fs.promises.readFile('/etc/apk/repositories', { - encoding: 'utf-8', - }); - const domainMatch = content.match(/(http.*)\/alpine\/.*/); - if (domainMatch) { - defaultDomain = domainMatch[1]; + + let command = ''; + + // Check if this is a Debian-based system (including Armbian) + const isDebianBased = await fs.promises.access('/etc/apt/sources.list') + .then(() => true) + .catch(() => false); + + if (isDebianBased) { + // Handle Debian/Ubuntu/Armbian systems + let defaultDomain = ''; + let targetDomain = info.linuxMirror || ''; + + try { + // Read the current sources.list + const content = await fs.promises.readFile('/etc/apt/sources.list', { + encoding: 'utf-8', + }); + + // Match the first deb line to extract the current mirror + const debMatch = content.match(/^deb\s+(https?:\/\/[^\s]+)/m); + if (debMatch) { + defaultDomain = debMatch[1]; + } + + if (defaultDomain && targetDomain) { + // Escape special characters for sed + const escapedDefault = defaultDomain.replace(/\//g, '\\/').replace(/\./g, '\\.'); + const escapedTarget = targetDomain.replace(/\//g, '\\/'); + + command = `sed -i 's/${escapedDefault}/${escapedTarget}/g' /etc/apt/sources.list`; + + // Also update sources.list.d if it exists + command += ` && if [ -d /etc/apt/sources.list.d ]; then find /etc/apt/sources.list.d -type f -name "*.list" -o -name "*.sources" | xargs -r sed -i 's/${escapedDefault}/${escapedTarget}/g'; fi`; + command += ` && apt-get update`; + } else if (targetDomain) { + // If we can't detect the current domain, just update + command = `apt-get update`; + } + } catch (error) { + this.logger.error('Failed to read /etc/apt/sources.list', error); + } + } else { + // Handle Alpine Linux systems + let defaultDomain = 'https://dl-cdn.alpinelinux.org'; + let targetDomain = 'https://dl-cdn.alpinelinux.org'; + + try { + const content = await fs.promises.readFile('/etc/apk/repositories', { + encoding: 'utf-8', + }); + const domainMatch = content.match(/(http.*)\/alpine\/.*/); + if (domainMatch) { + defaultDomain = domainMatch[1]; + } + if (info.linuxMirror) { + targetDomain = info.linuxMirror; + } + command = `sed -i 's/${defaultDomain.replace( + /\//g, + '\\/', + )}/${targetDomain.replace( + /\//g, + '\\/', + )}/g' /etc/apk/repositories && apk update -f`; + } catch (error) { + this.logger.error('Failed to read /etc/apk/repositories', error); + } } - if (info.linuxMirror) { - targetDomain = info.linuxMirror; + + if (!command) { + this.sockService.sendMessage({ + type: 'updateLinuxMirror', + message: 'No supported package manager found or mirror not configured', + }); + return; } - const command = `sed -i 's/${defaultDomain.replace( - /\//g, - '\\/', - )}/${targetDomain.replace( - /\//g, - '\\/', - )}/g' /etc/apk/repositories && apk update -f`; this.scheduleService.runTask( command, From 6397415d7f0caf1c36168b410fdc8aaae68e0e5c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 22 Dec 2025 14:47:23 +0000 Subject: [PATCH 3/5] Address code review feedback - improve distribution detection and error handling Co-authored-by: whyour <22700758+whyour@users.noreply.github.com> --- back/services/system.ts | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/back/services/system.ts b/back/services/system.ts index 6036dc74..0a79bd94 100644 --- a/back/services/system.ts +++ b/back/services/system.ts @@ -226,9 +226,14 @@ export default class SystemService { let command = ''; // Check if this is a Debian-based system (including Armbian) - const isDebianBased = await fs.promises.access('/etc/apt/sources.list') + // Check for both sources.list and debian_version for more reliable detection + const hasAptSourcesList = await fs.promises.access('/etc/apt/sources.list') .then(() => true) .catch(() => false); + const hasDebianVersion = await fs.promises.access('/etc/debian_version') + .then(() => true) + .catch(() => false); + const isDebianBased = hasAptSourcesList || hasDebianVersion; if (isDebianBased) { // Handle Debian/Ubuntu/Armbian systems @@ -242,6 +247,8 @@ export default class SystemService { }); // Match the first deb line to extract the current mirror + // Note: This assumes all mirrors in sources.list use the same base URL + // If multiple different mirrors are configured, only the first one will be replaced const debMatch = content.match(/^deb\s+(https?:\/\/[^\s]+)/m); if (debMatch) { defaultDomain = debMatch[1]; @@ -252,13 +259,23 @@ export default class SystemService { const escapedDefault = defaultDomain.replace(/\//g, '\\/').replace(/\./g, '\\.'); const escapedTarget = targetDomain.replace(/\//g, '\\/'); + // Replace mirror URL in main sources.list command = `sed -i 's/${escapedDefault}/${escapedTarget}/g' /etc/apt/sources.list`; // Also update sources.list.d if it exists - command += ` && if [ -d /etc/apt/sources.list.d ]; then find /etc/apt/sources.list.d -type f -name "*.list" -o -name "*.sources" | xargs -r sed -i 's/${escapedDefault}/${escapedTarget}/g'; fi`; + command += ` && if [ -d /etc/apt/sources.list.d ]; then find /etc/apt/sources.list.d -type f \\( -name "*.list" -o -name "*.sources" \\) -exec sed -i 's/${escapedDefault}/${escapedTarget}/g' {} \\;; fi`; + + // Update package lists command += ` && apt-get update`; + } else if (!defaultDomain && targetDomain) { + // Cannot detect current mirror, log warning + this.logger.warn('Unable to detect current mirror from /etc/apt/sources.list. Mirror update skipped.'); + this.sockService.sendMessage({ + type: 'updateLinuxMirror', + message: 'Warning: Unable to detect current mirror. Please manually configure /etc/apt/sources.list', + }); } else if (targetDomain) { - // If we can't detect the current domain, just update + // Fallback: just update package lists command = `apt-get update`; } } catch (error) { From 6f7a54a6145268b294bef87f7221f8bb358ee882 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 22 Dec 2025 14:50:31 +0000 Subject: [PATCH 4/5] Fix incomplete sanitization - properly escape backslashes in URL escaping Co-authored-by: whyour <22700758+whyour@users.noreply.github.com> --- back/services/system.ts | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/back/services/system.ts b/back/services/system.ts index 0a79bd94..20c948da 100644 --- a/back/services/system.ts +++ b/back/services/system.ts @@ -255,9 +255,15 @@ export default class SystemService { } if (defaultDomain && targetDomain) { - // Escape special characters for sed - const escapedDefault = defaultDomain.replace(/\//g, '\\/').replace(/\./g, '\\.'); - const escapedTarget = targetDomain.replace(/\//g, '\\/'); + // Sanitize and escape special characters for sed + // Escape backslashes first, then other special characters + const escapedDefault = defaultDomain + .replace(/\\/g, '\\\\') // Escape backslashes first + .replace(/\//g, '\\/') // Escape forward slashes + .replace(/\./g, '\\.'); // Escape dots + const escapedTarget = targetDomain + .replace(/\\/g, '\\\\') // Escape backslashes first + .replace(/\//g, '\\/'); // Escape forward slashes // Replace mirror URL in main sources.list command = `sed -i 's/${escapedDefault}/${escapedTarget}/g' /etc/apt/sources.list`; @@ -297,13 +303,13 @@ export default class SystemService { if (info.linuxMirror) { targetDomain = info.linuxMirror; } - command = `sed -i 's/${defaultDomain.replace( - /\//g, - '\\/', - )}/${targetDomain.replace( - /\//g, - '\\/', - )}/g' /etc/apk/repositories && apk update -f`; + // Sanitize and escape special characters for sed + // Escape backslashes first, then other special characters + command = `sed -i 's/${defaultDomain + .replace(/\\/g, '\\\\') // Escape backslashes first + .replace(/\//g, '\\/')}/${targetDomain + .replace(/\\/g, '\\\\') // Escape backslashes first + .replace(/\//g, '\\/')}/g' /etc/apk/repositories && apk update -f`; } catch (error) { this.logger.error('Failed to read /etc/apk/repositories', error); } From f099bd8e051896367a4b4d493e932f44453901d7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 22 Dec 2025 14:53:59 +0000 Subject: [PATCH 5/5] Remove unreachable code and ensure consistent escaping for both Debian and Alpine Co-authored-by: whyour <22700758+whyour@users.noreply.github.com> --- back/services/system.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/back/services/system.ts b/back/services/system.ts index 20c948da..ef3f5edb 100644 --- a/back/services/system.ts +++ b/back/services/system.ts @@ -280,9 +280,6 @@ export default class SystemService { type: 'updateLinuxMirror', message: 'Warning: Unable to detect current mirror. Please manually configure /etc/apt/sources.list', }); - } else if (targetDomain) { - // Fallback: just update package lists - command = `apt-get update`; } } catch (error) { this.logger.error('Failed to read /etc/apt/sources.list', error); @@ -307,7 +304,8 @@ export default class SystemService { // Escape backslashes first, then other special characters command = `sed -i 's/${defaultDomain .replace(/\\/g, '\\\\') // Escape backslashes first - .replace(/\//g, '\\/')}/${targetDomain + .replace(/\//g, '\\/') // Escape forward slashes + .replace(/\./g, '\\.')}/${targetDomain .replace(/\\/g, '\\\\') // Escape backslashes first .replace(/\//g, '\\/')}/g' /etc/apk/repositories && apk update -f`; } catch (error) {