Skip to content

Commit

Permalink
Merge pull request #113 from bugsnag/release/v48.0.0
Browse files Browse the repository at this point in the history
  • Loading branch information
yousif-bugsnag authored Mar 7, 2023
2 parents c9f5910 + 532bddd commit f77ff7d
Show file tree
Hide file tree
Showing 27 changed files with 343 additions and 327 deletions.
13 changes: 12 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,19 @@
# Changelog

## v48.0.0 (2023-03-07)

This release adds support for expo 48

### Fixed

- (bugsnag-expo-cli) CLI tool now installs a sourcemap plugin version that matches the Expo SDK version [#111](https://github.com/bugsnag/bugsnag-expo/pull/111)
- (plugin-expo-eas-sourcemaps) Use EAS Build lifecycle hook for Android source map uploads [#112](https://github.com/bugsnag/bugsnag-expo/pull/112)

## v47.1.1 (2023-03-02)

(plugin-expo-eas-sourcemaps) Restrict Bugsnag Android Gradle Plugin dependency to v7 [#104](https://github.com/bugsnag/bugsnag-expo/pull/104)
### Fixed

- (plugin-expo-eas-sourcemaps) Restrict Bugsnag Android Gradle Plugin dependency to v7 [#104](https://github.com/bugsnag/bugsnag-expo/pull/104)

## v47.1.0 (2023-01-09)

Expand Down
26 changes: 13 additions & 13 deletions features/fixtures/test-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,23 @@
"web": "expo start --web"
},
"dependencies": {
"@react-native-community/netinfo": "9.3.5",
"expo": "~47.0.3",
"expo-application": "~5.0.1",
"expo-constants": "~14.0.2",
"expo-crypto": "~12.0.0",
"expo-device": "~5.0.0",
"expo-file-system": "~15.1.1",
"expo-screen-orientation": "~5.0.1",
"expo-status-bar": "~1.4.2",
"react": "18.1.0",
"react-native": "0.70.5"
"@react-native-community/netinfo": "9.3.7",
"expo": "~48.0.4",
"expo-application": "~5.1.1",
"expo-constants": "~14.2.1",
"expo-crypto": "~12.2.1",
"expo-device": "~5.2.1",
"expo-file-system": "~15.2.2",
"expo-screen-orientation": "~5.1.1",
"expo-status-bar": "~1.4.4",
"react": "18.2.0",
"react-native": "0.71.3"
},
"resolutions": {
"expo-modules-core": "1.0.4"
"expo-modules-core": "1.2.3"
},
"devDependencies": {
"@babel/core": "^7.19.3"
"@babel/core": "^7.20.0"
},
"private": true
}
2 changes: 1 addition & 1 deletion features/fixtures/test-app/run-bugsnag-expo-cli-install
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ set timeout -1
# add-hook
spawn npx bugsnag-expo-cli upload-sourcemaps

expect "Do you want to automatically upload source maps to Bugsnag? (this will modify your app.json)"
expect "Do you want to automatically upload source maps to Bugsnag? (this will modify your app.json and package.json)"
send -- "\r"

expect eof
Expand Down
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@
"eslint-plugin-promise": "^4.2.1",
"eslint-plugin-react": "^7.18.3",
"eslint-plugin-standard": "^4.0.1",
"expo": "^47.0.0",
"expo": "^48.0.0",
"jest": "^26.6.3",
"jest-expo": "^44.0.1",
"jest-expo": "^48.0.1",
"lerna": "^6.0.1",
"react": "18.1.0",
"react-native": "0.70.5",
"react": "18.2.0",
"react-native": "0.71.3",
"verdaccio": "^5.10.2"
},
"scripts": {
Expand Down
12 changes: 6 additions & 6 deletions packages/delivery-expo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@
"license": "MIT",
"devDependencies": {
"@bugsnag/core": "^7.16.0",
"@react-native-community/netinfo": "9.3.5",
"expo-crypto": "~12.0.0",
"expo-file-system": "~15.1.1"
"@react-native-community/netinfo": "9.3.7",
"expo-crypto": "~12.2.1",
"expo-file-system": "~15.2.2"
},
"peerDependencies": {
"@bugsnag/core": "^7.0.0",
"@react-native-community/netinfo": "9.3.5",
"expo-crypto": "~12.0.0",
"expo-file-system": "~15.1.1"
"@react-native-community/netinfo": "9.3.7",
"expo-crypto": "~12.2.1",
"expo-file-system": "~15.2.2"
}
}
11 changes: 8 additions & 3 deletions packages/expo-cli/commands/upload-sourcemaps.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ const prompts = require('prompts')
const addPlugin = require('../lib/configure-plugin')
const installPlugin = require('../lib/install-plugin')
const { onCancel, getDependencies } = require('../lib/utils')
const { isEarlierVersionThan } = require('../lib/version-information')
const { isEarlierVersionThan, getBugsnagVersionForExpoVersion } = require('../lib/version-information')
const { blue, yellow } = require('kleur')

const PLUGIN_NAME = '@bugsnag/plugin-expo-eas-sourcemaps'
Expand All @@ -23,7 +23,7 @@ module.exports = async (argv, globalOpts) => {
const res = await prompts({
type: 'confirm',
name: 'addPlugin',
message: 'Do you want to automatically upload source maps to Bugsnag? (this will modify your app.json)',
message: 'Do you want to automatically upload source maps to Bugsnag? (this will modify your app.json and package.json)',
initial: true
}, { onCancel })

Expand All @@ -38,7 +38,12 @@ module.exports = async (argv, globalOpts) => {
yarn: globalOpts.yarn
}

await installPlugin(projectRoot, options)
// install a plugin version that matches the SDK version, i.e. SDK 48 -> @bugsnag/plugin-expo-eas-sourcemaps@^48.0.0
// if there is no suitable bugsnag version we haven't yet released support for this Expo version, so install the latest
const versionInformation = getBugsnagVersionForExpoVersion(installedExpoVersion)
const pluginVersion = versionInformation ? versionInformation.bugsnagVersion : 'latest'

await installPlugin(pluginVersion, projectRoot, options)
}

console.log(blue('> Inserting EAS plugin into app.json'))
Expand Down
57 changes: 36 additions & 21 deletions packages/expo-cli/lib/configure-plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,23 +59,38 @@ module.exports = async (projectRoot) => {
conf.expo = conf.expo || {}
conf.expo.plugins = conf.expo.plugins || []
if (conf.expo.plugins.includes(plugin)) {
return plugin + ' is already installed'
console.log(blue('Plugin is already configured in app.json'))
} else {
conf.expo.plugins.push(plugin)
await promisify(writeFile)(appJsonPath, JSON.stringify(conf, null, 2), 'utf8')
}
conf.expo.plugins.push(plugin)

await promisify(writeFile)(appJsonPath, JSON.stringify(conf, null, 2), 'utf8')
// update package.json
try {
const packageJsonPath = join(projectRoot, 'package.json')
const packageJson = JSON.parse(await promisify(readFile)(packageJsonPath))

// add the post-build hook (if it doesn't already exist)
const sourceMapBuildHook = 'npx bugsnag-eas-build-on-success'
packageJson.scripts = packageJson.scripts || {}
const existingBuildHook = packageJson.scripts['eas-build-on-success']

if (existingBuildHook && existingBuildHook.includes(sourceMapBuildHook)) {
console.log(blue('EAS Build hook already configured in package.json'))
} else if (existingBuildHook) {
packageJson.scripts['eas-build-on-success'] = `${existingBuildHook} && ${sourceMapBuildHook}`
} else {
packageJson.scripts['eas-build-on-success'] = sourceMapBuildHook
}

// do we need to add monorepo configuration?
const withYarnClassic = await usingYarnClassic(projectRoot)
const addMonorepoConfig = await usingWorkspaces(projectRoot, withYarnClassic)
// do we need to add monorepo configuration?
const withYarnClassic = await usingYarnClassic(projectRoot)
const addMonorepoConfig = await usingWorkspaces(projectRoot, withYarnClassic)

if (addMonorepoConfig) {
console.log(blue('> yarn workspaces detected, updating config'))
if (addMonorepoConfig) {
console.log(blue('> yarn workspaces detected, updating config'))

try {
const sourceMaps = '@bugsnag/source-maps'
const packageJsonPath = join(projectRoot, 'package.json')
const packageJson = JSON.parse(await promisify(readFile)(packageJsonPath))

if (withYarnClassic) {
packageJson.workspaces = packageJson.workspaces || {}
Expand All @@ -86,17 +101,17 @@ module.exports = async (projectRoot) => {
packageJson.installConfig = packageJson.installConfig || {}
packageJson.installConfig.hoistingLimits = 'workspaces'
}
}

await promisify(writeFile)(packageJsonPath, JSON.stringify(packageJson, null, 2), 'utf8')
} catch (e) {
// swallow and rethrow for errors that we can produce better messaging
if (e.code === 'ENOENT') {
throw new Error(`Couldn’t find package.json in "${projectRoot}".`)
}
if (e.name === 'SyntaxError') {
throw new Error(`Couldn’t parse package.json because it wasn’t valid JSON: "${e.message}"`)
}
throw e
await promisify(writeFile)(packageJsonPath, JSON.stringify(packageJson, null, 2), 'utf8')
} catch (e) {
// swallow and rethrow for errors that we can produce better messaging
if (e.code === 'ENOENT') {
throw new Error(`Couldn’t find package.json in "${projectRoot}".`)
}
if (e.name === 'SyntaxError') {
throw new Error(`Couldn’t parse package.json because it wasn’t valid JSON: "${e.message}"`)
}
throw e
}
}
56 changes: 12 additions & 44 deletions packages/expo-cli/lib/install-plugin.js
Original file line number Diff line number Diff line change
@@ -1,46 +1,14 @@
const { spawn } = require('child_process')

function resolveCommand (options) {
const command = ['install', '@bugsnag/plugin-expo-eas-sourcemaps', '@bugsnag/source-maps']

if (options.npm) {
command.push('--npm')
command.push('--')
command.push('--save-dev')
}

if (options.yarn) {
command.push('--yarn')
command.push('--')
command.push('--dev')
}

return command
}

module.exports = (projectRoot, options) => {
return new Promise((resolve, reject) => {
const command = resolveCommand(options)
const proc = spawn('expo', command, { cwd: projectRoot })

// buffer output in case of an error
let stdout = ''
let stderr = ''
proc.stdout.on('data', d => { stdout += d })
proc.stderr.on('data', d => { stderr += d })

proc.on('error', err => { reject(err) })

proc.on('close', code => {
if (code === 0) {
return resolve()
}

reject(
new Error(
`Command exited with non-zero exit code (${code}) "expo ${command.join(' ')}"\nstdout:\n${stdout}\n\nstderr:\n${stderr}`
)
)
})
const { createForProject } = require('@expo/package-manager')
const { resolvePackageName } = require('./utils')

module.exports = (version, projectRoot, options) => {
const packages = [resolvePackageName('@bugsnag/plugin-expo-eas-sourcemaps', version), '@bugsnag/source-maps']

// Expo's package manager will reject with an error if the child process exits with a non-zero code
// it also buffers the output and attaches it to any errors - https://github.com/expo/spawn-async/blob/main/src/spawnAsync.ts
const packageManager = createForProject(projectRoot, options)
return packageManager.addDevAsync(packages).catch(error => {
error.message += `\nstdout:\n${error.stdout}\n\nstderr:\n${error.stderr}`
throw error
})
}
12 changes: 2 additions & 10 deletions packages/expo-cli/lib/install.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
const { spawn } = require('child_process')
const { DEPENDENCIES } = require('./utils')
const { DEPENDENCIES, resolvePackageName } = require('./utils')

function resolveCommand (version, options) {
const command = ['install', resolvePackageName(version)].concat(DEPENDENCIES)
const command = ['install', resolvePackageName('@bugsnag/expo', version)].concat(DEPENDENCIES)

if (options.npm) {
command.push('--npm')
Expand All @@ -15,14 +15,6 @@ function resolveCommand (version, options) {
return command
}

function resolvePackageName (version) {
if (version === 'latest') {
return '@bugsnag/expo'
}

return `@bugsnag/expo@${version}`
}

module.exports = (version, projectRoot, options) => {
return new Promise((resolve, reject) => {
const command = resolveCommand(version, options)
Expand Down
44 changes: 39 additions & 5 deletions packages/expo-cli/lib/test/configure-plugin.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const withFixture = require('./lib/with-fixture')
const configurePlugin = require('../configure-plugin')
const { readFile } = require('fs/promises')
const { blue } = require('kleur')

describe('expo-cli: upload sourcemaps configure-plugin', () => {
it('should work on a fresh project', async () => {
Expand All @@ -10,25 +11,58 @@ describe('expo-cli: upload sourcemaps configure-plugin', () => {

const appJsonRaw = await readFile(`${projectRoot}/app.json`, 'utf8')
const appJson = JSON.parse(appJsonRaw)

expect(appJson.expo.plugins).toContain('@bugsnag/plugin-expo-eas-sourcemaps')

const packageJsonRaw = await readFile(`${projectRoot}/package.json`, 'utf8')
const packageJson = JSON.parse(packageJsonRaw)
expect(packageJson.scripts['eas-build-on-success']).toContain('npx bugsnag-eas-build-on-success')
})
})

it('shouldn’t duplicate the hook config', async () => {
await withFixture('already-installed-02', async (projectRoot) => {
const msg = await configurePlugin(projectRoot)
expect(msg).toMatch(/ is already installed/)
const logSpy = jest.spyOn(console, 'log').mockImplementation(() => {})

await configurePlugin(projectRoot)
expect(logSpy).toHaveBeenCalledWith(blue('Plugin is already configured in app.json'))

const appJsonRaw = await readFile(`${projectRoot}/app.json`, 'utf8')
const appJson = JSON.parse(appJsonRaw)

expect(appJson.expo.plugins.length).toBe(1)
})
})

it('should create a basic file when there is no app.json', async () => {
it('shouldn’t duplicate the EAS build hook', async () => {
await withFixture('already-installed-01', async (projectRoot) => {
const logSpy = jest.spyOn(console, 'log').mockImplementation(() => {})

await configurePlugin(projectRoot)
expect(logSpy).toHaveBeenCalledWith(blue('EAS Build hook already configured in package.json'))

const packageJsonRaw = await readFile(`${projectRoot}/package.json`, 'utf8')
const packageJson = JSON.parse(packageJsonRaw)
expect(packageJson.scripts['eas-build-on-success']).toStrictEqual('npx bugsnag-eas-build-on-success')
})
})

it('should chain to an existing EAS build hook if present', async () => {
await withFixture('already-installed-02', async (projectRoot) => {
await configurePlugin(projectRoot)

const packageJsonRaw = await readFile(`${projectRoot}/package.json`, 'utf8')
const packageJson = JSON.parse(packageJsonRaw)
expect(packageJson.scripts['eas-build-on-success']).toStrictEqual('pre-existing-command && npx bugsnag-eas-build-on-success')
})
})

it('should provide a reasonable error when there is no package.json', async () => {
await withFixture('empty-00', async (projectRoot) => {
await expect(configurePlugin(projectRoot)).rejects.toThrow(/Couldn’t find package\.json/)
})
})

it('should create a basic file when there is no app.json', async () => {
await withFixture('empty-01', async (projectRoot) => {
const msg = await configurePlugin(projectRoot)
expect(msg).toBe(undefined)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,8 @@
"expo-crypto": "*",
"expo-device": "*",
"expo-file-system": "*"
},
"scripts": {
"eas-build-on-success": "npx bugsnag-eas-build-on-success"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,8 @@
"expo-crypto": "*",
"expo-device": "*",
"expo-file-system": "*"
},
"scripts": {
"eas-build-on-success": "pre-existing-command"
}
}
2 changes: 1 addition & 1 deletion packages/expo-cli/lib/test/fixtures/empty-00/README.md
Original file line number Diff line number Diff line change
@@ -1 +1 @@
This project has no app.json or App.js
This project has no app.json, App.js or package.json
1 change: 1 addition & 0 deletions packages/expo-cli/lib/test/fixtures/empty-01/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
This project has no app.json or App.js
Loading

0 comments on commit f77ff7d

Please sign in to comment.