Skip to content

Commit

Permalink
Merge pull request #2014 from bugsnag/release/v6.4.0
Browse files Browse the repository at this point in the history
Release/v6.4.0
  • Loading branch information
YYChen01988 committed Apr 15, 2024
2 parents 7ad83fa + 2f2fc15 commit c300eff
Show file tree
Hide file tree
Showing 27 changed files with 433 additions and 86 deletions.
8 changes: 4 additions & 4 deletions .buildkite/pipeline.full.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,21 @@ steps:
key: "fixture-minimal"
timeout_in_minutes: 30
agents:
queue: macos-12-arm
queue: macos-14
artifact_paths: build/fixture-minimal.apk
command: make fixture-minimal

- label: ':android: Build Example App'
timeout_in_minutes: 5
agents:
queue: macos-12-arm
queue: macos-14
command: 'make example-app'

- label: ':android: Build debug fixture APK'
key: "fixture-debug"
timeout_in_minutes: 30
agents:
queue: macos-12-arm
queue: macos-14
artifact_paths:
- "build/fixture-debug.apk"
- "build/fixture-debug/*"
Expand All @@ -26,7 +26,7 @@ steps:
- label: ':android: Build Scan'
timeout_in_minutes: 10
agents:
queue: macos-12-arm
queue: macos-14
command: './gradlew clean assembleRelease check --scan'

#
Expand Down
26 changes: 14 additions & 12 deletions .buildkite/pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,16 @@ steps:
- label: 'Audit current licenses'
timeout_in_minutes: 30
agents:
queue: macos-12-arm
command: ./scripts/audit-dependency-licenses.sh
queue: macos-14
commands:
- bundle install
- ./scripts/audit-dependency-licenses.sh

- label: ':android: Build fixture APK r19'
key: "fixture-r19"
timeout_in_minutes: 30
agents:
queue: macos-12-arm
queue: macos-14
artifact_paths:
- "build/fixture-r19.apk"
- "build/fixture-r19-url.txt"
Expand All @@ -24,7 +26,7 @@ steps:
key: "fixture-r21"
timeout_in_minutes: 30
agents:
queue: macos-12-arm
queue: macos-14
artifact_paths:
- "build/fixture-r21.apk"
- "build/fixture-r21-url.txt"
Expand All @@ -37,45 +39,45 @@ steps:
- label: ':android: Coding standards checks'
timeout_in_minutes: 20
agents:
queue: macos-12-arm
queue: macos-14
command: './gradlew --continue checkstyle detekt lint ktlintCheck'

- label: ':android: Binary compatibility checks'
timeout_in_minutes: 20
agents:
queue: macos-12-arm
queue: macos-14
command: './gradlew apiCheck'

- label: ':android: CppCheck'
timeout_in_minutes: 10
agents:
queue: macos-12-arm
queue: macos-14
command: 'bash ./scripts/run-cpp-check.sh'

- label: ':android: ClangFormat'
timeout_in_minutes: 10
agents:
queue: macos-12-arm
queue: macos-14
command: 'bash ./scripts/run-clang-format-ci-check.sh'

- label: ':android: Lint mazerunner scenarios'
timeout_in_minutes: 10
agents:
queue: macos-12-arm
queue: macos-14
commands:
- cd features/fixtures/mazerunner
- ./gradlew ktlintCheck detekt checkstyle

- label: ':android: Android size reporting'
timeout_in_minutes: 10
agents:
queue: macos-12-arm
queue: macos-14
command: scripts/run-sizer.sh

- label: ':android: JVM tests'
timeout_in_minutes: 10
agents:
queue: macos-12-arm
queue: macos-14
command: './gradlew test'

#
Expand Down Expand Up @@ -346,6 +348,6 @@ steps:

- label: 'Conditionally include device farms/full tests'
agents:
queue: macos-12-arm
queue: macos-14
command: sh -c .buildkite/pipeline_trigger.sh

16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,21 @@
# Changelog

## 6.4.0 (2024-04-15)

### Enhancements

