|
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!')) |
|
}) |
I think so, but you need to change the files to .hbc
I’m also facing issues where the CLI output isn’t read properly so it isn’t sending to sentry. I’m also using auto increment build numbers so it’s not uploading properly.
All-in-all, it’s super annoying that sentry hasn’t shipped this yet. Please see my open issue on their repo.