diff --git a/bun.lock b/bun.lock index bcefb74..f43ac6b 100644 --- a/bun.lock +++ b/bun.lock @@ -10,7 +10,6 @@ "chalk": "4", "cli-arguments": "^0.2.1", "commander": "^13", - "compare-versions": "^6.1.1", "filesize-parser": "^1.5.1", "form-data": "^4.0.2", "fs-extra": "8", @@ -25,7 +24,7 @@ "properties": "^1.2.1", "read": "^4.1.0", "registry-auth-token": "^5.1.0", - "semver": "^7.7.1", + "semver": "^7.7.2", "tcp-ping": "^0.1.1", "tty-table": "4.2", "yauzl": "^3.2.0", @@ -33,11 +32,11 @@ }, "devDependencies": { "@biomejs/biome": "^1.9.4", - "@swc/cli": "0.7.3", + "@swc/cli": "0.7.7", "@swc/core": "^1.11.24", "@types/filesize-parser": "^1.5.3", "@types/fs-extra": "^11.0.4", - "@types/node": "^22.14.1", + "@types/node": "^22.15.18", "@types/node-fetch": "^2.6.12", "@types/progress": "^2.0.7", "@types/semver": "^7.7.0", @@ -125,7 +124,7 @@ "@sindresorhus/is": ["@sindresorhus/is@5.6.0", "", {}, "sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g=="], - "@swc/cli": ["@swc/cli@0.7.3", "", { "dependencies": { "@swc/counter": "^0.1.3", "@xhmikosr/bin-wrapper": "^13.0.5", "commander": "^8.3.0", "fast-glob": "^3.2.5", "minimatch": "^9.0.3", "piscina": "^4.3.1", "semver": "^7.3.8", "slash": "3.0.0", "source-map": "^0.7.3" }, "peerDependencies": { "@swc/core": "^1.2.66", "chokidar": "^4.0.1" }, "optionalPeers": ["chokidar"], "bin": { "swc": "bin/swc.js", "swcx": "bin/swcx.js", "spack": "bin/spack.js" } }, "sha512-rnVXNnlURjdOuPaBIwZ3TmBA44BF/eP0j154LanlgPEYfau74ige7cpKlKkZr1IBqMOG99lAnYNxQipDWA3hdg=="], + "@swc/cli": ["@swc/cli@0.7.7", "", { "dependencies": { "@swc/counter": "^0.1.3", "@xhmikosr/bin-wrapper": "^13.0.5", "commander": "^8.3.0", "fast-glob": "^3.2.5", "minimatch": "^9.0.3", "piscina": "^4.3.1", "semver": "^7.3.8", "slash": "3.0.0", "source-map": "^0.7.3" }, "peerDependencies": { "@swc/core": "^1.2.66", "chokidar": "^4.0.1" }, "optionalPeers": ["chokidar"], "bin": { "swc": "bin/swc.js", "swcx": "bin/swcx.js", "spack": "bin/spack.js" } }, "sha512-j4yYm9bx3pxWofaJKX1BFwj/3ngUDynN4UIQ2Xd2h0h/7Gt7zkReBTpDN7g5S13mgAYxacaTHTOUsz18097E8w=="], "@swc/core": ["@swc/core@1.11.24", "", { "dependencies": { "@swc/counter": "^0.1.3", "@swc/types": "^0.1.21" }, "optionalDependencies": { "@swc/core-darwin-arm64": "1.11.24", "@swc/core-darwin-x64": "1.11.24", "@swc/core-linux-arm-gnueabihf": "1.11.24", "@swc/core-linux-arm64-gnu": "1.11.24", "@swc/core-linux-arm64-musl": "1.11.24", "@swc/core-linux-x64-gnu": "1.11.24", "@swc/core-linux-x64-musl": "1.11.24", "@swc/core-win32-arm64-msvc": "1.11.24", "@swc/core-win32-ia32-msvc": "1.11.24", "@swc/core-win32-x64-msvc": "1.11.24" }, "peerDependencies": { "@swc/helpers": ">=0.5.17" }, "optionalPeers": ["@swc/helpers"] }, "sha512-MaQEIpfcEMzx3VWWopbofKJvaraqmL6HbLlw2bFZ7qYqYw3rkhM0cQVEgyzbHtTWwCwPMFZSC2DUbhlZgrMfLg=="], @@ -165,7 +164,7 @@ "@types/jsonfile": ["@types/jsonfile@6.1.4", "", { "dependencies": { "@types/node": "*" } }, "sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ=="], - "@types/node": ["@types/node@22.14.1", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-u0HuPQwe/dHrItgHHpmw3N2fYCR6x4ivMNbPHRkBVP4CvN+kiRrKHWk3i8tXiO/joPwXLMYvF9TTF0eqgHIuOw=="], + "@types/node": ["@types/node@22.15.18", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-v1DKRfUdyW+jJhZNEI1PYy29S2YRxMV5AOO/x/SjKmW0acCIOqmbj6Haf9eHAhsPmrhlHSxEhv/1WszcLWV4cg=="], "@types/node-fetch": ["@types/node-fetch@2.6.12", "", { "dependencies": { "@types/node": "*", "form-data": "^4.0.0" } }, "sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA=="], @@ -279,8 +278,6 @@ "commander": ["commander@13.1.0", "", {}, "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw=="], - "compare-versions": ["compare-versions@6.1.1", "", {}, "sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg=="], - "config-chain": ["config-chain@1.1.13", "", { "dependencies": { "ini": "^1.3.4", "proto-list": "~1.2.1" } }, "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ=="], "content-disposition": ["content-disposition@0.5.4", "", { "dependencies": { "safe-buffer": "5.2.1" } }, "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ=="], @@ -625,7 +622,7 @@ "seek-bzip": ["seek-bzip@2.0.0", "", { "dependencies": { "commander": "^6.0.0" }, "bin": { "seek-bunzip": "bin/seek-bunzip", "seek-table": "bin/seek-bzip-table" } }, "sha512-SMguiTnYrhpLdk3PwfzHeotrcwi8bNV4iemL9tx9poR/yeaMYwB9VzR1w7b57DuWpuqR8n6oZboi0hj3AxZxQg=="], - "semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="], + "semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="], "semver-regex": ["semver-regex@4.0.5", "", {}, "sha512-hunMQrEy1T6Jr2uEVjrAIqjwWcQTgOAcIM52C8MY1EZSD3DDNft04XzvYKPqjED65bNVVko0YI38nYeEHCX3yw=="], diff --git a/package.json b/package.json index 9e8b875..64b51b7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-native-update-cli", - "version": "1.44.7", + "version": "1.45.0", "description": "command line tool for react-native-update (remote updates for react native)", "main": "index.js", "bin": { @@ -42,7 +42,6 @@ "chalk": "4", "cli-arguments": "^0.2.1", "commander": "^13", - "compare-versions": "^6.1.1", "filesize-parser": "^1.5.1", "form-data": "^4.0.2", "fs-extra": "8", @@ -57,7 +56,7 @@ "properties": "^1.2.1", "read": "^4.1.0", "registry-auth-token": "^5.1.0", - "semver": "^7.7.1", + "semver": "^7.7.2", "tcp-ping": "^0.1.1", "tty-table": "4.2", "yauzl": "^3.2.0", @@ -68,11 +67,11 @@ }, "devDependencies": { "@biomejs/biome": "^1.9.4", - "@swc/cli": "0.7.3", + "@swc/cli": "0.7.7", "@swc/core": "^1.11.24", "@types/filesize-parser": "^1.5.3", "@types/fs-extra": "^11.0.4", - "@types/node": "^22.14.1", + "@types/node": "^22.15.18", "@types/node-fetch": "^2.6.12", "@types/progress": "^2.0.7", "@types/semver": "^7.7.0", diff --git a/src/api.ts b/src/api.ts index 9c9a5d9..f96cfe6 100644 --- a/src/api.ts +++ b/src/api.ts @@ -11,7 +11,7 @@ import { credentialFile, defaultEndpoint, } from './utils/constants'; -import type { Session } from 'types'; +import type { Session, Package } from 'types'; import FormData from 'form-data'; import { t } from './utils/i18n'; @@ -177,3 +177,8 @@ export async function uploadFile(fn: string, key?: string) { // const body = await response.json(); return { hash: key || formData.key }; } + +export const getAllPackages = async (appId: string) => { + const { data } = await get(`/app/${appId}/package/list?limit=1000`); + return data as Package[] | undefined | null; +}; diff --git a/src/app.ts b/src/app.ts index 09f82fd..276e066 100644 --- a/src/app.ts +++ b/src/app.ts @@ -8,7 +8,13 @@ import { t } from './utils/i18n'; const validPlatforms = ['ios', 'android', 'harmony']; -export function checkPlatform(platform: Platform) { +export async function getPlatform(platform?: string) { + return assertPlatform( + platform || (await question(t('platformQuestion'))), + ) as Platform; +} + +export function assertPlatform(platform: string) { if (!validPlatforms.includes(platform)) { throw new Error(t('unsupportedPlatform', { platform })); } @@ -16,7 +22,7 @@ export function checkPlatform(platform: Platform) { } export function getSelectedApp(platform: Platform) { - checkPlatform(platform); + assertPlatform(platform); if (!fs.existsSync('update.json')) { throw new Error(t('appNotSelected', { platform })); @@ -68,9 +74,7 @@ export const commands = { }) { const name = options.name || (await question(t('appNameQuestion'))); const { downloadUrl } = options; - const platform = checkPlatform( - options.platform || (await question(t('platformQuestion'))), - ); + const platform = await getPlatform(options.platform); const { id } = await post('/app/create', { name, platform, downloadUrl }); console.log(t('createAppSuccess', { id })); await this.selectApp({ @@ -104,9 +108,7 @@ export const commands = { args: string[]; options: { platform: Platform }; }) => { - const platform = checkPlatform( - options.platform || (await question(t('platformQuestion'))), - ); + const platform = await getPlatform(options.platform); const id = args[0] ? Number.parseInt(args[0]) : (await chooseApp(platform)).id; diff --git a/src/bundle.ts b/src/bundle.ts index 116909a..31fab88 100644 --- a/src/bundle.ts +++ b/src/bundle.ts @@ -8,7 +8,7 @@ import { type ZipFile as YauzlZipFile, } from 'yauzl'; import { question, checkPlugins } from './utils'; -import { checkPlatform } from './app'; +import { checkPlatform, getPlatform } from './app'; import { spawn, spawnSync } from 'child_process'; import semverSatisfies from 'semver/functions/satisfies'; const g2js = require('gradle-to-js/lib/parser'); @@ -902,9 +902,7 @@ function diffArgsCheck(args: string[], options: any, diffFn: string) { export const commands = { bundle: async ({ options }) => { - const platform = checkPlatform( - options.platform || (await question(t('platformPrompt'))), - ); + const platform = await getPlatform(options.platform); const { bundleName, diff --git a/src/locales/en.ts b/src/locales/en.ts index b180b85..0d4773f 100644 --- a/src/locales/en.ts +++ b/src/locales/en.ts @@ -73,9 +73,11 @@ This can reduce the risk of inconsistent dependencies and supply chain attacks. 'Multiple lock files detected ({{- lockFiles}}), which may cause inconsistent dependencies and hot-updating issues.', nativePackageId: 'Native Package ID', nativeVersion: 'Native Version', - nativeVersionNotFound: 'No native version found >= {{version}}', - nativeVersionNotFoundLess: 'No native version found <= {{version}}', + nativeVersionNotFoundGte: 'No native version found >= {{version}}', + nativeVersionNotFoundLte: 'No native version found <= {{version}}', nativeVersionNotFoundMatch: 'No matching native version found: {{version}}', + nativePackageIdNotFound: 'No native package id found: {{id}}', + noPackagesFound: 'No packages found. (appId: {{appId}})', offset: 'Offset {{offset}}', operationComplete: 'Operation complete, bound to {{count}} native versions', operationSuccess: 'Operation successful', @@ -122,4 +124,9 @@ This can reduce the risk of inconsistent dependencies and supply chain attacks. versionBind: 'Bound version {{version}} to native version {{nativeVersion}} (id: {{id}})', welcomeMessage: 'Welcome to Cresc hot update service, {{name}}.', + versionNameQuestion: 'Enter version name:', + versionDescriptionQuestion: 'Enter version description:', + versionMetaInfoQuestion: 'Enter custom meta info:', + updateNativePackageQuestion: 'Bind to native package now?(Y/N)', + unnamed: '(Unnamed)', }; diff --git a/src/locales/zh.ts b/src/locales/zh.ts index a5f9fbb..77782fb 100644 --- a/src/locales/zh.ts +++ b/src/locales/zh.ts @@ -69,9 +69,11 @@ export default { '检测到多种不同格式的锁文件({{- lockFiles}}),这可能导致依赖关系不一致而使热更异常。', nativePackageId: '原生包 Id', nativeVersion: '原生版本', - nativeVersionNotFound: '未查询到 >= {{version}} 的原生版本', - nativeVersionNotFoundLess: '未查询到 <= {{version}} 的原生版本', + nativeVersionNotFoundGte: '未查询到 >= {{version}} 的原生版本', + nativeVersionNotFoundLte: '未查询到 <= {{version}} 的原生版本', nativeVersionNotFoundMatch: '未查询到匹配原生版本:{{version}}', + nativePackageIdNotFound: '未查询到原生包 id: {{id}}', + noPackagesFound: '未查询到任何原生包(appId: {{appId}})', offset: '偏移量 {{offset}}', operationComplete: '操作完成,共已绑定 {{count}} 个原生版本', operationSuccess: '操作成功', @@ -115,4 +117,9 @@ export default { versionBind: '已将热更版本 {{version}} 绑定到原生版本 {{nativeVersion}} (id: {{id}})', welcomeMessage: '欢迎使用 pushy 热更新服务,{{name}}。', + versionNameQuestion: '输入版本名称:', + versionDescriptionQuestion: '输入版本描述:', + versionMetaInfoQuestion: '输入自定义的 meta info:', + updateNativePackageQuestion: '是否现在将此热更应用到原生包上?(Y/N)', + unnamed: '(未命名)', }; diff --git a/src/package.ts b/src/package.ts index 213f858..844581a 100644 --- a/src/package.ts +++ b/src/package.ts @@ -1,8 +1,8 @@ -import { get, post, uploadFile } from './api'; +import { get, getAllPackages, post, uploadFile } from './api'; import { question, saveToLocal } from './utils'; import { t } from './utils/i18n'; -import { checkPlatform, getSelectedApp } from './app'; +import { checkPlatform, getPlatform, getSelectedApp } from './app'; import { getApkInfo, getIpaInfo, getAppInfo } from './utils'; import Table from 'tty-table'; @@ -11,14 +11,14 @@ import { getCommitInfo } from './utils/git'; import type { Platform } from 'types'; export async function listPackage(appId: string) { - const { data } = await get(`/app/${appId}/package/list?limit=1000`); + const allPkgs = await getAllPackages(appId); const header = [ { value: t('nativePackageId') }, { value: t('nativeVersion') }, ]; const rows = []; - for (const pkg of data) { + for (const pkg of allPkgs) { const { version } = pkg; let versionInfo = ''; if (version) { @@ -36,8 +36,8 @@ export async function listPackage(appId: string) { } console.log(Table(header, rows).render()); - console.log(t('totalPackages', { count: data.length })); - return data; + console.log(t('totalPackages', { count: allPkgs.length })); + return allPkgs; } export async function choosePackage(appId: string) { @@ -174,9 +174,7 @@ export const commands = { console.log(await getApkInfo(fn)); }, packages: async ({ options }: { options: { platform: Platform } }) => { - const platform = checkPlatform( - options.platform || (await question(t('platformPrompt'))), - ); + const platform = await getPlatform(options.platform); const { appId } = await getSelectedApp(platform); await listPackage(appId); }, diff --git a/src/types.ts b/src/types.ts index 457f659..60160fd 100644 --- a/src/types.ts +++ b/src/types.ts @@ -8,3 +8,15 @@ export interface Session { } export type Platform = 'ios' | 'android' | 'harmony'; + +export interface Package { + id: string; + name: string; +} + +export interface Version { + id: string; + hash: string; + name: string; + packages?: Package[]; +} diff --git a/src/versions.ts b/src/versions.ts index 92925e3..c03f8d4 100644 --- a/src/versions.ts +++ b/src/versions.ts @@ -1,25 +1,13 @@ -import { get, post, put, uploadFile } from './api'; +import { get, getAllPackages, post, put, uploadFile } from './api'; import { question, saveToLocal } from './utils'; import { t } from './utils/i18n'; -import { checkPlatform, getSelectedApp } from './app'; +import { getPlatform, getSelectedApp } from './app'; import { choosePackage } from './package'; -import { compare } from 'compare-versions'; import { depVersions } from './utils/dep-versions'; import { getCommitInfo } from './utils/git'; -import type { Platform } from 'types'; - -interface Package { - id: string; - name: string; -} - -interface Version { - id: string; - hash: string; - name: string; - packages?: Package[]; -} +import type { Package, Platform, Version } from 'types'; +import semverSatisfies from 'semver/functions/satisfies'; interface CommandOptions { name?: string; @@ -31,6 +19,7 @@ interface CommandOptions { packageVersion?: string; minPackageVersion?: string; maxPackageVersion?: string; + semverRange?: string; rollout?: string; } @@ -113,6 +102,49 @@ async function chooseVersion(appId: string) { } } +export const bindVersionToPackages = async ({ + appId, + versionId, + pkgs, + rollout, +}: { + appId: string; + versionId: string; + pkgs: Package[]; + rollout?: number; +}) => { + if (rollout !== undefined) { + const rolloutConfig: Record = {}; + for (const pkg of pkgs) { + rolloutConfig[pkg.name] = rollout; + } + await put(`/app/${appId}/version/${versionId}`, { + config: { + rollout: rolloutConfig, + }, + }); + console.log( + `${t('rolloutConfigSet', { + versions: pkgs.map((pkg: Package) => pkg.name).join(', '), + rollout: rollout, + })}`, + ); + } + for (const pkg of pkgs) { + await put(`/app/${appId}/package/${pkg.id}`, { + versionId, + }); + console.log( + `${t('versionBind', { + version: versionId, + nativeVersion: pkg.name, + id: pkg.id, + })}`, + ); + } + console.log(t('operationComplete', { count: pkgs.length })); +}; + export const commands = { publish: async function ({ args, @@ -128,21 +160,18 @@ export const commands = { throw new Error(t('publishUsage')); } - const platform = checkPlatform( - options.platform || - ((await question('平台(ios/android/harmony):')) as Platform), - ); + const platform = await getPlatform(options.platform); const { appId } = await getSelectedApp(platform); const { hash } = await uploadFile(fn); const versionName = - name || (await question('输入版本名称: ')) || '(未命名)'; + name || (await question(t('versionNameQuestion'))) || t('unnamed'); const { id } = await post(`/app/${appId}/version/create`, { name: versionName, hash, - description: description || (await question('输入版本描述:')), - metaInfo: metaInfo || (await question('输入自定义的 meta info:')), + description: description || (await question(t('versionDescriptionQuestion'))), + metaInfo: metaInfo || (await question(t('versionMetaInfoQuestion'))), deps: depVersions, commit: await getCommitInfo(), }); @@ -150,17 +179,14 @@ export const commands = { saveToLocal(fn, `${appId}/ppk/${id}.ppk`); console.log(t('packageUploadSuccess', { id })); - const v = await question('是否现在将此热更应用到原生包上?(Y/N)'); + const v = await question(t('updateNativePackageQuestion')); if (v.toLowerCase() === 'y') { await this.update({ args: [], options: { versionId: id, platform } }); } return versionName; }, versions: async ({ options }: { options: CommandOptions }) => { - const platform = checkPlatform( - options.platform || - ((await question('平台(ios/android/harmony):')) as Platform), - ); + const platform = await getPlatform(options.platform); const { appId } = await getSelectedApp(platform); await listVersions(appId); }, @@ -171,20 +197,18 @@ export const commands = { args: string[]; options: CommandOptions; }) => { - const platform = checkPlatform( - options.platform || - ((await question('平台(ios/android/harmony):')) as Platform), - ); + const platform = await getPlatform(options.platform); const { appId } = await getSelectedApp(platform); let versionId = options.versionId || (await chooseVersion(appId)).id; if (versionId === 'null') { versionId = undefined; } - let pkgId: string | undefined; + let pkgId = options.packageId; let pkgVersion = options.packageVersion; let minPkgVersion = options.minPackageVersion; let maxPkgVersion = options.maxPackageVersion; + let semverRange = options.semverRange; let rollout: number | undefined = undefined; if (options.rollout !== undefined) { @@ -198,146 +222,78 @@ export const commands = { } } - if (minPkgVersion) { - minPkgVersion = String(minPkgVersion).trim(); - const { data } = await get(`/app/${appId}/package/list?limit=1000`); - const pkgs = data.filter((pkg: Package) => - compare(pkg.name, minPkgVersion!, '>='), - ); - if (pkgs.length === 0) { - throw new Error(t('nativeVersionNotFound', { version: minPkgVersion })); - } - if (rollout !== undefined) { - const rolloutConfig: Record = {}; - for (const pkg of pkgs) { - rolloutConfig[pkg.name] = rollout; - } - await put(`/app/${appId}/version/${versionId}`, { - config: { - rollout: rolloutConfig, - }, - }); - console.log( - `${t('rolloutConfigSet', { - versions: pkgs.map((pkg: Package) => pkg.name).join(', '), - rollout: rollout, - })}`, - ); - } - for (const pkg of pkgs) { - await put(`/app/${appId}/package/${pkg.id}`, { - versionId, - }); - console.log( - `${t('versionBind', { - version: versionId, - nativeVersion: pkg.name, - id: pkg.id, - })}`, - ); - } - console.log(t('operationComplete', { count: pkgs.length })); - return; - } - if (maxPkgVersion) { - maxPkgVersion = String(maxPkgVersion).trim(); - const { data } = await get(`/app/${appId}/package/list?limit=1000`); - const pkgs = data.filter((pkg: Package) => - compare(pkg.name, maxPkgVersion!, '<='), - ); - if (pkgs.length === 0) { - throw new Error( - t('nativeVersionNotFoundLess', { version: maxPkgVersion }), - ); - } - if (rollout !== undefined) { - const rolloutConfig: Record = {}; - for (const pkg of pkgs) { - rolloutConfig[pkg.name] = rollout; - } - await put(`/app/${appId}/version/${versionId}`, { - config: { - rollout: rolloutConfig, - }, - }); - console.log( - `${t('rolloutConfigSet', { - versions: pkgs.map((pkg: Package) => pkg.name).join(', '), - rollout: rollout, - })}`, - ); - } - for (const pkg of pkgs) { - await put(`/app/${appId}/package/${pkg.id}`, { - versionId, - }); - console.log( - `${t('versionBind', { - version: versionId, - nativeVersion: pkg.name, - id: pkg.id, - })}`, - ); - } - console.log(t('operationComplete', { count: pkgs.length })); - return; + const allPkgs = await getAllPackages(appId); + + if (!allPkgs) { + throw new Error(t('noPackagesFound', { appId })); } - const { data } = await get(`/app/${appId}/package/list?limit=1000`); - if (pkgVersion) { + let pkgsToBind: Package[] = []; + + if (minPkgVersion) { + minPkgVersion = String(minPkgVersion).trim(); + pkgsToBind = allPkgs.filter((pkg: Package) => + semverSatisfies(pkg.name, `>=${minPkgVersion}`), + ); + if (pkgsToBind.length === 0) { + throw new Error( + t('nativeVersionNotFoundGte', { version: minPkgVersion }), + ); + } + } else if (maxPkgVersion) { + maxPkgVersion = String(maxPkgVersion).trim(); + pkgsToBind = allPkgs.filter((pkg: Package) => + semverSatisfies(pkg.name, `<=${maxPkgVersion}`), + ); + if (pkgsToBind.length === 0) { + throw new Error( + t('nativeVersionNotFoundLte', { version: maxPkgVersion }), + ); + } + } else if (pkgVersion) { pkgVersion = pkgVersion.trim(); - const pkg = data.find((pkg: Package) => pkg.name === pkgVersion); + const pkg = allPkgs.find((pkg: Package) => pkg.name === pkgVersion); if (pkg) { - pkgId = pkg.id; + pkgsToBind = [pkg]; } else { throw new Error( t('nativeVersionNotFoundMatch', { version: pkgVersion }), ); } - } - if (!pkgId) { - pkgId = options.packageId || (await choosePackage(appId)).id; - } + } else if (semverRange) { + semverRange = semverRange.trim(); + pkgsToBind = allPkgs.filter((pkg: Package) => + semverSatisfies(pkg.name, semverRange!), + ); + if (pkgsToBind.length === 0) { + throw new Error( + t('nativeVersionNotFoundMatch', { version: semverRange }), + ); + } + } else { + if (!pkgId) { + pkgId = (await choosePackage(appId)).id; + } - if (!pkgId) { - throw new Error(t('packageIdRequired')); - } - - if (!pkgVersion) { - const pkg = data.find((pkg: Package) => String(pkg.id) === String(pkgId)); + if (!pkgId) { + throw new Error(t('packageIdRequired')); + } + const pkg = allPkgs.find( + (pkg: Package) => String(pkg.id) === String(pkgId), + ); if (pkg) { - pkgVersion = pkg.name; + pkgsToBind = [pkg]; + } else { + throw new Error(t('nativePackageIdNotFound', { id: pkgId })); } } - if (rollout !== undefined && pkgVersion) { - await put(`/app/${appId}/version/${versionId}`, { - config: { - rollout: { - [pkgVersion]: rollout, - }, - }, - }); - console.log( - `${t('rolloutConfigSet', { - versions: pkgVersion, - rollout: rollout, - })}`, - ); - } - - if (versionId !== undefined) { - await put(`/app/${appId}/package/${pkgId}`, { - versionId, - }); - console.log( - `${t('versionBind', { - version: versionId, - nativeVersion: pkgVersion, - id: pkgId, - })}`, - ); - } + await bindVersionToPackages({ + appId, + versionId, + pkgs: pkgsToBind, + rollout, + }); console.log(t('operationSuccess')); }, updateVersionInfo: async ({ @@ -347,10 +303,7 @@ export const commands = { args: string[]; options: CommandOptions; }) => { - const platform = checkPlatform( - options.platform || - ((await question('平台(ios/android/harmony):')) as Platform), - ); + const platform = await getPlatform(options.platform); const { appId } = await getSelectedApp(platform); const versionId = options.versionId || (await chooseVersion(appId)).id;