cicy-desktop 2.1.277 → 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 +1 -1
- package/src/sidecar/cicy-runtime-health.js +8 -0
- package/src/sidecar/container-scripts/cicy-code-update.sh +35 -5
- package/src/sidecar/wsl-docker.js +44 -6
- package/test/cicy-code-update.test.js +69 -0
- package/test/cicy-runtime-health.test.js +12 -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
|
@@ -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
|
|
@@ -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";
|
|
@@ -985,13 +986,36 @@ async function _bootstrap({ onProgress, port = 8008, container = "cicy-code-dock
|
|
|
985
986
|
done();
|
|
986
987
|
} else done(true);
|
|
987
988
|
|
|
988
|
-
// 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.
|
|
989
992
|
begin("wait-health");
|
|
990
993
|
emit({ phase: "container", status: "running", message: "等待 cicy-code 就绪…" });
|
|
991
|
-
|
|
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 });
|
|
992
1005
|
if (healthy) { done(); await ensureAutostart(); await ensureDesktopShortcut(volume, port); } // survive reboot + desktop shortcut
|
|
993
1006
|
else fail("health_timeout");
|
|
994
|
-
|
|
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 });
|
|
995
1019
|
finish(healthy, healthy ? null : "health_timeout");
|
|
996
1020
|
return { ok: healthy, container };
|
|
997
1021
|
}
|
|
@@ -1060,6 +1084,18 @@ async function installedCicyVersion(container) {
|
|
|
1060
1084
|
} catch { return null; }
|
|
1061
1085
|
}
|
|
1062
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
|
+
|
|
1063
1099
|
// Update cicy-code IN PLACE. SMART path (避免容器内 npm view 卡 ~2min):
|
|
1064
1100
|
// 1. 在宿主机解析最新版本号(host 网络,不过容器代理/DNS)
|
|
1065
1101
|
// 2. 和容器里已装版本比对——一样就直接「已是最新」,根本不 docker exec
|
|
@@ -1079,10 +1115,12 @@ async function update({ onProgress, container = "cicy-code-docker", port = 8008
|
|
|
1079
1115
|
emit({ phase: "image", status: "running", message: t("docker.updating.checking") });
|
|
1080
1116
|
let net = "unknown";
|
|
1081
1117
|
try { net = await require("./net-detect").detect(); } catch {}
|
|
1082
|
-
const [latest, current] = await Promise.all([
|
|
1083
|
-
|
|
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}`);
|
|
1084
1122
|
// 2) 已是最新 → 秒回,根本不进容器装(你点更新等 2 分钟的就是这种「其实已最新」的空跑)。
|
|
1085
|
-
if (latest
|
|
1123
|
+
if (shouldSkipCicyUpdate({ latest, current, platformReady })) {
|
|
1086
1124
|
emit({ phase: "done", status: "done", message: t("docker.updating.alreadyLatest", { v: current }) });
|
|
1087
1125
|
return { ok: true, alreadyLatest: true, version: current };
|
|
1088
1126
|
}
|
|
@@ -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
|
+
});
|