Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sentry gradle script refactoring, compatibility with Gradle 5.0 recovered #532

Merged
merged 2 commits into from
Jan 29, 2019
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
291 changes: 162 additions & 129 deletions sentry.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -6,54 +6,20 @@ import java.util.regex.Pattern
def config = project.hasProperty("sentryCli") ? project.sentryCli : [];

gradle.projectsEvaluated {
def releases = [:];
android.applicationVariants.each { variant ->
def releaseName = "${variant.getApplicationId()}-${variant.getVersionName()}";
variant.outputs.each { output ->
def versionCode = output.getVersionCode();
def variantName = variant.getName();
def outputName = output.getName();
if (releases[variantName] == null) {
releases[variantName] = [:];
}
releases[variantName][outputName] = [outputName, releaseName, versionCode];
}
}
def releases = extractReleasesInfo()

// separately we then hook into the bundle task of react native to inject
// sourcemap generation parameters. In case for whatever reason no release
// was found for the asset folder we just bail.
def bundleTasks = tasks.findAll { task ->
task.name.startsWith("bundle") && task.name.endsWith("JsAndAssets")
}
def bundleTasks = tasks.findAll { task -> task.name.startsWith("bundle") && task.name.endsWith("JsAndAssets") }
bundleTasks.each { bundleTask ->
def props = bundleTask.getProperties();
def cmd = props.get("commandLine") as List<String>;
def cmdArgs = props.get("args") as List<String>;
def bundleOutput = null;
def sourcemapOutput = null;
def reactRoot = props.get("workingDir");
def shouldCleanUp = false;

cmdArgs.eachWithIndex{ String arg, int i ->
if (arg == "--bundle-output") {
bundleOutput = cmdArgs[i + 1];
} else if (arg == "--sourcemap-output") {
sourcemapOutput = cmdArgs[i + 1];
}
}

if (sourcemapOutput == null) {
sourcemapOutput = bundleOutput + ".map";
cmd.push("--sourcemap-output");
cmd.push(sourcemapOutput);
cmdArgs.push("--sourcemap-output");
cmdArgs.push(sourcemapOutput);
def shouldCleanUp
def sourcemapOutput
def bundleOutput
def props = bundleTask.getProperties()
def reactRoot = props.get("workingDir")

shouldCleanUp = true
}

bundleTask.setProperty("commandLine", cmd);
bundleTask.setProperty("args", cmdArgs);
(shouldCleanUp, bundleOutput, sourcemapOutput) = forceSourceMapOutputFromBundleTask(bundleTask)

if (config.flavorAware) {
println "**********************************"
Expand All @@ -68,121 +34,188 @@ gradle.projectsEvaluated {
// .findAll{!['class', 'active'].contains(it.key)}
// .join('\n')

def currentRelease = "";
def pattern = Pattern.compile("bundle([A-Z][A-Za-z0-9]+)JsAndAssets")
Matcher matcher = pattern.matcher(bundleTask.name)
if (matcher.find()) {
def match = matcher.group(1);
currentRelease = match.substring(0, 1).toLowerCase() + match.substring(1);
}
def currentVariants = extractCurrentVariants(bundleTask, releases)
if (currentVariants == null) return

def currentVariants = null;
releases.each { key, release ->
if (key.equalsIgnoreCase(currentRelease)) {
currentVariants = release;
}
}

if (currentVariants == null) return;

def variant = null;
def releaseName = null;
def versionCodes = new ArrayList<Integer>(currentVariants.size());
def variant = null
def releaseName = null
def versionCodes = new ArrayList<Integer>(currentVariants.size())

currentVariants.each { key, currentVariant ->
variant = currentVariant[0];
releaseName = currentVariant[1];
versionCodes.push(currentVariant[2]);
variant = currentVariant[0]
releaseName = currentVariant[1]
versionCodes.push(currentVariant[2])
}

def cliTask = tasks.create(
name: bundleTask.getName() + variant + "SentryUpload",
type: Exec) {
def nameCliTask = "${bundleTask.name}_SentryUpload"
def nameCleanup = "${bundleTask.name}_SentryUploadCleanUp"

/** Upload source map file to the sentry server via CLI call. */
def cliTask = tasks.create(name: nameCliTask, type: Exec) {
description = "upload debug symbols to sentry"
group = 'sentry.io'

def propertiesFile = "$reactRoot/android/sentry.properties";
workingDir reactRoot

def propertiesFile = "$reactRoot/android/sentry.properties"
if (config.flavorAware) {
propertiesFile = "$reactRoot/android/sentry-$variant"+".properties"
println "For $variant using: $propertiesFile"
propertiesFile = "$reactRoot/android/sentry-${variant}.properties"
project.logger.info("For $variant using: $propertiesFile")
} else {
environment("SENTRY_PROPERTIES", propertiesFile)
}
Properties sentryProps = new Properties();

Properties sentryProps = new Properties()
try {
sentryProps.load(new FileInputStream(propertiesFile));
sentryProps.load(new FileInputStream(propertiesFile))
} catch (FileNotFoundException e) {
println "File not found: $propertiesFile"
project.logger.info("file not found '$propertiesFile' for '$variant'")
}
def cliExecutable = sentryProps.get("cli.executable", "$reactRoot/node_modules/@sentry/cli/bin/sentry-cli");
def cliExecutable = sentryProps.get("cli.executable", "$reactRoot/node_modules/@sentry/cli/bin/sentry-cli")

workingDir reactRoot

// fix path separator for Windows
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
cliExecutable = cliExecutable.replaceAll("/","\\\\");
cliExecutable = cliExecutable.replaceAll("/", "\\\\")
}

def args = [
cliExecutable
];
if (config.logLevel) {
args.push("--log-level");
args.push(config.logLevel);
}
//
// based on:
// https://github.com/getsentry/sentry-cli/blob/master/src/commands/react_native_gradle.rs
//
def args = [cliExecutable]

args.addAll( !config.logLevel ? [] : [
"--log-level", config.logLevel // control verbosity of the output
])
args.addAll(!config.flavorAware ? [] : [
"--url", sentryProps.get("defaults.url"),
"--auth-token", sentryProps.get("auth.token")
])
args.addAll(["react-native", "gradle",
"--bundle", bundleOutput, // The path to a bundle that should be uploaded.
"--sourcemap", sourcemapOutput, // The path to a sourcemap that should be uploaded.
"--release", releaseName // The name of the release to publish.
])
args.addAll(!config.flavorAware ? [] : [
"--org", sentryProps.get("defaults.org"),
"--project", sentryProps.get("defaults.project")
])

// The names of the distributions to publish. Can be supplied multiple times.
versionCodes.each { versionCode -> args.addAll(["--dist", versionCode]) }

project.logger.info("Sentry-CLI arguments: ${args}")

def osCompatibility = Os.isFamily(Os.FAMILY_WINDOWS) ? ['cmd', '/c', 'node'] : []
commandLine(*osCompatibility, *args)

if (config.flavorAware) {
args.push("--url");
args.push(sentryProps.get("defaults.url"));
args.push("--auth-token");
args.push(sentryProps.get("auth.token"));
}
enabled true
}

args.push("react-native");
args.push("gradle");
args.push("--bundle");
args.push(bundleOutput);
args.push("--sourcemap");
args.push(sourcemapOutput);
args.push("--release");
args.push(releaseName);
/** Delete sourcemap files */
def cliCleanUpTask = tasks.create(name: nameCleanup, type: Delete) {
description = "clean up extra sourcemap"
group = 'sentry.io'

if (config.flavorAware) {
args.push("--org");
args.push(sentryProps.get("defaults.org"));
args.push("--project");
args.push(sentryProps.get("defaults.project"));
}
delete sourcemapOutput
}

versionCodes.each { versionCode ->
args.add("--dist");
args.add(versionCode);
}
// dependsOn, mustRunAfter, shouldRunAfter, doFirst, doLast, finalizedBy
// bundleTask --> cliTask
bundleTask.finalizedBy cliTask

if (config.logLevel) {
println args
}
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
commandLine("cmd", "/c", "node", *args)
} else {
commandLine(*args)
// register clean task extension
cliCleanUpTask.onlyIf { shouldCleanUp }
project.tasks.clean.dependsOn cliCleanUpTask
}
}

/** Compose lookup map of build variants - to - outputs. */
def extractReleasesInfo() {
def releases = [:]

android.applicationVariants.each { variant ->
def releaseName = "${variant.getApplicationId()}-${variant.getVersionName()}"

variant.outputs.each { output ->
def versionCode = output.getVersionCode()
def variantName = variant.getName()
def outputName = output.getName()
if (releases[variantName] == null) {
releases[variantName] = [:]
}
enabled true
releases[variantName][outputName] = [outputName, releaseName, versionCode]
}
}

def cliCleanUpTask = tasks.create(
name: bundleTask.getName() + variant + "SentryUploadCleanUp",
type: Delete) {
description = "clean up extra sourcemap"
return releases
}

delete sourcemapOutput
};
/** Extract from arguments collection bundle and sourcemap files output names. */
static extractBundleTaskArguments(cmdArgs){
def bundleOutput = null
def sourcemapOutput = null

bundleTask.doLast {
cliTask.execute();
if (shouldCleanUp) {
cliCleanUpTask.execute();
}
cmdArgs.eachWithIndex { String arg, int i ->
if (arg == "--bundle-output") {
bundleOutput = cmdArgs[i + 1]
} else if (arg == "--sourcemap-output") {
sourcemapOutput = cmdArgs[i + 1]
}
}

return [ bundleOutput, sourcemapOutput ]
}

/** Force Bundle task to produce sourcemap files if they are not pre-configured by user yet. */
def forceSourceMapOutputFromBundleTask(bundleTask){
def props = bundleTask.getProperties()
def cmd = props.get("commandLine") as List<String>
def cmdArgs = props.get("args") as List<String>
def shouldCleanUp = false
def bundleOutput = null
def sourcemapOutput = null

(bundleOutput, sourcemapOutput) = extractBundleTaskArguments(cmdArgs)

if (sourcemapOutput == null) {
sourcemapOutput = bundleOutput + ".map"

cmd.addAll(["--sourcemap-output", sourcemapOutput])
cmdArgs.addAll(["--sourcemap-output", sourcemapOutput])

cliTask.dependsOn(bundleTask)
shouldCleanUp = true

bundleTask.setProperty("commandLine", cmd)
bundleTask.setProperty("args", cmdArgs)

project.logger.info("forced sourcemap file output for `${bundleTask.name}` task")
} else {
project.logger.info("Info: used pre-configured source map files: ${sourcemapOutput}")
}

return [shouldCleanUp, bundleOutput, sourcemapOutput]
}

/** compose array with one item - current build flavor name */
static extractCurrentVariants(bundleTask, releases) {
// examples: bundleLocalReleaseJsAndAssets, bundleYellowDebugJsAndAssets
def pattern = Pattern.compile("bundle([A-Z][A-Za-z0-9_]+)JsAndAssets")

def currentRelease = ""

Matcher matcher = pattern.matcher(bundleTask.name)
if (matcher.find()) {
def match = matcher.group(1)
currentRelease = match.substring(0, 1).toLowerCase() + match.substring(1)
}

def currentVariants = null
releases.each { key, release ->
if (key.equalsIgnoreCase(currentRelease)) {
currentVariants = release
}
}

return currentVariants
}