aaden-cli 1.6.119 → 1.6.121

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.
@@ -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,8 @@
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,不受影响
7
9
  # - 按应用灰度:manifest 带 perAppRollout=true 时,每个应用按自己的 apps.<App>.rollout 判断(没有 = 全量),
8
10
  # 顶层 rollout 只管 ensureNoCache;老版本 CLI 不认 perAppRollout,照旧整体按顶层 rollout
9
11
  # - ensureNoCache:manifest 打开时先确保 AppCenter 的 index.html 带 no-cache(ensureAppCenterNoCache.sh)
@@ -18,6 +20,7 @@
18
20
  # 用法: appCenterSync.sh [channel]
19
21
  # APPCENTER_BASE 产物根地址,默认 S3;测试可用 file:///var/tmp/...(curl 都支持)
20
22
  # APPCENTER_APPS 只同步这些应用(空格分隔);不传 = manifest 里列出的全部应用,发哪些由发布端决定
23
+ # APPCENTER_ROOT_APPS 允许同步到 docroot 根的应用白名单(空格分隔),默认 "App"
21
24
  # APPCENTER_DEVICE_ID 本机 deviceId(CLI 会传);不传就问本机 PHP
22
25
  # APPCENTER_RESULT 结果行另存的文件(CLI 会传)
23
26
  # 结果行(同时写进 $RESULT 给 CLI 上报):APP_OK <App> <sha12> | APP_UP_TO_DATE <App> | APP_SKIP <App> <原因> |
@@ -26,7 +29,9 @@
26
29
  set -uo pipefail
27
30
  BASE="${APPCENTER_BASE:-https://aaden-backend.s3.eu-central-1.amazonaws.com/appcenter}"
28
31
  CHANNEL="${1:-stable}"
29
- WEB=/var/www/html/AppCenter
32
+ DOCROOT=/var/www/html
33
+ WEB=$DOCROOT/AppCenter
34
+ ROOT_ALLOW=" ${APPCENTER_ROOT_APPS:-App} "
30
35
  STAGE=/var/tmp/aaden-appcenter-stage
31
36
  BACKUP=/var/lib/aaden-appcenter-backup
32
37
  LOCK=/var/tmp/aaden-appcenter.lock
@@ -50,7 +55,7 @@ flock -n 9 || { res "SKIP_LOCKED"; exit 0; }
50
55
 
51
56
  MANIFEST=$(curl -fsSL --max-time 30 "$BASE/$CHANNEL/manifest.json") \
52
57
  || { echo "取不到 $BASE/$CHANNEL/manifest.json"; res "ABORT_MANIFEST"; exit 1; }
