@xcanwin/manyoyo 8.6.3 → 8.6.4

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/lib/app-update.js CHANGED
@@ -11,7 +11,7 @@ const { spawnSync } = require('child_process');
11
11
  const { downloadVerified, sha256File } = require('./download-verified');
12
12
 
13
13
  const REPO = 'xcanwin/manyoyo';
14
- const API_BASE = 'https://api.github.com';
14
+ const WEB_BASE = 'https://github.com';
15
15
  // 不依赖 PATH 查找 tar(macOS 与常见 Linux 都在 /usr/bin)
16
16
  const TAR_COMMAND = fs.existsSync('/usr/bin/tar') ? '/usr/bin/tar' : 'tar';
17
17
  const VERSION_PATTERN = /^\d+\.\d+\.\d+$/;
@@ -103,35 +103,32 @@ async function request(fetchImpl, url, options = {}) {
103
103
  }
104
104
 
105
105
  /**
106
- * 查询最新 Release(匿名 API)。返回 { version, tag, assets: { name: url } }。
106
+ * 查询最新 Release。返回 { version, tag, assets: { name: url } }。
107
+ * 不调 GitHub API:匿名 API 按 IP 每小时 60 次,共享出口(公司网络、代理、CI runner)经常被限流。
108
+ * 改为跟随 github.com 的 releases/latest 跳转取版本(与 scripts/install.sh 一致),
109
+ * 升级包与 SHA256SUMS 的命名是固定的,下载地址直接拼在本仓库的 Release 下载路径下。
107
110
  */
108
- async function fetchLatestRelease({ fetchImpl = fetch, apiBase = API_BASE, now = Date.now } = {}) {
109
- const response = await request(fetchImpl, `${apiBase}/repos/${REPO}/releases/latest`);
110
- if (response.status === 403 || response.status === 429) {
111
- const remaining = response.headers.get('x-ratelimit-remaining');
112
- if (response.status === 429 || remaining === '0') {
113
- const reset = Number(response.headers.get('x-ratelimit-reset'));
114
- const when = reset > 0 ? new Date(reset * 1000).toLocaleTimeString('zh-CN', { hour12: false }) : '';
115
- throw new UpdateError('RATE_LIMIT', `GitHub 对匿名请求做了频率限制,${when ? `请在 ${when} 之后重试` : '请稍后重试'}。`);
116
- }
117
- }
118
- if (response.status === 404) throw new UpdateError('NO_RELEASE', '仓库还没有发布的 Release,暂时无法升级。');
119
- if (!response.ok) throw new UpdateError('HTTP', `查询最新版本失败(HTTP ${response.status})。`);
120
- let release;
121
- try {
122
- release = await response.json();
123
- } catch (error) {
124
- throw new UpdateError('HTTP', '查询最新版本失败(返回内容不是有效的 JSON)。');
125
- }
126
- const version = String(release.tag_name || '').replace(/^v/, '');
127
- if (!VERSION_PATTERN.test(version)) throw new UpdateError('HTTP', `最新 Release 的版本号无法识别: ${release.tag_name}`);
128
- // 默认 API 下,下载地址必须在本仓库的 Release 下载路径内(测试注入自定义 apiBase 时不限制)
129
- const allowedPrefix = apiBase === API_BASE ? `https://github.com/${REPO}/releases/download/` : '';
111
+ async function fetchLatestRelease({ fetchImpl = fetch, webBase = WEB_BASE } = {}) {
112
+ const response = await request(fetchImpl, `${webBase}/${REPO}/releases/latest`, { redirect: 'manual', headers: { Accept: 'text/html' } });
113
+ if (response.status === 429) throw new UpdateError('RATE_LIMIT', 'GitHub 对访问做了频率限制,请稍后重试。');
114
+ const location = response.status >= 300 && response.status < 400 ? String(response.headers.get('location') || '') : '';
115
+ if (!location && !response.ok && response.status !== 404) throw new UpdateError('HTTP', `查询最新版本失败(HTTP ${response.status})。`);
116
+ // 没有任何 Release 时 latest 跳到 Release 列表页(或 404)
117
+ const match = /\/releases\/tag\/([^/?#]+)$/.exec(location);
118
+ if (!match) throw new UpdateError('NO_RELEASE', '仓库还没有发布的 Release,暂时无法升级。');
119
+ const tag = decodeURIComponent(match[1]);
120
+ const version = tag.replace(/^v/, '');
121
+ if (!VERSION_PATTERN.test(version)) throw new UpdateError('HTTP', `最新 Release 的版本号无法识别: ${tag}`);
122
+ const download = `${webBase}/${REPO}/releases/download/${encodeURIComponent(tag)}/`;
130
123
  const assets = {};
131
- for (const asset of Array.isArray(release.assets) ? release.assets : []) {
132
- if (asset && typeof asset.name === 'string' && typeof asset.browser_download_url === 'string' && asset.browser_download_url.startsWith(allowedPrefix)) assets[asset.name] = asset.browser_download_url;
124
+ for (const targetOs of ['macos', 'linux']) {
125
+ for (const targetArch of ['arm64', 'x64']) {
126
+ const names = releaseAssetNames(version, targetArch, targetOs);
127
+ assets[names.app] = `${download}${names.app}`;
128
+ assets[names.sums] = `${download}${names.sums}`;
129
+ }
133
130
  }
134
- return { version, tag: release.tag_name, assets };
131
+ return { version, tag, assets };
135
132
  }
136
133
 
137
134
  function parseSha256Sums(text) {
@@ -250,6 +247,11 @@ async function installAppUpdate({ appRoot, release, fetchImpl = fetch, targetArc
250
247
  throw new UpdateError('NO_ASSET', `Release ${release.version} 里没有适合这台机器(${targetOs}-${targetArch})的升级包(需要 ${names.app} 与 ${names.sums})。`);
251
248
  }
252
249
 
250
+ // 下载地址是按固定命名拼出来的:先确认这个 Release 里确实有本机的升级包(404 就是没有)
251
+ const probe = await request(fetchImpl, appUrl, { method: 'HEAD' });
252
+ if (probe.status === 404) {
253
+ throw new UpdateError('NO_ASSET', `Release ${release.version} 里没有适合这台机器(${targetOs}-${targetArch})的升级包(需要 ${names.app} 与 ${names.sums})。`);
254
+ }
253
255
  log(`校验清单: ${sumsUrl}`);
254
256
  const sums = parseSha256Sums(await fetchText(fetchImpl, sumsUrl));
255
257
  const expected = sums[names.app];