Skip to content

Commit 43876b8

Browse files
didiergarciakostasniktaswenxi-zengclaude
authored
Supply Chain: move to AGP 8 / JDK 17 for curated Artifactory (#328)
* Supply Chain: Try curated Artifactory * Supply Chain: iterate * Add caching only if a fork * Supply Chain: bump AGP to 8.6.1 to drop blocked bcprov-jdk15on:1.67 AGP 7.4.2 hard-pinned org.bouncycastle:bcprov-jdk15on:1.67, which is blocked by Artifactory curation (403), failing extractDebugAnnotations. AGP 8.5+ replaces it with the curated bcprov-jdk18on:1.77. Includes required AGP 8.x migration: add namespace to Android modules, opt into buildConfig, drop removed disableAutomaticComponentCreation flag, and bump CI to JDK 17. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Supply Chain: force bcprov/bcutil-jdk18on to 1.81 (CVE-clean) AGP pulls bcprov-jdk18on 1.77/1.79 transitively via extractAnnotations, both blocked by Artifactory curation for CVEs. No shipping AGP pulls a clean version, so force 1.81 (fixes all known bcprov CVEs) on both the buildscript classpath and project configurations to cover AGP's detachedConfiguration used by the annotation-extraction task. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Supply Chain: disable extractAnnotations (unresolvable bcprov 1.77) AGP's extract{Debug,Release}Annotations tasks resolve bcprov-jdk18on:1.77 in a detached configuration that Artifactory curation blocks for CVEs. AGP creates this config via configurations.detachedConfiguration(), which is excluded from the project container and bypasses every resolutionStrategy hook (force, eachDependency, dependencySubstitution) -- verified locally by purging the cache and reproducing the CI download of 1.77. The tasks only emit an optional annotations.zip (IDE type-annotation hints for AAR consumers); the AAR is fully functional without it. Disabling them removes the bcprov dependency entirely. Reverts the ineffective force blocks. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Revert extractAnnotations task disable (broke syncLibJars) Disabling extract*Annotations left syncLibJars without its required typedefs.txt input (LibraryAarJarsTask.typedefRecipe), failing config. Reverting to the clean AGP 8.6.1 baseline while the bcprov curation issue is resolved via curation allowlist -- AGP's Lint tool resolves bcprov in an isolated detached configuration that no resolutionStrategy, substitution, or component rule can reach (verified against a purged cache with force/eachDependency/dependencySubstitution/configureEach). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Supply Chain: bump AGP to 8.10.1 to test bcprov 1.79 curation AGP 8.6.1 pulled bcprov-jdk18on 1.77 (curation-blocked). AGP 8.9+ moves to 1.79; 8.10.1 is the newest that works on Gradle 8.12 without a wrapper bump. Pushing to let CI resolve 1.79 through Artifactory and confirm empirically whether that version passes curation. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Supply Chain: pin BouncyCastle to 1.85 via component metadata rule AGP's lint tooling resolves bcprov 1.79 in a detached configuration that resolutionStrategy cannot reach, and curated Artifactory blocks it. Also fix the two sample-app issues the AGP 8 assemble surfaces: java-android-app exceeds the 64K dex limit at minSdk 16, and kotlin-android-app's PendingIntent lacks a mutability flag (fatal lint, crashes on Android 12+). * Supply Chain: make unit tests pass on JDK 17 Robolectric 4.7.3 and mockk 1.10.6 predate JDK 17 / SDK 33. Bump Robolectric to 4.11.1, align core's mockk with android's 1.12.2, and open the java.base packages mockk reflects into. Register SegmentDestinationTests' HTTPClient constructor stub up front so the first test's stub applies. * Supply Chain: build with JDK 17 in CI and keep release init script AGP 8 requires JDK 17. init.gradle is used by scripts/release.sh, so restore it rather than leaving it commented out. * CI: make the build workflow runnable under the Actions policy The repo requires SHA-pinned, GitHub-owned actions, so the workflow failed at startup on every run. Pin actions, replace the cancel action with native concurrency, drop the Codecov upload, and run on Twilio runners through curated Artifactory with JDK 17. This replaces the Supply Chain Build. * Supply Chain: pin BouncyCastle from test libraries too Robolectric 4.11.1 depends on bcprov-jdk18on 1.76, which curated Artifactory blocks. Apply the 1.85 rewrite to every component's BouncyCastle dependencies, and keep the jars out of Jetifier, which cannot process them. --------- Co-authored-by: Kostas Niktas <kniktas@twilio.com> Co-authored-by: Wenxi Zeng <wzeng@twilio.com> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
1 parent ae26360 commit 43876b8

16 files changed

Lines changed: 167 additions & 90 deletions

File tree

‎.github/workflows/build.yml‎

Lines changed: 99 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -7,101 +7,128 @@ on:
77
branches: [ main ]
88
workflow_dispatch:
99

10+
concurrency:
11+
group: ${{ github.workflow }}-${{ github.ref }}
12+
cancel-in-progress: true
13+
1014
jobs:
11-
cancel_previous:
15+
test:
16+
name: ${{ matrix.name }}
17+
runs-on: ${{ github.event.pull_request.head.repo.fork && 'ubuntu-latest' || 'ubuntu-x64' }}
18+
environment: test_docker
19+
permissions:
20+
id-token: write
21+
contents: read
22+
timeout-minutes: 30
23+
strategy:
24+
fail-fast: false
25+
matrix:
26+
include:
27+
- name: core-test
28+
module: core
29+
- name: android-test
30+
module: android
31+
- name: destination-test
32+
module: samples:kotlin-android-app-destinations
1233

13-
runs-on: ubuntu-22.04
1434
steps:
15-
- uses: styfle/cancel-workflow-action@0.9.1
16-
with:
17-
workflow_id: ${{ github.event.workflow.id }}
18-
19-
core-test:
20-
needs: cancel_previous
21-
runs-on: ubuntu-22.04
35+
- name: Checkout
36+
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
2237

23-
steps:
24-
- uses: actions/checkout@v4
25-
- name: Set up JDK 11
26-
uses: actions/setup-java@v4
27-
with:
28-
distribution: 'temurin'
29-
java-version: '11'
30-
31-
- name: Grant execute permission for gradlew
32-
run: chmod +x gradlew
33-
- name: cache gradle dependencies
34-
uses: actions/cache@v3
35-
with:
36-
path: |
37-
~/.gradle/caches
38-
~/.gradle/wrapper
39-
key: ${{ runner.os }}-gradle-core-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
40-
restore-keys: |
41-
${{ runner.os }}-gradle-core-
42-
- name: Run Tests
43-
run: ./gradlew core:test
44-
- name: Generate coverage report
45-
run: ./gradlew core:codeCoverageReport
46-
- name: Upload coverage to Codecov
47-
uses: codecov/codecov-action@v2
48-
49-
android-test:
50-
needs: cancel_previous
51-
runs-on: ubuntu-22.04
38+
- name: Set up Java
39+
uses: actions/setup-java@cf277c60eb25467037889841efdb72551f06f6c3 # v4.9.1
40+
with:
41+
distribution: "temurin"
42+
java-version: "17"
5243

53-
steps:
54-
- uses: actions/checkout@v2
55-
- name: Grant execute permission for gradlew
56-
run: chmod +x gradlew
57-
- name: cache gradle dependencies
58-
uses: actions/cache@v3
44+
# Only cache if it's a fork
45+
- name: Cache Gradle dependencies
46+
if: ${{ github.event.pull_request.head.repo.fork }}
47+
uses: actions/cache@6f8efc29b200d32929f49075959781ed54ec270c # v3.5.0
5948
with:
6049
path: |
6150
~/.gradle/caches
6251
~/.gradle/wrapper
63-
key: ${{ runner.os }}-gradle-android-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
52+
key: ${{ runner.os }}-gradle-${{ matrix.name }}-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
6453
restore-keys: |
65-
${{ runner.os }}-gradle-android-
54+
${{ runner.os }}-gradle-${{ matrix.name }}-
55+
56+
- name: Artifactory OIDC
57+
if: ${{ !github.event.pull_request.head.repo.fork }}
58+
uses: twilio/sdk-actions/artifactory-oidc@0d27cae699ef35881ab22fd965922d3d555cab9d # java_gradle
59+
with:
60+
ecosystem: "gradle"
61+
provider-name: "github-actions-segmentio"
62+
6663
- name: Run Tests
67-
run: ./gradlew android:test
68-
- name: Generate coverage report
69-
run: ./gradlew android:codeCoverageReport
70-
- name: Upload coverage to Codecov
71-
uses: codecov/codecov-action@v2
64+
run: ./gradlew ${{ matrix.module }}:test
7265

73-
destination-test:
74-
needs: cancel_previous
75-
runs-on: ubuntu-22.04
66+
build:
67+
name: Build
68+
runs-on: ${{ github.event.pull_request.head.repo.fork && 'ubuntu-latest' || 'ubuntu-x64' }}
69+
environment: test_docker
70+
permissions:
71+
id-token: write
72+
contents: read
73+
timeout-minutes: 20
7674

7775
steps:
78-
- uses: actions/checkout@v2
79-
- name: Grant execute permission for gradlew
80-
run: chmod +x gradlew
81-
- name: cache gradle dependencies
82-
uses: actions/cache@v3
76+
- name: Checkout
77+
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
78+
79+
- name: Set up Java
80+
uses: actions/setup-java@cf277c60eb25467037889841efdb72551f06f6c3 # v4.9.1
81+
with:
82+
distribution: "temurin"
83+
java-version: "17"
84+
85+
# Only cache if it's a fork
86+
- name: Cache Gradle dependencies
87+
if: ${{ github.event.pull_request.head.repo.fork }}
88+
uses: actions/cache@6f8efc29b200d32929f49075959781ed54ec270c # v3.5.0
8389
with:
8490
path: |
8591
~/.gradle/caches
8692
~/.gradle/wrapper
87-
key: ${{ runner.os }}-gradle-dest-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
93+
key: ${{ runner.os }}-gradle-build-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
8894
restore-keys: |
89-
${{ runner.os }}-gradle-dest-
90-
- name: Run Tests
91-
run: ./gradlew samples:kotlin-android-app-destinations:test
92-
- name: Generate coverage report
93-
run: ./gradlew samples:kotlin-android-app-destinations:codeCoverageReport
94-
- name: Upload coverage to Codecov
95-
uses: codecov/codecov-action@v2
95+
${{ runner.os }}-gradle-build-
96+
97+
- name: Artifactory OIDC
98+
if: ${{ !github.event.pull_request.head.repo.fork }}
99+
uses: twilio/sdk-actions/artifactory-oidc@0d27cae699ef35881ab22fd965922d3d555cab9d # java_gradle
100+
with:
101+
ecosystem: "gradle"
102+
provider-name: "github-actions-segmentio"
103+
104+
- name: Build
105+
run: ./gradlew assemble
96106

97107
security:
98-
needs: cancel_previous
99-
runs-on: ubuntu-22.04
108+
runs-on: ${{ github.event.pull_request.head.repo.fork && 'ubuntu-latest' || 'ubuntu-x64' }}
109+
environment: test_docker
110+
permissions:
111+
id-token: write
112+
contents: read
113+
timeout-minutes: 20
100114

101115
steps:
102-
- uses: actions/checkout@v2
103-
- name: Grant execute permission for gradlew
104-
run: chmod +x gradlew
116+
- name: Checkout
117+
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
118+
119+
- name: Set up Java
120+
uses: actions/setup-java@cf277c60eb25467037889841efdb72551f06f6c3 # v4.9.1
121+
with:
122+
distribution: "temurin"
123+
java-version: "17"
124+
125+
- name: Artifactory OIDC
126+
if: ${{ !github.event.pull_request.head.repo.fork }}
127+
uses: twilio/sdk-actions/artifactory-oidc@0d27cae699ef35881ab22fd965922d3d555cab9d # java_gradle
128+
with:
129+
ecosystem: "gradle"
130+
provider-name: "github-actions-segmentio"
131+
105132
- name: Snyk
106133
run: ./gradlew snyk-test
107134
env:

‎.github/workflows/release.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ jobs:
1515
uses: actions/setup-java@v4
1616
with:
1717
distribution: 'temurin'
18-
java-version: '11'
18+
java-version: '17'
1919
- name: Get tag
2020
id: vars
2121
run: echo ::set-output name=tag::${GITHUB_REF#refs/*/}

‎.github/workflows/snapshot.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ jobs:
1414
uses: actions/setup-java@v4
1515
with:
1616
distribution: 'temurin'
17-
java-version: '11'
17+
java-version: '17'
1818
- name: Grant execute permission for gradlew
1919
run: chmod +x gradlew
2020
- name: cache gradle dependencies

‎android/build.gradle‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,13 @@ plugins {
66
}
77

88
android {
9+
namespace 'com.segment.analytics'
910
compileSdkVersion 33
1011

12+
buildFeatures {
13+
buildConfig true
14+
}
15+
1116
defaultConfig {
1217
// Required when setting minSdkVersion to 20 or lower
1318
multiDexEnabled true
@@ -72,7 +77,7 @@ dependencies {
7277
testRuntimeOnly 'org.junit.vintage:junit-vintage-engine:5.8.2'
7378

7479
// Add Roboelectric dependencies.
75-
testImplementation 'org.robolectric:robolectric:4.7.3'
80+
testImplementation 'org.robolectric:robolectric:4.11.1'
7681
testImplementation 'androidx.test:core:1.5.0'
7782
}
7883

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
<?xml version="1.0" encoding="utf-8"?>
2-
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
3-
package="com.segment.analytics">
2+
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
43

54
</manifest>

‎build.gradle‎

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ buildscript {
66
google()
77
}
88
dependencies {
9-
classpath 'com.android.tools.build:gradle:7.4.2'
9+
classpath 'com.android.tools.build:gradle:8.10.1'
1010
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
1111
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
1212

@@ -27,8 +27,8 @@ allprojects {
2727
repositories {
2828
google()
2929
mavenCentral()
30-
maven { url "https://kotlin.bintray.com/kotlinx" }
31-
maven { url 'https://dl.bintray.com/kotlin/kotlin-eap' }
30+
// maven { url "https://kotlin.bintray.com/kotlinx" }
31+
// maven { url 'https://dl.bintray.com/kotlin/kotlin-eap' }
3232
}
3333
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
3434
kotlinOptions {
@@ -38,6 +38,34 @@ allprojects {
3838
}
3939
group GROUP
4040
version getVersionName()
41+
42+
// mockk mocks and copies JDK internals (java.time, HttpURLConnection, atomics) that JDK 17 closes by default.
43+
tasks.withType(Test).configureEach {
44+
jvmArgs '--add-opens=java.base/java.time=ALL-UNNAMED',
45+
'--add-opens=java.base/java.lang=ALL-UNNAMED',
46+
'--add-opens=java.base/sun.net.www.protocol.https=ALL-UNNAMED',
47+
'--add-opens=java.base/javax.net.ssl=ALL-UNNAMED',
48+
'--add-opens=java.base/java.net=ALL-UNNAMED',
49+
'--add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED'
50+
}
51+
52+
/*
53+
* AGP's lint tooling (used by extractAnnotations and lint) resolves BouncyCastle 1.79 inside an
54+
* internal detached configuration, which resolutionStrategy rules do not reach. Curated Artifactory
55+
* blocks bcprov-jdk18on up to 1.84, so rewrite every component's BouncyCastle dependencies (the
56+
* lint tooling's, and test libraries' such as Robolectric) to a version it serves. Component
57+
* metadata rules apply to detached configurations too.
58+
* Build-time only: BouncyCastle is not part of the published artifact.
59+
*/
60+
dependencies.components.all { ComponentMetadataDetails details ->
61+
details.allVariants {
62+
withDependencies { deps ->
63+
deps.findAll { it.group == "org.bouncycastle" }.each { dep ->
64+
dep.version { strictly "1.85" }
65+
}
66+
}
67+
}
68+
}
4169
}
4270

4371
snyk {

‎core/build.gradle‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ dependencies {
4343

4444
// TESTING
4545
repositories { mavenCentral() }
46-
testImplementation 'io.mockk:mockk:1.10.6'
46+
testImplementation 'io.mockk:mockk:1.12.2'
4747
testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.1'
4848
testImplementation 'com.squareup.okhttp3:mockwebserver:4.12.0'
4949

‎core/src/test/kotlin/com/segment/analytics/kotlin/core/platform/plugins/SegmentDestinationTests.kt‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@ class SegmentDestinationTests {
6060
every { UUID.randomUUID().toString() } returns "qwerty-qwerty-123"
6161
mockkStatic(Base64::class)
6262
mockkConstructor(HTTPClient::class)
63+
// register the constructor stub up front; mockk 1.12 doesn't apply a test's first
64+
// anyConstructed stub to HTTPClients built before it (in setup), so they'd hit the network.
65+
every { anyConstructed<HTTPClient>().upload(any()) } answers { callOriginal() }
6366
}
6467

6568
@BeforeEach

‎gradle.properties‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,10 @@ org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
1717
android.useAndroidX=true
1818
# Automatically convert third-party libraries to use AndroidX
1919
android.enableJetifier=true
20+
# BouncyCastle (test-only, via Robolectric) has no support-library refs and Jetifier cannot process it
21+
android.jetifier.ignorelist=bcprov-jdk18on,bcpkix-jdk18on,bcutil-jdk18on
2022
# Kotlin code style for this project: "official" or "obsolete":
2123
kotlin.code.style=official
22-
android.disableAutomaticComponentCreation=true
2324

2425
# Deployment variables
2526
GROUP=com.segment.analytics.kotlin

‎samples/java-android-app/build.gradle‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,17 @@ plugins {
33
}
44

55
android {
6+
namespace 'com.segment.analytics.javaandroidapp'
67
compileSdkVersion 33
78

9+
buildFeatures {
10+
buildConfig true
11+
}
12+
813
defaultConfig {
914
applicationId "com.segment.analytics.javaandroidapp"
15+
// Required when setting minSdkVersion to 20 or lower
16+
multiDexEnabled true
1017
minSdkVersion 16
1118
targetSdkVersion 33
1219
versionCode 1

0 commit comments

Comments
 (0)