mirror of
https://gitcode.com/github-mirrors/react-native-update-cli.git
synced 2025-11-09 18:55:48 +08:00
Support sentry (#8)
* add logic to support sentry * udpate * change reference path * support git commits and version info * udate * add try catch for require.resolve * update upload sourcemap workflow
This commit is contained in:
115
src/bundle.js
115
src/bundle.js
@@ -3,7 +3,7 @@ import { getRNVersion, translateOptions } from './utils';
|
|||||||
import * as fs from 'fs-extra';
|
import * as fs from 'fs-extra';
|
||||||
import { ZipFile } from 'yazl';
|
import { ZipFile } from 'yazl';
|
||||||
import { open as openZipFile } from 'yauzl';
|
import { open as openZipFile } from 'yauzl';
|
||||||
import { question, printVersionCommand } from './utils';
|
import { question, checkPlugins } from './utils';
|
||||||
import { checkPlatform } from './app';
|
import { checkPlatform } from './app';
|
||||||
import { spawn, spawnSync } from 'node:child_process';
|
import { spawn, spawnSync } from 'node:child_process';
|
||||||
import semverSatisfies from 'semver/functions/satisfies';
|
import semverSatisfies from 'semver/functions/satisfies';
|
||||||
@@ -86,6 +86,9 @@ async function runReactNativeBundleCommand(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const bundleParams = await checkPlugins();
|
||||||
|
const minifyOption = bundleParams.minify;
|
||||||
|
const isSentry = bundleParams.sentry;
|
||||||
const bundleCommand = usingExpo
|
const bundleCommand = usingExpo
|
||||||
? 'export:embed'
|
? 'export:embed'
|
||||||
: platform === 'harmony'
|
: platform === 'harmony'
|
||||||
@@ -123,6 +126,8 @@ async function runReactNativeBundleCommand(
|
|||||||
'--platform',
|
'--platform',
|
||||||
platform,
|
platform,
|
||||||
'--reset-cache',
|
'--reset-cache',
|
||||||
|
'--minify',
|
||||||
|
minifyOption,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (sourcemapOutput) {
|
if (sourcemapOutput) {
|
||||||
@@ -190,6 +195,7 @@ async function runReactNativeBundleCommand(
|
|||||||
bundleName,
|
bundleName,
|
||||||
outputFolder,
|
outputFolder,
|
||||||
sourcemapOutput,
|
sourcemapOutput,
|
||||||
|
!isSentry,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
resolve(null);
|
resolve(null);
|
||||||
@@ -253,6 +259,7 @@ async function compileHermesByteCode(
|
|||||||
bundleName,
|
bundleName,
|
||||||
outputFolder,
|
outputFolder,
|
||||||
sourcemapOutput,
|
sourcemapOutput,
|
||||||
|
shouldCleanSourcemap,
|
||||||
) {
|
) {
|
||||||
console.log('Hermes enabled, now compiling to hermes bytecode:\n');
|
console.log('Hermes enabled, now compiling to hermes bytecode:\n');
|
||||||
// >= rn 0.69
|
// >= rn 0.69
|
||||||
@@ -309,9 +316,98 @@ async function compileHermesByteCode(
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if (shouldCleanSourcemap) {
|
||||||
|
fs.removeSync(path.join(outputFolder, `${bundleName}.txt.map`));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function copyDebugidForSentry(bundleName, outputFolder, sourcemapOutput) {
|
||||||
|
if (sourcemapOutput) {
|
||||||
|
let copyDebugidPath;
|
||||||
|
try {
|
||||||
|
copyDebugidPath = require.resolve(
|
||||||
|
'@sentry/react-native/scripts/copy-debugid.js',
|
||||||
|
{
|
||||||
|
paths: [process.cwd()],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(
|
||||||
|
'无法找到 Sentry copy-debugid.js 脚本文件,请确保已正确安装 @sentry/react-native',
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fs.existsSync(copyDebugidPath)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.log('Copying debugid');
|
||||||
|
spawnSync(
|
||||||
|
'node',
|
||||||
|
[
|
||||||
|
copyDebugidPath,
|
||||||
|
path.join(outputFolder, `${bundleName}.txt.map`),
|
||||||
|
path.join(outputFolder, `${bundleName}.map`),
|
||||||
|
],
|
||||||
|
{
|
||||||
|
stdio: 'ignore',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
fs.removeSync(path.join(outputFolder, `${bundleName}.txt.map`));
|
fs.removeSync(path.join(outputFolder, `${bundleName}.txt.map`));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function uploadSourcemapForSentry(
|
||||||
|
bundleName,
|
||||||
|
outputFolder,
|
||||||
|
sourcemapOutput,
|
||||||
|
version,
|
||||||
|
) {
|
||||||
|
if (sourcemapOutput) {
|
||||||
|
let sentryCliPath;
|
||||||
|
try {
|
||||||
|
sentryCliPath = require.resolve('@sentry/cli/bin/sentry-cli', {
|
||||||
|
paths: [process.cwd()],
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('无法找到 Sentry CLI 工具,请确保已正确安装 @sentry/cli');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fs.existsSync(sentryCliPath)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
spawnSync(
|
||||||
|
'node',
|
||||||
|
[sentryCliPath, 'releases', 'set-commits', version, '--auto'],
|
||||||
|
{
|
||||||
|
stdio: 'inherit',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
console.log(`Sentry release created for version: ${version}`);
|
||||||
|
|
||||||
|
console.log('Uploading sourcemap');
|
||||||
|
spawnSync(
|
||||||
|
'node',
|
||||||
|
[
|
||||||
|
sentryCliPath,
|
||||||
|
'releases',
|
||||||
|
'files',
|
||||||
|
version,
|
||||||
|
'upload-sourcemaps',
|
||||||
|
'--strip-prefix',
|
||||||
|
path.join(process.cwd(), outputFolder),
|
||||||
|
path.join(outputFolder, bundleName),
|
||||||
|
path.join(outputFolder, `${bundleName}.map`),
|
||||||
|
],
|
||||||
|
{
|
||||||
|
stdio: 'inherit',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function pack(dir, output) {
|
async function pack(dir, output) {
|
||||||
console.log('Packing');
|
console.log('Packing');
|
||||||
fs.ensureDirSync(path.dirname(output));
|
fs.ensureDirSync(path.dirname(output));
|
||||||
@@ -718,12 +814,16 @@ export const commands = {
|
|||||||
options.platform || (await question('平台(ios/android/harmony):')),
|
options.platform || (await question('平台(ios/android/harmony):')),
|
||||||
);
|
);
|
||||||
|
|
||||||
const { bundleName, entryFile, intermediaDir, output, dev, sourcemap } =
|
const { bundleName, entryFile, intermediaDir, output, dev } =
|
||||||
translateOptions({
|
translateOptions({
|
||||||
...options,
|
...options,
|
||||||
platform,
|
platform,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const bundleParams = await checkPlugins();
|
||||||
|
const sourcemap = bundleParams.sourcemap;
|
||||||
|
const isSentry = bundleParams.sentry;
|
||||||
|
|
||||||
const sourcemapOutput = path.join(intermediaDir, `${bundleName}.map`);
|
const sourcemapOutput = path.join(intermediaDir, `${bundleName}.map`);
|
||||||
|
|
||||||
const realOutput = output.replace(/\$\{time\}/g, `${Date.now()}`);
|
const realOutput = output.replace(/\$\{time\}/g, `${Date.now()}`);
|
||||||
@@ -749,12 +849,21 @@ export const commands = {
|
|||||||
|
|
||||||
const v = await question('是否现在上传此热更包?(Y/N)');
|
const v = await question('是否现在上传此热更包?(Y/N)');
|
||||||
if (v.toLowerCase() === 'y') {
|
if (v.toLowerCase() === 'y') {
|
||||||
await this.publish({
|
const versionName = await this.publish({
|
||||||
args: [realOutput],
|
args: [realOutput],
|
||||||
options: {
|
options: {
|
||||||
platform,
|
platform,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
if (isSentry) {
|
||||||
|
await copyDebugidForSentry(bundleName, intermediaDir, sourcemapOutput);
|
||||||
|
await uploadSourcemapForSentry(
|
||||||
|
bundleName,
|
||||||
|
intermediaDir,
|
||||||
|
sourcemapOutput,
|
||||||
|
versionName,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
30
src/utils/check-plugin.ts
Normal file
30
src/utils/check-plugin.ts
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import { plugins } from './plugin-config';
|
||||||
|
|
||||||
|
interface BundleParams {
|
||||||
|
sentry: boolean;
|
||||||
|
minify: boolean;
|
||||||
|
sourcemap: boolean;
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function checkPlugins(): Promise<BundleParams> {
|
||||||
|
const params: BundleParams = {
|
||||||
|
sentry: false,
|
||||||
|
minify: true,
|
||||||
|
sourcemap: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const plugin of plugins) {
|
||||||
|
try {
|
||||||
|
const isEnabled = await plugin.detect();
|
||||||
|
if (isEnabled && plugin.bundleParams) {
|
||||||
|
Object.assign(params, plugin.bundleParams);
|
||||||
|
console.log(`检测到 ${plugin.name} 插件,应用相应打包配置`);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.warn(`检测 ${plugin.name} 插件时出错:`, err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return params;
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ import AppInfoParser from './app-info-parser';
|
|||||||
import semverSatisfies from 'semver/functions/satisfies';
|
import semverSatisfies from 'semver/functions/satisfies';
|
||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
import latestVersion from '@badisi/latest-version';
|
import latestVersion from '@badisi/latest-version';
|
||||||
|
import { checkPlugins } from './check-plugin';
|
||||||
|
|
||||||
import { read } from 'read';
|
import { read } from 'read';
|
||||||
|
|
||||||
@@ -225,3 +226,5 @@ export async function printVersionCommand() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const pricingPageUrl = 'https://pushy.reactnative.cn/pricing.html';
|
export const pricingPageUrl = 'https://pushy.reactnative.cn/pricing.html';
|
||||||
|
|
||||||
|
export { checkPlugins };
|
||||||
|
|||||||
34
src/utils/plugin-config.ts
Normal file
34
src/utils/plugin-config.ts
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import fs from 'fs-extra';
|
||||||
|
|
||||||
|
interface PluginConfig {
|
||||||
|
name: string;
|
||||||
|
bundleParams?: {
|
||||||
|
minify?: boolean;
|
||||||
|
[key: string]: any;
|
||||||
|
};
|
||||||
|
detect: () => Promise<boolean>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const plugins: PluginConfig[] = [
|
||||||
|
{
|
||||||
|
name: 'sentry',
|
||||||
|
bundleParams: {
|
||||||
|
sentry: true,
|
||||||
|
minify: false,
|
||||||
|
sourcemap: true,
|
||||||
|
},
|
||||||
|
detect: async () => {
|
||||||
|
try {
|
||||||
|
await fs.access('ios/sentry.properties');
|
||||||
|
return true;
|
||||||
|
} catch {
|
||||||
|
try {
|
||||||
|
await fs.access('android/sentry.properties');
|
||||||
|
return true;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
@@ -97,8 +97,9 @@ export const commands = {
|
|||||||
|
|
||||||
const { hash } = await uploadFile(fn);
|
const { hash } = await uploadFile(fn);
|
||||||
|
|
||||||
|
const versionName = name || (await question('输入版本名称: ')) || '(未命名)';
|
||||||
const { id } = await post(`/app/${appId}/version/create`, {
|
const { id } = await post(`/app/${appId}/version/create`, {
|
||||||
name: name || (await question('输入版本名称: ')) || '(未命名)',
|
name: versionName,
|
||||||
hash,
|
hash,
|
||||||
description: description || (await question('输入版本描述:')),
|
description: description || (await question('输入版本描述:')),
|
||||||
metaInfo: metaInfo || (await question('输入自定义的 meta info:')),
|
metaInfo: metaInfo || (await question('输入自定义的 meta info:')),
|
||||||
@@ -111,6 +112,7 @@ export const commands = {
|
|||||||
if (v.toLowerCase() === 'y') {
|
if (v.toLowerCase() === 'y') {
|
||||||
await this.update({ args: [], options: { versionId: id, platform } });
|
await this.update({ args: [], options: { versionId: id, platform } });
|
||||||
}
|
}
|
||||||
|
return versionName;
|
||||||
},
|
},
|
||||||
versions: async ({ options }) => {
|
versions: async ({ options }) => {
|
||||||
const platform = checkPlatform(
|
const platform = checkPlatform(
|
||||||
|
|||||||
Reference in New Issue
Block a user