* FeatureFlags are now a copy-on-write structure, and so don't need to be defensive copied on a crashing thread
[#2005](https://github.com/bugsnag/bugsnag-android/pull/2005)
* The default redactedKeys in ObjectJsonStreamer is now static and shared, reducing the overhead of some leaveBreadcrumb calls (those with complex metadata)
[#2008](https://github.com/bugsnag/bugsnag-android/pull/2008)
* Allow `Bugsnag.startSession` to be called with automatic session tracking, and not have the first manual session be over written by the first automatic session.
[#2006](https://github.com/bugsnag/bugsnag-android/pull/2006)

### Bug fixes

* Refreshing the NDK symbol table is now protected by a soft-lock to avoid possible double-free conditions
[#2007](https://github.com/bugsnag/bugsnag-android/pull/2007)

## 6.3.0 (2024-03-19)

### Enhancements
Expand Down
2 changes: 1 addition & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ gem 'bugsnag-maze-runner', '~>9.0'
# Or follow master:
#gem 'bugsnag-maze-runner', git: 'https://github.com/bugsnag/maze-runner'

gem "license_finder", "~> 6.13"
gem "license_finder", "~> 7.0"
10 changes: 5 additions & 5 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -78,13 +78,13 @@ GEM
regexp_parser (~> 2.0)
simpleidn (~> 0.2)
uri_template (~> 0.7)
license_finder (6.15.0)
license_finder (7.1.0)
bundler
rubyzip (>= 1, < 3)
thor (~> 1.0.1)
thor (~> 1.2)
tomlrb (>= 1.3, < 2.1)
with_env (= 1.1.0)
xml-simple (~> 1.1.5)
xml-simple (~> 1.1.9)
mime-types (3.5.2)
mime-types-data (~> 3.2015)
mime-types-data (3.2024.0206)
Expand Down Expand Up @@ -113,7 +113,7 @@ GEM
ffi (~> 1.1)
test-unit (3.5.9)
power_assert
thor (1.0.1)
thor (1.3.1)
tomlrb (2.0.3)
unf (0.1.4)
unf_ext
Expand All @@ -135,7 +135,7 @@ PLATFORMS

DEPENDENCIES
bugsnag-maze-runner (~> 9.0)
license_finder (~> 6.13)
license_finder (~> 7.0)

BUNDLED WITH
2.4.8
4 changes: 4 additions & 0 deletions bugsnag-android-core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ dokkaHtml.configure {
dokkaSourceSets {
named("main") {
noAndroidSdkLink.set(false)
perPackageOption {
matchingRegex.set("com\\.bugsnag\\.android\\..*")
suppress.set(true)
}
}
}
}
Expand Down
1 change: 0 additions & 1 deletion bugsnag-android-core/detekt-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
<ID>LongParameterList:Device.kt$Device$( buildInfo: DeviceBuildInfo, /** * The Application Binary Interface used */ var cpuAbi: Array&lt;String>?, /** * Whether the device has been jailbroken */ var jailbroken: Boolean?, /** * A UUID generated by Bugsnag and used for the individual application on a device */ var id: String?, /** * The IETF language tag of the locale used */ var locale: String?, /** * The total number of bytes of memory on the device */ var totalMemory: Long?, /** * A collection of names and their versions of the primary languages, frameworks or * runtimes that the application is running on */ runtimeVersions: MutableMap&lt;String, Any>? )</ID>
<ID>LongParameterList:DeviceBuildInfo.kt$DeviceBuildInfo$( val manufacturer: String?, val model: String?, val osVersion: String?, val apiLevel: Int?, val osBuild: String?, val fingerprint: String?, val tags: String?, val brand: String?, val cpuAbis: Array&lt;String>? )</ID>
<ID>LongParameterList:DeviceDataCollector.kt$DeviceDataCollector$( private val connectivity: Connectivity, private val appContext: Context, resources: Resources, private val deviceId: String?, private val internalDeviceId: String?, private val buildInfo: DeviceBuildInfo, private val dataDirectory: File, rootDetector: RootDetector, private val bgTaskService: BackgroundTaskService, private val logger: Logger )</ID>
<ID>LongParameterList:DeviceIdStore.kt$DeviceIdStore$( context: Context, deviceIdfile: File = File(context.filesDir, "device-id"), deviceIdGenerator: () -> UUID = { UUID.randomUUID() }, internalDeviceIdfile: File = File(context.filesDir, "internal-device-id"), internalDeviceIdGenerator: () -> UUID = { UUID.randomUUID() }, private val sharedPrefMigrator: SharedPrefMigrator, logger: Logger )</ID>
<ID>LongParameterList:DeviceWithState.kt$DeviceWithState$( buildInfo: DeviceBuildInfo, jailbroken: Boolean?, id: String?, locale: String?, totalMemory: Long?, runtimeVersions: MutableMap&lt;String, Any>, /** * The number of free bytes of storage available on the device */ var freeDisk: Long?, /** * The number of free bytes of memory available on the device */ var freeMemory: Long?, /** * The orientation of the device when the event occurred: either portrait or landscape */ var orientation: String?, /** * The timestamp on the device when the event occurred */ var time: Date? )</ID>
<ID>LongParameterList:EventFilenameInfo.kt$EventFilenameInfo.Companion$( obj: Any, uuid: String = UUID.randomUUID().toString(), apiKey: String?, timestamp: Long = System.currentTimeMillis(), config: ImmutableConfig, isLaunching: Boolean? = null )</ID>
<ID>LongParameterList:EventInternal.kt$EventInternal$( apiKey: String, logger: Logger, breadcrumbs: MutableList&lt;Breadcrumb> = mutableListOf(), discardClasses: Set&lt;Pattern> = setOf(), errors: MutableList&lt;Error> = mutableListOf(), metadata: Metadata = Metadata(), featureFlags: FeatureFlags = FeatureFlags(), originalError: Throwable? = null, projectPackages: Collection&lt;String> = setOf(), severityReason: SeverityReason = SeverityReason.newInstance(SeverityReason.REASON_HANDLED_EXCEPTION), threads: MutableList&lt;Thread> = mutableListOf(), user: User = User(), redactionKeys: Set&lt;Pattern>? = null )</ID>
Expand Down
107 changes: 88 additions & 19 deletions bugsnag-android-core/src/main/java/com/bugsnag/android/FeatureFlags.kt
Original file line number Diff line number Diff line change
@@ -1,52 +1,121 @@
package com.bugsnag.android

import java.io.IOException
import kotlin.math.max

internal class FeatureFlags(
internal val store: MutableMap<String, String?> = mutableMapOf()
internal class FeatureFlags private constructor(
@Volatile
private var flags: Array<FeatureFlag>
) : JsonStream.Streamable, FeatureFlagAware {
private val emptyVariant = "__EMPTY_VARIANT_SENTINEL__"

@Synchronized override fun addFeatureFlag(name: String) {
/*
* Implemented as *effectively* a CopyOnWriteArrayList - but since FeatureFlags are
* key/value pairs, CopyOnWriteArrayList would require external locking (in addition to it's
* internal locking) for us to be sure we are not adding duplicates.
*
* This class aims to have similar performance while also ensuring that the FeatureFlag object
* themselves don't leak, as they are mutable and we want 'copy' to be an O(1) snapshot
* operation for when an Event is created.
*
* It's assumed that *most* FeatureFlags will be added up-front, or during the normal app
* lifecycle (not during an Event).
*
* As such a copy-on-write structure allows an Event to simply capture a reference to the
* "snapshot" of FeatureFlags that were active when the Event was created.
*/

constructor() : this(emptyArray<FeatureFlag>())

override fun addFeatureFlag(name: String) {
addFeatureFlag(name, null)
}

@Synchronized override fun addFeatureFlag(name: String, variant: String?) {
store[name] = variant ?: emptyVariant
override fun addFeatureFlag(name: String, variant: String?) {
synchronized(this) {
val flagArray = flags
val index = flagArray.indexOfFirst { it.name == name }

flags = when {
// this is a new FeatureFlag
index == -1 -> flagArray + FeatureFlag(name, variant)

// this is a change to an existing FeatureFlag
flagArray[index].variant != variant -> flagArray.copyOf().also {
// replace the existing FeatureFlag in-place
it[index] = FeatureFlag(name, variant)
}

// no actual change, so we return
else -> return
}
}
}

@Synchronized override fun addFeatureFlags(featureFlags: Iterable<FeatureFlag>) {
featureFlags.forEach { (name, variant) ->
addFeatureFlag(name, variant)
override fun addFeatureFlags(featureFlags: Iterable<FeatureFlag>) {
synchronized(this) {
val flagArray = flags

val newFlags = ArrayList<FeatureFlag>(
// try to guess a reasonable upper-bound for the output array
if (featureFlags is Collection<*>) flagArray.size + featureFlags.size
else max(flagArray.size * 2, flagArray.size)
)

newFlags.addAll(flagArray)

featureFlags.forEach { (name, variant) ->
val existingIndex = newFlags.indexOfFirst { it.name == name }
when (existingIndex) {
// add a new flag to the end of the list
-1 -> newFlags.add(FeatureFlag(name, variant))
// replace the existing flag
else -> newFlags[existingIndex] = FeatureFlag(name, variant)
}
}

flags = newFlags.toTypedArray()
}
}

@Synchronized override fun clearFeatureFlag(name: String) {
store.remove(name)
override fun clearFeatureFlag(name: String) {
synchronized(this) {
val flagArray = flags
val index = flagArray.indexOfFirst { it.name == name }
if (index == -1) {
return
}

val out = arrayOfNulls<FeatureFlag>(flagArray.size - 1)
flagArray.copyInto(out, 0, 0, index)
flagArray.copyInto(out, index, index + 1)

@Suppress("UNCHECKED_CAST")
flags = out as Array<FeatureFlag>
}
}

@Synchronized override fun clearFeatureFlags() {
store.clear()
override fun clearFeatureFlags() {
synchronized(this) {
flags = emptyArray()
}
}

@Throws(IOException::class)
override fun toStream(stream: JsonStream) {
val storeCopy = synchronized(this) { store.toMap() }
val storeCopy = flags
stream.beginArray()
storeCopy.forEach { (name, variant) ->
stream.beginObject()
stream.name("featureFlag").value(name)
if (variant != emptyVariant) {
if (variant != null) {
stream.name("variant").value(variant)
}
stream.endObject()
}
stream.endArray()
}

@Synchronized fun toList(): List<FeatureFlag> = store.entries.map { (name, variant) ->
FeatureFlag(name, variant.takeUnless { it == emptyVariant })
}
fun toList(): List<FeatureFlag> = flags.map { (name, variant) -> FeatureFlag(name, variant) }

@Synchronized fun copy() = FeatureFlags(store.toMutableMap())
fun copy() = FeatureFlags(flags)
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import java.io.IOException
*/
class Notifier @JvmOverloads constructor(
var name: String = "Android Bugsnag Notifier",
var version: String = "6.3.0",
var version: String = "6.4.0",
var url: String = "https://bugsnag.com"
) : JsonStream.Streamable {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@ internal class ObjectJsonStreamer {
companion object {
internal const val REDACTED_PLACEHOLDER = "[REDACTED]"
internal const val OBJECT_PLACEHOLDER = "[OBJECT]"

internal val DEFAULT_REDACTED_KEYS = setOf(Pattern.compile(".*password.*", Pattern.CASE_INSENSITIVE))
}

var redactedKeys = setOf(Pattern.compile(".*password.*", Pattern.CASE_INSENSITIVE))
var redactedKeys = DEFAULT_REDACTED_KEYS

// Write complex/nested values to a JsonStreamer
@Throws(IOException::class)
Expand Down
Loading

0 comments on commit c300eff

Please sign in to comment.