-
-
Notifications
You must be signed in to change notification settings - Fork 48
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(android): Support for Android wizard (#389)
- Loading branch information
Showing
17 changed files
with
823 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,7 +14,7 @@ build | |
node_modules | ||
npm-debug.log | ||
ios | ||
android | ||
./android | ||
yarn-error.log | ||
|
||
scratch/ | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import { Answers } from 'inquirer'; | ||
import { BaseIntegration } from './BaseIntegration'; | ||
import { Args } from '../../Constants'; | ||
import { runAndroidWizard } from '../../../src/android/android-wizard'; | ||
|
||
export class Android extends BaseIntegration { | ||
public constructor(protected _argv: Args) { | ||
super(_argv); | ||
} | ||
|
||
public async emit(_answers: Answers): Promise<Answers> { | ||
await runAndroidWizard({ | ||
promoCode: this._argv.promoCode, | ||
url: this._argv.url, | ||
telemetryEnabled: !this._argv.disableTelemetry, | ||
}); | ||
return {}; | ||
} | ||
|
||
public shouldConfigure(_answers: Answers): Promise<Answers> { | ||
return this._shouldConfigure; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */ | ||
import * as fs from 'fs'; | ||
// @ts-ignore - clack is ESM and TS complains about that. It works though | ||
import * as clack from '@clack/prompts'; | ||
import * as path from 'path'; | ||
import * as gradle from './gradle'; | ||
import * as manifest from './manifest'; | ||
import * as codetools from './code-tools'; | ||
import { | ||
abort, | ||
confirmContinueEvenThoughNoGitRepo, | ||
getOrAskForProjectData, | ||
printWelcome, | ||
} from '../utils/clack-utils'; | ||
import { WizardOptions } from '../utils/types'; | ||
import { traceStep, withTelemetry } from '../telemetry'; | ||
import chalk from 'chalk'; | ||
|
||
export async function runAndroidWizard(options: WizardOptions): Promise<void> { | ||
return withTelemetry( | ||
{ | ||
enabled: options.telemetryEnabled, | ||
integration: 'android', | ||
}, | ||
() => runAndroidWizardWithTelemetry(options), | ||
); | ||
} | ||
|
||
async function runAndroidWizardWithTelemetry( | ||
options: WizardOptions, | ||
): Promise<void> { | ||
printWelcome({ | ||
wizardName: 'Sentry Android Wizard', | ||
promoCode: options.promoCode, | ||
}); | ||
|
||
await confirmContinueEvenThoughNoGitRepo(); | ||
|
||
const projectDir = process.cwd(); | ||
const buildGradleFiles = findFilesWithExtensions(projectDir, [ | ||
'.gradle', | ||
'gradle.kts', | ||
]); | ||
|
||
if (!buildGradleFiles || buildGradleFiles.length === 0) { | ||
clack.log.error( | ||
'No Gradle project found. Please run this command from the root of your project.', | ||
); | ||
await abort(); | ||
return; | ||
} | ||
|
||
const appFile = await traceStep('Select App File', () => | ||
gradle.selectAppFile(buildGradleFiles), | ||
); | ||
|
||
// ======== STEP 1. Add Sentry Gradle Plugin to build.gradle(.kts) ============ | ||
clack.log.step( | ||
`Adding ${chalk.bold('Sentry Gradle plugin')} to your app's ${chalk.cyan( | ||
'build.gradle', | ||
)} file.`, | ||
); | ||
const pluginAdded = await traceStep('Add Gradle Plugin', () => | ||
gradle.addGradlePlugin(appFile), | ||
); | ||
if (!pluginAdded) { | ||
clack.log.warn( | ||
"Could not add Sentry Gradle plugin to your app's build.gradle file. You'll have to add it manually.\nPlease follow the instructions at https://docs.sentry.io/platforms/android/#install", | ||
); | ||
} | ||
|
||
const { selectedProject } = await getOrAskForProjectData(options, 'android'); | ||
|
||
// ======== STEP 2. Configure Sentry SDK via AndroidManifest ============ | ||
clack.log.step( | ||
`Configuring Sentry SDK via ${chalk.cyan('AndroidManifest.xml')}`, | ||
); | ||
const appDir = path.dirname(appFile); | ||
const manifestFile = path.join(appDir, 'src', 'main', 'AndroidManifest.xml'); | ||
|
||
const manifestUpdated = traceStep('Update Android Manifest', () => | ||
manifest.addManifestSnippet( | ||
manifestFile, | ||
selectedProject.keys[0].dsn.public, | ||
), | ||
); | ||
if (!manifestUpdated) { | ||
clack.log.warn( | ||
"Could not configure the Sentry SDK. You'll have to do it manually.\nPlease follow the instructions at https://docs.sentry.io/platforms/android/#configure", | ||
); | ||
} | ||
|
||
// ======== STEP 3. Patch Main Activity with a test error snippet ============ | ||
clack.log.step( | ||
`Patching ${chalk.bold('Main Activity')} with a test error snippet.`, | ||
); | ||
const mainActivity = traceStep('Find Main Activity', () => | ||
manifest.getMainActivity(manifestFile), | ||
); | ||
let packageName = mainActivity.packageName; | ||
if (!packageName) { | ||
// if no package name in AndroidManifest, look into gradle script | ||
packageName = gradle.getNamespace(appFile); | ||
} | ||
const activityName = mainActivity.activityName; | ||
if (!activityName || !packageName) { | ||
clack.log.warn( | ||
"Could not find Activity with intent action MAIN. You'll have to manually verify the setup.\nPlease follow the instructions at https://docs.sentry.io/platforms/android/#verify", | ||
); | ||
} else { | ||
const packageNameStable = packageName; | ||
const activityFile = traceStep('Find Main Activity Source File', () => | ||
codetools.findActivitySourceFile(appDir, packageNameStable, activityName), | ||
); | ||
|
||
const activityPatched = traceStep('Patch Main Activity', () => | ||
codetools.patchMainActivity(activityFile), | ||
); | ||
if (!activityPatched) { | ||
clack.log.warn( | ||
"Could not patch main activity. You'll have to manually verify the setup.\nPlease follow the instructions at https://docs.sentry.io/platforms/android/#verify", | ||
); | ||
} | ||
} | ||
|
||
// ======== OUTRO ======== | ||
clack.outro(` | ||
${chalk.green('Successfully installed the Sentry Android SDK!')} | ||
${chalk.cyan( | ||
'You can validate your setup by launching your application and checking Sentry issues page afterwards', | ||
)} | ||
Check out the SDK documentation for further configuration: | ||
https://docs.sentry.io/platforms/android/ | ||
`); | ||
} | ||
|
||
//find files with the given extension | ||
function findFilesWithExtensions( | ||
dir: string, | ||
extensions: string[], | ||
filesWithExtensions: string[] = [], | ||
): string[] { | ||
const cwd = process.cwd(); | ||
const files = fs.readdirSync(dir, { withFileTypes: true }); | ||
for (const file of files) { | ||
if (file.isDirectory()) { | ||
const childDir = path.join(dir, file.name); | ||
findFilesWithExtensions(childDir, extensions, filesWithExtensions); | ||
} else if (extensions.some((ext) => file.name.endsWith(ext))) { | ||
filesWithExtensions.push(path.relative(cwd, path.join(dir, file.name))); | ||
} | ||
} | ||
return filesWithExtensions; | ||
} |
Oops, something went wrong.