diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index c3668054..99087fc5 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -6,9 +6,8 @@ name: CI on: # Triggers the workflow on push or pull request events but only for the main branch push: - branches: [ main ] + branches: [main] pull_request: - branches: [ main ] # Allows you to run this workflow manually from the Actions tab workflow_dispatch: @@ -19,46 +18,121 @@ jobs: lint-and-check: runs-on: ubuntu-latest steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # ratchet:actions/checkout@v6.0.2 - name: Validate Gradle Wrapper - uses: gradle/actions/wrapper-validation@0723195856401067f7a2779048b490ace7a47d7c # v5.0.2 + uses: gradle/actions/wrapper-validation@0723195856401067f7a2779048b490ace7a47d7c # ratchet:gradle/actions/wrapper-validation@v5.0.2 - name: Lint GitHub Actions - uses: abcxyz/actions/.github/actions/lint-github-actions@e32ec3bd6af6d87d79fe7c441f435eb7ad11d527 # main + uses: abcxyz/actions/.github/actions/lint-github-actions@e32ec3bd6af6d87d79fe7c441f435eb7ad11d527 # ratchet:abcxyz/actions/.github/actions/lint-github-actions@main - name: Ratchet Check - uses: sethvargo/ratchet@8b4ca256dbed184350608a3023620f267f0a5253 # main + uses: sethvargo/ratchet@8b4ca256dbed184350608a3023620f267f0a5253 # ratchet:sethvargo/ratchet@main with: files: .github/workflows/*.yml - # This workflow contains a single job called "build" + # Build the simple plugins (no special test infrastructure needed) build: needs: lint-and-check - # The type of runner that the job will run on runs-on: ubuntu-latest - - # Runs this job in parallel for each sub-project strategy: matrix: project-dir: - strict-version-matcher-plugin - google-services-plugin - - oss-licenses-plugin - - # Steps represent a sequence of tasks that will be executed as part of the job steps: - # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # ratchet:actions/checkout@v6.0.2 + - name: Set up JDK 17 - uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 + uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # ratchet:actions/setup-java@v5.2.0 with: java-version: '17' distribution: 'temurin' - name: Setup Gradle - uses: gradle/actions/setup-gradle@0723195856401067f7a2779048b490ace7a47d7c # v5.0.2 + uses: gradle/actions/setup-gradle@0723195856401067f7a2779048b490ace7a47d7c # ratchet:gradle/actions/setup-gradle@v5.0.2 with: dependency-graph: generate-and-submit - # Runs a build which includes `check` and `test` tasks - name: Perform a Gradle build run: ./gradlew build working-directory: ./${{ matrix.project-dir }} + + # Build and test the oss-licenses plugin, then publish for downstream jobs. + oss-licenses-build: + needs: lint-and-check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # ratchet:actions/checkout@v6.0.2 + + - name: Set up JDK 17 + uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # ratchet:actions/setup-java@v5.2.0 + with: + java-version: '17' + distribution: 'temurin' + + - name: Set up JDK 21 + uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # ratchet:actions/setup-java@v5.2.0 + with: + java-version: '21' + distribution: 'temurin' + + - name: Setup Gradle + uses: gradle/actions/setup-gradle@0723195856401067f7a2779048b490ace7a47d7c # ratchet:gradle/actions/setup-gradle@v5.0.2 + with: + dependency-graph: generate-and-submit + + - name: Build and test + run: ./gradlew build + working-directory: ./oss-licenses-plugin + + - name: Publish to local repo + run: ./gradlew publish + working-directory: ./oss-licenses-plugin + + - name: Upload local repo artifact + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # ratchet:actions/upload-artifact@v4.6.2 + with: + name: oss-licenses-local-repo + path: oss-licenses-plugin/build/repo/ + + # Build the testapp and verify configuration cache store → clean → reuse. + # CC strictness flags are in testapp/gradle.properties (always on). + oss-licenses-testapp: + needs: oss-licenses-build + runs-on: ubuntu-latest + env: + ANDROID_USER_HOME: /home/runner/.android + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # ratchet:actions/checkout@v6.0.2 + + - name: Download local repo artifact + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # ratchet:actions/download-artifact@v4.3.0 + with: + name: oss-licenses-local-repo + path: oss-licenses-plugin/build/repo/ + + - name: Set up JDK 21 + uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # ratchet:actions/setup-java@v5.2.0 + with: + java-version: '21' + distribution: 'temurin' + + - name: Setup Gradle + uses: gradle/actions/setup-gradle@0723195856401067f7a2779048b490ace7a47d7c # ratchet:gradle/actions/setup-gradle@v5.0.2 + + - name: Pre-create Android SDK cache directory + run: mkdir -p "$ANDROID_USER_HOME/cache" + + - name: Build testapp (store configuration cache) + run: ./gradlew build -PusePublishedPluginFrom=../build/repo + working-directory: oss-licenses-plugin/testapp + + - name: Clean testapp outputs + run: ./gradlew clean -PusePublishedPluginFrom=../build/repo + working-directory: oss-licenses-plugin/testapp + + - name: Rebuild testapp (verify configuration cache reuse) + working-directory: oss-licenses-plugin/testapp + run: | + OUTPUT=$(./gradlew build -PusePublishedPluginFrom=../build/repo 2>&1) + echo "$OUTPUT" + echo "$OUTPUT" | grep -q "Configuration cache entry reused" || \ + { echo "::error::Configuration cache was NOT reused"; exit 1; } diff --git a/.gitignore b/.gitignore index 09913245..c1e4d3ae 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .idea/ repo/ .kotlin/ +.DS_Store diff --git a/oss-licenses-plugin/gradle/gradle-daemon-jvm.properties b/oss-licenses-plugin/gradle/gradle-daemon-jvm.properties new file mode 100644 index 00000000..1b1b6f29 --- /dev/null +++ b/oss-licenses-plugin/gradle/gradle-daemon-jvm.properties @@ -0,0 +1,12 @@ +#This file is generated by updateDaemonJvm +toolchainUrl.FREE_BSD.AARCH64=https\://api.foojay.io/disco/v3.0/ids/4945f00643ec68e7c7a6b66f90124f89/redirect +toolchainUrl.FREE_BSD.X86_64=https\://api.foojay.io/disco/v3.0/ids/93aeea858331bd6bb00ba94759830234/redirect +toolchainUrl.LINUX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/4945f00643ec68e7c7a6b66f90124f89/redirect +toolchainUrl.LINUX.X86_64=https\://api.foojay.io/disco/v3.0/ids/93aeea858331bd6bb00ba94759830234/redirect +toolchainUrl.MAC_OS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/46949723aaa20c7b64d7ecfed7207034/redirect +toolchainUrl.MAC_OS.X86_64=https\://api.foojay.io/disco/v3.0/ids/d6690dfd71c4c91e08577437b5b2beb0/redirect +toolchainUrl.UNIX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/4945f00643ec68e7c7a6b66f90124f89/redirect +toolchainUrl.UNIX.X86_64=https\://api.foojay.io/disco/v3.0/ids/93aeea858331bd6bb00ba94759830234/redirect +toolchainUrl.WINDOWS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/3cd7045fca9a72cd9bc7d14a385e594c/redirect +toolchainUrl.WINDOWS.X86_64=https\://api.foojay.io/disco/v3.0/ids/552c7bffe0370c66410a51c55985b511/redirect +toolchainVersion=21 diff --git a/oss-licenses-plugin/settings.gradle b/oss-licenses-plugin/settings.gradle index 995d72ee..3250f57b 100644 --- a/oss-licenses-plugin/settings.gradle +++ b/oss-licenses-plugin/settings.gradle @@ -1 +1,5 @@ +plugins { + id 'org.gradle.toolchains.foojay-resolver-convention' version '1.0.0' +} + rootProject.name = 'oss-licenses-plugin' diff --git a/oss-licenses-plugin/testapp/app/build.gradle.kts b/oss-licenses-plugin/testapp/app/build.gradle.kts new file mode 100644 index 00000000..a04e90d7 --- /dev/null +++ b/oss-licenses-plugin/testapp/app/build.gradle.kts @@ -0,0 +1,108 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import com.android.build.api.variant.HostTestBuilder +import org.gradle.api.tasks.testing.Test +import org.gradle.jvm.toolchain.JavaToolchainService + +plugins { + alias(libs.plugins.android.application) + alias(libs.plugins.kotlin.android) + alias(libs.plugins.kotlin.compose) + alias(libs.plugins.oss.licenses) +} + +android { + namespace = "com.google.android.gms.oss.licenses.testapp" + compileSdk = libs.versions.compileSdk.get().toInt() + testBuildType = "release" + + defaultConfig { + applicationId = "com.google.android.gms.oss.licenses.testapp" + minSdk = libs.versions.minSdk.get().toInt() + targetSdk = libs.versions.targetSdk.get().toInt() + versionCode = 1 + versionName = "1.0" + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + debug { isMinifyEnabled = false } + release { + signingConfig = signingConfigs.getByName("debug") + isMinifyEnabled = true + proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt")) + } + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_21 + targetCompatibility = JavaVersion.VERSION_21 + } + testOptions { unitTests { isIncludeAndroidResources = true } } + lint { + abortOnError = true + checkDependencies = true + ignoreWarnings = false + } +} + +tasks.withType().configureEach { + val javaToolchains = project.extensions.getByType() + javaLauncher.set(javaToolchains.launcherFor { languageVersion.set(JavaLanguageVersion.of(21)) }) + + // Enable parallel execution for faster Robolectric runs + maxParallelForks = (Runtime.getRuntime().availableProcessors() / 2).coerceAtLeast(1) + + testLogging { + events("passed", "skipped", "failed", "standardOut", "standardError") + showStandardStreams = true + } +} + +androidComponents { + beforeVariants { variantBuilder -> + // AGP 9.0 only enables unit tests for the "tested build type" by default. + // We explicitly enable them for all variants to ensure both Debug and Release coverage. + variantBuilder.hostTests[HostTestBuilder.UNIT_TEST_TYPE]?.enable = true + } +} + +kotlin { + jvmToolchain(21) + compilerOptions { jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_21) } +} + +dependencies { + implementation(libs.play.services.oss.licenses) + implementation(libs.androidx.appcompat) + implementation(libs.androidx.activity) + implementation("androidx.activity:activity-compose:${libs.versions.androidx.activity.get()}") + implementation(platform(libs.androidx.compose.bom)) + implementation("androidx.compose.material3:material3") + + + testImplementation(libs.junit) + testImplementation(libs.androidx.test.ext.junit) + testImplementation(libs.androidx.test.espresso.core) + testImplementation(libs.androidx.test.espresso.contrib) + testImplementation(libs.androidx.test.core) + testImplementation(libs.robolectric) + + // Compose Test (required for testing the V2 activity) + testImplementation(platform(libs.androidx.compose.bom)) + testImplementation(libs.androidx.compose.ui.test.junit4) + debugImplementation(libs.androidx.compose.ui.test.manifest) +} diff --git a/oss-licenses-plugin/testapp/app/src/main/AndroidManifest.xml b/oss-licenses-plugin/testapp/app/src/main/AndroidManifest.xml new file mode 100644 index 00000000..482292ae --- /dev/null +++ b/oss-licenses-plugin/testapp/app/src/main/AndroidManifest.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + diff --git a/oss-licenses-plugin/testapp/app/src/main/java/com/google/android/gms/oss/licenses/testapp/MainActivity.kt b/oss-licenses-plugin/testapp/app/src/main/java/com/google/android/gms/oss/licenses/testapp/MainActivity.kt new file mode 100644 index 00000000..db0394bd --- /dev/null +++ b/oss-licenses-plugin/testapp/app/src/main/java/com/google/android/gms/oss/licenses/testapp/MainActivity.kt @@ -0,0 +1,58 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.gms.oss.licenses.testapp + +import android.content.Intent +import android.os.Bundle +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height +import androidx.compose.material3.Button +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.google.android.gms.oss.licenses.OssLicensesMenuActivity as V1Activity +import com.google.android.gms.oss.licenses.v2.OssLicensesMenuActivity as V2Activity + +class MainActivity : ComponentActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContent { + MaterialTheme { + Column( + modifier = Modifier.fillMaxSize(), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Button(onClick = { startActivity(Intent(this@MainActivity, V1Activity::class.java)) }) { + Text("Launch V1 Licenses") + } + Spacer(modifier = Modifier.height(16.dp)) + Button(onClick = { startActivity(Intent(this@MainActivity, V2Activity::class.java)) }) { + Text("Launch V2 Licenses") + } + } + } + } + } +} diff --git a/oss-licenses-plugin/testapp/app/src/test/resources/robolectric.properties b/oss-licenses-plugin/testapp/app/src/test/resources/robolectric.properties new file mode 100644 index 00000000..71a66445 --- /dev/null +++ b/oss-licenses-plugin/testapp/app/src/test/resources/robolectric.properties @@ -0,0 +1,3 @@ +# Global Robolectric Configuration +# Note: SDK 36 (Baklava) requires Java 21+ — Robolectric will fail to run it on older JVMs. +sdk=24, 33, 34, 35, 36 \ No newline at end of file diff --git a/oss-licenses-plugin/testapp/app/src/testDebug/java/com/google/android/gms/oss/licenses/testapp/OssLicensesDebugV1Test.kt b/oss-licenses-plugin/testapp/app/src/testDebug/java/com/google/android/gms/oss/licenses/testapp/OssLicensesDebugV1Test.kt new file mode 100644 index 00000000..25e99657 --- /dev/null +++ b/oss-licenses-plugin/testapp/app/src/testDebug/java/com/google/android/gms/oss/licenses/testapp/OssLicensesDebugV1Test.kt @@ -0,0 +1,39 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.gms.oss.licenses.testapp + +import androidx.test.core.app.ActivityScenario +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.withText +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.google.android.gms.oss.licenses.OssLicensesMenuActivity +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class OssLicensesDebugV1Test { + + @Test + fun testV1DebugActivityLoadsCorrectly() { + ActivityScenario.launch(OssLicensesMenuActivity::class.java).use { + // In debug mode, the plugin injects a placeholder entry + onView(withText("Debug License Info")).check(matches(isDisplayed())) + } + } +} diff --git a/oss-licenses-plugin/testapp/app/src/testDebug/java/com/google/android/gms/oss/licenses/testapp/OssLicensesDebugV2Test.kt b/oss-licenses-plugin/testapp/app/src/testDebug/java/com/google/android/gms/oss/licenses/testapp/OssLicensesDebugV2Test.kt new file mode 100644 index 00000000..9b1ad147 --- /dev/null +++ b/oss-licenses-plugin/testapp/app/src/testDebug/java/com/google/android/gms/oss/licenses/testapp/OssLicensesDebugV2Test.kt @@ -0,0 +1,40 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.gms.oss.licenses.testapp + +import androidx.compose.ui.test.junit4.createEmptyComposeRule +import androidx.compose.ui.test.onNodeWithText +import androidx.test.core.app.ActivityScenario +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.google.android.gms.oss.licenses.v2.OssLicensesMenuActivity +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class OssLicensesDebugV2Test { + + @get:Rule val composeTestRule = createEmptyComposeRule() + + @Test + fun testV2DebugActivityLoadsCorrectly() { + ActivityScenario.launch(OssLicensesMenuActivity::class.java).use { + // In debug mode, the plugin injects a placeholder entry + composeTestRule.onNodeWithText("Debug License Info").assertExists() + } + } +} diff --git a/oss-licenses-plugin/testapp/app/src/testRelease/java/com/google/android/gms/oss/licenses/testapp/OssLicensesV1Test.kt b/oss-licenses-plugin/testapp/app/src/testRelease/java/com/google/android/gms/oss/licenses/testapp/OssLicensesV1Test.kt new file mode 100644 index 00000000..0386faf3 --- /dev/null +++ b/oss-licenses-plugin/testapp/app/src/testRelease/java/com/google/android/gms/oss/licenses/testapp/OssLicensesV1Test.kt @@ -0,0 +1,95 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.gms.oss.licenses.testapp + +import android.widget.ListView +import androidx.test.core.app.ActivityScenario +import androidx.test.espresso.Espresso.onData +import androidx.test.espresso.action.ViewActions.click +import androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.google.android.gms.oss.licenses.OssLicensesMenuActivity +import org.hamcrest.CoreMatchers.anything +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNotEquals +import org.junit.Assert.assertTrue +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.Shadows.shadowOf + +@RunWith(AndroidJUnit4::class) +class OssLicensesV1Test { + + @Test + fun testV1ActivityLoadsLicenses() { + ActivityScenario.launch(OssLicensesMenuActivity::class.java).use { scenario -> + scenario.onActivity { activity -> + assertEquals("Open source licenses", activity.title) + + val res = activity.resources + val packageName = activity.packageName + val metadataId = + res.getIdentifier("third_party_license_metadata", "raw", packageName) + val licensesId = res.getIdentifier("third_party_licenses", "raw", packageName) + + assertNotEquals( + "Resource 'raw/third_party_license_metadata' not found.", + 0, + metadataId, + ) + assertNotEquals("Resource 'raw/third_party_licenses' not found.", 0, licensesId) + + res.openRawResource(metadataId).use { + assertNotEquals("Metadata file is empty.", 0, it.available()) + } + res.openRawResource(licensesId).use { + assertNotEquals("Licenses file is empty.", 0, it.available()) + } + } + } + } + + @Test + fun testV1DetailNavigation() { + ActivityScenario.launch(OssLicensesMenuActivity::class.java).use { scenario -> + // Use Espresso to click the first item in the list. + // Targeting by class type (ListView) is more robust than using internal library IDs. + onData(anything()) + .inAdapterView(isAssignableFrom(ListView::class.java)) + .atPosition(0) + .perform(click()) + + scenario.onActivity { activity -> + // Use ShadowActivity to verify the next activity was started + val shadowActivity = shadowOf(activity) + val nextIntent = shadowActivity.nextStartedActivity + assertNotEquals("Detail activity should have been started", null, nextIntent) + assertTrue( + "Started activity should be OssLicensesActivity", + nextIntent.component?.className?.contains("OssLicensesActivity") == true, + ) + } + } + } + + @Test + fun testV1ActivityMenuLoadsCorrectly() { + ActivityScenario.launch(OssLicensesMenuActivity::class.java).use { scenario -> + scenario.onActivity { activity -> assertNotEquals(null, activity) } + } + } +} diff --git a/oss-licenses-plugin/testapp/app/src/testRelease/java/com/google/android/gms/oss/licenses/testapp/OssLicensesV2Test.kt b/oss-licenses-plugin/testapp/app/src/testRelease/java/com/google/android/gms/oss/licenses/testapp/OssLicensesV2Test.kt new file mode 100644 index 00000000..177d2175 --- /dev/null +++ b/oss-licenses-plugin/testapp/app/src/testRelease/java/com/google/android/gms/oss/licenses/testapp/OssLicensesV2Test.kt @@ -0,0 +1,98 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.gms.oss.licenses.testapp + +import android.content.Intent +import androidx.compose.ui.test.junit4.createEmptyComposeRule +import androidx.compose.ui.test.onNodeWithText +import androidx.compose.ui.test.performClick +import androidx.test.core.app.ActivityScenario +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.google.android.gms.oss.licenses.v2.OssLicensesMenuActivity +import org.junit.Assert.assertTrue +import org.junit.Ignore +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class OssLicensesV2Test { + + @get:Rule val composeTestRule = createEmptyComposeRule() + + @Test + fun testV2ActivityMenuLoadsCorrectly() { + ActivityScenario.launch(OssLicensesMenuActivity::class.java).use { + // Verify a standard library is visible + composeTestRule.onNodeWithText("Activity", ignoreCase = true).assertExists() + } + } + + @Test + fun testV2DetailNavigation() { + ActivityScenario.launch(OssLicensesMenuActivity::class.java).use { + // Click on a visible entry + composeTestRule.onNodeWithText("Activity", ignoreCase = true).performClick() + + // Verify detail screen shows license text + try { + composeTestRule + .onNodeWithText("Apache License", substring = true, ignoreCase = true) + .assertExists() + } catch (e: AssertionError) { + composeTestRule + .onNodeWithText("http", substring = true, ignoreCase = true) + .assertExists() + } + } + } + + @Test + fun testV2LicenseSourceTypes() { + // Verifies that the plugin correctly extracts licenses from both sources: + // 1. POM files (standard Maven deps like AndroidX) — license URL in the POM XML + // 2. AAR-embedded license files (Google Play Services) — third_party_licenses.txt in the AAR + ActivityScenario.launch(OssLicensesMenuActivity::class.java).use { scenario -> + scenario.onActivity { activity -> + val res = activity.resources + val pkg = activity.packageName + val metadataId = res.getIdentifier("third_party_license_metadata", "raw", pkg) + val metadata = res.openRawResource(metadataId).bufferedReader().readText() + + // POM-based: AndroidX libraries have licenses declared in their POM XML + assertTrue("Expected POM-based entry (e.g. AppCompat)", metadata.contains("AppCompat")) + + // AAR-embedded: Play Services bundles third_party_licenses.txt inside the AAR + assertTrue("Expected AAR-embedded entry (e.g. play-services-base)", metadata.contains("play-services-base")) + } + } + } + + @Test + fun testV2ActivityCustomTitleViaIntent() { + val customTitle = "My Custom Licenses Title" + val intent = + Intent(ApplicationProvider.getApplicationContext(), OssLicensesMenuActivity::class.java) + .apply { putExtra("title", customTitle) } + + ActivityScenario.launch(intent).use { + // The v2 library does not update activity.title, it only displays it in the Compose UI. + composeTestRule.onNodeWithText(customTitle).assertExists() + } + } +} diff --git a/oss-licenses-plugin/testapp/build.gradle.kts b/oss-licenses-plugin/testapp/build.gradle.kts new file mode 100644 index 00000000..9e7c2bfa --- /dev/null +++ b/oss-licenses-plugin/testapp/build.gradle.kts @@ -0,0 +1,22 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Standalone root project for the testapp + +plugins { + alias(libs.plugins.android.application) apply false + alias(libs.plugins.kotlin.android) apply false +} diff --git a/oss-licenses-plugin/testapp/gradle.properties b/oss-licenses-plugin/testapp/gradle.properties new file mode 100644 index 00000000..8f9b5a5f --- /dev/null +++ b/oss-licenses-plugin/testapp/gradle.properties @@ -0,0 +1,34 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Modern Android defaults +android.nonFinalResIds=true +android.nonTransitiveRClass=true +android.useAndroidX=true + +# Gradle performance and stability +org.gradle.configuration-cache=true +org.gradle.configuration-cache.problems=fail +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 + +# Plugin specific flags +# This property silences warnings about vulnerable protobuf generated types +# in older versions of AGP/Gradle. +com.google.protobuf.use_unsafe_pre22_gencode=true + +# AGP 9.0's built-in Kotlin is incompatible with the kotlin-android plugin, which +# is required for AGP 8.x backward compatibility in TestAppEndToEndTest. +# These opt-outs are removed in AGP 10.0 — drop AGP 8.x from the test matrix then. +android.builtInKotlin=false +android.newDsl=false diff --git a/oss-licenses-plugin/testapp/gradle/gradle-daemon-jvm.properties b/oss-licenses-plugin/testapp/gradle/gradle-daemon-jvm.properties new file mode 100644 index 00000000..7e918e6b --- /dev/null +++ b/oss-licenses-plugin/testapp/gradle/gradle-daemon-jvm.properties @@ -0,0 +1,12 @@ +#This file is generated by updateDaemonJvm +toolchainUrl.FREE_BSD.AARCH64=https\://api.foojay.io/disco/v3.0/ids/df211d3c3eefdc408b462041881bc575/redirect +toolchainUrl.FREE_BSD.X86_64=https\://api.foojay.io/disco/v3.0/ids/b41931cf1e70bc8e08d7dd19c343ef00/redirect +toolchainUrl.LINUX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/4945f00643ec68e7c7a6b66f90124f89/redirect +toolchainUrl.LINUX.X86_64=https\://api.foojay.io/disco/v3.0/ids/b41931cf1e70bc8e08d7dd19c343ef00/redirect +toolchainUrl.MAC_OS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/46949723aaa20c7b64d7ecfed7207034/redirect +toolchainUrl.MAC_OS.X86_64=https\://api.foojay.io/disco/v3.0/ids/f636c800fdb3f9ae33f019dfa048ba72/redirect +toolchainUrl.UNIX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/4945f00643ec68e7c7a6b66f90124f89/redirect +toolchainUrl.UNIX.X86_64=https\://api.foojay.io/disco/v3.0/ids/b41931cf1e70bc8e08d7dd19c343ef00/redirect +toolchainUrl.WINDOWS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/1e91f45234d88a64dafb961c93ddc75a/redirect +toolchainUrl.WINDOWS.X86_64=https\://api.foojay.io/disco/v3.0/ids/0ef34dd9312b12d61ba1b8e66126d140/redirect +toolchainVersion=21 diff --git a/oss-licenses-plugin/testapp/gradle/libs.versions.toml b/oss-licenses-plugin/testapp/gradle/libs.versions.toml new file mode 100644 index 00000000..486e26cc --- /dev/null +++ b/oss-licenses-plugin/testapp/gradle/libs.versions.toml @@ -0,0 +1,36 @@ +[versions] +agp = "9.0.1" +androidx-activity = "1.13.0" +androidx-appcompat = "1.7.1" +androidx-compose-bom = "2026.03.00" +androidx-test = "1.7.0" +androidx-test-espresso = "3.7.0" +androidx-test-ext = "1.3.0" +compileSdk = "36" +junit = "4.13.2" +kotlin = "2.1.10" +minSdk = "24" +oss-licenses-plugin = "+" +oss-licenses-library = "17.4.0" +robolectric = "4.16.1" +targetSdk = "36" + +[libraries] +androidx-activity = { module = "androidx.activity:activity", version.ref = "androidx-activity" } +androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "androidx-appcompat" } +androidx-compose-bom = { module = "androidx.compose:compose-bom", version.ref = "androidx-compose-bom" } +androidx-compose-ui-test-junit4 = { module = "androidx.compose.ui:ui-test-junit4" } +androidx-compose-ui-test-manifest = { module = "androidx.compose.ui:ui-test-manifest" } +androidx-test-core = { module = "androidx.test:core", version.ref = "androidx-test" } +androidx-test-espresso-contrib = { module = "androidx.test.espresso:espresso-contrib", version.ref = "androidx-test-espresso" } +androidx-test-espresso-core = { module = "androidx.test.espresso:espresso-core", version.ref = "androidx-test-espresso" } +androidx-test-ext-junit = { module = "androidx.test.ext:junit", version.ref = "androidx-test-ext" } +junit = { module = "junit:junit", version.ref = "junit" } +play-services-oss-licenses = { module = "com.google.android.gms:play-services-oss-licenses", version.ref = "oss-licenses-library" } +robolectric = { module = "org.robolectric:robolectric", version.ref = "robolectric" } + +[plugins] +android-application = { id = "com.android.application", version.ref = "agp" } +kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } +kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } +oss-licenses = { id = "com.google.android.gms.oss-licenses-plugin", version.ref = "oss-licenses-plugin" } diff --git a/oss-licenses-plugin/testapp/gradle/wrapper/gradle-wrapper.jar b/oss-licenses-plugin/testapp/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..d997cfc6 Binary files /dev/null and b/oss-licenses-plugin/testapp/gradle/wrapper/gradle-wrapper.jar differ diff --git a/oss-licenses-plugin/testapp/gradle/wrapper/gradle-wrapper.properties b/oss-licenses-plugin/testapp/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..dbc3ce4a --- /dev/null +++ b/oss-licenses-plugin/testapp/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.0-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/oss-licenses-plugin/testapp/gradlew b/oss-licenses-plugin/testapp/gradlew new file mode 100755 index 00000000..0262dcbd --- /dev/null +++ b/oss-licenses-plugin/testapp/gradlew @@ -0,0 +1,248 @@ +#!/bin/sh + +# +# Copyright © 2015 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/b631911858264c0b6e4d6603d677ff5218766cee/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/oss-licenses-plugin/testapp/gradlew.bat b/oss-licenses-plugin/testapp/gradlew.bat new file mode 100644 index 00000000..e509b2dd --- /dev/null +++ b/oss-licenses-plugin/testapp/gradlew.bat @@ -0,0 +1,93 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/oss-licenses-plugin/testapp/settings.gradle.kts b/oss-licenses-plugin/testapp/settings.gradle.kts new file mode 100644 index 00000000..5736c5b7 --- /dev/null +++ b/oss-licenses-plugin/testapp/settings.gradle.kts @@ -0,0 +1,83 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// If no local.properties exists, copy from the parent plugin project (which Android Studio generates). +val localProps = file("local.properties") +if (!localProps.exists()) { + val parentProps = file("../local.properties") + if (parentProps.exists()) { + parentProps.copyTo(localProps) + } +} + +pluginManagement { + // -PusePublishedPluginFrom=../build/repo → resolve the plugin from a pre-published Maven repo (CI, e2e tests). + // (unset, default) → build the plugin from source via includeBuild (local dev). + val publishedPluginRepo = providers.gradleProperty("usePublishedPluginFrom").orNull + if (publishedPluginRepo != null) { + repositories { + // The oss-licenses plugin MUST come from the local repo, never Google Maven. + // exclusiveContent ensures Gradle won't silently fall back to an old published version. + exclusiveContent { + forRepository { maven { url = uri(file(publishedPluginRepo)) } } + filter { + includeModule("com.google.android.gms", "oss-licenses-plugin") + includeModule("com.google.android.gms.oss-licenses-plugin", "com.google.android.gms.oss-licenses-plugin.gradle.plugin") + } + } + google() + mavenCentral() + gradlePluginPortal() + } + } else { + // Local dev: includeBuild substitutes the plugin automatically. + includeBuild("..") + repositories { + google() + mavenCentral() + gradlePluginPortal() + } + } +} + +plugins { + id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0" +} + +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + // Allow overriding the 'play-services-oss-licenses' runtime library with a local version. + // Usage: ./gradlew :app:test -PlibraryRepoPath=/path/to/your/mavenrepo + val libraryRepo = providers.gradleProperty("libraryRepoPath").orNull + if (libraryRepo != null) { + println("Registering libraryRepoPath: $libraryRepo") + exclusiveContent { + forRepository { maven { url = uri(libraryRepo) } } + filter { + includeModule("com.google.android.gms", "play-services-oss-licenses") + } + } + } + + google() + mavenCentral() + } +} +enableFeaturePreview("STABLE_CONFIGURATION_CACHE") + +rootProject.name = "OSS Licenses Test App" +include(":app")