53
- # 读 manifest:mf apps.Admin.zip / mf ensureNoCache / mf eligible <deviceId> / mf eligible_app <App> <deviceId>
58
+ # 读 manifest:mf apps.Admin.zip / mf ensureNoCache / mf eligible <deviceId> / mf eligible_app <App> <deviceId> [apps|rootApps]
54
59
  mf(){
55
60
  printf '%s' "$MANIFEST" | python3 -c '
56
61
  import json, sys
@@ -67,8 +72,10 @@ def gate(r, dev):
67
72
  if sys.argv[1] == "eligible":
68
73
  print(gate(m.get("rollout"), sys.argv[2])); sys.exit()
69
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"
70
77
  if m.get("perAppRollout") is True:
71
- app = (m.get("apps") or {}).get(sys.argv[2]) or {}
78
+ app = (m.get(kind) or {}).get(sys.argv[2]) or {}
72
79
  print(gate(app.get("rollout"), sys.argv[3]))
73
80
  else:
74
81
  print(gate(m.get("rollout"), sys.argv[3]))
@@ -109,26 +116,32 @@ tree_ok(){ # $1=目录 $2=期望 treeSha256
109
116
  }
110
117
 
111
118
  sync_app(){
112
- local APP="$1" LIVE="$WEB/$1" NEW="$STAGE/$1" TREE ZIP ZSHA
113
- TREE=$(mf "apps.$APP.treeSha256"); ZIP=$(mf "apps.$APP.zip"); ZSHA=$(mf "apps.$APP.zipSha256")
114
- if [ -z "$TREE" ]; then res "APP_SKIP $APP 不在manifest里"; return 0; fi
115
- if [ "$(mf eligible_app "$APP" "${DEVICE_ID:-x}")" != "yes" ]; then res "APP_SKIP_ROLLOUT $APP"; return 0; fi
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
116
129
  # manifest 来自网络:只接受预期格式,防止路径穿越
117
130
  if ! [[ "$TREE" =~ ^[0-9a-f]{64}$ && "$ZSHA" =~ ^[0-9a-f]{64}$ && "$ZIP" =~ ^blobs/[A-Za-z0-9._-]+\.zip$ ]]; then
118
- res "APP_ABORT_BAD_MANIFEST $APP"; return 1
131
+ res "APP_ABORT_BAD_MANIFEST $TAG"; return 1
119
132
  fi
120
133
 
121
134
  # ---- manifest 门 ----
122
- if tree_ok "$LIVE" "$TREE"; then res "APP_UP_TO_DATE $APP"; return 0; fi
123
- echo "$APP: 本机与 manifest 不一致 → 同步到 ${TREE:0:12}"
135
+ if tree_ok "$LIVE" "$TREE"; then res "APP_UP_TO_DATE $TAG"; return 0; fi
136
+ echo "$TAG: 本机与 manifest 不一致 → 同步到 ${TREE:0:12}"
124
137
 
125
138
  # ---- 下载 + 校验(此时还没动线上目录)----
126
139
  sudoS "rm -rf '$NEW' '$NEW.zip' '$NEW.failed'"
127
- curl -fsSL --max-time 1200 "$BASE/$ZIP" -o "$NEW.zip" || { res "APP_ABORT_DOWNLOAD $APP"; return 1; }
128
- [ "$(sha256sum "$NEW.zip" | cut -d' ' -f1)" = "$ZSHA" ] || { res "APP_ABORT_CHECKSUM $APP"; rm -f "$NEW.zip"; return 1; }
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; }
129
142
  mkdir -p "$NEW" && unzip -q -o "$NEW.zip" -d "$NEW"; rm -f "$NEW.zip"
130
143
  if ! { [ -f "$NEW/index.html" ] && tree_ok "$NEW" "$TREE"; }; then
131
- res "APP_ABORT_BAD_ARTIFACT $APP"; rm -rf "$NEW"; return 1
144
+ res "APP_ABORT_BAD_ARTIFACT $TAG"; rm -rf "$NEW"; return 1
132
145
  fi
133
146
  # zip 里带的是打包时的旧 mtime;浏览器按 Last-Modified 的年龄启发式缓存,所以改成现在(只改 mtime 不改内容,不影响校验)
134
147
  touch "$NEW/index.html"
@@ -137,7 +150,7 @@ sync_app(){
137
150
  local CARRIED=0
138
151
  if [ -d "$LIVE" ]; then
139
152
  python3 "$HERE/appcenterRefs.py" "$LIVE" | LC_ALL=C sort > "$NEW.old" \
140
- || { res "APP_ABORT_REFS $APP"; rm -rf "$NEW" "$NEW.old"; return 1; }
153
+ || { res "APP_ABORT_REFS $TAG"; rm -rf "$NEW" "$NEW.old"; return 1; }
141
154
  paths "$NEW/.appcenter-manifest" > "$NEW.new"
142
155
  LC_ALL=C comm -23 "$NEW.old" "$NEW.new" > "$NEW.carry"
143
156
  CARRIED=$(wc -l < "$NEW.carry" | tr -d ' ')
@@ -145,34 +158,42 @@ sync_app(){
145
158
  [ "$CARRIED" -gt 0 ] && tar -C "$LIVE" -cf - -T "$NEW.carry" | tar -C "$NEW" -xf -
146
159
  rm -f "$NEW.old" "$NEW.new" "$NEW.carry"
147
160
  fi
148
- echo "$APP: 保留上一代文件 $CARRIED 个"
161
+ echo "$TAG: 保留上一代文件 $CARRIED 个"
149
162
 
150
163
  # ---- 替换:两次 rename,中间只有毫秒级空窗 ----
151
- sudoS "mkdir -p '$BACKUP' && rm -rf '$BACKUP/$APP' && chown -R www-data:www-data '$NEW'"
152
- [ -d "$LIVE" ] && sudoS "mv '$LIVE' '$BACKUP/$APP'"
164
+ sudoS "mkdir -p '$BACKUP' && rm -rf '$BACKUP/$BAK' && chown -R www-data:www-data '$NEW'"
165
+ [ -d "$LIVE" ] && sudoS "mv '$LIVE' '$BACKUP/$BAK'"
153
166
  sudoS "mv '$NEW' '$LIVE'"
154
167
 
155
168
  # ---- 健康检查:首页、入口 js、文件校验 ----
156
169
  local HEALTHY=1 MAIN c
157
170
  MAIN=$(grep -oE 'src=["'"'"']?[^"'"'"' >]+\.js' "$LIVE/index.html" | grep -vE '//' | head -1 | sed -E 's/^src=["'"'"']?(\.\/)?//')
158
171
  for p in "" "$MAIN"; do
159
- c=$(curl -s -o /dev/null -w '%{http_code}' "http://localhost/AppCenter/$APP/$p" --max-time 15)
160
- echo " 健康检查 /AppCenter/$APP/$p -> $c"; [ "$c" = "200" ] || HEALTHY=0
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
161
174
  done
162
175
  tree_ok "$LIVE" "$TREE" || { echo " 线上目录校验失败"; HEALTHY=0; }
163
176
 
164
177
  if [ "$HEALTHY" != "1" ]; then
165
- sudoS "mv '$LIVE' '$NEW.failed'; [ -d '$BACKUP/$APP' ] && mv '$BACKUP/$APP' '$LIVE'"
166
- res "APP_ROLLED_BACK $APP"; return 1
178
+ sudoS "mv '$LIVE' '$NEW.failed'; [ -d '$BACKUP/$BAK' ] && mv '$BACKUP/$BAK' '$LIVE'"
179
+ res "APP_ROLLED_BACK $TAG"; return 1
167
180
  fi
168
- res "APP_OK $APP ${TREE:0:12} carried=$CARRIED"
181
+ res "APP_OK $TAG ${TREE:0:12} carried=$CARRIED"
169
182
  }
170
183
 
171
184
  # 不指定就同步 manifest 里的全部应用(营业中一直开着的 App/KDS 等,发布端先别放进 manifest)
172
185
  APPS="${APPCENTER_APPS:-$(printf '%s' "$MANIFEST" | python3 -c 'import json, sys; print(" ".join(sorted(json.load(sys.stdin).get("apps", {}))))')}"
173
186
  sudoS "mkdir -p '$STAGE' && chown $(id -un):$(id -gn) '$STAGE'"
174
187
  for APP in $APPS; do
188
+ [[ "$APP" == /* ]] && continue # APPCENTER_APPS 里 /<App> 指的是根目录应用,下面单独处理
175
189
  if ! [[ "$APP" =~ ^[A-Za-z0-9_-]+$ ]]; then res "APP_SKIP $APP 名字不合法"; continue; fi
176
- 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
177
198
  done
178
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.119"};
19272
+ module.exports = {"i8":"1.6.121"};
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
- // snap info 加超时:snapd 刷新卡住时它会一直不返回,do-log 就再也报不上来,
20245
- // 设备在云端看就是「失联」,云端命令也领不到
20246
- const frontendVersion = doLogField(() => {
20247
- const list = (cli.executeInShellSync('timeout 20 snap info aaden').output || '').split('\n')
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
- // snap info 加超时:snapd 刷新卡住时它会一直不返回,do-log 就再也报不上来,
925
- // 设备在云端看就是「失联」,云端命令也领不到
926
- const frontendVersion = doLogField(() => {
927
- const list = (cli.executeInShellSync('timeout 20 snap info aaden').output || '').split('\n')
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.119",
3
+ "version": "1.6.121",
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')
package/test.js DELETED
@@ -1,6 +0,0 @@
1
- let i = 35
2
- while (i > 0) {
3
- //console.log(`http://ik1017.ngrok.aaden.io/QR?TableName=${i}&Base=ik1017.ngrok.aaden.io`)
4
- console.log(i)
5
- i--
6
- }