aaden-cli 1.6.118 → 1.6.121-rc.0
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/AadenDeploy/aadensetup.sh +2 -0
- package/AadenDeploy/appCenterSync.sh +63 -30
- package/AadenDeploy/farmRange.sh +20 -0
- package/AadenDeploy/setDeviceId.sh +1 -0
- package/dist/index.js +29 -7
- package/index.js +5 -6
- package/modules/snap-version.js +16 -0
- package/package.json +2 -2
- package/test/snap-version.test.js +24 -0
- package/test.js +0 -6
|
@@ -32,6 +32,8 @@ DEVICE_ID="$1"
|
|
|
32
32
|
if [ -z "$DEVICE_ID" ]; then
|
|
33
33
|
read -r -p '设备号: ' DEVICE_ID
|
|
34
34
|
fi
|
|
35
|
+
# 测试门店号段先拦(不用联网),再查云端撞号
|
|
36
|
+
source "$DIR/farmRange.sh" && farm_range_guard "$DEVICE_ID" || exit 1
|
|
35
37
|
bash "$DIR/checkDeviceId.sh" "$DEVICE_ID" || exit 1
|
|
36
38
|
DEVICE_ID=$((10#$DEVICE_ID))
|
|
37
39
|
export AADEN_SKIP_DEVICE_CHECK=1 # 后面 set-device-id 不再问第二遍
|
|
@@ -4,6 +4,10 @@
|
|
|
4
4
|
#
|
|
5
5
|
# light-update 只发 PHP/,AppCenter 以前只能跟 465MB 的 latest.zip 走;这里单独按应用同步。
|
|
6
6
|
# - 灰度门:manifest 的 rollout = {"devices":[...], "percent":N},本机在名单里或 deviceId%100 < N 才动;没有 rollout = 全量
|
|
7
|
+
# - 根目录应用:manifest 顶层 rootApps 里的应用装到 docroot 根(/var/www/html/<App>,如老的 /App),其余逻辑同 apps;
|
|
8
|
+
# 只认白名单 APPCENTER_ROOT_APPS(默认 "App"),防止 manifest 写错把 PHP 之类的目录换掉。老 CLI 只读 apps,不受影响
|
|
9
|
+
# - 按应用灰度:manifest 带 perAppRollout=true 时,每个应用按自己的 apps.<App>.rollout 判断(没有 = 全量),
|
|
10
|
+
# 顶层 rollout 只管 ensureNoCache;老版本 CLI 不认 perAppRollout,照旧整体按顶层 rollout
|
|
7
11
|
# - ensureNoCache:manifest 打开时先确保 AppCenter 的 index.html 带 no-cache(ensureAppCenterNoCache.sh)
|
|
8
12
|
# - manifest 门:本机 .appcenter-manifest 的 sha256 == manifest 的 treeSha256 且 `sha256sum -c` 通过 → 什么都不做,
|
|
9
13
|
# 每天只花一次几 KB 的请求。被 latest.zip 全量覆盖过、文件对不上 → 校验失败 → 重新铺一遍(自愈)
|
|
@@ -16,15 +20,18 @@
|
|
|
16
20
|
# 用法: appCenterSync.sh [channel]
|
|
17
21
|
# APPCENTER_BASE 产物根地址,默认 S3;测试可用 file:///var/tmp/...(curl 都支持)
|
|
18
22
|
# APPCENTER_APPS 只同步这些应用(空格分隔);不传 = manifest 里列出的全部应用,发哪些由发布端决定
|
|
23
|
+
# APPCENTER_ROOT_APPS 允许同步到 docroot 根的应用白名单(空格分隔),默认 "App"
|
|
19
24
|
# APPCENTER_DEVICE_ID 本机 deviceId(CLI 会传);不传就问本机 PHP
|
|
20
25
|
# APPCENTER_RESULT 结果行另存的文件(CLI 会传)
|
|
21
26
|
# 结果行(同时写进 $RESULT 给 CLI 上报):APP_OK <App> <sha12> | APP_UP_TO_DATE <App> | APP_SKIP <App> <原因> |
|
|
22
|
-
# APP_ROLLED_BACK <App> | APP_ABORT_* <App> | NOCACHE_* | SKIP_ROLLOUT | SKIP_DEPS | SKIP_LOCKED | ABORT_*
|
|
27
|
+
# APP_ROLLED_BACK <App> | APP_ABORT_* <App> | APP_SKIP_ROLLOUT <App> | NOCACHE_* | SKIP_ROLLOUT | SKIP_DEPS | SKIP_LOCKED | ABORT_*
|
|
23
28
|
# 退出码:0=全部成功、无需更新或不在灰度内;1=有失败(失败的已回滚或未动线上目录)
|
|
24
29
|
set -uo pipefail
|
|
25
30
|
BASE="${APPCENTER_BASE:-https://aaden-backend.s3.eu-central-1.amazonaws.com/appcenter}"
|
|
26
31
|
CHANNEL="${1:-stable}"
|
|
27
|
-
|
|
32
|
+
DOCROOT=/var/www/html
|
|
33
|
+
WEB=$DOCROOT/AppCenter
|
|
34
|
+
ROOT_ALLOW=" ${APPCENTER_ROOT_APPS:-App} "
|
|
28
35
|
STAGE=/var/tmp/aaden-appcenter-stage
|
|
29
36
|
BACKUP=/var/lib/aaden-appcenter-backup
|
|
30
37
|
LOCK=/var/tmp/aaden-appcenter.lock
|
|
@@ -48,21 +55,31 @@ flock -n 9 || { res "SKIP_LOCKED"; exit 0; }
|
|
|
48
55
|
|
|
49
56
|
MANIFEST=$(curl -fsSL --max-time 30 "$BASE/$CHANNEL/manifest.json") \
|
|
50
57
|
|| { echo "取不到 $BASE/$CHANNEL/manifest.json"; res "ABORT_MANIFEST"; exit 1; }
|
|
51
|
-
# 读 manifest:mf apps.Admin.zip / mf ensureNoCache / mf eligible <deviceId>
|
|
58
|
+
# 读 manifest:mf apps.Admin.zip / mf ensureNoCache / mf eligible <deviceId> / mf eligible_app <App> <deviceId> [apps|rootApps]
|
|
52
59
|
mf(){
|
|
53
60
|
printf '%s' "$MANIFEST" | python3 -c '
|
|
54
61
|
import json, sys
|
|
55
62
|
m = json.load(sys.stdin)
|
|
56
|
-
|
|
57
|
-
r = m.get("rollout")
|
|
63
|
+
def gate(r, dev):
|
|
58
64
|
if r is None:
|
|
59
|
-
|
|
65
|
+
return "yes"
|
|
60
66
|
try:
|
|
61
|
-
d = int(
|
|
67
|
+
d = int(dev)
|
|
62
68
|
except ValueError:
|
|
63
|
-
|
|
69
|
+
return "no"
|
|
64
70
|
ok = d in [int(x) for x in r.get("devices", [])] or d % 100 < int(r.get("percent", 0))
|
|
65
|
-
|
|
71
|
+
return "yes" if ok else "no"
|
|
72
|
+
if sys.argv[1] == "eligible":
|
|
73
|
+
print(gate(m.get("rollout"), sys.argv[2])); sys.exit()
|
|
74
|
+
if sys.argv[1] == "eligible_app":
|
|
75
|
+
# eligible_app <App> <deviceId> [apps|rootApps]
|
|
76
|
+
kind = sys.argv[4] if len(sys.argv) > 4 else "apps"
|
|
77
|
+
if m.get("perAppRollout") is True:
|
|
78
|
+
app = (m.get(kind) or {}).get(sys.argv[2]) or {}
|
|
79
|
+
print(gate(app.get("rollout"), sys.argv[3]))
|
|
80
|
+
else:
|
|
81
|
+
print(gate(m.get("rollout"), sys.argv[3]))
|
|
82
|
+
sys.exit()
|
|
66
83
|
v = m
|
|
67
84
|
for k in sys.argv[1].split("."):
|
|
68
85
|
v = v.get(k) if isinstance(v, dict) else None
|
|
@@ -76,13 +93,14 @@ if [ -z "$DEVICE_ID" ] || [ "$DEVICE_ID" = "-1" ]; then
|
|
|
76
93
|
DEVICE_ID=$(curl -s --max-time 10 "http://localhost/PHP/AccessLog.php?op=deviceId" \
|
|
77
94
|
| python3 -c 'import json,sys; print(json.load(sys.stdin).get("content",""))' 2>/dev/null)
|
|
78
95
|
fi
|
|
79
|
-
|
|
96
|
+
TOP_ELIGIBLE=$(mf eligible "${DEVICE_ID:-x}")
|
|
97
|
+
if [ "$TOP_ELIGIBLE" != "yes" ] && [ "$(mf perAppRollout)" != "true" ]; then
|
|
80
98
|
res "SKIP_ROLLOUT device=${DEVICE_ID:-?}"; exit 0
|
|
81
99
|
fi
|
|
82
100
|
|
|
83
101
|
FAILED=0
|
|
84
102
|
# ---- index.html no-cache(先于换前端:让之后拿到的 index.html 都不再被启发式缓存)----
|
|
85
|
-
if [ "$(mf ensureNoCache)" = "true" ]; then
|
|
103
|
+
if [ "$TOP_ELIGIBLE" = "yes" ] && [ "$(mf ensureNoCache)" = "true" ]; then
|
|
86
104
|
NC=$(bash "$HERE/ensureAppCenterNoCache.sh"); NC_CODE=$?
|
|
87
105
|
printf '%s\n' "$NC" | sed '$d'
|
|
88
106
|
res "$(printf '%s\n' "$NC" | tail -1)"
|
|
@@ -98,25 +116,32 @@ tree_ok(){ # $1=目录 $2=期望 treeSha256
|
|
|
98
116
|
}
|
|
99
117
|
|
|
100
118
|
sync_app(){
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
if [
|
|
119
|
+
# $1=应用名 $2=apps(AppCenter/<App>,默认)或 rootApps(docroot 根 /<App>)
|
|
120
|
+
local APP="$1" KIND="${2:-apps}" LIVE NEW BAK URLP TAG TREE ZIP ZSHA
|
|
121
|
+
if [ "$KIND" = "rootApps" ]; then
|
|
122
|
+
LIVE="$DOCROOT/$APP"; NEW="$STAGE/root-$APP"; BAK="root-$APP"; URLP="$APP"; TAG="/$APP"
|
|
123
|
+
else
|
|
124
|
+
LIVE="$WEB/$APP"; NEW="$STAGE/$APP"; BAK="$APP"; URLP="AppCenter/$APP"; TAG="$APP"
|
|
125
|
+
fi
|
|
126
|
+
TREE=$(mf "$KIND.$APP.treeSha256"); ZIP=$(mf "$KIND.$APP.zip"); ZSHA=$(mf "$KIND.$APP.zipSha256")
|
|
127
|
+
if [ -z "$TREE" ]; then res "APP_SKIP $TAG 不在manifest里"; return 0; fi
|
|
128
|
+
if [ "$(mf eligible_app "$APP" "${DEVICE_ID:-x}" "$KIND")" != "yes" ]; then res "APP_SKIP_ROLLOUT $TAG"; return 0; fi
|
|
104
129
|
# manifest 来自网络:只接受预期格式,防止路径穿越
|
|
105
130
|
if ! [[ "$TREE" =~ ^[0-9a-f]{64}$ && "$ZSHA" =~ ^[0-9a-f]{64}$ && "$ZIP" =~ ^blobs/[A-Za-z0-9._-]+\.zip$ ]]; then
|
|
106
|
-
res "APP_ABORT_BAD_MANIFEST $
|
|
131
|
+
res "APP_ABORT_BAD_MANIFEST $TAG"; return 1
|
|
107
132
|
fi
|
|
108
133
|
|
|
109
134
|
# ---- manifest 门 ----
|
|
110
|
-
if tree_ok "$LIVE" "$TREE"; then res "APP_UP_TO_DATE $
|
|
111
|
-
echo "$
|
|
135
|
+
if tree_ok "$LIVE" "$TREE"; then res "APP_UP_TO_DATE $TAG"; return 0; fi
|
|
136
|
+
echo "$TAG: 本机与 manifest 不一致 → 同步到 ${TREE:0:12}"
|
|
112
137
|
|
|
113
138
|
# ---- 下载 + 校验(此时还没动线上目录)----
|
|
114
139
|
sudoS "rm -rf '$NEW' '$NEW.zip' '$NEW.failed'"
|
|
115
|
-
curl -fsSL --max-time 1200 "$BASE/$ZIP" -o "$NEW.zip" || { res "APP_ABORT_DOWNLOAD $
|
|
116
|
-
[ "$(sha256sum "$NEW.zip" | cut -d' ' -f1)" = "$ZSHA" ] || { res "APP_ABORT_CHECKSUM $
|
|
140
|
+
curl -fsSL --max-time 1200 "$BASE/$ZIP" -o "$NEW.zip" || { res "APP_ABORT_DOWNLOAD $TAG"; return 1; }
|
|
141
|
+
[ "$(sha256sum "$NEW.zip" | cut -d' ' -f1)" = "$ZSHA" ] || { res "APP_ABORT_CHECKSUM $TAG"; rm -f "$NEW.zip"; return 1; }
|
|
117
142
|
mkdir -p "$NEW" && unzip -q -o "$NEW.zip" -d "$NEW"; rm -f "$NEW.zip"
|
|
118
143
|
if ! { [ -f "$NEW/index.html" ] && tree_ok "$NEW" "$TREE"; }; then
|
|
119
|
-
res "APP_ABORT_BAD_ARTIFACT $
|
|
144
|
+
res "APP_ABORT_BAD_ARTIFACT $TAG"; rm -rf "$NEW"; return 1
|
|
120
145
|
fi
|
|
121
146
|
# zip 里带的是打包时的旧 mtime;浏览器按 Last-Modified 的年龄启发式缓存,所以改成现在(只改 mtime 不改内容,不影响校验)
|
|
122
147
|
touch "$NEW/index.html"
|
|
@@ -125,7 +150,7 @@ sync_app(){
|
|
|
125
150
|
local CARRIED=0
|
|
126
151
|
if [ -d "$LIVE" ]; then
|
|
127
152
|
python3 "$HERE/appcenterRefs.py" "$LIVE" | LC_ALL=C sort > "$NEW.old" \
|
|
128
|
-
|| { res "APP_ABORT_REFS $
|
|
153
|
+
|| { res "APP_ABORT_REFS $TAG"; rm -rf "$NEW" "$NEW.old"; return 1; }
|
|
129
154
|
paths "$NEW/.appcenter-manifest" > "$NEW.new"
|
|
130
155
|
LC_ALL=C comm -23 "$NEW.old" "$NEW.new" > "$NEW.carry"
|
|
131
156
|
CARRIED=$(wc -l < "$NEW.carry" | tr -d ' ')
|
|
@@ -133,34 +158,42 @@ sync_app(){
|
|
|
133
158
|
[ "$CARRIED" -gt 0 ] && tar -C "$LIVE" -cf - -T "$NEW.carry" | tar -C "$NEW" -xf -
|
|
134
159
|
rm -f "$NEW.old" "$NEW.new" "$NEW.carry"
|
|
135
160
|
fi
|
|
136
|
-
echo "$
|
|
161
|
+
echo "$TAG: 保留上一代文件 $CARRIED 个"
|
|
137
162
|
|
|
138
163
|
# ---- 替换:两次 rename,中间只有毫秒级空窗 ----
|
|
139
|
-
sudoS "mkdir -p '$BACKUP' && rm -rf '$BACKUP/$
|
|
140
|
-
[ -d "$LIVE" ] && sudoS "mv '$LIVE' '$BACKUP/$
|
|
164
|
+
sudoS "mkdir -p '$BACKUP' && rm -rf '$BACKUP/$BAK' && chown -R www-data:www-data '$NEW'"
|
|
165
|
+
[ -d "$LIVE" ] && sudoS "mv '$LIVE' '$BACKUP/$BAK'"
|
|
141
166
|
sudoS "mv '$NEW' '$LIVE'"
|
|
142
167
|
|
|
143
168
|
# ---- 健康检查:首页、入口 js、文件校验 ----
|
|
144
169
|
local HEALTHY=1 MAIN c
|
|
145
170
|
MAIN=$(grep -oE 'src=["'"'"']?[^"'"'"' >]+\.js' "$LIVE/index.html" | grep -vE '//' | head -1 | sed -E 's/^src=["'"'"']?(\.\/)?//')
|
|
146
171
|
for p in "" "$MAIN"; do
|
|
147
|
-
c=$(curl -s -o /dev/null -w '%{http_code}' "http://localhost
|
|
148
|
-
echo " 健康检查
|
|
172
|
+
c=$(curl -s -o /dev/null -w '%{http_code}' "http://localhost/$URLP/$p" --max-time 15)
|
|
173
|
+
echo " 健康检查 /$URLP/$p -> $c"; [ "$c" = "200" ] || HEALTHY=0
|
|
149
174
|
done
|
|
150
175
|
tree_ok "$LIVE" "$TREE" || { echo " 线上目录校验失败"; HEALTHY=0; }
|
|
151
176
|
|
|
152
177
|
if [ "$HEALTHY" != "1" ]; then
|
|
153
|
-
sudoS "mv '$LIVE' '$NEW.failed'; [ -d '$BACKUP/$
|
|
154
|
-
res "APP_ROLLED_BACK $
|
|
178
|
+
sudoS "mv '$LIVE' '$NEW.failed'; [ -d '$BACKUP/$BAK' ] && mv '$BACKUP/$BAK' '$LIVE'"
|
|
179
|
+
res "APP_ROLLED_BACK $TAG"; return 1
|
|
155
180
|
fi
|
|
156
|
-
res "APP_OK $
|
|
181
|
+
res "APP_OK $TAG ${TREE:0:12} carried=$CARRIED"
|
|
157
182
|
}
|
|
158
183
|
|
|
159
184
|
# 不指定就同步 manifest 里的全部应用(营业中一直开着的 App/KDS 等,发布端先别放进 manifest)
|
|
160
185
|
APPS="${APPCENTER_APPS:-$(printf '%s' "$MANIFEST" | python3 -c 'import json, sys; print(" ".join(sorted(json.load(sys.stdin).get("apps", {}))))')}"
|
|
161
186
|
sudoS "mkdir -p '$STAGE' && chown $(id -un):$(id -gn) '$STAGE'"
|
|
162
187
|
for APP in $APPS; do
|
|
188
|
+
[[ "$APP" == /* ]] && continue # APPCENTER_APPS 里 /<App> 指的是根目录应用,下面单独处理
|
|
163
189
|
if ! [[ "$APP" =~ ^[A-Za-z0-9_-]+$ ]]; then res "APP_SKIP $APP 名字不合法"; continue; fi
|
|
164
|
-
sync_app "$APP" || FAILED=1
|
|
190
|
+
sync_app "$APP" apps || FAILED=1
|
|
191
|
+
done
|
|
192
|
+
# 根目录应用(老的 /App 等):只同步白名单里的;APPCENTER_APPS 指定了就只按它过滤
|
|
193
|
+
ROOT_APPS=$(printf '%s' "$MANIFEST" | python3 -c 'import json, sys; print(" ".join(sorted(json.load(sys.stdin).get("rootApps") or {})))')
|
|
194
|
+
for APP in $ROOT_APPS; do
|
|
195
|
+
if ! [[ "$APP" =~ ^[A-Za-z0-9_-]+$ ]] || [[ "$ROOT_ALLOW" != *" $APP "* ]]; then res "APP_SKIP /$APP 不在根目录白名单"; continue; fi
|
|
196
|
+
if [ -n "${APPCENTER_APPS:-}" ] && [[ " $APPCENTER_APPS " != *" /$APP "* ]]; then continue; fi
|
|
197
|
+
sync_app "$APP" rootApps || FAILED=1
|
|
165
198
|
done
|
|
166
199
|
exit $FAILED
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# 测试门店农场号段:只有农场的容器测试店能用(它设 AADEN_FARM=1)。真机装机、改号拿到这些号就停下——
|
|
3
|
+
# 同一个号装两台机器,上报、备份、ngrok 会互相覆盖。
|
|
4
|
+
# ⚠ 要和云端的 TEST_STORE_RANGES(aaden.test-stores.ranges,默认 9020-9099)保持一致,改号段两边一起改。
|
|
5
|
+
# 用法:source 本文件,然后 farm_range_guard <设备号> || exit 1
|
|
6
|
+
FARM_RANGE_MIN=9020
|
|
7
|
+
FARM_RANGE_MAX=9099
|
|
8
|
+
|
|
9
|
+
# 号在号段里且没有 AADEN_FARM=1 → 打提示、返回 1;不是数字的交给后面的检查去拦
|
|
10
|
+
farm_range_guard() {
|
|
11
|
+
local id
|
|
12
|
+
id=$(printf '%s' "$1" | tr -d '[:space:]')
|
|
13
|
+
[[ "$id" =~ ^[0-9]+$ ]] || return 0
|
|
14
|
+
id=$((10#$id))
|
|
15
|
+
if [ "$id" -ge "$FARM_RANGE_MIN" ] && [ "$id" -le "$FARM_RANGE_MAX" ] && [ "${AADEN_FARM:-}" != "1" ]; then
|
|
16
|
+
echo "✗ $id 在测试门店号段($FARM_RANGE_MIN–$FARM_RANGE_MAX)里,只能给测试门店农场用。真门店请用 ERP 里建档的设备号。"
|
|
17
|
+
return 1
|
|
18
|
+
fi
|
|
19
|
+
return 0
|
|
20
|
+
}
|
|
@@ -31,6 +31,7 @@ if ! [[ "$NEW_ID" =~ ^[0-9]+$ ]]; then
|
|
|
31
31
|
exit 1
|
|
32
32
|
fi
|
|
33
33
|
NEW_ID=$((10#$NEW_ID)) # 去掉前导零,和云端、ERP 编号一致
|
|
34
|
+
source "$DIR/farmRange.sh" && farm_range_guard "$NEW_ID" || exit 1
|
|
34
35
|
NAME="IK$(printf '%04d' "$NEW_ID")"
|
|
35
36
|
HOST="aaden$(printf '%04d' "$NEW_ID")" # 和 ngrok 的 ik 号一样补到 4 位:aaden0001、aaden1958
|
|
36
37
|
|
package/dist/index.js
CHANGED
|
@@ -570,6 +570,29 @@ function collectHostInfo() {
|
|
|
570
570
|
module.exports = {collectHostInfo}
|
|
571
571
|
|
|
572
572
|
|
|
573
|
+
/***/ }),
|
|
574
|
+
|
|
575
|
+
/***/ 5906:
|
|
576
|
+
/***/ ((module) => {
|
|
577
|
+
|
|
578
|
+
// 盒子上装的 aaden snap(Electron 桌面收银)版本,do-log 报给云端 device_log_model.frontend_version。
|
|
579
|
+
// 以前解析 `snap info aaden` 的最后一行:装了 snap 时是 `installed: 1.2.x …`,没装时是 channels 表的
|
|
580
|
+
// `latest/edge: …`,于是全网 500 多台没装 snap 的盒子都报成了字面量 "latest/edge:"。
|
|
581
|
+
// 现在读 `snap list aaden`:装了就有一行以 "aaden " 开头(表头会随系统语言变,不能按行号取);
|
|
582
|
+
// 没装时 stdout 为空、报错在 stderr,返回空串。写法兼容老盒子上的 Node(不用 ?. 和 ??)。
|
|
583
|
+
|
|
584
|
+
function parseSnapListVersion(output) {
|
|
585
|
+
const lines = String(output || '').split('\n')
|
|
586
|
+
for (let i = 0; i < lines.length; i++) {
|
|
587
|
+
const cols = lines[i].trim().split(/\s+/)
|
|
588
|
+
if (cols[0] === 'aaden' && cols.length >= 2) return cols[1]
|
|
589
|
+
}
|
|
590
|
+
return ''
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
module.exports = {parseSnapListVersion}
|
|
594
|
+
|
|
595
|
+
|
|
573
596
|
/***/ }),
|
|
574
597
|
|
|
575
598
|
/***/ 8226:
|
|
@@ -19246,7 +19269,7 @@ module.exports = JSON.parse('{"name":"axios","version":"0.19.2","description":"P
|
|
|
19246
19269
|
/***/ ((module) => {
|
|
19247
19270
|
|
|
19248
19271
|
"use strict";
|
|
19249
|
-
module.exports = {"i8":"1.6.
|
|
19272
|
+
module.exports = JSON.parse('{"i8":"1.6.121-rc.0"}');
|
|
19250
19273
|
|
|
19251
19274
|
/***/ })
|
|
19252
19275
|
|
|
@@ -19334,6 +19357,7 @@ const prompts = __nccwpck_require__(9646)
|
|
|
19334
19357
|
const {deviceEcho, logAction, getCurrentDeviceId} = __nccwpck_require__(7415)
|
|
19335
19358
|
const {collectHostInfo} = __nccwpck_require__(3727)
|
|
19336
19359
|
const {collectTseInfo} = __nccwpck_require__(6677)
|
|
19360
|
+
const {parseSnapListVersion} = __nccwpck_require__(5906)
|
|
19337
19361
|
const deviceCommands = __nccwpck_require__(5363)
|
|
19338
19362
|
const GlobalDeviceId = 'notset'
|
|
19339
19363
|
|
|
@@ -20241,12 +20265,10 @@ function doLogField(fn) {
|
|
|
20241
20265
|
}
|
|
20242
20266
|
|
|
20243
20267
|
async function doLog() {
|
|
20244
|
-
//
|
|
20245
|
-
//
|
|
20246
|
-
const frontendVersion = doLogField(() =>
|
|
20247
|
-
|
|
20248
|
-
return (list[list.length - 2] || '').replace(/\s+/g, ' ').split(' ')[1]
|
|
20249
|
-
})
|
|
20268
|
+
// 加超时:snapd 刷新卡住时 snap 命令会一直不返回,do-log 就再也报不上来,
|
|
20269
|
+
// 设备在云端看就是「失联」,云端命令也领不到。没装 aaden snap 报空串(见 modules/snap-version.js)
|
|
20270
|
+
const frontendVersion = doLogField(() =>
|
|
20271
|
+
parseSnapListVersion(cli.executeInShellSync('timeout 20 snap list aaden').output))
|
|
20250
20272
|
const diskUsage = doLogField(() => {
|
|
20251
20273
|
const parts = (cli.executeInShellSync('timeout 20 df -h --total | command grep total | head -c-3 && echo \'\'').output || '').split(' ')
|
|
20252
20274
|
return parts[parts.length - 1].replace('\n', '')
|
package/index.js
CHANGED
|
@@ -14,6 +14,7 @@ const prompts = require('prompts')
|
|
|
14
14
|
const {deviceEcho, logAction, getCurrentDeviceId} = require('./modules/firebase')
|
|
15
15
|
const {collectHostInfo} = require('./modules/host-info')
|
|
16
16
|
const {collectTseInfo} = require('./modules/tse-info')
|
|
17
|
+
const {parseSnapListVersion} = require('./modules/snap-version')
|
|
17
18
|
const deviceCommands = require('./modules/device-commands')
|
|
18
19
|
const GlobalDeviceId = 'notset'
|
|
19
20
|
|
|
@@ -921,12 +922,10 @@ function doLogField(fn) {
|
|
|
921
922
|
}
|
|
922
923
|
|
|
923
924
|
async function doLog() {
|
|
924
|
-
//
|
|
925
|
-
//
|
|
926
|
-
const frontendVersion = doLogField(() =>
|
|
927
|
-
|
|
928
|
-
return (list[list.length - 2] || '').replace(/\s+/g, ' ').split(' ')[1]
|
|
929
|
-
})
|
|
925
|
+
// 加超时:snapd 刷新卡住时 snap 命令会一直不返回,do-log 就再也报不上来,
|
|
926
|
+
// 设备在云端看就是「失联」,云端命令也领不到。没装 aaden snap 报空串(见 modules/snap-version.js)
|
|
927
|
+
const frontendVersion = doLogField(() =>
|
|
928
|
+
parseSnapListVersion(cli.executeInShellSync('timeout 20 snap list aaden').output))
|
|
930
929
|
const diskUsage = doLogField(() => {
|
|
931
930
|
const parts = (cli.executeInShellSync('timeout 20 df -h --total | command grep total | head -c-3 && echo \'\'').output || '').split(' ')
|
|
932
931
|
return parts[parts.length - 1].replace('\n', '')
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// 盒子上装的 aaden snap(Electron 桌面收银)版本,do-log 报给云端 device_log_model.frontend_version。
|
|
2
|
+
// 以前解析 `snap info aaden` 的最后一行:装了 snap 时是 `installed: 1.2.x …`,没装时是 channels 表的
|
|
3
|
+
// `latest/edge: …`,于是全网 500 多台没装 snap 的盒子都报成了字面量 "latest/edge:"。
|
|
4
|
+
// 现在读 `snap list aaden`:装了就有一行以 "aaden " 开头(表头会随系统语言变,不能按行号取);
|
|
5
|
+
// 没装时 stdout 为空、报错在 stderr,返回空串。写法兼容老盒子上的 Node(不用 ?. 和 ??)。
|
|
6
|
+
|
|
7
|
+
function parseSnapListVersion(output) {
|
|
8
|
+
const lines = String(output || '').split('\n')
|
|
9
|
+
for (let i = 0; i < lines.length; i++) {
|
|
10
|
+
const cols = lines[i].trim().split(/\s+/)
|
|
11
|
+
if (cols[0] === 'aaden' && cols.length >= 2) return cols[1]
|
|
12
|
+
}
|
|
13
|
+
return ''
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
module.exports = {parseSnapListVersion}
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aaden-cli",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.121-rc.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"scripts": {
|
|
7
|
-
"test": "node test.js",
|
|
7
|
+
"test": "node test/snap-version.test.js",
|
|
8
8
|
"update": "git add . && release-it --ci"
|
|
9
9
|
},
|
|
10
10
|
"bin": {
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// node test/snap-version.test.js;只用 Node 自带的 assert,老盒子上也能跑
|
|
2
|
+
const assert = require('assert')
|
|
3
|
+
const {parseSnapListVersion} = require('../modules/snap-version')
|
|
4
|
+
|
|
5
|
+
// 装了 snap(stable 频道)
|
|
6
|
+
assert.strictEqual(parseSnapListVersion(
|
|
7
|
+
'Name Version Rev Tracking Publisher Notes\n' +
|
|
8
|
+
'aaden 1.2.158 183 latest/stable aaden classic\n'), '1.2.158')
|
|
9
|
+
// 中文系统的表头;还停在 2020 年 edge 版本的机器
|
|
10
|
+
assert.strictEqual(parseSnapListVersion(
|
|
11
|
+
'名称 版本 修订版本 追踪 发布者 注记\n' +
|
|
12
|
+
'aaden 1.4.7 73 latest/edge aaden classic\n'), '1.4.7')
|
|
13
|
+
// 没装:stdout 为空(「未找到匹配的 snap」在 stderr)
|
|
14
|
+
assert.strictEqual(parseSnapListVersion(''), '')
|
|
15
|
+
assert.strictEqual(parseSnapListVersion(undefined), '')
|
|
16
|
+
// 不能把别的 snap 的行当成 aaden
|
|
17
|
+
assert.strictEqual(parseSnapListVersion(
|
|
18
|
+
'Name Version Rev Tracking Publisher Notes\n' +
|
|
19
|
+
'aaden-other 9.9.9 1 latest/stable x -\n'), '')
|
|
20
|
+
// 以前的 bug:snap info 没装时的输出,换了解析方式以后也不会再拿到 "latest/edge:"
|
|
21
|
+
assert.strictEqual(parseSnapListVersion(
|
|
22
|
+
'channels:\n latest/stable: 1.2.159 2026-10-11 (184) 122MB -\n latest/edge: 1.4.7 2020-08-22 (73) 106MB -\n'), '')
|
|
23
|
+
|
|
24
|
+
console.log('snap-version: ok')
|