cicy-desktop 2.1.276 → 2.1.278
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/linux-app-release.yml +3 -1
- package/.github/workflows/mac-app-release.yml +2 -2
- package/.github/workflows/windows-exe-release.yml +6 -4
- package/package.json +5 -5
- package/src/sidecar/cicy-runtime-health.js +8 -0
- package/src/sidecar/container-scripts/cicy-code-update.sh +35 -5
- package/src/sidecar/docker.js +13 -5
- package/src/sidecar/wsl-docker.js +48 -11
- package/src/sidecar/wsl-prerequisites.js +15 -0
- package/test/cicy-code-update.test.js +69 -0
- package/test/cicy-runtime-health.test.js +12 -0
- package/test/wsl-prerequisites.test.js +37 -0
|
@@ -68,10 +68,12 @@ jobs:
|
|
|
68
68
|
shell: bash
|
|
69
69
|
env:
|
|
70
70
|
GH_TOKEN: ${{ github.token }}
|
|
71
|
+
PUBLISH_OWNER: ${{ github.repository_owner }}
|
|
72
|
+
PUBLISH_REPO: ${{ github.event.repository.name }}
|
|
71
73
|
CICY_OBFUSCATE: "1" # 发版混淆 JS(prebuild 主进程 + Vite 渲染层),仅 release CI
|
|
72
74
|
run: |
|
|
73
75
|
set -euo pipefail
|
|
74
|
-
npm run build:linux -- --x64 --config.linux.target=AppImage --publish always
|
|
76
|
+
npm run build:linux -- --x64 --config.linux.target=AppImage --config.publish.owner="$PUBLISH_OWNER" --config.publish.repo="$PUBLISH_REPO" --publish always
|
|
75
77
|
|
|
76
78
|
# AppImage + 版本指针传到 OSS —— app-updater 现已全走 OSS(不碰 GitHub),linux 也要在
|
|
77
79
|
# OSS 上有 versioned AppImage(CiCy-Desktop-<ver>.AppImage)和自己的指针 linux-latest-version.txt。
|
|
@@ -139,7 +139,7 @@ jobs:
|
|
|
139
139
|
GH_TOKEN: ${{ github.token }}
|
|
140
140
|
run: |
|
|
141
141
|
gh release create "${{ steps.meta.outputs.tag }}" \
|
|
142
|
-
--repo "
|
|
142
|
+
--repo "${{ github.repository }}" \
|
|
143
143
|
--title "CiCy Desktop v${{ steps.meta.outputs.version }}" \
|
|
144
144
|
--generate-notes \
|
|
145
145
|
--draft \
|
|
@@ -183,7 +183,7 @@ jobs:
|
|
|
183
183
|
pkgbuild --root "$ROOT" --component-plist "$PLIST" --scripts "$SCR" \
|
|
184
184
|
--identifier com.cicy.desktop --version "$VER" --install-location / "$PKG"
|
|
185
185
|
gh release upload "${{ steps.meta.outputs.tag }}" "$PKG" \
|
|
186
|
-
--repo "
|
|
186
|
+
--repo "${{ github.repository }}" --clobber
|
|
187
187
|
echo "uploaded $PKG ($(du -h "$PKG" | cut -f1))"
|
|
188
188
|
done
|
|
189
189
|
|
|
@@ -75,7 +75,7 @@ jobs:
|
|
|
75
75
|
GH_TOKEN: ${{ github.token }}
|
|
76
76
|
run: |
|
|
77
77
|
gh release create "${{ steps.meta.outputs.tag }}" \
|
|
78
|
-
--repo "
|
|
78
|
+
--repo "${{ github.repository }}" \
|
|
79
79
|
--title "CiCy Desktop v${{ steps.meta.outputs.version }}" \
|
|
80
80
|
--generate-notes \
|
|
81
81
|
--draft \
|
|
@@ -86,14 +86,16 @@ jobs:
|
|
|
86
86
|
shell: powershell
|
|
87
87
|
env:
|
|
88
88
|
GH_TOKEN: ${{ github.token }}
|
|
89
|
+
PUBLISH_OWNER: ${{ github.repository_owner }}
|
|
90
|
+
PUBLISH_REPO: ${{ github.event.repository.name }}
|
|
89
91
|
CICY_OBFUSCATE: "1" # 发版混淆 JS(prebuild 主进程 + Vite 渲染层),仅 release CI
|
|
90
92
|
run: |
|
|
91
93
|
if (Test-Path dist) { Remove-Item -Recurse -Force dist }
|
|
92
94
|
if (Test-Path "build\logo.ico") {
|
|
93
|
-
npm run build:win -- --config.win.icon=build/logo.ico --publish always
|
|
95
|
+
npm run build:win -- --config.win.icon=build/logo.ico --config.publish.owner="$env:PUBLISH_OWNER" --config.publish.repo="$env:PUBLISH_REPO" --publish always
|
|
94
96
|
} else {
|
|
95
97
|
Write-Host "::warning::no icon, using default"
|
|
96
|
-
npm run build:win -- --publish always
|
|
98
|
+
npm run build:win -- --config.publish.owner="$env:PUBLISH_OWNER" --config.publish.repo="$env:PUBLISH_REPO" --publish always
|
|
97
99
|
}
|
|
98
100
|
|
|
99
101
|
# 推 exe 到 OSS。三件事(不再是"永远一个版本"):
|
|
@@ -179,6 +181,6 @@ jobs:
|
|
|
179
181
|
GH_TOKEN: ${{ github.token }}
|
|
180
182
|
run: |
|
|
181
183
|
gh release edit "${{ steps.meta.outputs.tag }}" \
|
|
182
|
-
--repo "
|
|
184
|
+
--repo "${{ github.repository }}" \
|
|
183
185
|
--draft=false \
|
|
184
186
|
--latest
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cicy-desktop",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.278",
|
|
4
4
|
"description": "CiCy - AI-powered operating system browser",
|
|
5
5
|
"main": "src/main.js",
|
|
6
6
|
"bin": {
|
|
@@ -143,10 +143,10 @@
|
|
|
143
143
|
"//optionalDependencies": "(2026-06 回调): mac/linux 改回 native cicy-code(:8008,colima 在 16G mac 上压垮内存)→ 重新内置 cicy-code-<plat> / cicy-mihomo-<plat>,localbin.fromBundle 零网络 seed(npm 仅作更新通道,带 npmmirror→npmjs 回退)。这些由 scripts/sync-runtime-deps.cjs 在 tag-push 时同步到最新版。npm 的 os/cpu 字段保证每个平台只装自己那份。Windows 仍走 docker(WSL :8009),它那份 cicy-code-windows 用不到但 bundle 着无害。",
|
|
144
144
|
"optionalDependencies": {
|
|
145
145
|
"electron": "41.0.3",
|
|
146
|
-
"cicy-code-darwin-x64": "2.3.
|
|
147
|
-
"cicy-code-darwin-arm64": "2.3.
|
|
148
|
-
"cicy-code-linux-x64": "2.3.
|
|
149
|
-
"cicy-code-linux-arm64": "2.3.
|
|
146
|
+
"cicy-code-darwin-x64": "2.3.563",
|
|
147
|
+
"cicy-code-darwin-arm64": "2.3.563",
|
|
148
|
+
"cicy-code-linux-x64": "2.3.563",
|
|
149
|
+
"cicy-code-linux-arm64": "2.3.563",
|
|
150
150
|
"cicy-code-windows-x64": "2.3.193",
|
|
151
151
|
"cicy-mihomo-darwin-x64": "1.10.4",
|
|
152
152
|
"cicy-mihomo-darwin-arm64": "1.10.4",
|
|
@@ -71,16 +71,46 @@ fi
|
|
|
71
71
|
[ -n "$ver" ] || { log "could not resolve cicy-code@${want}"; exit 1; }
|
|
72
72
|
dest="$RT/$ver"
|
|
73
73
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
74
|
+
case "$(uname -s):$(uname -m)" in
|
|
75
|
+
Linux:x86_64) platform_pkg="cicy-code-linux-x64" ;;
|
|
76
|
+
Linux:aarch64|Linux:arm64) platform_pkg="cicy-code-linux-arm64" ;;
|
|
77
|
+
*) log "unsupported platform: $(uname -s)-$(uname -m)"; exit 1 ;;
|
|
78
|
+
esac
|
|
79
|
+
|
|
80
|
+
platform_installed() {
|
|
81
|
+
[ -f "$dest/lib/node_modules/$platform_pkg/package.json" ] ||
|
|
82
|
+
[ -f "$dest/lib/node_modules/cicy-code/node_modules/$platform_pkg/package.json" ]
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
install_package() {
|
|
86
|
+
spec="$1"
|
|
87
|
+
if npm install -g "$spec" --prefix "$dest" --registry "$REG" \
|
|
88
|
+
--fetch-retries=2 --fetch-timeout=60000 --fetch-retry-maxtimeout=30000; then
|
|
89
|
+
return 0
|
|
90
|
+
fi
|
|
91
|
+
alt="$NPM_CN"; [ "$REG" = "$NPM_CN" ] && alt="$NPM_OFFICIAL"
|
|
92
|
+
log "install from $REG failed → falling back to $alt"
|
|
93
|
+
npm install -g "$spec" --prefix "$dest" --registry "$alt" \
|
|
94
|
+
--fetch-retries=2 --fetch-timeout=60000 --fetch-retry-maxtimeout=30000
|
|
95
|
+
REG="$alt"
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if [ ! -x "$dest/bin/cicy-code" ]; then
|
|
77
99
|
log "installing v$ver from $REG"
|
|
78
100
|
rm -rf "$dest"
|
|
79
101
|
mkdir -p "$dest"
|
|
80
|
-
|
|
81
|
-
--fetch-retries=2 --fetch-timeout=60000 --fetch-retry-maxtimeout=30000
|
|
102
|
+
install_package "cicy-code@$ver"
|
|
82
103
|
fi
|
|
83
104
|
|
|
105
|
+
if ! platform_installed; then
|
|
106
|
+
log "repairing missing $platform_pkg@$ver"
|
|
107
|
+
install_package "$platform_pkg@$ver"
|
|
108
|
+
fi
|
|
109
|
+
|
|
110
|
+
[ -x "$dest/bin/cicy-code" ] || { log "incomplete runtime: missing bin/cicy-code"; exit 1; }
|
|
111
|
+
platform_installed || { log "incomplete runtime: missing $platform_pkg@$ver"; exit 1; }
|
|
112
|
+
log "v$ver runtime complete → repointing"
|
|
113
|
+
|
|
84
114
|
mkdir -p "$(dirname "$LINK")"
|
|
85
115
|
ln -sfn "$dest/bin/cicy-code" "$LINK"
|
|
86
116
|
# Keep the npm-global bin (first on PATH) following the canonical link too, so
|
package/src/sidecar/docker.js
CHANGED
|
@@ -17,6 +17,7 @@ const http = require("http");
|
|
|
17
17
|
const fs = require("fs");
|
|
18
18
|
const os = require("os");
|
|
19
19
|
const path = require("path");
|
|
20
|
+
const { classifyWslPrerequisites } = require("./wsl-prerequisites");
|
|
20
21
|
|
|
21
22
|
const IMAGE = process.env.CICY_DOCKER_IMAGE || "cicybot/cicy-code:latest";
|
|
22
23
|
// Image tarball on Aliyun OSS (oss-cn-shanghai, public-read) — CN-domestic and
|
|
@@ -660,7 +661,12 @@ async function dismEnableFeature(feature, label, { emit } = {}) {
|
|
|
660
661
|
// features are verified-enabled (a Windows reboot is then needed before Docker
|
|
661
662
|
// can use WSL2), or { failed } if a feature couldn't be enabled.
|
|
662
663
|
async function ensureWsl({ emit } = {}) {
|
|
663
|
-
|
|
664
|
+
const executableMissing = await wslMissing();
|
|
665
|
+
const subsystemEnabled = await featureEnabled("Microsoft-Windows-Subsystem-Linux");
|
|
666
|
+
const vmPlatformEnabled = await featureEnabled("VirtualMachinePlatform");
|
|
667
|
+
const state = classifyWslPrerequisites({ executableMissing, subsystemEnabled, vmPlatformEnabled });
|
|
668
|
+
if (state.ready) return { ok: true };
|
|
669
|
+
|
|
664
670
|
emit && emit({ phase: "install-docker", status: "running", message: "Docker 需要 WSL2 后端,开始启用所需的 Windows 功能…" });
|
|
665
671
|
const a = await dismEnableFeature("Microsoft-Windows-Subsystem-Linux", "启用 WSL 功能 1/2 · Linux 子系统", { emit });
|
|
666
672
|
const b = await dismEnableFeature("VirtualMachinePlatform", "启用 WSL 功能 2/2 · 虚拟机平台", { emit });
|
|
@@ -668,10 +674,12 @@ async function ensureWsl({ emit } = {}) {
|
|
|
668
674
|
emit && emit({ phase: "done", status: "error", message: "WSL 功能未能全部启用——请点「重试」" });
|
|
669
675
|
return { ok: false, needsReboot: false, failed: true };
|
|
670
676
|
}
|
|
671
|
-
// Best-effort: also pull the WSL2 kernel/plumbing
|
|
672
|
-
//
|
|
673
|
-
|
|
674
|
-
|
|
677
|
+
// Best-effort: also pull the WSL2 kernel/plumbing when the executable itself
|
|
678
|
+
// is missing. Feature-only repairs must stop here and wait for reboot.
|
|
679
|
+
if (state.installWsl) {
|
|
680
|
+
await launchElevated("wsl", ["--install", "--no-distribution"], { emit }).catch(() => {});
|
|
681
|
+
}
|
|
682
|
+
emit && emit({ phase: "install-docker", status: "running", message: "WSL2 功能已启用 ✓,请【重启 Windows】;重启后回来点「重试」继续。" });
|
|
675
683
|
return { ok: false, needsReboot: true };
|
|
676
684
|
}
|
|
677
685
|
|
|
@@ -21,6 +21,7 @@ const fs = require("fs");
|
|
|
21
21
|
const docker = require("./docker"); // shared: downloads, waitUntil, probeHealth, launchElevated, ensureWsl…
|
|
22
22
|
const log = require("electron-log"); // persisted main.log — bootstrap timing/failures land here
|
|
23
23
|
const { t } = require("../i18n"); // 打开/读 token 的可见日志走 i18n
|
|
24
|
+
const { shouldSkipCicyUpdate } = require("./cicy-runtime-health");
|
|
24
25
|
|
|
25
26
|
// Dedicated distro name — NEVER reuse/clobber a user's own "Ubuntu" distro.
|
|
26
27
|
const DISTRO = process.env.CICY_WSL_DISTRO || "cicy-code-wsl";
|
|
@@ -929,11 +930,10 @@ async function _bootstrap({ onProgress, port = 8008, container = "cicy-code-dock
|
|
|
929
930
|
|
|
930
931
|
// 1) WSL2 platform
|
|
931
932
|
begin("ensure-wsl");
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
} else done(true);
|
|
933
|
+
const w = await docker.ensureWsl({ emit });
|
|
934
|
+
if (w.needsReboot) { fail("wsl_reboot_required"); emit({ phase: "done", status: "reboot", message: "WSL2 所需功能已启用——请【重启 Windows】后回来点「重试」继续。" }); finish(false, "wsl_reboot_required"); return { ok: false, reason: "wsl_reboot_required" }; }
|
|
935
|
+
if (!w.ok) { fail("wsl_enable_failed"); finish(false, "wsl_enable_failed"); return { ok: false, reason: "wsl_enable_failed" }; }
|
|
936
|
+
done(true);
|
|
937
937
|
|
|
938
938
|
// 2) Ubuntu distro
|
|
939
939
|
begin("ensure-distro");
|
|
@@ -986,13 +986,36 @@ async function _bootstrap({ onProgress, port = 8008, container = "cicy-code-dock
|
|
|
986
986
|
done();
|
|
987
987
|
} else done(true);
|
|
988
988
|
|
|
989
|
-
// 7) Health — the ONLY path to ok:true.
|
|
989
|
+
// 7) Health — the ONLY path to ok:true. A short failed probe triggers the
|
|
990
|
+
// runtime updater once: it repairs the common partial-install state where the
|
|
991
|
+
// JS launcher exists but cicy-code-linux-<arch> is missing from the volume.
|
|
990
992
|
begin("wait-health");
|
|
991
993
|
emit({ phase: "container", status: "running", message: "等待 cicy-code 就绪…" });
|
|
992
|
-
|
|
994
|
+
let healthy = await docker.waitUntil(() => probeHealth(port), { totalMs: 15000, everyMs: 3000 });
|
|
995
|
+
if (!healthy) {
|
|
996
|
+
emit({ phase: "container", status: "running", message: "服务尚未响应,正在检查并修复运行时完整性…" });
|
|
997
|
+
try {
|
|
998
|
+
const repaired = await update({ onProgress: emit, container, port });
|
|
999
|
+
healthy = Boolean(repaired && repaired.ok && await probeHealth(port));
|
|
1000
|
+
} catch (e) {
|
|
1001
|
+
log.warn(`[bootstrap] runtime auto-repair failed: ${e.message}`);
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
if (!healthy) healthy = await docker.waitUntil(() => probeHealth(port), { totalMs: 105000, everyMs: 3000 });
|
|
993
1005
|
if (healthy) { done(); await ensureAutostart(); await ensureDesktopShortcut(volume, port); } // survive reboot + desktop shortcut
|
|
994
1006
|
else fail("health_timeout");
|
|
995
|
-
|
|
1007
|
+
let healthMessage = "Docker cicy-code 已就绪 🎉";
|
|
1008
|
+
if (!healthy) {
|
|
1009
|
+
let tail = "";
|
|
1010
|
+
try {
|
|
1011
|
+
const r = await wslRun(`docker logs --tail 60 ${container}`, { timeout: 15000 });
|
|
1012
|
+
tail = String((r.stderr || "") + (r.stdout || "")).trim().slice(-1200);
|
|
1013
|
+
} catch (e) {
|
|
1014
|
+
tail = String((e && (e.stderr || e.stdout)) || "").trim().slice(-1200);
|
|
1015
|
+
}
|
|
1016
|
+
healthMessage = `服务未能响应 :${port}。${tail ? `\n\n容器日志:\n${tail}` : "请点「重试」。"}`;
|
|
1017
|
+
}
|
|
1018
|
+
emit({ phase: healthy ? "done" : "container", status: healthy ? "done" : "error", message: healthMessage });
|
|
996
1019
|
finish(healthy, healthy ? null : "health_timeout");
|
|
997
1020
|
return { ok: healthy, container };
|
|
998
1021
|
}
|
|
@@ -1061,6 +1084,18 @@ async function installedCicyVersion(container) {
|
|
|
1061
1084
|
} catch { return null; }
|
|
1062
1085
|
}
|
|
1063
1086
|
|
|
1087
|
+
// True only when the launcher AND this Linux architecture's optional binary
|
|
1088
|
+
// package are present. The version pointer alone is insufficient: npm can leave
|
|
1089
|
+
// a partial global install that has bin/cicy-code but cannot start.
|
|
1090
|
+
async function cicyRuntimePlatformReady(container) {
|
|
1091
|
+
const check = `target=$(readlink -f "$HOME/.local/bin/cicy-code" 2>/dev/null || true); ` +
|
|
1092
|
+
`dest=$(dirname "$(dirname "$target")" 2>/dev/null); ` +
|
|
1093
|
+
`case "$(uname -m)" in x86_64) p=cicy-code-linux-x64;; aarch64|arm64) p=cicy-code-linux-arm64;; *) exit 1;; esac; ` +
|
|
1094
|
+
`[ -x "$target" ] && { [ -f "$dest/lib/node_modules/$p/package.json" ] || [ -f "$dest/lib/node_modules/cicy-code/node_modules/$p/package.json" ]; }`;
|
|
1095
|
+
try { await wslRun(`docker exec ${container} bash -lc '${check}'`, { timeout: 15000 }); return true; }
|
|
1096
|
+
catch { return false; }
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1064
1099
|
// Update cicy-code IN PLACE. SMART path (避免容器内 npm view 卡 ~2min):
|
|
1065
1100
|
// 1. 在宿主机解析最新版本号(host 网络,不过容器代理/DNS)
|
|
1066
1101
|
// 2. 和容器里已装版本比对——一样就直接「已是最新」,根本不 docker exec
|
|
@@ -1080,10 +1115,12 @@ async function update({ onProgress, container = "cicy-code-docker", port = 8008
|
|
|
1080
1115
|
emit({ phase: "image", status: "running", message: t("docker.updating.checking") });
|
|
1081
1116
|
let net = "unknown";
|
|
1082
1117
|
try { net = await require("./net-detect").detect(); } catch {}
|
|
1083
|
-
const [latest, current] = await Promise.all([
|
|
1084
|
-
|
|
1118
|
+
const [latest, current, platformReady] = await Promise.all([
|
|
1119
|
+
resolveLatestCicy(net), installedCicyVersion(container), cicyRuntimePlatformReady(container),
|
|
1120
|
+
]);
|
|
1121
|
+
log.info(`[wsl-docker] update: net=${net} latest=${latest} current=${current} platformReady=${platformReady}`);
|
|
1085
1122
|
// 2) 已是最新 → 秒回,根本不进容器装(你点更新等 2 分钟的就是这种「其实已最新」的空跑)。
|
|
1086
|
-
if (latest
|
|
1123
|
+
if (shouldSkipCicyUpdate({ latest, current, platformReady })) {
|
|
1087
1124
|
emit({ phase: "done", status: "done", message: t("docker.updating.alreadyLatest", { v: current }) });
|
|
1088
1125
|
return { ok: true, alreadyLatest: true, version: current };
|
|
1089
1126
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// Copyright 2026 CiCy AI
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
function classifyWslPrerequisites({ executableMissing, subsystemEnabled, vmPlatformEnabled }) {
|
|
5
|
+
const featuresReady = subsystemEnabled && vmPlatformEnabled;
|
|
6
|
+
const ready = !executableMissing && featuresReady;
|
|
7
|
+
return {
|
|
8
|
+
ready,
|
|
9
|
+
enableFeatures: !featuresReady,
|
|
10
|
+
installWsl: executableMissing,
|
|
11
|
+
needsReboot: !ready,
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
module.exports = { classifyWslPrerequisites };
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
const test = require("node:test");
|
|
2
|
+
const assert = require("node:assert/strict");
|
|
3
|
+
const fs = require("node:fs");
|
|
4
|
+
const os = require("node:os");
|
|
5
|
+
const path = require("node:path");
|
|
6
|
+
const { spawnSync } = require("node:child_process");
|
|
7
|
+
|
|
8
|
+
const script = path.join(__dirname, "..", "src", "sidecar", "container-scripts", "cicy-code-update.sh");
|
|
9
|
+
|
|
10
|
+
function writeExecutable(file, content) {
|
|
11
|
+
fs.writeFileSync(file, content, { mode: 0o755 });
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function runUpdate({ complete = false } = {}) {
|
|
15
|
+
const root = fs.mkdtempSync(path.join(os.tmpdir(), "cicy-code-update-"));
|
|
16
|
+
const home = path.join(root, "home");
|
|
17
|
+
const store = path.join(root, "store");
|
|
18
|
+
const bin = path.join(root, "bin");
|
|
19
|
+
const dest = path.join(store, "2.3.563");
|
|
20
|
+
fs.mkdirSync(path.join(dest, "bin"), { recursive: true });
|
|
21
|
+
fs.mkdirSync(bin, { recursive: true });
|
|
22
|
+
writeExecutable(path.join(dest, "bin", "cicy-code"), "#!/bin/sh\nexit 0\n");
|
|
23
|
+
if (complete) {
|
|
24
|
+
fs.mkdirSync(path.join(dest, "lib", "node_modules", "cicy-code-linux-x64"), { recursive: true });
|
|
25
|
+
fs.writeFileSync(path.join(dest, "lib", "node_modules", "cicy-code-linux-x64", "package.json"), "{}\n");
|
|
26
|
+
}
|
|
27
|
+
writeExecutable(path.join(bin, "supervisorctl"), "#!/bin/sh\nexit 1\n");
|
|
28
|
+
writeExecutable(path.join(bin, "npm"), `#!/bin/sh
|
|
29
|
+
set -eu
|
|
30
|
+
printf '%s\\n' "$*" >> "${root}/npm.log"
|
|
31
|
+
prefix=""
|
|
32
|
+
prev=""
|
|
33
|
+
for arg in "$@"; do
|
|
34
|
+
if [ "$prev" = "--prefix" ]; then prefix="$arg"; break; fi
|
|
35
|
+
prev="$arg"
|
|
36
|
+
done
|
|
37
|
+
[ -n "$prefix" ]
|
|
38
|
+
mkdir -p "$prefix/lib/node_modules/cicy-code-linux-x64"
|
|
39
|
+
printf '{}\\n' > "$prefix/lib/node_modules/cicy-code-linux-x64/package.json"
|
|
40
|
+
`);
|
|
41
|
+
const result = spawnSync("bash", [script, "2.3.563"], {
|
|
42
|
+
encoding: "utf8",
|
|
43
|
+
env: {
|
|
44
|
+
...process.env,
|
|
45
|
+
HOME: home,
|
|
46
|
+
CICY_CODE_STORE: store,
|
|
47
|
+
NPM_REGISTRY: "https://registry.npmjs.org",
|
|
48
|
+
PATH: `${bin}:${process.env.PATH}`,
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
return {
|
|
52
|
+
...result,
|
|
53
|
+
platformPackage: path.join(dest, "lib", "node_modules", "cicy-code-linux-x64", "package.json"),
|
|
54
|
+
npmLog: fs.existsSync(path.join(root, "npm.log")) ? fs.readFileSync(path.join(root, "npm.log"), "utf8") : "",
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
test("repairs an existing cicy-code runtime when its linux-x64 package is missing", () => {
|
|
59
|
+
const result = runUpdate();
|
|
60
|
+
assert.equal(result.status, 0, result.stderr);
|
|
61
|
+
assert.equal(fs.existsSync(result.platformPackage), true);
|
|
62
|
+
assert.match(result.npmLog, /cicy-code-linux-x64@2\.3\.563/);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test("does not reinstall an existing runtime when its platform package is present", () => {
|
|
66
|
+
const result = runUpdate({ complete: true });
|
|
67
|
+
assert.equal(result.status, 0, result.stderr);
|
|
68
|
+
assert.equal(result.npmLog, "");
|
|
69
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
const test = require("node:test");
|
|
2
|
+
const assert = require("node:assert/strict");
|
|
3
|
+
|
|
4
|
+
const { shouldSkipCicyUpdate } = require("../src/sidecar/cicy-runtime-health");
|
|
5
|
+
|
|
6
|
+
test("does not skip an update when the recorded version matches but the platform package is missing", () => {
|
|
7
|
+
assert.equal(shouldSkipCicyUpdate({ latest: "2.3.563", current: "2.3.563", platformReady: false }), false);
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
test("skips an update only when the recorded version matches and the runtime is complete", () => {
|
|
11
|
+
assert.equal(shouldSkipCicyUpdate({ latest: "2.3.563", current: "2.3.563", platformReady: true }), true);
|
|
12
|
+
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
const test = require("node:test");
|
|
2
|
+
const assert = require("node:assert/strict");
|
|
3
|
+
|
|
4
|
+
const { classifyWslPrerequisites } = require("../src/sidecar/wsl-prerequisites");
|
|
5
|
+
|
|
6
|
+
test("requires feature enablement and reboot when wsl.exe exists but Windows features are disabled", () => {
|
|
7
|
+
assert.deepEqual(
|
|
8
|
+
classifyWslPrerequisites({
|
|
9
|
+
executableMissing: false,
|
|
10
|
+
subsystemEnabled: false,
|
|
11
|
+
vmPlatformEnabled: false,
|
|
12
|
+
}),
|
|
13
|
+
{ ready: false, enableFeatures: true, installWsl: false, needsReboot: true },
|
|
14
|
+
);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test("is ready only when wsl.exe and both Windows features are available", () => {
|
|
18
|
+
assert.deepEqual(
|
|
19
|
+
classifyWslPrerequisites({
|
|
20
|
+
executableMissing: false,
|
|
21
|
+
subsystemEnabled: true,
|
|
22
|
+
vmPlatformEnabled: true,
|
|
23
|
+
}),
|
|
24
|
+
{ ready: true, enableFeatures: false, installWsl: false, needsReboot: false },
|
|
25
|
+
);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test("installs WSL and enables features when wsl.exe is missing", () => {
|
|
29
|
+
assert.deepEqual(
|
|
30
|
+
classifyWslPrerequisites({
|
|
31
|
+
executableMissing: true,
|
|
32
|
+
subsystemEnabled: false,
|
|
33
|
+
vmPlatformEnabled: false,
|
|
34
|
+
}),
|
|
35
|
+
{ ready: false, enableFeatures: true, installWsl: true, needsReboot: true },
|
|
36
|
+
);
|
|
37
|
+
});
|