1
0
mirror of https://gitcode.com/github-mirrors/react-native-update-cli.git synced 2025-11-23 00:36:11 +08:00
Code Issues Packages Projects Releases Wiki Activity GitHub Gitee

Compare commits

...

20 Commits

Author SHA1 Message Date
sunnylqm
ebd6d36b30 Update version to 2.4.1 in package.json and streamline file and keyword declarations; import fetch in http-helper.ts for enhanced HTTP functionality. 2025-11-19 13:41:41 +08:00
sunnylqm
818907f811 Refactor rollout configuration handling in bindVersionToPackages; replace rollout logic with direct package binding and update API call to use POST method. 2025-11-04 21:52:25 +08:00
sunnylqm
6751daf8e0 Update version to 2.3.2 in package.json and enhance search paths for @expo/cli in runReactNativeBundleCommand to include expo package directory. 2025-10-31 16:07:02 +08:00
sunnylqm
b973ace443 Update version to 2.3.1 in package.json and add support for harmony platform in runReactNativeBundleCommand to handle asset copying and file movement. 2025-10-26 18:05:18 +08:00
sunnylqm
e81744b52a Refactor bundle file handling by introducing isPPKBundleFileName utility; streamline diff logic for bundle files in bundle.ts. 2025-10-25 16:07:41 +08:00
sunnylqm
89da1e5238 fix typo 2025-10-25 15:52:53 +08:00
sunnylqm
603e2adf47 add install command 2025-10-25 15:49:35 +08:00
sunnylqm
317975cdba Update version to 2.3.0 in package.json 2025-10-25 12:19:18 +08:00
sunnylqm
f8edbb8083 Refactor Hermes option handling in CLI and bundle commands; rename 'disableHermes' to 'hermes' for clarity and update related logic in bundle.ts and provider.ts. 2025-10-25 11:20:09 +08:00
sunnylqm
15c8052459 Update version to 2.2.4 in package.json and enhance argument handling in runReactNativeBundleCommand for improved functionality. 2025-10-25 10:53:50 +08:00
sunnylqm
c633af549d Update version to 2.2.3 in package.json and implement recursive file copying with special handling for assets in bundle.ts 2025-10-24 23:58:57 +08:00
sunnylqm
78159f362b Update version to 2.2.2 in package.json and refactor argument handling in runReactNativeBundleCommand for better clarity and efficiency. 2025-10-24 22:04:24 +08:00
sunnylqm
b76440d018 Update version to 2.2.1 in package.json and refactor argument handling in runReactNativeBundleCommand for improved clarity and efficiency. 2025-10-24 20:41:46 +08:00
Sunny Luo
e3c951bc1b Update package.json 2025-09-23 15:37:35 +08:00
波仔糕
f6ed8872bd add logic to support android aab package hot update (#17)
* add logic to support android aab package hot update

* update

* udpate
2025-09-23 15:22:34 +08:00
sunnylqm
fd46bafb98 Update version to 2.1.3 in package.json; set baseUrl in .swcrc; refactor API query logic to use dynamic base URL and update defaultEndpoints in constants.ts 2025-09-14 17:25:35 +08:00
sunny.ll
917f99f2ab Fix package selection logic to match by package name instead of version; improve error handling for package retrieval. 2025-09-10 17:11:12 +08:00
sunny.ll
ac13464d4d Enhance package command options to include packageId and packageVersion; improve package selection logic with error handling for missing packages. 2025-09-10 16:18:34 +08:00
sunny.ll
f82bab887a Remove console log for module registration in module-manager.ts 2025-09-10 15:38:56 +08:00
sunny.ll
07ff19fbe5 Update version to 2.1.1 in package.json and improve error handling for package loading in dep-versions.ts 2025-09-10 11:05:24 +08:00
18 changed files with 293 additions and 218 deletions

1
.swcrc
View File

@@ -1,5 +1,6 @@
{ {
"jsc": { "jsc": {
"baseUrl": "./src",
"loose": true, "loose": true,
"target": "es2018", "target": "es2018",
"parser": { "parser": {

View File

@@ -66,6 +66,12 @@
"options": { "options": {
"appId": { "appId": {
"hasValue": true "hasValue": true
},
"packageVersion": {
"hasValue": true
},
"packageId": {
"hasValue": true
} }
} }
}, },
@@ -207,7 +213,7 @@
"rncli": { "rncli": {
"default": false "default": false
}, },
"disableHermes": { "hermes": {
"default": false "default": false
}, },
"name": { "name": {
@@ -293,15 +299,6 @@
} }
} }
}, },
"hdiffFromPPK": {
"description": "Create hdiff patch from a Prepare package(.ppk)",
"options": {
"output": {
"default": "${tempDir}/output/hdiff-${time}.ppk-patch",
"hasValue": true
}
}
},
"hdiffFromApp": { "hdiffFromApp": {
"description": "Create hdiff patch from a Harmony package(.app)", "description": "Create hdiff patch from a Harmony package(.app)",
"options": { "options": {
@@ -337,6 +334,10 @@
"hasValue": true "hasValue": true
} }
} }
},
"install": {
"description": "Install optional dependencies to the CLI",
"options": {}
} }
}, },
"globalOptions": { "globalOptions": {

View File

@@ -1,17 +1,13 @@
{ {
"name": "react-native-update-cli", "name": "react-native-update-cli",
"version": "2.1.0", "version": "2.4.1",
"description": "command line tool for react-native-update (remote updates for react native)", "description": "command line tool for react-native-update (remote updates for react native)",
"main": "index.js", "main": "index.js",
"bin": { "bin": {
"pushy": "lib/index.js", "pushy": "lib/index.js",
"cresc": "lib/index.js" "cresc": "lib/index.js"
}, },
"files": [ "files": ["lib", "src", "cli.json"],
"lib",
"src",
"cli.json"
],
"scripts": { "scripts": {
"build": "swc src -d lib --strip-leading-paths", "build": "swc src -d lib --strip-leading-paths",
"prepublishOnly": "npm run build && chmod +x lib/index.js", "prepublishOnly": "npm run build && chmod +x lib/index.js",
@@ -21,13 +17,7 @@
"type": "git", "type": "git",
"url": "git+https://github.com/reactnativecn/react-native-update-cli.git" "url": "git+https://github.com/reactnativecn/react-native-update-cli.git"
}, },
"keywords": [ "keywords": ["react-native", "ios", "android", "harmony", "update"],
"react-native",
"ios",
"android",
"harmony",
"update"
],
"author": "reactnativecn", "author": "reactnativecn",
"license": "BSD-3-Clause", "license": "BSD-3-Clause",
"bugs": { "bugs": {
@@ -81,8 +71,5 @@
"@types/yazl": "^2.4.6", "@types/yazl": "^2.4.6",
"typescript": "^5.8.3" "typescript": "^5.8.3"
}, },
"trustedDependencies": [ "trustedDependencies": ["@biomejs/biome", "@swc/core"]
"@biomejs/biome",
"@swc/core"
]
} }

View File

@@ -10,18 +10,16 @@ import packageJson from '../package.json';
import type { Package, Session } from './types'; import type { Package, Session } from './types';
import { import {
credentialFile, credentialFile,
defaultEndpoint,
pricingPageUrl, pricingPageUrl,
} from './utils/constants'; } from './utils/constants';
import { t } from './utils/i18n'; import { t } from './utils/i18n';
import { getBaseUrl } from 'utils/http-helper';
const tcpPing = util.promisify(tcpp.ping); const tcpPing = util.promisify(tcpp.ping);
let session: Session | undefined; let session: Session | undefined;
let savedSession: Session | undefined; let savedSession: Session | undefined;
const host =
process.env.PUSHY_REGISTRY || process.env.RNU_API || defaultEndpoint;
const userAgent = `react-native-update-cli/${packageJson.version}`; const userAgent = `react-native-update-cli/${packageJson.version}`;
@@ -64,7 +62,9 @@ export const closeSession = () => {
}; };
async function query(url: string, options: fetch.RequestInit) { async function query(url: string, options: fetch.RequestInit) {
const resp = await fetch(url, options); const baseUrl = await getBaseUrl;
const fullUrl = `${baseUrl}${url}`;
const resp = await fetch(fullUrl, options);
const text = await resp.text(); const text = await resp.text();
let json: any; let json: any;
try { try {
@@ -83,7 +83,7 @@ async function query(url: string, options: fetch.RequestInit) {
function queryWithoutBody(method: string) { function queryWithoutBody(method: string) {
return (api: string) => return (api: string) =>
query(host + api, { query(api, {
method, method,
headers: { headers: {
'User-Agent': userAgent, 'User-Agent': userAgent,
@@ -94,7 +94,7 @@ function queryWithoutBody(method: string) {
function queryWithBody(method: string) { function queryWithBody(method: string) {
return (api: string, body?: Record<string, any>) => return (api: string, body?: Record<string, any>) =>
query(host + api, { query(api, {
method, method,
headers: { headers: {
'User-Agent': userAgent, 'User-Agent': userAgent,
@@ -116,6 +116,7 @@ export async function uploadFile(fn: string, key?: string) {
}); });
let realUrl = url; let realUrl = url;
if (backupUrl) { if (backupUrl) {
// @ts-ignore
if (global.USE_ACC_OSS) { if (global.USE_ACC_OSS) {
realUrl = backupUrl; realUrl = backupUrl;
} else { } else {

View File

@@ -16,7 +16,7 @@ import os from 'os';
const properties = require('properties'); const properties = require('properties');
import { addGitIgnore } from './utils/add-gitignore'; import { addGitIgnore } from './utils/add-gitignore';
import { checkLockFiles } from './utils/check-lockfile'; import { checkLockFiles } from './utils/check-lockfile';
import { tempDir } from './utils/constants'; import { isPPKBundleFileName, scriptName, tempDir } from './utils/constants';
import { depVersions } from './utils/dep-versions'; import { depVersions } from './utils/dep-versions';
import { t } from './utils/i18n'; import { t } from './utils/i18n';
import { versionCommands } from './versions'; import { versionCommands } from './versions';
@@ -42,7 +42,7 @@ async function runReactNativeBundleCommand({
platform, platform,
sourcemapOutput, sourcemapOutput,
config, config,
disableHermes, forceHermes,
cli, cli,
}: { }: {
bundleName: string; bundleName: string;
@@ -52,7 +52,7 @@ async function runReactNativeBundleCommand({
platform: string; platform: string;
sourcemapOutput: string; sourcemapOutput: string;
config?: string; config?: string;
disableHermes?: boolean; forceHermes?: boolean;
cli: { cli: {
taro?: boolean; taro?: boolean;
expo?: boolean; expo?: boolean;
@@ -75,27 +75,40 @@ async function runReactNativeBundleCommand({
const envArgs = process.env.PUSHY_ENV_ARGS; const envArgs = process.env.PUSHY_ENV_ARGS;
if (envArgs) { if (envArgs) {
Array.prototype.push.apply( reactNativeBundleArgs.push(...envArgs.trim().split(/\s+/));
reactNativeBundleArgs,
envArgs.trim().split(/\s+/),
);
} }
fs.emptyDirSync(outputFolder); fs.emptyDirSync(outputFolder);
let cliPath: string | undefined; let cliPath = '';
let usingExpo = false; let usingExpo = false;
const getExpoCli = () => { const getExpoCli = () => {
try { try {
const searchPaths = [process.cwd()];
// 尝试添加 expo 包的路径作为额外的搜索路径
try {
const expoPath = require.resolve('expo/package.json', {
paths: [process.cwd()],
});
// 获取 expo 包的目录路径
const expoDir = expoPath.replace(/\/package\.json$/, '');
searchPaths.push(expoDir);
} catch {
// expo 包不存在,忽略
}
// 尝试从搜索路径中解析 @expo/cli
cliPath = require.resolve('@expo/cli', { cliPath = require.resolve('@expo/cli', {
paths: [process.cwd()], paths: searchPaths,
}); });
const expoCliVersion = JSON.parse( const expoCliVersion = JSON.parse(
fs fs
.readFileSync( .readFileSync(
require.resolve('@expo/cli/package.json', { require.resolve('@expo/cli/package.json', {
paths: [process.cwd()], paths: searchPaths,
}), }),
) )
.toString(), .toString(),
@@ -104,7 +117,7 @@ async function runReactNativeBundleCommand({
if (satisfies(expoCliVersion, '>= 0.10.17')) { if (satisfies(expoCliVersion, '>= 0.10.17')) {
usingExpo = true; usingExpo = true;
} else { } else {
cliPath = undefined; cliPath = '';
} }
} catch (e) {} } catch (e) {}
}; };
@@ -167,48 +180,38 @@ async function runReactNativeBundleCommand({
} }
if (platform === 'harmony') { if (platform === 'harmony') {
Array.prototype.push.apply(reactNativeBundleArgs, [ bundleName = 'bundle.harmony.js';
cliPath, if (forceHermes === undefined) {
bundleCommand, // enable hermes by default for harmony
'--dev', forceHermes = true;
dev,
'--entry-file',
entryFile,
]);
if (sourcemapOutput) {
reactNativeBundleArgs.push('--sourcemap-output', sourcemapOutput);
} }
}
if (config) { reactNativeBundleArgs.push(
reactNativeBundleArgs.push('--config', config); cliPath,
} bundleCommand,
'--assets-dest',
outputFolder,
'--bundle-output',
path.join(outputFolder, bundleName),
);
if (platform !== 'harmony') {
reactNativeBundleArgs.push('--platform', platform, '--reset-cache');
}
if (cli.taro) {
reactNativeBundleArgs.push('--type', 'rn');
} else { } else {
Array.prototype.push.apply(reactNativeBundleArgs, [ reactNativeBundleArgs.push('--dev', dev, '--entry-file', entryFile);
cliPath, }
bundleCommand,
'--assets-dest',
outputFolder,
'--bundle-output',
path.join(outputFolder, bundleName),
'--platform',
platform,
'--reset-cache',
]);
if (cli.taro) { if (sourcemapOutput) {
reactNativeBundleArgs.push(...['--type', 'rn']); reactNativeBundleArgs.push('--sourcemap-output', sourcemapOutput);
} else { }
reactNativeBundleArgs.push(...['--dev', dev, '--entry-file', entryFile]);
}
if (sourcemapOutput) { if (config) {
reactNativeBundleArgs.push('--sourcemap-output', sourcemapOutput); reactNativeBundleArgs.push('--config', config);
}
if (config) {
reactNativeBundleArgs.push('--config', config);
}
} }
const reactNativeBundleProcess = spawn('node', reactNativeBundleArgs); const reactNativeBundleProcess = spawn('node', reactNativeBundleArgs);
@@ -231,9 +234,9 @@ async function runReactNativeBundleCommand({
} else { } else {
let hermesEnabled: boolean | undefined = false; let hermesEnabled: boolean | undefined = false;
if (disableHermes) { if (forceHermes) {
hermesEnabled = false; hermesEnabled = true;
console.log(t('hermesDisabled')); console.log(t('forceHermes'));
} else if (platform === 'android') { } else if (platform === 'android') {
const gradlePropeties = await new Promise<{ const gradlePropeties = await new Promise<{
hermesEnabled?: boolean; hermesEnabled?: boolean;
@@ -260,8 +263,6 @@ async function runReactNativeBundleCommand({
fs.existsSync('ios/Pods/hermes-engine') fs.existsSync('ios/Pods/hermes-engine')
) { ) {
hermesEnabled = true; hermesEnabled = true;
} else if (platform === 'harmony') {
await copyHarmonyBundle(outputFolder);
} }
if (hermesEnabled) { if (hermesEnabled) {
await compileHermesByteCode( await compileHermesByteCode(
@@ -271,45 +272,25 @@ async function runReactNativeBundleCommand({
!isSentry, !isSentry,
); );
} }
if (platform === 'harmony') {
const harmonyRawAssetsPath =
'harmony/entry/src/main/resources/rawfile/assets';
// copy all files in outputFolder to harmonyRawPath
// assets should be in rawfile/assets
fs.ensureDirSync(harmonyRawAssetsPath);
fs.copySync(outputFolder, harmonyRawAssetsPath, { overwrite: true });
fs.moveSync(
`${harmonyRawAssetsPath}/bundle.harmony.js`,
`${harmonyRawAssetsPath}/../bundle.harmony.js`,
{ overwrite: true },
);
}
resolve(null); resolve(null);
} }
}); });
}); });
} }
async function copyHarmonyBundle(outputFolder: string) {
const harmonyRawPath = 'harmony/entry/src/main/resources/rawfile';
try {
await fs.ensureDir(harmonyRawPath);
try {
await fs.access(harmonyRawPath, fs.constants.W_OK);
} catch (error) {
await fs.chmod(harmonyRawPath, 0o755);
}
await fs.remove(path.join(harmonyRawPath, 'update.json'));
await fs.copy('update.json', path.join(harmonyRawPath, 'update.json'));
await fs.ensureDir(outputFolder);
const files = await fs.readdir(harmonyRawPath);
for (const file of files) {
if (file !== 'update.json' && file !== 'meta.json') {
const sourcePath = path.join(harmonyRawPath, file);
const destPath = path.join(outputFolder, file);
const stat = await fs.stat(sourcePath);
if (stat.isFile()) {
await fs.copy(sourcePath, destPath);
} else if (stat.isDirectory()) {
await fs.copy(sourcePath, destPath);
}
}
}
} catch (error: any) {
console.error(t('copyHarmonyBundleError', { error }));
throw new Error(t('copyFileFailed', { error: error.message }));
}
}
function getHermesOSBin() { function getHermesOSBin() {
if (os.platform() === 'win32') return 'win64-bin'; if (os.platform() === 'win32') return 'win64-bin';
if (os.platform() === 'darwin') return 'osx-bin'; if (os.platform() === 'darwin') return 'osx-bin';
@@ -498,7 +479,12 @@ async function uploadSourcemapForSentry(
} }
} }
const ignorePackingFileNames = ['.', '..', 'index.bundlejs.map']; const ignorePackingFileNames = [
'.',
'..',
'index.bundlejs.map',
'bundle.harmony.js.map',
];
const ignorePackingExtensions = ['DS_Store', 'txt.map']; const ignorePackingExtensions = ['DS_Store', 'txt.map'];
async function pack(dir: string, output: string) { async function pack(dir: string, output: string) {
console.log(t('packing')); console.log(t('packing'));
@@ -580,10 +566,7 @@ async function diffFromPPK(origin: string, next: string, output: string) {
// isFile // isFile
originMap[entry.crc32] = entry.fileName; originMap[entry.crc32] = entry.fileName;
if ( if (isPPKBundleFileName(entry.fileName)) {
entry.fileName === 'index.bundlejs' ||
entry.fileName === 'bundle.harmony.js'
) {
// This is source. // This is source.
return readEntry(entry, zipFile).then((v) => (originSource = v)); return readEntry(entry, zipFile).then((v) => (originSource = v));
} }
@@ -591,12 +574,11 @@ async function diffFromPPK(origin: string, next: string, output: string) {
}); });
if (!originSource) { if (!originSource) {
throw new Error( throw new Error(t('bundleFileNotFound'));
'Bundle file not found! Please use default bundle file name and path.',
);
} }
const copies = {}; const copies = {};
const copiesv2 = {};
const zipfile = new YazlZipFile(); const zipfile = new YazlZipFile();
@@ -633,23 +615,13 @@ async function diffFromPPK(origin: string, next: string, output: string) {
if (!originEntries[entry.fileName]) { if (!originEntries[entry.fileName]) {
addEntry(entry.fileName); addEntry(entry.fileName);
} }
} else if (entry.fileName === 'index.bundlejs') { } else if (isPPKBundleFileName(entry.fileName)) {
//console.log('Found bundle'); //console.log('Found bundle');
return readEntry(entry, nextZipfile).then((newSource) => { return readEntry(entry, nextZipfile).then((newSource) => {
//console.log('Begin diff'); //console.log('Begin diff');
zipfile.addBuffer( zipfile.addBuffer(
diff(originSource, newSource), diff(originSource, newSource),
'index.bundlejs.patch', `${entry.fileName}.patch`,
);
//console.log('End diff');
});
} else if (entry.fileName === 'bundle.harmony.js') {
//console.log('Found bundle');
return readEntry(entry, nextZipfile).then((newSource) => {
//console.log('Begin diff');
zipfile.addBuffer(
diff(originSource, newSource),
'bundle.harmony.js.patch',
); );
//console.log('End diff'); //console.log('End diff');
}); });
@@ -668,6 +640,7 @@ async function diffFromPPK(origin: string, next: string, output: string) {
addEntry(base); addEntry(base);
} }
copies[entry.fileName] = originMap[entry.crc32]; copies[entry.fileName] = originMap[entry.crc32];
copiesv2[entry.crc32] = entry.fileName;
return; return;
} }
@@ -700,7 +673,7 @@ async function diffFromPPK(origin: string, next: string, output: string) {
//console.log({copies, deletes}); //console.log({copies, deletes});
zipfile.addBuffer( zipfile.addBuffer(
Buffer.from(JSON.stringify({ copies, deletes })), Buffer.from(JSON.stringify({ copies, copiesv2, deletes })),
'__diff.json', '__diff.json',
); );
zipfile.end(); zipfile.end();
@@ -741,12 +714,11 @@ async function diffFromPackage(
}); });
if (!originSource) { if (!originSource) {
throw new Error( throw new Error(t('bundleFileNotFound'));
'Bundle file not found! Please use default bundle file name and path.',
);
} }
const copies = {}; const copies = {};
const copiesv2 = {};
const zipfile = new YazlZipFile(); const zipfile = new YazlZipFile();
@@ -763,23 +735,13 @@ async function diffFromPackage(
if (/\/$/.test(entry.fileName)) { if (/\/$/.test(entry.fileName)) {
// Directory // Directory
zipfile.addEmptyDirectory(entry.fileName); zipfile.addEmptyDirectory(entry.fileName);
} else if (entry.fileName === 'index.bundlejs') { } else if (isPPKBundleFileName(entry.fileName)) {
//console.log('Found bundle'); //console.log('Found bundle');
return readEntry(entry, nextZipfile).then((newSource) => { return readEntry(entry, nextZipfile).then((newSource) => {
//console.log('Begin diff'); //console.log('Begin diff');
zipfile.addBuffer( zipfile.addBuffer(
diff(originSource, newSource), diff(originSource, newSource),
'index.bundlejs.patch', `${entry.fileName}.patch`,
);
//console.log('End diff');
});
} else if (entry.fileName === 'bundle.harmony.js') {
//console.log('Found bundle');
return readEntry(entry, nextZipfile).then((newSource) => {
//console.log('Begin diff');
zipfile.addBuffer(
diff(originSource, newSource),
'bundle.harmony.js.patch',
); );
//console.log('End diff'); //console.log('End diff');
}); });
@@ -792,6 +754,7 @@ async function diffFromPackage(
// If moved from other place // If moved from other place
if (originMap[entry.crc32]) { if (originMap[entry.crc32]) {
copies[entry.fileName] = originMap[entry.crc32]; copies[entry.fileName] = originMap[entry.crc32];
copiesv2[entry.crc32] = entry.fileName;
return; return;
} }
@@ -810,7 +773,10 @@ async function diffFromPackage(
} }
}); });
zipfile.addBuffer(Buffer.from(JSON.stringify({ copies })), '__diff.json'); zipfile.addBuffer(
Buffer.from(JSON.stringify({ copies, copiesv2 })),
'__diff.json',
);
zipfile.end(); zipfile.end();
await writePromise; await writePromise;
} }
@@ -892,24 +858,21 @@ function diffArgsCheck(args: string[], options: any, diffFn: string) {
if (diffFn.startsWith('hdiff')) { if (diffFn.startsWith('hdiff')) {
if (!hdiff) { if (!hdiff) {
console.error( console.error(t('nodeHdiffpatchRequired', { scriptName }));
`This function needs "node-hdiffpatch".
Please run "npm i node-hdiffpatch" to install`,
);
process.exit(1); process.exit(1);
} }
diff = hdiff; diff = hdiff;
} else { } else {
if (!bsdiff) { if (!bsdiff) {
console.error( console.error(t('nodeBsdiffRequired', { scriptName }));
`This function needs "node-bsdiff".
Please run "npm i node-bsdiff" to install`,
);
process.exit(1); process.exit(1);
} }
diff = bsdiff; diff = bsdiff;
} }
const { output } = options; const { output } = translateOptions({
...options,
tempDir,
});
return { return {
origin, origin,
@@ -932,7 +895,7 @@ export const bundleCommands = {
taro, taro,
expo, expo,
rncli, rncli,
disableHermes, hermes,
name, name,
description, description,
metaInfo, metaInfo,
@@ -973,7 +936,7 @@ export const bundleCommands = {
outputFolder: intermediaDir, outputFolder: intermediaDir,
platform, platform,
sourcemapOutput: sourcemap || sourcemapPlugin ? sourcemapOutput : '', sourcemapOutput: sourcemap || sourcemapPlugin ? sourcemapOutput : '',
disableHermes: !!disableHermes, forceHermes: hermes as unknown as boolean,
cli: { cli: {
taro: !!taro, taro: !!taro,
expo: !!expo, expo: !!expo,
@@ -1040,14 +1003,14 @@ export const bundleCommands = {
const { origin, next, realOutput } = diffArgsCheck(args, options, 'diff'); const { origin, next, realOutput } = diffArgsCheck(args, options, 'diff');
await diffFromPPK(origin, next, realOutput); await diffFromPPK(origin, next, realOutput);
console.log(`${realOutput} generated.`); console.log(t('diffPackageGenerated', { output: realOutput }));
}, },
async hdiff({ args, options }) { async hdiff({ args, options }) {
const { origin, next, realOutput } = diffArgsCheck(args, options, 'hdiff'); const { origin, next, realOutput } = diffArgsCheck(args, options, 'hdiff');
await diffFromPPK(origin, next, realOutput); await diffFromPPK(origin, next, realOutput);
console.log(`${realOutput} generated.`); console.log(t('diffPackageGenerated', { output: realOutput }));
}, },
async diffFromApk({ args, options }) { async diffFromApk({ args, options }) {
@@ -1063,7 +1026,7 @@ export const bundleCommands = {
realOutput, realOutput,
'assets/index.android.bundle', 'assets/index.android.bundle',
); );
console.log(`${realOutput} generated.`); console.log(t('diffPackageGenerated', { output: realOutput }));
}, },
async hdiffFromApk({ args, options }) { async hdiffFromApk({ args, options }) {
@@ -1079,7 +1042,7 @@ export const bundleCommands = {
realOutput, realOutput,
'assets/index.android.bundle', 'assets/index.android.bundle',
); );
console.log(`${realOutput} generated.`); console.log(t('diffPackageGenerated', { output: realOutput }));
}, },
async diffFromApp({ args, options }) { async diffFromApp({ args, options }) {
@@ -1094,7 +1057,7 @@ export const bundleCommands = {
realOutput, realOutput,
'resources/rawfile/bundle.harmony.js', 'resources/rawfile/bundle.harmony.js',
); );
console.log(`${realOutput} generated.`); console.log(t('diffPackageGenerated', { output: realOutput }));
}, },
async hdiffFromApp({ args, options }) { async hdiffFromApp({ args, options }) {
@@ -1109,7 +1072,7 @@ export const bundleCommands = {
realOutput, realOutput,
'resources/rawfile/bundle.harmony.js', 'resources/rawfile/bundle.harmony.js',
); );
console.log(`${realOutput} generated.`); console.log(t('diffPackageGenerated', { output: realOutput }));
}, },
async diffFromIpa({ args, options }) { async diffFromIpa({ args, options }) {
@@ -1124,7 +1087,7 @@ export const bundleCommands = {
return m?.[1]; return m?.[1];
}); });
console.log(`${realOutput} generated.`); console.log(t('diffPackageGenerated', { output: realOutput }));
}, },
async hdiffFromIpa({ args, options }) { async hdiffFromIpa({ args, options }) {
@@ -1139,6 +1102,6 @@ export const bundleCommands = {
return m?.[1]; return m?.[1];
}); });
console.log(`${realOutput} generated.`); console.log(t('diffPackageGenerated', { output: realOutput }));
}, },
}; };

View File

@@ -3,6 +3,7 @@
import { loadSession } from './api'; import { loadSession } from './api';
import { appCommands } from './app'; import { appCommands } from './app';
import { bundleCommands } from './bundle'; import { bundleCommands } from './bundle';
import { installCommands } from './install';
import { moduleManager } from './module-manager'; import { moduleManager } from './module-manager';
import { builtinModules } from './modules'; import { builtinModules } from './modules';
import { packageCommands } from './package'; import { packageCommands } from './package';
@@ -33,6 +34,7 @@ function printUsage() {
...appCommands, ...appCommands,
...packageCommands, ...packageCommands,
...versionCommands, ...versionCommands,
...installCommands,
}; };
for (const [name, handler] of Object.entries(legacyCommands)) { for (const [name, handler] of Object.entries(legacyCommands)) {
@@ -76,6 +78,7 @@ const legacyCommands = {
...appCommands, ...appCommands,
...packageCommands, ...packageCommands,
...versionCommands, ...versionCommands,
...installCommands,
help: printUsage, help: printUsage,
}; };

19
src/install.ts Normal file
View File

@@ -0,0 +1,19 @@
import { spawnSync } from 'child_process';
import path from 'path';
import type { CommandContext } from './types';
export const installCommands = {
install: async ({ args }: CommandContext) => {
if (args.length === 0) {
return;
}
const cliDir = path.resolve(__dirname, '..');
spawnSync('npm', ['install', ...args], {
cwd: cliDir,
stdio: 'inherit',
shell: true,
});
},
};

View File

@@ -49,7 +49,7 @@ export default {
fileGenerated: '{{- file}} generated.', fileGenerated: '{{- file}} generated.',
fileSizeExceeded: fileSizeExceeded:
'This file size is {{fileSize}} , exceeding the current quota {{maxSize}} . You may consider upgrading to a higher plan to increase this quota. Details can be found at: {{- pricingPageUrl}}', 'This file size is {{fileSize}} , exceeding the current quota {{maxSize}} . You may consider upgrading to a higher plan to increase this quota. Details can be found at: {{- pricingPageUrl}}',
hermesDisabled: 'Hermes disabled', forceHermes: 'Forcing Hermes enabled for this build',
hermesEnabledCompiling: 'Hermes enabled, now compiling to hermes bytecode:\n', hermesEnabledCompiling: 'Hermes enabled, now compiling to hermes bytecode:\n',
ipaUploadSuccess: ipaUploadSuccess:
'Successfully uploaded IPA native package (id: {{id}}, version: {{version}}, buildTime: {{buildTime}})', 'Successfully uploaded IPA native package (id: {{id}}, version: {{version}}, buildTime: {{buildTime}})',
@@ -137,4 +137,11 @@ This can reduce the risk of inconsistent dependencies and supply chain attacks.
deletePackageError: deletePackageError:
'Failed to delete native package {{packageId}}: {{error}}', 'Failed to delete native package {{packageId}}: {{error}}',
usageDeletePackage: 'Usage: cresc deletePackage [packageId] --appId [appId]', usageDeletePackage: 'Usage: cresc deletePackage [packageId] --appId [appId]',
bundleFileNotFound:
'Bundle file not found! Please use default bundle file name and path.',
diffPackageGenerated: '{{- output}} generated.',
nodeBsdiffRequired:
'This function needs "node-bsdiff". Please run "{{scriptName}} install node-bsdiff" to install',
nodeHdiffpatchRequired:
'This function needs "node-hdiffpatch". Please run "{{scriptName}} install node-hdiffpatch" to install',
}; };

View File

@@ -47,7 +47,7 @@ export default {
fileGenerated: '已生成 {{- file}}', fileGenerated: '已生成 {{- file}}',
fileSizeExceeded: fileSizeExceeded:
'此文件大小 {{fileSize}} , 超出当前额度 {{maxSize}} 。您可以考虑升级付费业务以提升此额度。详情请访问: {{- pricingPageUrl}}', '此文件大小 {{fileSize}} , 超出当前额度 {{maxSize}} 。您可以考虑升级付费业务以提升此额度。详情请访问: {{- pricingPageUrl}}',
hermesDisabled: 'Hermes 已禁用', forceHermes: '强制启用 Hermes 编译',
hermesEnabledCompiling: 'Hermes 已启用,正在编译为 hermes 字节码:\n', hermesEnabledCompiling: 'Hermes 已启用,正在编译为 hermes 字节码:\n',
ipaUploadSuccess: ipaUploadSuccess:
'已成功上传ipa原生包id: {{id}}, version: {{version}}, buildTime: {{buildTime}}', '已成功上传ipa原生包id: {{id}}, version: {{version}}, buildTime: {{buildTime}}',
@@ -129,4 +129,10 @@ export default {
deletePackageError: '删除原生包 {{packageId}} 失败: {{error}}', deletePackageError: '删除原生包 {{packageId}} 失败: {{error}}',
usageDeletePackage: usageDeletePackage:
'使用方法: pushy deletePackage [packageId] --appId [appId]', '使用方法: pushy deletePackage [packageId] --appId [appId]',
bundleFileNotFound: '未找到 bundle 文件!请使用默认的 bundle 文件名和路径。',
diffPackageGenerated: '{{- output}} 已生成。',
nodeBsdiffRequired:
'此功能需要 "node-bsdiff"。请运行 "{{scriptName}} install node-bsdiff" 来安装',
nodeHdiffpatchRequired:
'此功能需要 "node-hdiffpatch"。请运行 "{{scriptName}} install node-hdiffpatch" 来安装',
}; };

View File

@@ -39,9 +39,9 @@ export class ModuleManager {
module.init(this.provider); module.init(this.provider);
} }
console.log( // console.log(
`Module '${module.name}' (v${module.version}) registered successfully`, // `Module '${module.name}' (v${module.version}) registered successfully`,
); // );
} }
unregisterModule(moduleName: string): void { unregisterModule(moduleName: string): void {

View File

@@ -53,7 +53,7 @@ export const bundleModule: CLIModule = {
taro = false, taro = false,
expo = false, expo = false,
rncli = false, rncli = false,
disableHermes = false, hermes = false,
output, output,
} = context.options; } = context.options;
@@ -73,7 +73,7 @@ export const bundleModule: CLIModule = {
taro, taro,
expo, expo,
rncli, rncli,
disableHermes, hermes,
intermediaDir: '${tempDir}/intermedia/${platform}', intermediaDir: '${tempDir}/intermedia/${platform}',
output: '${tempDir}/output/${platform}.${time}.ppk', output: '${tempDir}/output/${platform}.${time}.ppk',
}; };
@@ -170,10 +170,10 @@ export const bundleModule: CLIModule = {
default: false, default: false,
description: 'Use React Native CLI', description: 'Use React Native CLI',
}, },
disableHermes: { hermes: {
hasValue: false, hasValue: false,
default: false, default: false,
description: 'Disable Hermes', description: 'Force enable Hermes',
}, },
name: { name: {
hasValue: true, hasValue: true,

View File

@@ -217,10 +217,9 @@ export const packageCommands = {
options, options,
}: { }: {
args: string[]; args: string[];
options: { appId?: string }; options: { appId?: string, packageId?: string, packageVersion?: string };
}) => { }) => {
let packageId = args[0]; let { appId, packageId, packageVersion } = options;
let { appId } = options;
if (!appId) { if (!appId) {
const platform = await getPlatform(); const platform = await getPlatform();
@@ -229,7 +228,14 @@ export const packageCommands = {
// If no packageId provided as argument, let user choose from list // If no packageId provided as argument, let user choose from list
if (!packageId) { if (!packageId) {
const selectedPackage = await choosePackage(appId); const allPkgs = await getAllPackages(appId);
if (!allPkgs) {
throw new Error(t('noPackagesFound', { appId }));
}
const selectedPackage = allPkgs.find((pkg) => pkg.name === packageVersion);
if (!selectedPackage) {
throw new Error(t('packageNotFound', { packageVersion }));
}
packageId = selectedPackage.id; packageId = selectedPackage.id;
} }

View File

@@ -42,7 +42,7 @@ export class CLIProviderImpl implements CLIProvider {
taro: options.taro || false, taro: options.taro || false,
expo: options.expo || false, expo: options.expo || false,
rncli: options.rncli || false, rncli: options.rncli || false,
disableHermes: options.disableHermes || false, hermes: options.hermes || false,
}, },
}; };

View File

@@ -65,7 +65,7 @@ export interface BundleOptions {
taro?: boolean; taro?: boolean;
expo?: boolean; expo?: boolean;
rncli?: boolean; rncli?: boolean;
disableHermes?: boolean; hermes?: boolean;
} }
export interface PublishOptions { export interface PublishOptions {

View File

@@ -1,8 +1,12 @@
import path from 'path'; import path from 'path';
const scriptName = path.basename(process.argv[1]) as 'cresc' | 'pushy'; export const scriptName = path.basename(process.argv[1]) as 'cresc' | 'pushy';
export const IS_CRESC = scriptName === 'cresc'; export const IS_CRESC = scriptName === 'cresc';
export const ppkBundleFileNames = ['index.bundlejs', 'bundle.harmony.js'];
export const isPPKBundleFileName = (fileName: string) =>
ppkBundleFileNames.includes(fileName);
export const credentialFile = IS_CRESC ? '.cresc.token' : '.update'; export const credentialFile = IS_CRESC ? '.cresc.token' : '.update';
export const updateJson = IS_CRESC ? 'cresc.config.json' : 'update.json'; export const updateJson = IS_CRESC ? 'cresc.config.json' : 'update.json';
export const tempDir = IS_CRESC ? '.cresc.temp' : '.pushy'; export const tempDir = IS_CRESC ? '.cresc.temp' : '.pushy';
@@ -10,6 +14,6 @@ export const pricingPageUrl = IS_CRESC
? 'https://cresc.dev/pricing' ? 'https://cresc.dev/pricing'
: 'https://pushy.reactnative.cn/pricing.html'; : 'https://pushy.reactnative.cn/pricing.html';
export const defaultEndpoint = IS_CRESC export const defaultEndpoints = IS_CRESC
? 'https://api.cresc.dev' ? ['https://api.cresc.dev', 'https://api.cresc.app']
: 'https://update.reactnative.cn/api'; : ['https://update.reactnative.cn/api', 'https://update.react-native.cn/api'];

View File

@@ -1,4 +1,9 @@
const currentPackage = require(`${process.cwd()}/package.json`); let currentPackage = null;
try {
currentPackage = require(`${process.cwd()}/package.json`);
} catch (e) {
// console.warn('No package.json file were found');
}
const _depVersions: Record<string, string> = {}; const _depVersions: Record<string, string> = {};
@@ -24,12 +29,9 @@ if (currentPackage) {
export const depVersions = Object.keys(_depVersions) export const depVersions = Object.keys(_depVersions)
.sort() // Sort the keys alphabetically .sort() // Sort the keys alphabetically
.reduce( .reduce((obj, key) => {
(obj, key) => { obj[key] = _depVersions[key]; // Rebuild the object with sorted keys
obj[key] = _depVersions[key]; // Rebuild the object with sorted keys return obj;
return obj; }, {} as Record<string, string>);
},
{} as Record<string, string>,
);
// console.log({ depVersions }); // console.log({ depVersions });

84
src/utils/http-helper.ts Normal file
View File

@@ -0,0 +1,84 @@
import { defaultEndpoints } from './constants';
import fetch from 'node-fetch';
// const baseUrl = `http://localhost:9000`;
// let baseUrl = SERVER.main[0];
// const baseUrl = `https://p.reactnative.cn/api`;
export function promiseAny<T>(promises: Promise<T>[]) {
return new Promise<T>((resolve, reject) => {
let count = 0;
for (const promise of promises) {
Promise.resolve(promise)
.then(resolve)
.catch(() => {
count++;
if (count === promises.length) {
reject(new Error('All promises were rejected'));
}
});
}
});
}
export const ping = async (url: string) => {
let pingFinished = false;
return Promise.race([
fetch(url, {
method: 'HEAD',
})
.then(({ status, statusText }) => {
pingFinished = true;
if (status === 200) {
// console.log('ping success', url);
return url;
}
// console.log('ping failed', url, status, statusText);
throw new Error('ping failed');
})
.catch((e) => {
pingFinished = true;
// console.log('ping error', url, e);
throw new Error('ping error');
}),
new Promise((_, reject) =>
setTimeout(() => {
reject(new Error('ping timeout'));
if (!pingFinished) {
// console.log('ping timeout', url);
}
}, 2000),
),
]) as Promise<string | null>;
};
export const testUrls = async (urls?: string[]) => {
if (!urls?.length) {
return null;
}
const ret = await promiseAny(urls.map(ping));
if (ret) {
return ret;
}
// console.log('all ping failed, use first url:', urls[0]);
return urls[0];
};
export const getBaseUrl = (async () => {
const testEndpoint = process.env.PUSHY_REGISTRY || process.env.RNU_API;
if (testEndpoint) {
return testEndpoint;
}
return testUrls(defaultEndpoints.map((url) => `${url}/status`)).then(
(ret) => {
let baseUrl = defaultEndpoints[0];
if (ret) {
// remove /status
baseUrl = ret.replace('/status', '');
}
// console.log('baseUrl', baseUrl);
return baseUrl;
},
);
})();

View File

@@ -122,17 +122,6 @@ export const bindVersionToPackages = async ({
console.log(chalk.yellow(t('dryRun'))); console.log(chalk.yellow(t('dryRun')));
} }
if (rollout !== undefined) { if (rollout !== undefined) {
const rolloutConfig: Record<string, number> = {};
for (const pkg of pkgs) {
rolloutConfig[pkg.name] = rollout;
}
if (!dryRun) {
await put(`/app/${appId}/version/${versionId}`, {
config: {
rollout: rolloutConfig,
},
});
}
console.log( console.log(
`${t('rolloutConfigSet', { `${t('rolloutConfigSet', {
versions: pkgs.map((pkg: Package) => pkg.name).join(', '), versions: pkgs.map((pkg: Package) => pkg.name).join(', '),
@@ -142,8 +131,10 @@ export const bindVersionToPackages = async ({
} }
for (const pkg of pkgs) { for (const pkg of pkgs) {
if (!dryRun) { if (!dryRun) {
await put(`/app/${appId}/package/${pkg.id}`, { await post(`/app/${appId}/binding`, {
versionId, versionId,
rollout,
packageId: pkg.id,
}); });
} }
console.log( console.log(