|
import { getConfig } from '@expo/config' |
|
import fs from 'fs' |
|
import spawnAsync from '@expo/spawn-async' |
|
import chalk from 'chalk' |
|
import path from 'path' |
|
|
|
const appDir = process.cwd() |
|
|
|
console.log() |
|
console.log(chalk.green('Sentry source maps script. Working directory:')) |
|
console.log(appDir) |
|
|
|
console.log() |
|
|
|
console.log(chalk.green('Importing Expo Config...')) |
|
const config = getConfig(appDir, { skipSDKVersionRequirement: true }) |
|
if (!config) { |
|
throw new Error( |
|
'Failed to import Expo config. Are you in your app directory?' |
|
) |
|
} |
|
console.log('Expo Config imported for', chalk.blue(config.exp.name)) |
|
|
|
const skipUpdate = 'SKIP_EAS_UPDATE' in process.env |
|
|
|
const run = async () => { |
|
// read in arguments from the CLI script |
|
const [command, ..._args] = process.argv.slice(2) |
|
|
|
const args = [ |
|
..._args, |
|
'--message', |
|
`${process.env.EAS_UPDATE_MESSAGE!}`, |
|
'--branch', |
|
`${process.env.EAS_UPDATE_BRANCH!}`, |
|
] |
|
|
|
try { |
|
const updateProcess = spawnAsync(command, args, { |
|
stdio: ['inherit', 'pipe', 'pipe'], |
|
env: process.env, |
|
cwd: process.cwd(), |
|
}) |
|
const { |
|
child: { stdout, stderr }, |
|
} = updateProcess |
|
|
|
if (!(stdout && stderr)) { |
|
throw new Error('Failed to spawn eas-cli') |
|
} |
|
|
|
let output: string[] = [] |
|
console.log() |
|
console.log( |
|
chalk.green('[eas-update-sentry] Running the follwing command:') |
|
) |
|
console.log(command, args.join(' ')) |
|
console.log() |
|
stdout.on('data', (data: any) => { |
|
const stringData: string = data.toString('utf8') |
|
console.log(chalk.green('[eas-update-sentry]'), stringData) |
|
output = stringData.split('\n').map((s) => s.trim()) |
|
}) |
|
await updateProcess |
|
|
|
const findUpdateId = (output: string[], platform: 'android' | 'ios') => { |
|
return output |
|
.find((line) => line.toLowerCase().includes(`${platform} update id`)) |
|
?.split(' ') |
|
.map((r) => r.trim()) |
|
.pop() |
|
?.trim() |
|
} |
|
|
|
const iosUpdateId = findUpdateId(output, 'ios') |
|
const androidUpdateId = findUpdateId(output, 'android') |
|
|
|
const getBundles = () => { |
|
const bundles = fs.readdirSync(path.resolve(appDir, 'dist/bundles')) |
|
|
|
const iosBundle = bundles.find( |
|
(s) => s.startsWith('ios-') && s.endsWith('.js') |
|
) |
|
const iosMap = bundles.find( |
|
(s) => s.startsWith('ios-') && s.endsWith('.map') |
|
) |
|
|
|
const androidBundle = bundles.find( |
|
(s) => s.startsWith('android-') && s.endsWith('.js') |
|
) |
|
const androidMap = bundles.find( |
|
(s) => s.startsWith('android-') && s.endsWith('.map') |
|
) |
|
|
|
return { iosBundle, iosMap, androidBundle, androidMap } |
|
} |
|
const { iosBundle, iosMap, androidBundle, androidMap } = getBundles() |
|
|
|
const uploadSourceMap = async ({ |
|
updateId, |
|
buildNumber, |
|
bundleIdentifier, |
|
platform, |
|
}: { |
|
updateId: string |
|
|
|
buildNumber: string | number |
|
bundleIdentifier: string |
|
platform: 'android' | 'ios' |
|
}) => { |
|
const sentryConfig = config.exp.hooks?.postPublish?.find((h) => |
|
h.file?.includes('upload-sourcemaps') |
|
)?.config |
|
|
|
const version = config.exp.version || config.exp.runtimeVersion |
|
|
|
const result = spawnAsync( |
|
'npx', |
|
[ |
|
'@sentry/cli', |
|
'releases', |
|
'files', |
|
`${bundleIdentifier}@${version}+${buildNumber}`, |
|
'upload-sourcemaps', |
|
'--dist', |
|
updateId, |
|
'--rewrite', |
|
platform == 'ios' |
|
? `dist/bundles/main.jsbundle` |
|
: `dist/bundles/index.android.bundle`, |
|
platform == 'ios' |
|
? `dist/bundles/${iosMap}` |
|
: `dist/bundles/${androidMap}`, |
|
], |
|
{ |
|
env: { |
|
...process.env, |
|
SENTRY_ORG: sentryConfig?.organization || process.env.SENTRY_ORG, |
|
SENTRY_PROJECT: sentryConfig?.project || process.env.SENTRY_PROJECT, |
|
SENTRY_AUTH_TOKEN: |
|
sentryConfig?.authToken || process.env.SENTRY_AUTH_TOKEN, |
|
SENTRY_URL: |
|
sentryConfig?.url || |
|
process.env.SENTRY_URL || |
|
'https://sentry.io/', |
|
}, |
|
} |
|
) |
|
|
|
result.child.stdout?.on('data', (data) => { |
|
console.log( |
|
chalk.green('[eas-update-sentry]'), |
|
data |
|
.toString('utf8') |
|
.split('\n') |
|
.map((l: string) => `[Upload ${platform} sourcemaps] ${l}`) |
|
.join('\n') |
|
) |
|
}) |
|
|
|
await result |
|
} |
|
|
|
const uploadIosSourceMap = async () => { |
|
if (iosUpdateId && iosBundle && iosMap) { |
|
console.log() |
|
console.log( |
|
chalk.green('[eas-update-sentry] Updating iOS Bundle File...\n'), |
|
console.log('[update-id]', iosUpdateId) |
|
) |
|
|
|
fs.renameSync(`dist/bundles/${iosBundle}`, 'dist/bundles/main.jsbundle') |
|
const iOSConfig = { |
|
bundleIdentifier: config.exp.ios?.bundleIdentifier, |
|
buildNumber: config.exp.ios?.buildNumber, |
|
} |
|
if (Object.values(iOSConfig).every(Boolean)) { |
|
await uploadSourceMap({ |
|
updateId: iosUpdateId, |
|
buildNumber: iOSConfig.buildNumber!, |
|
bundleIdentifier: iOSConfig.bundleIdentifier!, |
|
platform: 'ios', |
|
}) |
|
} else { |
|
console.log( |
|
chalk.yellow( |
|
'[eas-update-sentry] Skipping iOS, missing the following values from your app.config file:', |
|
Object.entries(iOSConfig) |
|
.filter(([key, value]) => !value) |
|
.map(([key]) => key) |
|
.join(' ') |
|
) |
|
) |
|
} |
|
} else { |
|
console.log( |
|
chalk.yellow( |
|
'[eas-update-sentry] Skipping iOS, missing the following values:', |
|
Object.entries({ iosUpdateId, iosBundle, iosMap }) |
|
.filter(([key, value]) => !value) |
|
.map(([key]) => key) |
|
.join(' ') |
|
) |
|
) |
|
} |
|
} |
|
|
|
const uploadAndroidSourceMap = async () => { |
|
if (androidUpdateId && androidBundle && androidMap) { |
|
console.log() |
|
console.log( |
|
chalk.green('[eas-update-sentry] Updating Android Bundle File...') |
|
) |
|
|
|
fs.renameSync( |
|
`dist/bundles/${androidBundle}`, |
|
'dist/bundles/index.android.bundle' |
|
) |
|
const androidConfig = { |
|
package: config.exp.android?.package, |
|
versionCode: config.exp.android?.versionCode, |
|
} |
|
if (Object.values(androidConfig).every(Boolean)) { |
|
await uploadSourceMap({ |
|
updateId: androidUpdateId, |
|
buildNumber: androidConfig.versionCode!, |
|
bundleIdentifier: androidConfig.package!, |
|
platform: 'android', |
|
}) |
|
} else { |
|
console.log( |
|
chalk.yellow( |
|
'[eas-update-sentry] Skipping Android, missing the following values from your app.config file:', |
|
Object.entries(androidConfig) |
|
.filter(([key, value]) => !value) |
|
.map(([key]) => key) |
|
.join(' ') |
|
) |
|
) |
|
} |
|
} else { |
|
console.log( |
|
chalk.yellow( |
|
'[eas-update-sentry] Skipping Android, missing the following values:', |
|
Object.entries({ androidUpdateId, androidBundle, androidMap }) |
|
.filter(([key, value]) => !value) |
|
.map(([key]) => key) |
|
.join(' ') |
|
) |
|
) |
|
} |
|
} |
|
|
|
await Promise.all([uploadIosSourceMap(), uploadAndroidSourceMap()]) |
|
} catch (error: any) { |
|
process.exit() |
|
} |
|
} |
|
|
|
run().then((r) => { |
|
console.log(chalk.yellow('Done!')) |
|
}) |
Maybe try tweeting for expo to add support for this. I’ve tried my best