Skip to content

Commit

Permalink
Merge pull request #154 from bugsnag/next-release
Browse files Browse the repository at this point in the history
Next release
  • Loading branch information
simonbowring authored Mar 21, 2019
2 parents b584aef + bfdf34e commit f402ad5
Show file tree
Hide file tree
Showing 5 changed files with 182 additions and 87 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 4.1.1 (2019-03-21)

* Add BUILD_UUID to app bundle manifests
[#153](https://github.com/bugsnag/bugsnag-android-gradle-plugin/pull/153)

## 4.1.0 (2019-03-13)

Note: this version of the plugin will fail the build if a mapping file is not uploaded successfully.
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
group = com.bugsnag
version = 4.1.0
version = 4.1.1

ANDROID_MIN_SDK_VERSION=14
ANDROID_TARGET_SDK_VERSION=27
Expand Down
108 changes: 56 additions & 52 deletions src/main/groovy/com/bugsnag/android/gradle/BugsnagManifestTask.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -21,52 +21,54 @@ class BugsnagManifestTask extends BugsnagVariantOutputTask {

@TaskAction
def updateManifest() {
def manifestPath = getManifestPath()
def manifestPaths = getManifestPaths()

if (!manifestPath.exists()) {
return
}
// Uniquely identify the build so that we can identify the proguard file.
def buildUUID = UUID.randomUUID().toString()

project.logger.debug("Updating manifest with build UUID: " + manifestPath)
for (def manifestPath in manifestPaths) {
if (!manifestPath.exists()) {
continue
}

// Parse the AndroidManifest.xml
def ns = new Namespace("http://schemas.android.com/apk/res/android", "android")
def xml = new XmlParser().parse(manifestPath)
project.logger.debug("Updating manifest with build UUID: " + manifestPath)

def application = xml.application[0]
if (application) {
def metaDataTags = application['meta-data']
// Parse the AndroidManifest.xml
def ns = new Namespace("http://schemas.android.com/apk/res/android", "android")
def xml = new XmlParser().parse(manifestPath)

// remove any old BUILD_UUID_TAG elements
metaDataTags.findAll {
(it.attributes()[ns.name] == BugsnagPlugin.BUILD_UUID_TAG)
}.each {
it.parent().remove(it)
}
def application = xml.application[0]
if (application) {
def metaDataTags = application['meta-data']

// remove any old BUILD_UUID_TAG elements
metaDataTags.findAll {
(it.attributes()[ns.name] == BugsnagPlugin.BUILD_UUID_TAG)
}.each {
it.parent().remove(it)
}

// Uniquely identify the build so that we can identify the proguard file.
def buildUUID = UUID.randomUUID().toString()

// Add the new BUILD_UUID_TAG element
application.appendNode('meta-data', [(ns.name): BugsnagPlugin.BUILD_UUID_TAG, (ns.value): buildUUID])

// Write the manifest file
FileWriter writer = null

try {
writer = new FileWriter(manifestPath)
def printer = new XmlNodePrinter(new PrintWriter(writer))
printer.preserveWhitespace = true
printer.print(xml)
} catch (Exception e) {
project.logger.warn("Failed to update manifest with Bugsnag metadata", e)
} finally {
if (writer != null) {
writer.close()
// Add the new BUILD_UUID_TAG element
application.appendNode('meta-data', [(ns.name): BugsnagPlugin.BUILD_UUID_TAG, (ns.value): buildUUID])

// Write the manifest file
FileWriter writer = null

try {
writer = new FileWriter(manifestPath)
def printer = new XmlNodePrinter(new PrintWriter(writer))
printer.preserveWhitespace = true
printer.print(xml)
} catch (Exception e) {
project.logger.warn("Failed to update manifest with Bugsnag metadata", e)
} finally {
if (writer != null) {
writer.close()
}
}
} else {
project.logger.error("Bugsnag detected invalid manifest with no application element so did not write Build UUID")
}
} else {
project.logger.error("Bugsnag detected invalid manifest with no application element so did not write Build UUID")
}
}

Expand All @@ -75,22 +77,24 @@ class BugsnagManifestTask extends BugsnagVariantOutputTask {
}

def shouldRun() {
def manifestPath = getManifestPath()
def manifestPaths = getManifestPaths()

if (!manifestPath.exists()) {
return false
}
for (def manifestPath in manifestPaths) {
if (!manifestPath.exists()) {
continue
}

def ns = new Namespace("http://schemas.android.com/apk/res/android", "android")
def app = new XmlParser().parse(manifestPath).application[0]
if (app) {
def tagCount = app['meta-data'].findAll {
(it.attributes()[ns.name] == BugsnagPlugin.BUILD_UUID_TAG)
}.size()
tagCount == 0 || !isInstantRun()
} else {
false
def ns = new Namespace("http://schemas.android.com/apk/res/android", "android")
def app = new XmlParser().parse(manifestPath).application[0]
if (app) {
def tagCount = app['meta-data'].findAll {
(it.attributes()[ns.name] == BugsnagPlugin.BUILD_UUID_TAG)
}.size()
if (tagCount == 0 || !isInstantRun()) {
return true
}
}
}
return false
}

}
40 changes: 40 additions & 0 deletions src/main/groovy/com/bugsnag/android/gradle/BugsnagPlugin.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import com.android.build.gradle.api.ApplicationVariant
import com.android.build.gradle.api.BaseVariant
import com.android.build.gradle.api.BaseVariantOutput
import com.android.build.gradle.api.LibraryVariant
import com.android.build.gradle.tasks.ManifestProcessorTask
import org.gradle.api.DomainObjectSet
import org.gradle.api.Plugin
import org.gradle.api.Project
Expand Down Expand Up @@ -266,6 +267,16 @@ class BugsnagPlugin implements Plugin<Project> {
}
}

static File resolveBundleManifestOutputDirectory(ManifestProcessorTask processManifest) {
if (processManifest.hasProperty("bundleManifestOutputDirectory")) {
// For AGP versions >= 3.3.0 the bundle manifest is output to its own directory
return processManifest.bundleManifestOutputDirectory
} else {
// For AGP versions < 3.3.0 the bundle manifest is the merged manifest
return processManifest.manifestOutputDirectory
}
}

/**
* Automatically add the "edit proguard settings" task to the
* build process.
Expand Down Expand Up @@ -354,6 +365,35 @@ class BugsnagPlugin implements Plugin<Project> {
return outputSize > variantSize
}

/**
* Whether or not an assemble task is going to be run for this variant
*/
static boolean isRunningAssembleTask(BaseVariant variant, BaseVariantOutput output, Project project) {
return isRunningTaskWithPrefix(variant, output, project, "assemble")
}

/**
* Whether or not a bundle task is going to be run for this variant
*/
static boolean isRunningBundleTask(BaseVariant variant, BaseVariantOutput output, Project project) {
return isRunningTaskWithPrefix(variant, output, project, "bundle")
}

/**
* Whether or any of a list of the task names for a prefix are going to be run by checking the list
* against all of the tasks in the task graph
*/
private static boolean isRunningTaskWithPrefix(BaseVariant variant, BaseVariantOutput output, Project project, String prefix) {
Set<String> taskNames = new HashSet<>()
taskNames.addAll(findTaskNamesForPrefix(variant, output, prefix))

return project.gradle.taskGraph.getAllTasks().any { task ->
taskNames.any {
task.name.endsWith(it)
}
}
}

private static class BugsnagTaskDeps {
BaseVariant variant
BaseVariantOutput output
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,62 +30,99 @@ class BugsnagVariantOutputTask extends DefaultTask {
* See: https://developer.android.com/studio/build/configure-apk-splits.html#build-apks-filename
* https://issuetracker.google.com/issues/37085185
*/
File getManifestPath() {
File directory
List<File> getManifestPaths() {
File directoryMerged
File directoryBundle
List<File> manifestPaths = new ArrayList<>()

boolean getMergedManifest = BugsnagPlugin.isRunningAssembleTask(variant, variantOutput, project)
boolean getBundleManifest = BugsnagPlugin.isRunningBundleTask(variant, variantOutput, project)

// If the manifest location could not be reliably determined, attempt to get both
if (!getMergedManifest && !getBundleManifest) {
getMergedManifest = true
getBundleManifest = true
}

def processManifest = BugsnagPlugin.resolveProcessManifest(variantOutput)
def outputDir = processManifest.manifestOutputDirectory

if (outputDir instanceof File) {
directory = outputDir
} else {
// gradle 4.7 introduced a provider API for lazy evaluation of properties,
// AGP subsequently changed the API from File to Provider<File>
// see https://docs.gradle.org/4.7/userguide/lazy_configuration.html
directory = outputDir.get().asFile
if (getMergedManifest) {
def outputDir = processManifest.manifestOutputDirectory

if (outputDir instanceof File) {
directoryMerged = outputDir
} else {
// gradle 4.7 introduced a provider API for lazy evaluation of properties,
// AGP subsequently changed the API from File to Provider<File>
// see https://docs.gradle.org/4.7/userguide/lazy_configuration.html
directoryMerged = outputDir.get().asFile
}

addManifestPath(manifestPaths, directoryMerged)
}

// Attempt to get the bundle manifest directory if required
if (getBundleManifest) {
directoryBundle = BugsnagPlugin.resolveBundleManifestOutputDirectory(processManifest)
addManifestPath(manifestPaths, directoryBundle)
}

manifestPaths
}

void addManifestPath(List<File> manifestPaths, File directory) {
File manifestFile = Paths.get(directory.toString(), variantOutput.dirName, "AndroidManifest.xml").toFile()

if (!manifestFile.exists()) {
project.logger.error("Failed to find manifest at ${manifestFile}")
} else {
project.logger.info("Found manifest at ${manifestFile}")
manifestPaths.add(manifestFile)
}
manifestFile
}

// Read the API key and Build ID etc..
void readManifestFile() {
// Parse the AndroidManifest.xml
Namespace ns = new Namespace("http://schemas.android.com/apk/res/android", "android")
File manifestPath = getManifestPath()
List<File> manifestPaths = getManifestPaths()

if (!manifestPath.exists()) {
return
}
project.logger.debug("Reading manifest at: ${manifestPath}")
for (def manifestPath in manifestPaths) {
if (!manifestPath.exists()) {
continue
}

Node xml = new XmlParser().parse(manifestPath)
def metaDataTags = xml.application['meta-data']
project.logger.debug("Reading manifest at: ${manifestPath}")

// Get the Bugsnag API key
apiKey = getApiKey(metaDataTags, ns)
if (!apiKey) {
project.logger.warn("Could not find apiKey in '$BugsnagPlugin.API_KEY_TAG' <meta-data> tag in your AndroidManifest.xml or in your gradle config")
}
Node xml = new XmlParser().parse(manifestPath)
def metaDataTags = xml.application['meta-data']

// Get the build version
versionCode = getVersionCode(xml, ns)
if (versionCode == null) {
project.logger.warn("Could not find 'android:versionCode' value in your AndroidManifest.xml")
return
}
// If the current manifest does not contain the build ID then try the next manifest in the list (if any)
if (!(manifestPath == manifestPaths.last()) && !hasBuildUuid(metaDataTags, ns)) {
continue
}

// Get the Bugsnag API key
apiKey = getApiKey(metaDataTags, ns)
if (!apiKey) {
project.logger.warn("Could not find apiKey in '$BugsnagPlugin.API_KEY_TAG' <meta-data> tag in your AndroidManifest.xml or in your gradle config")
}

// Get the build version
versionCode = getVersionCode(xml, ns)
if (versionCode == null) {
project.logger.warn("Could not find 'android:versionCode' value in your AndroidManifest.xml")
continue
}

// Uniquely identify the build so that we can identify the proguard file.
buildUUID = getManifestMetaData(metaDataTags, ns, BugsnagPlugin.BUILD_UUID_TAG)
// Uniquely identify the build so that we can identify the proguard file.
buildUUID = getBuildUuid(metaDataTags, ns)

// Get the version name
versionName = getVersionName(xml, ns)
// Get the version name
versionName = getVersionName(xml, ns)

return
}
}

String getApiKey(metaDataTags, Namespace ns) {
Expand All @@ -99,6 +136,16 @@ class BugsnagVariantOutputTask extends DefaultTask {
return apiKey
}

boolean hasBuildUuid(metaDataTags, Namespace ns) {
return metaDataTags.any {
it.attributes()[ns.name] == BugsnagPlugin.BUILD_UUID_TAG
}
}

String getBuildUuid(metaDataTags, Namespace ns) {
return getManifestMetaData(metaDataTags, ns, BugsnagPlugin.BUILD_UUID_TAG)
}

private String getManifestMetaData(metaDataTags, Namespace ns, String key) {
String value = null

Expand All @@ -120,5 +167,4 @@ class BugsnagVariantOutputTask extends DefaultTask {
String getVersionCode(Node xml, Namespace ns) {
xml.attributes()[ns.versionCode]
}

}

0 comments on commit f402ad5

Please sign in to comment.