From 63d03f11f17ed8aa88ea2508a1c0ccf0c71911ed Mon Sep 17 00:00:00 2001 From: Abhay Garg <54636525+prayutsu@users.noreply.github.com> Date: Sat, 17 Jul 2021 03:49:40 +0530 Subject: [PATCH] Fix #3285: Create Kotlin script to compile list of maven dependencies and license links (#3434) * Generate maven_install.json * Add fail_if_repin_required attribute * Update rules_jvm_external version in versions.bzl and use fail_if_repin_required attribute in maven_install() * Fix bazel lint error. * Update code ownership for maven_install.json * Fix #3290: Add support for generic regex pattern matching * Apply review suggestions for proto location * Delete main directory * Implment script logic and tests * CI setup part 1 * syntax fix in yaml file * import nits in dummy file * Use regex patterns from script in script test * Make PR suggestions into effect * Make PR suggestions into effect * Make PR suggestions into effect * Improve naming of script_assets variables * Make PR suggestions into effect * Create maven_dependencies_list.kts and complete the parsing of the maven_install.json * Make PR suggestions * Make PR suggestions * Improve naming in script and add KDocs for data classes. * Add EOF for all classes. * Change data structure to store backup dependencies. * Revamp testing approach * nit fixes * Add maven re-pin command. * Add BUILD.bazel file and Test file for script.creatre-script-to-compile-list-of-maven-dependencies * Fix test build error. * Improve code. * Make nit suggestions * Try changing location of maven_install.json * Convert .kts to .kt * Save work. * Rename data classes. * Remove kscript. * Fix lints. * Apply review suggestions on PR * Refactor PR as per feedback recieved * Implement review suggestions * Set different output_base for bazel query command * nits * Nit fixes * nit fix * Change maven_install_json attribute * Move maven_install.json to third_party * Remove maven_install code ownership from CODEOWNERS * Save changes. * remove class * Fix errors * Review suggestions part 1 * Implement review suggestions part 2 * Fix broken script * nit fixes * update static_checks * add test to repository file * nit fix * nit fix * Validate all Links. * nit fix * Implement review suggestions * bazel files nit fix * Introduce a library for the regex assets * Change structure of the script. * Make directory structure same as that of #3374 * diable ktlint max-line-length * disable ktlint-max-line * disable ktlint max-length * save changes. * commit * nit fix * Add bazel pipeline to app module * save. * save. * save. * save. * save. * save. * nit fix * fix broken script * Fix bug of printing enum values in output. * Fix bug of printing enum values in output. * Apply review suggestions * ktlint fix * nit fix * Remove script constants * nit fix * Make script to call out the dependencies and licenses for which manual work needs to be completed. * add todo * Complete first script * Add first script to scripts package * Remove unwanted files * Remove extra files and fix formatting * Fix lints. * Remove unnecessary lines of code * Add comments for proto * Add test file. * Fix lints. * Remove unwanted dependencies. * Add suggested changes. * nit fixes * Fix lint. * add testOnly * Fix nits * Add suggested changes. * Add suggested changes * Update textproto * Update maven_dependencies.textproto * Always force https over http * Always force https over http * Correct origin type * Use common/BazelClient * Fix lints * Save progress * create test_maven_install.json * Restructure script * Break script in two parts * Add DependencyListsProvider * Add DependencyListsProvider * correct query command * Correct structure of the script * Correct structure of the script * Fix not suggestions * Use * Add test file for NetworkAndBazelUtilsImpl.kt * Add KDocs * Add KDocs * try using mockito * Add test case in BazelClientTest.kt * Add mockito-kotlin * Fix nit * Fix nit * try fixing tests * Restructure maven_dependencies.proto * Fix lint * add --max_idle_secs * Fix nits * Fix nit * Place KDoc at correct position * Fix BazelClientTest.kt * Add xml parser * Fix pb error * Revert data module changes * Add tests * Add first test that passes * Add all test cases. * Fix nit * Add suggested changes * Add suggested changes * Add suggested changes * Nit changes * Fix nits * Fix BazelClient test case * Add test cases for TestBazelWorkspace * Add suggested changes * Add suggested changes * Fix one test. * Fix all test cases * Add TODO for issue #3486 * Add suggestions. * Fix urls. * Fix isFalse * Add all suggested changes. * Fix nits. Co-authored-by: Sparsh1212 --- app/build.gradle | 2 +- scripts/BUILD.bazel | 29 +- scripts/assets/maven_dependencies.textproto | 875 ++++++++++++++++++ scripts/assets/test_file_exemptions.textproto | 5 + scripts/script_assets.bzl | 25 + .../android/scripts/common/BazelClient.kt | 11 + .../oppia/android/scripts/maven/BUILD.bazel | 29 + .../maven/GenerateMavenDependenciesList.kt | 337 +++++++ .../android/scripts/maven/LicenseFetcher.kt | 7 + .../scripts/maven/LicenseFetcherImpl.kt | 11 + .../android/scripts/maven/data/BUILD.bazel | 17 + .../maven/data/MavenListDependencies.kt | 19 + .../scripts/maven/data/MavenListDependency.kt | 17 + .../maven/data/MavenListDependencyTree.kt | 14 + .../oppia/android/scripts/proto/BUILD.bazel | 20 +- .../scripts/proto/maven_dependencies.proto | 76 ++ .../scripts/testing/TestBazelWorkspace.kt | 43 +- .../android/scripts/common/BazelClientTest.kt | 103 ++- .../oppia/android/scripts/maven/BUILD.bazel | 20 + .../GenerateMavenDependenciesListTest.kt | 806 ++++++++++++++++ .../scripts/testing/TestBazelWorkspaceTest.kt | 111 +++ third_party/maven_install.json | 271 ++++-- third_party/versions.bzl | 2 + 23 files changed, 2742 insertions(+), 108 deletions(-) create mode 100644 scripts/assets/maven_dependencies.textproto create mode 100644 scripts/src/java/org/oppia/android/scripts/maven/BUILD.bazel create mode 100644 scripts/src/java/org/oppia/android/scripts/maven/GenerateMavenDependenciesList.kt create mode 100644 scripts/src/java/org/oppia/android/scripts/maven/LicenseFetcher.kt create mode 100644 scripts/src/java/org/oppia/android/scripts/maven/LicenseFetcherImpl.kt create mode 100644 scripts/src/java/org/oppia/android/scripts/maven/data/BUILD.bazel create mode 100644 scripts/src/java/org/oppia/android/scripts/maven/data/MavenListDependencies.kt create mode 100644 scripts/src/java/org/oppia/android/scripts/maven/data/MavenListDependency.kt create mode 100644 scripts/src/java/org/oppia/android/scripts/maven/data/MavenListDependencyTree.kt create mode 100644 scripts/src/java/org/oppia/android/scripts/proto/maven_dependencies.proto create mode 100644 scripts/src/javatests/org/oppia/android/scripts/maven/BUILD.bazel create mode 100644 scripts/src/javatests/org/oppia/android/scripts/maven/GenerateMavenDependenciesListTest.kt diff --git a/app/build.gradle b/app/build.gradle index ec08255ea3e..cf6a6add513 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -162,7 +162,7 @@ dependencies { ) // Adding the testing module directly causes duplicates of the below groups so we need to // exclude them before adding the testing module to the androidTestImplementation dependencies - androidTestImplementation (project(":testing")) { + androidTestImplementation(project(":testing")) { exclude group: 'org.apache.maven', module: 'maven-artifact' exclude group: 'org.apache.maven', module: 'maven-artifact-manager' exclude group: 'org.apache.maven', module: 'maven-model' diff --git a/scripts/BUILD.bazel b/scripts/BUILD.bazel index bdf46ac67b4..9d24d49ce24 100644 --- a/scripts/BUILD.bazel +++ b/scripts/BUILD.bazel @@ -5,6 +5,7 @@ Kotlin-based scripts to help developers or perform continuous integration tasks. load("@io_bazel_rules_kotlin//kotlin:kotlin.bzl", "kt_jvm_binary", "kt_jvm_library") load( "//scripts:script_assets.bzl", + "generate_maven_assets_list_from_text_protos", "generate_regex_assets_list_from_text_protos", "generate_test_file_assets_list_from_text_protos", ) @@ -34,18 +35,14 @@ package_group( includes = [ ":oppia_script_test_visibility", ], - packages = [ - "//scripts/src/java/...", - ], + packages = ["//scripts/src/java/..."], ) kt_jvm_binary( name = "compute_affected_tests", testonly = True, main_class = "org.oppia.android.scripts.ci.ComputeAffectedTestsKt", - runtime_deps = [ - "//scripts/src/java/org/oppia/android/scripts/ci:compute_affected_tests_lib", - ], + runtime_deps = ["//scripts/src/java/org/oppia/android/scripts/ci:compute_affected_tests_lib"], ) # TODO(#3428): Refactor textproto assets to subpackage level. @@ -55,6 +52,11 @@ REGEX_PATTERN_CHECK_ASSETS = generate_regex_assets_list_from_text_protos( filepath_pattern_validation_file_names = ["filename_pattern_validation_checks"], ) +MAVEN_ASSETS = generate_maven_assets_list_from_text_protos( + name = "maven_assets", + maven_dependency_filenames = ["maven_dependencies"], +) + kt_jvm_library( name = "regex_check_assets", testonly = True, @@ -98,3 +100,18 @@ kt_jvm_binary( main_class = "org.oppia.android.scripts.testfile.TestFileCheckKt", runtime_deps = ["//scripts/src/java/org/oppia/android/scripts/testfile:test_file_check_lib"], ) + +kt_jvm_library( + name = "maven_assets_lib", + testonly = True, + data = MAVEN_ASSETS, + visibility = [":oppia_script_test_visibility"], +) + +kt_jvm_binary( + name = "generate_maven_dependencies_list", + testonly = True, + data = MAVEN_ASSETS, + main_class = "org.oppia.android.scripts.maven.GenerateMavenDependenciesListKt", + runtime_deps = ["//scripts/src/java/org/oppia/android/scripts/maven:generate_maven_dependencies_list_lib"], +) diff --git a/scripts/assets/maven_dependencies.textproto b/scripts/assets/maven_dependencies.textproto new file mode 100644 index 00000000000..8c1ffe093bb --- /dev/null +++ b/scripts/assets/maven_dependencies.textproto @@ -0,0 +1,875 @@ +maven_dependency { + artifact_name: "androidx.annotation:annotation:1.1.0" + artifact_version: "1.1.0" + license { + license_name: "The Apache Software License, Version 2.0" + original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt" + scrapable_link { + url: "https://www.apache.org/licenses/LICENSE-2.0.txt" + } + } +} +maven_dependency { + artifact_name: "androidx.appcompat:appcompat:1.2.0" + artifact_version: "1.2.0" + license { + license_name: "The Apache Software License, Version 2.0" + original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt" + scrapable_link { + url: "https://www.apache.org/licenses/LICENSE-2.0.txt" + } + } +} +maven_dependency { + artifact_name: "androidx.arch.core:core-common:2.1.0" + artifact_version: "2.1.0" + license { + license_name: "The Apache Software License, Version 2.0" + original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt" + scrapable_link { + url: "https://www.apache.org/licenses/LICENSE-2.0.txt" + } + } +} +maven_dependency { + artifact_name: "androidx.collection:collection:1.1.0" + artifact_version: "1.1.0" + license { + license_name: "The Apache Software License, Version 2.0" + original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt" + scrapable_link { + url: "https://www.apache.org/licenses/LICENSE-2.0.txt" + } + } +} +maven_dependency { + artifact_name: "androidx.constraintlayout:constraintlayout-solver:1.1.3" + artifact_version: "1.1.3" + license { + license_name: "The Apache Software License, Version 2.0" + original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt" + scrapable_link { + url: "https://www.apache.org/licenses/LICENSE-2.0.txt" + } + } +} +maven_dependency { + artifact_name: "androidx.constraintlayout:constraintlayout:1.1.3" + artifact_version: "1.1.3" + license { + license_name: "The Apache Software License, Version 2.0" + original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt" + scrapable_link { + url: "https://www.apache.org/licenses/LICENSE-2.0.txt" + } + } +} +maven_dependency { + artifact_name: "androidx.core:core-ktx:1.0.1" + artifact_version: "1.0.1" + license { + license_name: "The Apache Software License, Version 2.0" + original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt" + scrapable_link { + url: "https://www.apache.org/licenses/LICENSE-2.0.txt" + } + } +} +maven_dependency { + artifact_name: "androidx.core:core:1.3.0" + artifact_version: "1.3.0" + license { + license_name: "The Apache Software License, Version 2.0" + original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt" + scrapable_link { + url: "https://www.apache.org/licenses/LICENSE-2.0.txt" + } + } +} +maven_dependency { + artifact_name: "androidx.databinding:databinding-adapters:3.4.2" + artifact_version: "3.4.2" + license { + license_name: "The Apache Software License, Version 2.0" + original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt" + scrapable_link { + url: "https://www.apache.org/licenses/LICENSE-2.0.txt" + } + } +} +maven_dependency { + artifact_name: "androidx.databinding:databinding-common:3.4.2" + artifact_version: "3.4.2" + license { + license_name: "The Apache Software License, Version 2.0" + original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt" + scrapable_link { + url: "https://www.apache.org/licenses/LICENSE-2.0.txt" + } + } +} +maven_dependency { + artifact_name: "androidx.databinding:databinding-runtime:3.4.2" + artifact_version: "3.4.2" + license { + license_name: "The Apache Software License, Version 2.0" + original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt" + scrapable_link { + url: "https://www.apache.org/licenses/LICENSE-2.0.txt" + } + } +} +maven_dependency { + artifact_name: "androidx.drawerlayout:drawerlayout:1.1.0" + artifact_version: "1.1.0" + license { + license_name: "The Apache Software License, Version 2.0" + original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt" + scrapable_link { + url: "https://www.apache.org/licenses/LICENSE-2.0.txt" + } + } +} +maven_dependency { + artifact_name: "androidx.exifinterface:exifinterface:1.0.0" + artifact_version: "1.0.0" + license { + license_name: "The Apache Software License, Version 2.0" + original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt" + scrapable_link { + url: "https://www.apache.org/licenses/LICENSE-2.0.txt" + } + } +} +maven_dependency { + artifact_name: "androidx.fragment:fragment:1.2.0" + artifact_version: "1.2.0" + license { + license_name: "The Apache Software License, Version 2.0" + original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt" + scrapable_link { + url: "https://www.apache.org/licenses/LICENSE-2.0.txt" + } + } +} +maven_dependency { + artifact_name: "androidx.legacy:legacy-support-core-utils:1.0.0" + artifact_version: "1.0.0" + license { + license_name: "The Apache Software License, Version 2.0" + original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt" + scrapable_link { + url: "https://www.apache.org/licenses/LICENSE-2.0.txt" + } + } +} +maven_dependency { + artifact_name: "androidx.lifecycle:lifecycle-common:2.2.0" + artifact_version: "2.2.0" + license { + license_name: "The Apache Software License, Version 2.0" + original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt" + scrapable_link { + url: "https://www.apache.org/licenses/LICENSE-2.0.txt" + } + } +} +maven_dependency { + artifact_name: "androidx.lifecycle:lifecycle-extensions:2.2.0" + artifact_version: "2.2.0" + license { + license_name: "The Apache Software License, Version 2.0" + original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt" + scrapable_link { + url: "https://www.apache.org/licenses/LICENSE-2.0.txt" + } + } +} +maven_dependency { + artifact_name: "androidx.lifecycle:lifecycle-livedata-core:2.2.0" + artifact_version: "2.2.0" + license { + license_name: "The Apache Software License, Version 2.0" + original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt" + scrapable_link { + url: "https://www.apache.org/licenses/LICENSE-2.0.txt" + } + } +} +maven_dependency { + artifact_name: "androidx.lifecycle:lifecycle-livedata-ktx:2.2.0" + artifact_version: "2.2.0" + license { + license_name: "The Apache Software License, Version 2.0" + original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt" + scrapable_link { + url: "https://www.apache.org/licenses/LICENSE-2.0.txt" + } + } +} +maven_dependency { + artifact_name: "androidx.lifecycle:lifecycle-runtime:2.2.0" + artifact_version: "2.2.0" + license { + license_name: "The Apache Software License, Version 2.0" + original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt" + scrapable_link { + url: "https://www.apache.org/licenses/LICENSE-2.0.txt" + } + } +} +maven_dependency { + artifact_name: "androidx.multidex:multidex:2.0.1" + artifact_version: "2.0.1" + license { + license_name: "The Apache Software License, Version 2.0" + original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt" + scrapable_link { + url: "https://www.apache.org/licenses/LICENSE-2.0.txt" + } + } +} +maven_dependency { + artifact_name: "androidx.recyclerview:recyclerview:1.1.0" + artifact_version: "1.1.0" + license { + license_name: "The Apache Software License, Version 2.0" + original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt" + scrapable_link { + url: "https://www.apache.org/licenses/LICENSE-2.0.txt" + } + } +} +maven_dependency { + artifact_name: "androidx.room:room-common:2.2.5" + artifact_version: "2.2.5" + license { + license_name: "The Apache Software License, Version 2.0" + original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt" + scrapable_link { + url: "https://www.apache.org/licenses/LICENSE-2.0.txt" + } + } +} +maven_dependency { + artifact_name: "androidx.room:room-runtime:2.2.5" + artifact_version: "2.2.5" + license { + license_name: "The Apache Software License, Version 2.0" + original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt" + scrapable_link { + url: "https://www.apache.org/licenses/LICENSE-2.0.txt" + } + } +} +maven_dependency { + artifact_name: "androidx.vectordrawable:vectordrawable-animated:1.1.0" + artifact_version: "1.1.0" + license { + license_name: "The Apache Software License, Version 2.0" + original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt" + scrapable_link { + url: "https://www.apache.org/licenses/LICENSE-2.0.txt" + } + } +} +maven_dependency { + artifact_name: "androidx.viewpager2:viewpager2:1.0.0" + artifact_version: "1.0.0" + license { + license_name: "The Apache Software License, Version 2.0" + original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt" + scrapable_link { + url: "https://www.apache.org/licenses/LICENSE-2.0.txt" + } + } +} +maven_dependency { + artifact_name: "androidx.viewpager:viewpager:1.0.0" + artifact_version: "1.0.0" + license { + license_name: "The Apache Software License, Version 2.0" + original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt" + scrapable_link { + url: "https://www.apache.org/licenses/LICENSE-2.0.txt" + } + } +} +maven_dependency { + artifact_name: "androidx.work:work-runtime-ktx:2.4.0" + artifact_version: "2.4.0" + license { + license_name: "The Apache Software License, Version 2.0" + original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt" + scrapable_link { + url: "https://www.apache.org/licenses/LICENSE-2.0.txt" + } + } +} +maven_dependency { + artifact_name: "androidx.work:work-runtime:2.4.0" + artifact_version: "2.4.0" + license { + license_name: "The Apache Software License, Version 2.0" + original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt" + scrapable_link { + url: "https://www.apache.org/licenses/LICENSE-2.0.txt" + } + } +} +maven_dependency { + artifact_name: "com.crashlytics.sdk.android:crashlytics:2.9.8" + artifact_version: "2.9.8" + license { + license_name: "Crashlytics Terms of Service" + original_link: "https://try.crashlytics.com/terms/terms-of-service.pdf" + direct_link_only { + url: "https://firebase.google.com/terms/crashlytics" + } + is_original_link_invalid: true + } + license { + license_name: "The Apache Software License, Version 2.0" + original_link: "https://raw.githubusercontent.com/firebase/firebase-android-sdk/master/LICENSE" + scrapable_link { + url: "https://raw.githubusercontent.com/firebase/firebase-android-sdk/52e1cf89efd795d798ae5485b8503e04e4a936b8/LICENSE" + } + } +} +maven_dependency { + artifact_name: "com.github.bumptech.glide:annotations:4.11.0" + artifact_version: "4.11.0" + license { + license_name: "Simplified BSD License" + original_link: "https://www.opensource.org/licenses/bsd-license" + extracted_copy_link { + url: "https://raw.githubusercontent.com/oppia/oppia-android-licenses/develop/simplified-bsd-license.txt" + } + } + license { + license_name: "The Apache Software License, Version 2.0" + original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt" + scrapable_link { + url: "https://www.apache.org/licenses/LICENSE-2.0.txt" + } + } +} +maven_dependency { + artifact_name: "com.github.bumptech.glide:compiler:4.11.0" + artifact_version: "4.11.0" + license { + license_name: "Simplified BSD License" + original_link: "https://www.opensource.org/licenses/bsd-license" + extracted_copy_link { + url: "https://raw.githubusercontent.com/oppia/oppia-android-licenses/develop/simplified-bsd-license.txt" + } + } + license { + license_name: "The Apache Software License, Version 2.0" + original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt" + scrapable_link { + url: "https://www.apache.org/licenses/LICENSE-2.0.txt" + } + } +} +maven_dependency { + artifact_name: "com.github.bumptech.glide:disklrucache:4.11.0" + artifact_version: "4.11.0" + license { + license_name: "Simplified BSD License" + original_link: "https://www.opensource.org/licenses/bsd-license" + extracted_copy_link { + url: "https://raw.githubusercontent.com/oppia/oppia-android-licenses/develop/simplified-bsd-license.txt" + } + } + license { + license_name: "The Apache Software License, Version 2.0" + original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt" + scrapable_link { + url: "https://www.apache.org/licenses/LICENSE-2.0.txt" + } + } +} +maven_dependency { + artifact_name: "com.github.bumptech.glide:gifdecoder:4.11.0" + artifact_version: "4.11.0" + license { + license_name: "Simplified BSD License" + original_link: "https://www.opensource.org/licenses/bsd-license" + extracted_copy_link { + url: "https://raw.githubusercontent.com/oppia/oppia-android-licenses/develop/simplified-bsd-license.txt" + } + } + license { + license_name: "The Apache Software License, Version 2.0" + original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt" + scrapable_link { + url: "https://www.apache.org/licenses/LICENSE-2.0.txt" + } + } +} +maven_dependency { + artifact_name: "com.github.bumptech.glide:glide:4.11.0" + artifact_version: "4.11.0" + license { + license_name: "Simplified BSD License" + original_link: "https://www.opensource.org/licenses/bsd-license" + extracted_copy_link { + url: "https://raw.githubusercontent.com/oppia/oppia-android-licenses/develop/simplified-bsd-license.txt" + } + } + license { + license_name: "The Apache Software License, Version 2.0" + original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt" + scrapable_link { + url: "https://www.apache.org/licenses/LICENSE-2.0.txt" + } + } +} +maven_dependency { + artifact_name: "com.google.android.flexbox:flexbox:3.0.0" + artifact_version: "3.0.0" + license { + license_name: "The Apache Software License, Version 2.0" + original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt" + scrapable_link { + url: "https://www.apache.org/licenses/LICENSE-2.0.txt" + } + } +} +maven_dependency { + artifact_name: "com.google.android.material:material:1.2.0-alpha02" + artifact_version: "1.2.0-alpha02" + license { + license_name: "The Apache Software License, Version 2.0" + original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt" + scrapable_link { + url: "https://www.apache.org/licenses/LICENSE-2.0.txt" + } + } +} +maven_dependency { + artifact_name: "com.google.auto.service:auto-service-annotations:1.0-rc7" + artifact_version: "1.0-rc7" + license { + license_name: "The Apache Software License, Version 2.0" + scrapable_link { + url: "https://www.apache.org/licenses/LICENSE-2.0.txt" + } + } +} +maven_dependency { + artifact_name: "com.google.auto.value:auto-value-annotations:1.6.5" + artifact_version: "1.6.5" + license { + license_name: "The Apache Software License, Version 2.0" + scrapable_link { + url: "https://www.apache.org/licenses/LICENSE-2.0.txt" + } + } +} +maven_dependency { + artifact_name: "com.google.code.findbugs:jsr305:3.0.2" + artifact_version: "3.0.2" + license { + license_name: "The Apache Software License, Version 2.0" + original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt" + scrapable_link { + url: "https://www.apache.org/licenses/LICENSE-2.0.txt" + } + } +} +maven_dependency { + artifact_name: "com.google.dagger:dagger:2.28.1" + artifact_version: "2.28.1" + license { + license_name: "Apache 2.0" + original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt" + scrapable_link { + url: "https://www.apache.org/licenses/LICENSE-2.0.txt" + } + } +} +maven_dependency { + artifact_name: "com.google.errorprone:error_prone_annotations:2.3.2" + artifact_version: "2.3.2" + license { + license_name: "Apache 2.0" + original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt" + scrapable_link { + url: "https://www.apache.org/licenses/LICENSE-2.0.txt" + } + } +} +maven_dependency { + artifact_name: "com.google.firebase:firebase-analytics:17.5.0" + artifact_version: "17.5.0" + license { + license_name: "Android Software Development Kit License" + original_link: "https://developer.android.com/studio/terms.html" + direct_link_only { + url: "https://developer.android.com/studio/terms.html" + } + } +} +maven_dependency { + artifact_name: "com.google.firebase:firebase-crashlytics:17.1.1" + artifact_version: "17.1.1" + license { + license_name: "The Apache Software License, Version 2.0" + original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt" + scrapable_link { + url: "https://www.apache.org/licenses/LICENSE-2.0.txt" + } + } +} +maven_dependency { + artifact_name: "com.google.guava:failureaccess:1.0.1" + artifact_version: "1.0.1" + license { + license_name: "The Apache Software License, Version 2.0" + scrapable_link { + url: "https://www.apache.org/licenses/LICENSE-2.0.txt" + } + } +} +maven_dependency { + artifact_name: "com.google.guava:guava:28.1-android" + artifact_version: "28.1-android" + license { + license_name: "The Apache Software License, Version 2.0" + scrapable_link { + url: "https://www.apache.org/licenses/LICENSE-2.0.txt" + } + } +} +maven_dependency { + artifact_name: "com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava" + artifact_version: "9999.0-empty-to-avoid-conflict-with-guava" + license { + license_name: "The Apache Software License, Version 2.0" + scrapable_link { + url: "https://www.apache.org/licenses/LICENSE-2.0.txt" + } + } +} +maven_dependency { + artifact_name: "com.google.j2objc:j2objc-annotations:1.3" + artifact_version: "1.3" + license { + license_name: "The Apache Software License, Version 2.0" + original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt" + scrapable_link { + url: "https://www.apache.org/licenses/LICENSE-2.0.txt" + } + } +} +maven_dependency { + artifact_name: "com.google.protobuf:protobuf-lite:3.0.0" + artifact_version: "3.0.0" + license { + license_name: "Simplified BSD License" + extracted_copy_link { + url: "https://raw.githubusercontent.com/oppia/oppia-android-licenses/develop/simplified-bsd-license.txt" + } + } +} +maven_dependency { + artifact_name: "com.squareup.moshi:moshi-kotlin-codegen:1.11.0" + artifact_version: "1.11.0" + license { + license_name: "The Apache Software License, Version 2.0" + original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt" + scrapable_link { + url: "https://www.apache.org/licenses/LICENSE-2.0.txt" + } + } +} +maven_dependency { + artifact_name: "com.squareup.moshi:moshi-kotlin:1.11.0" + artifact_version: "1.11.0" + license { + license_name: "The Apache Software License, Version 2.0" + original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt" + scrapable_link { + url: "https://www.apache.org/licenses/LICENSE-2.0.txt" + } + } +} +maven_dependency { + artifact_name: "com.squareup.moshi:moshi:1.11.0" + artifact_version: "1.11.0" + license { + license_name: "The Apache Software License, Version 2.0" + original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt" + scrapable_link { + url: "https://www.apache.org/licenses/LICENSE-2.0.txt" + } + } +} +maven_dependency { + artifact_name: "com.squareup.okhttp3:okhttp:4.1.0" + artifact_version: "4.1.0" + license { + license_name: "The Apache Software License, Version 2.0" + original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt" + scrapable_link { + url: "https://www.apache.org/licenses/LICENSE-2.0.txt" + } + } +} +maven_dependency { + artifact_name: "com.squareup.okio:okio:2.2.2" + artifact_version: "2.2.2" + license { + license_name: "The Apache Software License, Version 2.0" + original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt" + scrapable_link { + url: "https://www.apache.org/licenses/LICENSE-2.0.txt" + } + } +} +maven_dependency { + artifact_name: "com.squareup.retrofit2:converter-moshi:2.5.0" + artifact_version: "2.5.0" + license { + license_name: "The Apache Software License, Version 2.0" + scrapable_link { + url: "https://www.apache.org/licenses/LICENSE-2.0.txt" + } + } +} +maven_dependency { + artifact_name: "com.squareup.retrofit2:retrofit:2.9.0" + artifact_version: "2.9.0" + license { + license_name: "The Apache Software License, Version 2.0" + original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt" + scrapable_link { + url: "https://www.apache.org/licenses/LICENSE-2.0.txt" + } + } +} +maven_dependency { + artifact_name: "com.squareup:kotlinpoet:1.6.0" + artifact_version: "1.6.0" + license { + license_name: "The Apache Software License, Version 2.0" + original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt" + scrapable_link { + url: "https://www.apache.org/licenses/LICENSE-2.0.txt" + } + } +} +maven_dependency { + artifact_name: "de.hdodenhof:circleimageview:3.0.1" + artifact_version: "3.0.1" + license { + license_name: "The Apache Software License, Version 2.0" + original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt" + scrapable_link { + url: "https://www.apache.org/licenses/LICENSE-2.0.txt" + } + } +} +maven_dependency { + artifact_name: "io.fabric.sdk.android:fabric:1.4.7" + artifact_version: "1.4.7" + license { + license_name: "Terms of Service for Firebase Services" + original_link: "https://fabric.io/terms" + direct_link_only { + url: "https://firebase.google.com/terms" + } + is_original_link_invalid: true + } + license { + license_name: "The Apache Software License, Version 2.0" + original_link: "https://raw.githubusercontent.com/firebase/firebase-android-sdk/master/LICENSE" + scrapable_link { + url: "https://raw.githubusercontent.com/firebase/firebase-android-sdk/52e1cf89efd795d798ae5485b8503e04e4a936b8/LICENSE" + } + } +} +maven_dependency { + artifact_name: "io.github.chaosleung:pinview:1.4.4" + artifact_version: "1.4.4" + license { + license_name: "The Apache Software License, Version 2.0" + original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt" + scrapable_link { + url: "https://www.apache.org/licenses/LICENSE-2.0.txt" + } + } +} +maven_dependency { + artifact_name: "javax.annotation:javax.annotation-api:1.3.2" + artifact_version: "1.3.2" + license { + license_name: "CDDL + GPLv2 with classpath exception" + original_link: "https://github.com/javaee/javax.annotation/blob/master/LICENSE" + extracted_copy_link { + url: "https://raw.githubusercontent.com/javaee/javax.annotation/83417807ad402ee1022c0307208d4510c80c68b6/LICENSE" + } + } +} +maven_dependency { + artifact_name: "javax.inject:javax.inject:1" + artifact_version: "1" + license { + license_name: "The Apache Software License, Version 2.0" + original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt" + scrapable_link { + url: "https://www.apache.org/licenses/LICENSE-2.0.txt" + } + } +} +maven_dependency { + artifact_name: "net.ltgt.gradle.incap:incap:0.3" + artifact_version: "0.3" + license { + license_name: "The Apache License, Version 2.0" + original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt" + scrapable_link { + url: "https://www.apache.org/licenses/LICENSE-2.0.txt" + } + } +} +maven_dependency { + artifact_name: "nl.dionsegijn:konfetti:1.2.5" + artifact_version: "1.2.5" + license { + license_name: "ISC License" + original_link: "https://github.com/DanielMartinus/Konfetti/blob/main/LICENSE" + extracted_copy_link { + url: "https://raw.githubusercontent.com/DanielMartinus/Konfetti/0604696970f7269fce1a5d1c3f839052f898e015/LICENSE" + } + } +} +maven_dependency { + artifact_name: "org.checkerframework:checker-compat-qual:2.5.5" + artifact_version: "2.5.5" + license { + license_name: "GNU General Public License, version 2 (GPL2), with the classpath exception" + original_link: "https://www.gnu.org/software/classpath/license.html" + direct_link_only { + url: "https://www.gnu.org/software/classpath/license.html" + } + } + license { + license_name: "The MIT License" + original_link: "https://opensource.org/licenses/MIT" + extracted_copy_link { + url: "https://raw.githubusercontent.com/oppia/oppia-android-licenses/develop/mit-license.txt" + } + } +} +maven_dependency { + artifact_name: "org.codehaus.mojo:animal-sniffer-annotations:1.18" + artifact_version: "1.18" + license { + license_name: "The Apache Software License, Version 2.0" + scrapable_link { + url: "https://www.apache.org/licenses/LICENSE-2.0.txt" + } + } +} +maven_dependency { + artifact_name: "org.jetbrains.kotlin:kotlin-reflect:1.4.10" + artifact_version: "1.4.10" + license { + license_name: "The Apache License, Version 2.0" + original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt" + scrapable_link { + url: "https://www.apache.org/licenses/LICENSE-2.0.txt" + } + } +} +maven_dependency { + artifact_name: "org.jetbrains.kotlin:kotlin-stdlib-common:1.4.32" + artifact_version: "1.4.32" + license { + license_name: "The Apache License, Version 2.0" + original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt" + scrapable_link { + url: "https://www.apache.org/licenses/LICENSE-2.0.txt" + } + } +} +maven_dependency { + artifact_name: "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.4.10" + artifact_version: "1.4.10" + license { + license_name: "The Apache License, Version 2.0" + original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt" + scrapable_link { + url: "https://www.apache.org/licenses/LICENSE-2.0.txt" + } + } +} +maven_dependency { + artifact_name: "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.10" + artifact_version: "1.4.10" + license { + license_name: "The Apache License, Version 2.0" + original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt" + scrapable_link { + url: "https://www.apache.org/licenses/LICENSE-2.0.txt" + } + } +} +maven_dependency { + artifact_name: "org.jetbrains.kotlin:kotlin-stdlib:1.4.32" + artifact_version: "1.4.32" + license { + license_name: "The Apache License, Version 2.0" + original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt" + scrapable_link { + url: "https://www.apache.org/licenses/LICENSE-2.0.txt" + } + } +} +maven_dependency { + artifact_name: "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.4" + artifact_version: "1.3.4" + license { + license_name: "The Apache Software License, Version 2.0" + original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt" + scrapable_link { + url: "https://www.apache.org/licenses/LICENSE-2.0.txt" + } + } +} +maven_dependency { + artifact_name: "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.4" + artifact_version: "1.3.4" + license { + license_name: "The Apache Software License, Version 2.0" + original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt" + scrapable_link { + url: "https://www.apache.org/licenses/LICENSE-2.0.txt" + } + } +} +maven_dependency { + artifact_name: "org.jetbrains:annotations:13.0" + artifact_version: "13.0" + license { + license_name: "The Apache Software License, Version 2.0" + original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt" + scrapable_link { + url: "https://www.apache.org/licenses/LICENSE-2.0.txt" + } + } +} +maven_dependency { + artifact_name: "org.ow2.asm:asm:7.2" + artifact_version: "7.2" + license { + license_name: "BSD-3-Clause" + original_link: "https://asm.ow2.io/license.html" + extracted_copy_link { + url: "https://raw.githubusercontent.com/oppia/oppia-android-licenses/develop/org.ow2.asm%3Aasm%3A7.2-license.txt" + } + } +} diff --git a/scripts/assets/test_file_exemptions.textproto b/scripts/assets/test_file_exemptions.textproto index ff784e5fc06..fda242c899e 100644 --- a/scripts/assets/test_file_exemptions.textproto +++ b/scripts/assets/test_file_exemptions.textproto @@ -558,6 +558,11 @@ exempted_file_path: "domain/src/main/java/org/oppia/android/domain/util/JsonAsse exempted_file_path: "domain/src/test/java/org/oppia/android/domain/classify/InteractionObjectTestBuilder.kt" exempted_file_path: "scripts/src/java/org/oppia/android/scripts/common/CommandExecutor.kt" exempted_file_path: "scripts/src/java/org/oppia/android/scripts/common/CommandResult.kt" +exempted_file_path: "scripts/src/java/org/oppia/android/scripts/maven/data/MavenListDependency.kt" +exempted_file_path: "scripts/src/java/org/oppia/android/scripts/maven/data/MavenListDependencies.kt" +exempted_file_path: "scripts/src/java/org/oppia/android/scripts/maven/data/MavenListDependencyTree.kt" +exempted_file_path: "scripts/src/java/org/oppia/android/scripts/maven/LicenseFetcher.kt" +exempted_file_path: "scripts/src/java/org/oppia/android/scripts/maven/LicenseFetcherImpl.kt" exempted_file_path: "testing/src/main/java/org/oppia/android/testing/AccessibilityTestRule.kt" exempted_file_path: "testing/src/main/java/org/oppia/android/testing/AssertionHelpers.kt" exempted_file_path: "testing/src/main/java/org/oppia/android/testing/DisableAccessibilityChecks.kt" diff --git a/scripts/script_assets.bzl b/scripts/script_assets.bzl index 1946a12110d..4095e83cc52 100644 --- a/scripts/script_assets.bzl +++ b/scripts/script_assets.bzl @@ -62,3 +62,28 @@ def generate_test_file_assets_list_from_text_protos( proto_dep_bazel_target_prefix = "//scripts/src/java/org/oppia/android/scripts/proto", proto_package = "proto", ) + +def generate_maven_assets_list_from_text_protos( + name, + maven_dependency_filenames): + """ + Converts a single list of text proto assets to binary. + + Args: + name: str. The name of this generation instance. This will be a prefix for derived targets. + maven_dependency_filenames: The list of maven_dependencies text proto file names under the + assets directory that should be converted. + + Returns: + list of str. The list of new proto binary asset files that were generated. + """ + return generate_proto_binary_assets( + name = name, + names = maven_dependency_filenames, + proto_dep_name = "maven_dependencies", + proto_type_name = "MavenDependencyList", + name_prefix = name, + asset_dir = "assets", + proto_dep_bazel_target_prefix = "//scripts/src/java/org/oppia/android/scripts/proto", + proto_package = "proto", + ) diff --git a/scripts/src/java/org/oppia/android/scripts/common/BazelClient.kt b/scripts/src/java/org/oppia/android/scripts/common/BazelClient.kt index 2fa33671935..2df225f632f 100644 --- a/scripts/src/java/org/oppia/android/scripts/common/BazelClient.kt +++ b/scripts/src/java/org/oppia/android/scripts/common/BazelClient.kt @@ -82,6 +82,17 @@ class BazelClient( } else listOf() } + /** + * Returns the list of direct and indirect maven third-party dependencies on which the specified + * binary depends. + */ + fun retrieveThirdPartyMavenDepsListForBinary(binaryTarget: String): List { + return executeBazelCommand( + "query", + "deps(deps($binaryTarget) intersect //third_party/...) intersect @maven//..." + ) + } + private fun retrieveFilteredSiblings( filterRuleType: String, buildFileTarget: String diff --git a/scripts/src/java/org/oppia/android/scripts/maven/BUILD.bazel b/scripts/src/java/org/oppia/android/scripts/maven/BUILD.bazel new file mode 100644 index 00000000000..d8badf6f5ed --- /dev/null +++ b/scripts/src/java/org/oppia/android/scripts/maven/BUILD.bazel @@ -0,0 +1,29 @@ +""" +Libraries corresponding to attributing third-party library copyright licenses in Oppia Android. +""" + +load("@io_bazel_rules_kotlin//kotlin:kotlin.bzl", "kt_jvm_library") + +kt_jvm_library( + name = "license_fetcher", + testonly = True, + srcs = [ + "LicenseFetcher.kt", + "LicenseFetcherImpl.kt", + ], +) + +kt_jvm_library( + name = "generate_maven_dependencies_list_lib", + testonly = True, + srcs = ["GenerateMavenDependenciesList.kt"], + visibility = ["//scripts:oppia_script_binary_visibility"], + deps = [ + ":license_fetcher", + "//scripts/src/java/org/oppia/android/scripts/common:bazel_client", + "//scripts/src/java/org/oppia/android/scripts/maven/data:data_lib", + "//scripts/src/java/org/oppia/android/scripts/proto:maven_dependencies_java_proto", + "//scripts/src/java/org/oppia/android/scripts/proto:maven_dependencies_java_proto_lite", + "//third_party:com_google_protobuf_protobuf-java", + ], +) diff --git a/scripts/src/java/org/oppia/android/scripts/maven/GenerateMavenDependenciesList.kt b/scripts/src/java/org/oppia/android/scripts/maven/GenerateMavenDependenciesList.kt new file mode 100644 index 00000000000..e055761b386 --- /dev/null +++ b/scripts/src/java/org/oppia/android/scripts/maven/GenerateMavenDependenciesList.kt @@ -0,0 +1,337 @@ +package org.oppia.android.scripts.maven + +import com.google.protobuf.TextFormat +import com.squareup.moshi.Moshi +import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory +import org.oppia.android.scripts.common.BazelClient +import org.oppia.android.scripts.common.CommandExecutor +import org.oppia.android.scripts.common.CommandExecutorImpl +import org.oppia.android.scripts.maven.data.MavenListDependency +import org.oppia.android.scripts.maven.data.MavenListDependencyTree +import org.oppia.android.scripts.proto.License +import org.oppia.android.scripts.proto.MavenDependency +import org.oppia.android.scripts.proto.MavenDependencyList +import org.w3c.dom.Element +import org.w3c.dom.Node +import org.xml.sax.InputSource +import java.io.File +import java.io.FileInputStream +import javax.xml.parsers.DocumentBuilderFactory + +/** + * Script to compile the list of the third-party Maven dependencies (direct and indirect both) + * on which Oppia Android depends. + * + * Usage: + * bazel run //scripts:generate_maven_dependencies_list -- + * + * + * + * Arguments: + * - path_to_directory_root: directory path to the root of the Oppia Android repository. + * - path_to_maven_install_json: relative path to the maven_install.json file. + * - path_to_maven_dependencies_textproto: relative path to the maven_dependencies.textproto + * - path_to_maven_dependencies_pb: relative path to the maven_dependencies.pb file. + * Example: + * bazel run //scripts:generate_maven_dependencies_list -- $(pwd) + * third_party/maven_install.json scripts/assets/maven_dependencies.textproto + * scripts/assets/maven_dependencies.pb + */ +fun main(args: Array) { + GenerateMavenDependenciesList(LicenseFetcherImpl()).main(args) +} + +/** Wrapper class to pass dependencies to be utilized by the the main method. */ +class GenerateMavenDependenciesList( + private val licenseFetcher: LicenseFetcher, + private val commandExecutor: CommandExecutor = CommandExecutorImpl() +) { + private val MAVEN_PREFIX = "@maven//:" + + /** + * Compiles a list of third-party maven dependencies along with their license links on + * which Oppia Android depends and write them in maven_dependencies.textproto. + */ + fun main(args: Array) { + if (args.size < 4) { + throw Exception("Too few Arguments passed") + } + val pathToRoot = args[0] + val pathToMavenInstall = "$pathToRoot/${args[1]}" + val pathToMavenDependenciesTextProto = + "$pathToRoot/${args[2]}" + val pathToProtoBinary = args[3] + + val bazelQueryDepsList = retrieveThirdPartyMavenDependenciesList(pathToRoot) + val mavenInstallDepsList = getDependencyListFromMavenInstall( + pathToMavenInstall, + bazelQueryDepsList + ) + + val dependenciesListFromPom = + retrieveDependencyListFromPom(mavenInstallDepsList).mavenDependencyList + + val dependenciesListFromTextProto = retrieveMavenDependencyList(pathToProtoBinary) + + val updatedDependneciesList = addChangesFromTextProto( + dependenciesListFromPom, + dependenciesListFromTextProto + ) + + val manuallyUpdatedLicenses = retrieveManuallyUpdatedLicensesSet(updatedDependneciesList) + + val finalDependenciesList = updateMavenDependenciesList( + updatedDependneciesList, + manuallyUpdatedLicenses + ) + writeTextProto( + pathToMavenDependenciesTextProto, + MavenDependencyList.newBuilder().addAllMavenDependency(finalDependenciesList).build() + ) + + val licensesToBeFixed = getAllBrokenLicenses(finalDependenciesList) + + // TODO(#3486): Update GenerateMavenDependenciesList.kt to call out first coordinate name + // that should be updated to update all occurrences of the license. + if (licensesToBeFixed.isNotEmpty()) { + println("\nPlease verify the license link(s) for the following license(s) manually:") + licensesToBeFixed.forEach { + println("\nlicense_name: ${it.licenseName}") + println("original_link: ${it.originalLink}") + println("verified_link_case: ${it.verifiedLinkCase}") + println("is_original_link_invalid: ${it.isOriginalLinkInvalid}") + } + throw Exception("Licenses details are not completed") + } + + val dependenciesWithoutAnyLinks = getDependenciesThatNeedIntervention(finalDependenciesList) + if (dependenciesWithoutAnyLinks.isNotEmpty()) { + println( + """ + Please remove all the invalid links (if any) for the below mentioned dependencies + and provide the valid license links manually: + """.trimIndent() + ) + dependenciesWithoutAnyLinks.forEach { dependency -> + println(dependency) + } + throw Exception("License links are invalid or not available for some dependencies") + } + println("\nScript executed succesfully: maven_dependencies.textproto updated successfully.") + } + + private fun retrieveThirdPartyMavenDependenciesList( + rootPath: String + ): List { + return BazelClient(File(rootPath), commandExecutor) + .retrieveThirdPartyMavenDepsListForBinary("//:oppia") + .map { dep -> + dep.removePrefix(MAVEN_PREFIX) + } + } + + private fun addChangesFromTextProto( + dependencyListFromPom: List, + dependencyListFromProto: List + ): List { + return dependencyListFromPom.map { dependency -> + dependencyListFromProto.find { + it.artifactName == dependency.artifactName + } ?: dependency + } + } + + private fun retrieveManuallyUpdatedLicensesSet( + mavenDependenciesList: List + ): Set { + return mavenDependenciesList.flatMap { dependency -> + dependency.licenseList.filter { license -> + license.verifiedLinkCase != License.VerifiedLinkCase.VERIFIEDLINK_NOT_SET + } + }.toSet() + } + + private fun updateMavenDependenciesList( + latestDependenciesList: List, + manuallyUpdatedLicenses: Set + ): List { + return latestDependenciesList.map { mavenDependency -> + val updatedLicenseList = mavenDependency.licenseList.map { license -> + manuallyUpdatedLicenses.find { + it.originalLink == license.originalLink && it.licenseName == license.licenseName + } ?: license + } + MavenDependency.newBuilder().apply { + this.artifactName = mavenDependency.artifactName + this.artifactVersion = mavenDependency.artifactVersion + this.addAllLicense(updatedLicenseList) + }.build() + } + } + + private fun writeTextProto( + pathToTextProto: String, + mavenDependencyList: MavenDependencyList + ) { + File(pathToTextProto).outputStream().bufferedWriter().use { writer -> + TextFormat.printer().print(mavenDependencyList, writer) + } + } + + // TODO(#3486): Update GenerateMavenDependenciesList.kt to call out first coordinate name + // that should be updated to update all occurrences of the license. + private fun getAllBrokenLicenses( + mavenDependenciesList: List + ): Set { + // Here broken licenses are those licenses that do not have verified_link set or + // there original link is found to be invalid by the developers. + return mavenDependenciesList.flatMap { dependency -> + dependency.licenseList.filter { license -> + license.verifiedLinkCase.equals(License.VerifiedLinkCase.VERIFIEDLINK_NOT_SET) && + !license.isOriginalLinkInvalid + } + }.toSet() + } + + private fun getDependenciesThatNeedIntervention( + mavenDependenciesList: List + ): Set { + // The dependencies whose license list is empty or some of their license link was found to + // be invalid need intervention. In this case, the developer needs to manually fill in + // the license links for each of these dependencies. + return mavenDependenciesList.filter { dependency -> + dependency.licenseList.isEmpty() || + dependency.licenseList.filter { license -> + license.verifiedLinkCase.equals(License.VerifiedLinkCase.VERIFIEDLINK_NOT_SET) && + license.isOriginalLinkInvalid + }.isNotEmpty() + }.toSet() + } + + /** Retrieves the list of [MavenDependency] from maven_dependencies.textproto. */ + private fun retrieveMavenDependencyList(pathToPbFile: String): List { + return getProto( + pathToPbFile, + MavenDependencyList.getDefaultInstance() + ).mavenDependencyList + } + + /** + * Helper function to parse the text proto file to a proto class. + * + * @param pathToPbFile path to the pb file to be parsed + * @param proto instance of the proto class + * @return proto class from the parsed text proto file + */ + private fun getProto( + pathToPbFile: String, + proto: MavenDependencyList + ): MavenDependencyList { + return FileInputStream(File(pathToPbFile)).use { + proto.newBuilderForType().mergeFrom(it) + }.build() as MavenDependencyList + } + + private fun genearateDependenciesListFromMavenInstall( + pathToMavenInstall: String, + bazelQueryDepsNames: List + ): List { + val dependencyTree = retrieveDependencyTree(pathToMavenInstall) + val mavenInstallDependencyList = dependencyTree.mavenListDependencies.dependencyList + return mavenInstallDependencyList.filter { dep -> + bazelQueryDepsNames.contains(omitVersionAndReplaceColonsHyphensPeriods(dep.coord)) + } + } + + private fun omitVersionAndReplaceColonsHyphensPeriods(artifactName: String): String { + return artifactName.substring(0, artifactName.lastIndexOf(':')) + .replace('.', '_') + .replace(':', '_') + .replace('-', '_') + } + + private fun retrieveDependencyListFromPom( + finalDependenciesList: List + ): MavenDependencyList { + val mavenDependencyList = finalDependenciesList.map { it -> + // Remove ".jar" or ".aar" or any other extension from the specified url. + val pomFileUrl = "${it.url?.substringBeforeLast('.')}.pom" + val artifactName = it.coord + val artifactVersion = artifactName.substringAfterLast(':') + val pomFile = licenseFetcher.scrapeText(pomFileUrl) + val mavenDependency = MavenDependency.newBuilder().apply { + this.artifactName = it.coord + this.artifactVersion = artifactVersion.toString() + this.addAllLicense(extractLicenseLinksFromPom(pomFile)) + } + mavenDependency.build() + } + return MavenDependencyList.newBuilder().addAllMavenDependency(mavenDependencyList).build() + } + + private fun extractLicenseLinksFromPom( + pomText: String + ): List { + val licenseList = mutableListOf() + val builderFactory = DocumentBuilderFactory.newInstance() + val docBuilder = builderFactory.newDocumentBuilder() + val doc = docBuilder.parse(InputSource(pomText.byteInputStream())) + + val licenses = doc.getElementsByTagName("license") + for (i in 0 until licenses.getLength()) { + if (licenses.item(0).getNodeType() == Node.ELEMENT_NODE) { + val element = licenses.item(i) as Element + val licenseName = getNodeValue("name", element) + val licenseLink = replaceHttpWithHttps(getNodeValue("url", element)) + licenseList.add( + License.newBuilder().apply { + this.licenseName = licenseName + this.originalLink = licenseLink + }.build() + ) + } + } + return licenseList + } + + private fun getDependencyListFromMavenInstall( + pathToMavenInstall: String, + bazelQueryDepsNames: List + ): List { + val dependencyTree = retrieveDependencyTree(pathToMavenInstall) + return dependencyTree.mavenListDependencies.dependencyList.filter { dep -> + omitVersionAndReplaceColonsHyphensPeriods(dep.coord) in bazelQueryDepsNames + } + } + + private fun retrieveDependencyTree(pathToMavenInstall: String): MavenListDependencyTree { + val mavenInstallJsonText = + File(pathToMavenInstall).inputStream().bufferedReader().use { it.readText() } + val moshi = Moshi.Builder().addLast(KotlinJsonAdapterFactory()).build() + val adapter = moshi.adapter(MavenListDependencyTree::class.java) + return adapter.fromJson(mavenInstallJsonText) + ?: throw Exception("Failed to parse $pathToMavenInstall") + } + + private fun getNodeValue(tag: String, element: Element): String { + val nodeList = element.getElementsByTagName(tag) + val node = nodeList.item(0) + if (node != null) { + if (node.hasChildNodes()) { + val child = node.getFirstChild() + while (child != null) { + if (child.getNodeType() === Node.TEXT_NODE) { + return child.getNodeValue() + } + } + } + } + return "" + } + + private fun replaceHttpWithHttps( + url: String + ): String { + return url.replaceFirst("http://", "https://") + } +} diff --git a/scripts/src/java/org/oppia/android/scripts/maven/LicenseFetcher.kt b/scripts/src/java/org/oppia/android/scripts/maven/LicenseFetcher.kt new file mode 100644 index 00000000000..399c8bef377 --- /dev/null +++ b/scripts/src/java/org/oppia/android/scripts/maven/LicenseFetcher.kt @@ -0,0 +1,7 @@ +package org.oppia.android.scripts.maven + +/** Utility class to extract the license text from a given URL. */ +interface LicenseFetcher { + /** Scrapes and returns the text from a given URL. */ + fun scrapeText(link: String): String +} diff --git a/scripts/src/java/org/oppia/android/scripts/maven/LicenseFetcherImpl.kt b/scripts/src/java/org/oppia/android/scripts/maven/LicenseFetcherImpl.kt new file mode 100644 index 00000000000..907a3a46422 --- /dev/null +++ b/scripts/src/java/org/oppia/android/scripts/maven/LicenseFetcherImpl.kt @@ -0,0 +1,11 @@ +package org.oppia.android.scripts.maven + +import java.net.URL + +/** Default implementation of [LicenseFetcher]. */ +class LicenseFetcherImpl() : LicenseFetcher { + + override fun scrapeText(link: String): String { + return URL(link).openStream().bufferedReader().readText() + } +} diff --git a/scripts/src/java/org/oppia/android/scripts/maven/data/BUILD.bazel b/scripts/src/java/org/oppia/android/scripts/maven/data/BUILD.bazel new file mode 100644 index 00000000000..0da4a01bdff --- /dev/null +++ b/scripts/src/java/org/oppia/android/scripts/maven/data/BUILD.bazel @@ -0,0 +1,17 @@ +""" +Libraries corresponding to parse maven_install.json file. +""" + +load("@io_bazel_rules_kotlin//kotlin:kotlin.bzl", "kt_jvm_library") + +kt_jvm_library( + name = "data_lib", + testonly = True, + srcs = [ + "MavenListDependencies.kt", + "MavenListDependency.kt", + "MavenListDependencyTree.kt", + ], + visibility = ["//scripts:oppia_script_library_visibility"], + deps = ["//third_party:moshi"], +) diff --git a/scripts/src/java/org/oppia/android/scripts/maven/data/MavenListDependencies.kt b/scripts/src/java/org/oppia/android/scripts/maven/data/MavenListDependencies.kt new file mode 100644 index 00000000000..fc8e7e91e93 --- /dev/null +++ b/scripts/src/java/org/oppia/android/scripts/maven/data/MavenListDependencies.kt @@ -0,0 +1,19 @@ +package org.oppia.android.scripts.maven.data + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +/** + * Data class that stores the list of dependencies present in `dependencies` array in + * maven_install.json. + */ +@JsonClass(generateAdapter = true) +data class MavenListDependencies( + /** + * The list of dependencies parsed from the maven_install.json file where each dependency + * would contain the full name of the dependency, and a url that refers to the .aar or + * .jar file of the dependency. This url can also take us to the POM file of the dependency + * by just repplacing the extension in the url to .pom. + */ + @Json(name = "dependencies") val dependencyList: List +) diff --git a/scripts/src/java/org/oppia/android/scripts/maven/data/MavenListDependency.kt b/scripts/src/java/org/oppia/android/scripts/maven/data/MavenListDependency.kt new file mode 100644 index 00000000000..3a44b8d6839 --- /dev/null +++ b/scripts/src/java/org/oppia/android/scripts/maven/data/MavenListDependency.kt @@ -0,0 +1,17 @@ +package org.oppia.android.scripts.maven.data + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +/** + * Data class that contains all the details relevant to a dependency that is present in + * maven_install.json. + */ +@JsonClass(generateAdapter = true) +data class MavenListDependency( + /** The name of the maven dependency. */ + @Json(name = "coord") val coord: String, + + /** The url that points to the .jar or .aar file of the dependeny. */ + @Json(name = "url") val url: String? +) diff --git a/scripts/src/java/org/oppia/android/scripts/maven/data/MavenListDependencyTree.kt b/scripts/src/java/org/oppia/android/scripts/maven/data/MavenListDependencyTree.kt new file mode 100644 index 00000000000..10ed297aa7d --- /dev/null +++ b/scripts/src/java/org/oppia/android/scripts/maven/data/MavenListDependencyTree.kt @@ -0,0 +1,14 @@ +package org.oppia.android.scripts.maven.data + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +/** Data class to parse the list of maven dependencies from maven_install.json. */ +@JsonClass(generateAdapter = true) +data class MavenListDependencyTree( + /** + * Parses the `dependeny_tree` key of the maven_install.json file that maps to an array of + * maven dependencies. + */ + @Json(name = "dependency_tree") val mavenListDependencies: MavenListDependencies +) diff --git a/scripts/src/java/org/oppia/android/scripts/proto/BUILD.bazel b/scripts/src/java/org/oppia/android/scripts/proto/BUILD.bazel index a3852279622..8d6093c24be 100644 --- a/scripts/src/java/org/oppia/android/scripts/proto/BUILD.bazel +++ b/scripts/src/java/org/oppia/android/scripts/proto/BUILD.bazel @@ -6,7 +6,7 @@ The java_lite_proto_library() rule takes in a proto_library target and generates For more context on adding a new proto library, please refer to model/BUILD.bazel """ -load("@rules_java//java:defs.bzl", "java_lite_proto_library") +load("@rules_java//java:defs.bzl", "java_lite_proto_library", "java_proto_library") load("@rules_proto//proto:defs.bzl", "proto_library") proto_library( @@ -44,3 +44,21 @@ java_lite_proto_library( visibility = ["//scripts:oppia_script_library_visibility"], deps = [":test_file_exemptions_proto"], ) + +proto_library( + name = "maven_dependencies_proto", + srcs = ["maven_dependencies.proto"], + visibility = ["//scripts:oppia_script_binary_visibility"], +) + +java_proto_library( + name = "maven_dependencies_java_proto", + visibility = ["//scripts:oppia_script_library_visibility"], + deps = [":maven_dependencies_proto"], +) + +java_lite_proto_library( + name = "maven_dependencies_java_proto_lite", + visibility = ["//scripts:oppia_script_library_visibility"], + deps = [":maven_dependencies_proto"], +) diff --git a/scripts/src/java/org/oppia/android/scripts/proto/maven_dependencies.proto b/scripts/src/java/org/oppia/android/scripts/proto/maven_dependencies.proto new file mode 100644 index 00000000000..6f983a2c395 --- /dev/null +++ b/scripts/src/java/org/oppia/android/scripts/proto/maven_dependencies.proto @@ -0,0 +1,76 @@ +syntax = "proto3"; + +package proto; + +option java_package = "org.oppia.android.scripts.proto"; +option java_multiple_files = true; + +// Represents the list of all third-party maven dependencies. +message MavenDependencyList { + // Stores the list of all third-party maven dependencies. + repeated MavenDependency maven_dependency = 1; +} + +// Stores all the information about a third-party maven dependency. +message MavenDependency { + // Name of the third-party dependency. + string artifact_name = 1; + + // Version of the third-party dependency. + string artifact_version = 2; + + // List of licenses corresponding to a third-party dependency. This would be the final list + // verified by the developers. + repeated License license = 3; +} + +// Stores all the details corresponding to a copyright license. +message License { + // Represents the name of the license. + string license_name = 1; + + // Represents the initial link extracted originally from the POM file of the dependency. + string original_link = 2; + + // Represents the manually verified link of the license that would finally be scraped or + // shown directly in the UI. + oneof verified_link { + // Indicates that the original link extracted from the POM contains plain license text + // and hence can be directly scraped. + ScrapableLink scrapable_link = 3; + + // Indicates that the original link extracted from the POM contains plain license text but + // can't be scraped directly and hence an alternate link should be provided that can be scraped + // directly. The link provided in this case would point to the corresponding license stored + // in Oppia Android licenses repository: https://github.com/oppia/oppia-android-licenses + ExtractedCopyLink extracted_copy_link = 4; + + // Indicates that the link does not contain plain license text and therefore can't be scraped. + // Only the link should be shown in this case. + DirectLinkOnly direct_link_only = 5; + } + + // Represents whether the original_link extracted from the POM points to a valid + // license or not. The original_link extracted from the POM can be an invalid link + // or the link may be a valid link but does not contain any license. + bool is_original_link_invalid = 6; +} + +// Stores the URL of a license that can be scraped directly. +message ScrapableLink { + // URL of the directly scrapable license text. + string url = 1; +} + +// Stores the URL of a license that should be scraped from the extracted copy. +message ExtractedCopyLink { + // URL of the license that is stored in the extracted copy. + string url = 1; +} + +// Stores the URL of a license that can't be easily scraped and only the link should be shown +// in the UI. +message DirectLinkOnly { + // URL to be shown directly in the UI. + string url = 1; +} diff --git a/scripts/src/java/org/oppia/android/scripts/testing/TestBazelWorkspace.kt b/scripts/src/java/org/oppia/android/scripts/testing/TestBazelWorkspace.kt index 41b5dd24f4d..4f2bdf07431 100644 --- a/scripts/src/java/org/oppia/android/scripts/testing/TestBazelWorkspace.kt +++ b/scripts/src/java/org/oppia/android/scripts/testing/TestBazelWorkspace.kt @@ -13,7 +13,9 @@ import java.io.File * [initEmptyWorkspace] must be called first. */ class TestBazelWorkspace(private val temporaryRootFolder: TemporaryFolder) { - private val workspaceFile by lazy { temporaryRootFolder.newFile("WORKSPACE") } + + /** The [File] corresponding to the Bazel WORKSPACE file. */ + val workspaceFile by lazy { temporaryRootFolder.newFile("WORKSPACE") } /** * The root BUILD.bazel file which will, by default, hold generated libraries & tests (for those @@ -25,6 +27,7 @@ class TestBazelWorkspace(private val temporaryRootFolder: TemporaryFolder) { private val libraryFileMap = mutableMapOf() private val testDependencyNameMap = mutableMapOf() private var isConfiguredForKotlin = false + private var isConfiguredForRulesJvmExternal = false private val filesConfiguredForTests = mutableListOf() private val filesConfiguredForLibraries = mutableListOf() @@ -178,6 +181,44 @@ class TestBazelWorkspace(private val temporaryRootFolder: TemporaryFolder) { ) } + /** Appends rules_jvm_external configuration to the WORKSPACE file if not done already. */ + fun setUpWorkspaceForRulesJvmExternal(depsList: List) { + if (!isConfiguredForRulesJvmExternal) { + workspaceFile.appendText("artifactsList = [") + for (dep in depsList) { + workspaceFile.appendText("\"$dep\",\n") + } + workspaceFile.appendText("]\n") + workspaceFile.appendText( + """ + load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") + + RULES_JVM_EXTERNAL_TAG = "4.0" + RULES_JVM_EXTERNAL_SHA = "31701ad93dbfe544d597dbe62c9a1fdd76d81d8a9150c2bf1ecf928ecdf97169" + + http_archive( + name = "rules_jvm_external", + strip_prefix = "rules_jvm_external-%s" % RULES_JVM_EXTERNAL_TAG, + sha256 = RULES_JVM_EXTERNAL_SHA, + url = "https://github.com/bazelbuild/rules_jvm_external/archive/%s.zip" % RULES_JVM_EXTERNAL_TAG, + ) + + load("@rules_jvm_external//:defs.bzl", "maven_install") + + maven_install( + artifacts = artifactsList, + repositories = [ + "https://maven.google.com", + "https://repo1.maven.org/maven2", + ], + ) + """.trimIndent() + "\n" + ) + + isConfiguredForRulesJvmExternal = true + } + } + private fun ensureWorkspaceIsConfiguredForKotlin(): List { if (!isConfiguredForKotlin) { // Add support for Kotlin: https://github.com/bazelbuild/rules_kotlin. diff --git a/scripts/src/javatests/org/oppia/android/scripts/common/BazelClientTest.kt b/scripts/src/javatests/org/oppia/android/scripts/common/BazelClientTest.kt index fa119d0e7bb..f2e7914682c 100644 --- a/scripts/src/javatests/org/oppia/android/scripts/common/BazelClientTest.kt +++ b/scripts/src/javatests/org/oppia/android/scripts/common/BazelClientTest.kt @@ -13,6 +13,7 @@ import org.oppia.android.scripts.testing.TestBazelWorkspace import org.oppia.android.testing.assertThrows import org.oppia.android.testing.mockito.anyOrNull import java.io.File +import java.util.concurrent.TimeUnit /** * Tests for [BazelClient]. @@ -32,8 +33,11 @@ class BazelClientTest { @JvmField val mockitoRule: MockitoRule = MockitoJUnit.rule() + private val commandExecutor by lazy { initiazeCommandExecutorWithLongProcessWaitTime() } private lateinit var testBazelWorkspace: TestBazelWorkspace - @Mock lateinit var mockCommandExecutor: CommandExecutor + + @Mock + lateinit var mockCommandExecutor: CommandExecutor @Before fun setUp() { @@ -319,6 +323,62 @@ class BazelClientTest { assertThat(testTargets).containsExactly("//:FirstTest", "//:SecondTest") } + @Test + fun testRetrieveMavenDepsList_binaryDependsOnArtifactViaThirdParty_returnsArtifact() { + testBazelWorkspace.initEmptyWorkspace() + testBazelWorkspace.setUpWorkspaceForRulesJvmExternal( + listOf("com.android.support:support-annotations:28.0.0") + ) + tempFolder.newFile("AndroidManifest.xml") + createAndroidBinary( + binaryName = "test_oppia", + manifestName = "AndroidManifest.xml", + dependencyName = "//third_party:com_android_support_support-annotations" + ) + tempFolder.newFolder("third_party") + val thirdPartyBuild = tempFolder.newFile("third_party/BUILD.bazel") + createAndroidLibrary( + artifactName = "com.android.support:support-annotations:28.0.0", + buildFile = thirdPartyBuild + ) + val bazelClient = BazelClient(tempFolder.root, commandExecutor) + val thirdPartyDependenciesList = + bazelClient.retrieveThirdPartyMavenDepsListForBinary("//:test_oppia") + + assertThat(thirdPartyDependenciesList) + .contains("@maven//:com_android_support_support_annotations") + } + + @Test + fun testRetrieveMavenDepsList_binaryDependsOnArtifactNotViaThirdParty_doesNotreturnArtifact() { + testBazelWorkspace.initEmptyWorkspace() + testBazelWorkspace.setUpWorkspaceForRulesJvmExternal( + listOf("com.android.support:support-annotations:28.0.0") + ) + tempFolder.newFile("AndroidManifest.xml") + createAndroidBinary( + binaryName = "test_oppia", + manifestName = "AndroidManifest.xml", + dependencyName = ":com_android_support_support-annotations" + ) + tempFolder.newFolder("third_party") + val thirdPartyBuild = tempFolder.newFile("third_party/BUILD.bazel") + createAndroidLibrary( + artifactName = "io.fabric.sdk.android:fabric:1.4.7", + buildFile = thirdPartyBuild + ) + createAndroidLibrary( + artifactName = "com.android.support:support-annotations:28.0.0", + buildFile = testBazelWorkspace.rootBuildFile + ) + val bazelClient = BazelClient(tempFolder.root, commandExecutor) + val thirdPartyDependenciesList = + bazelClient.retrieveThirdPartyMavenDepsListForBinary("//:test_oppia") + + assertThat(thirdPartyDependenciesList) + .doesNotContain("@maven//:com_android_support_support_annotations") + } + private fun fakeCommandExecutorWithResult(singleLine: String) { // Fake a Bazel command's results to return jumbled results. This has been observed to happen // sometimes in CI, but doesn't have a known cause. The utility is meant to de-jumble these in @@ -335,6 +395,43 @@ class BazelClientTest { ) } + private fun createAndroidLibrary(artifactName: String, buildFile: File) { + buildFile.appendText( + """ + load("@rules_jvm_external//:defs.bzl", "artifact") + android_library( + name = "${omitVersionAndReplacePeriodsAndColons(artifactName)}", + visibility = ["//visibility:public"], + exports = [ + artifact("$artifactName") + ], + ) + """.trimIndent() + "\n" + ) + } + + private fun omitVersionAndReplacePeriodsAndColons(artifactName: String): String { + return artifactName.substringBeforeLast(':').replace('.', '_').replace(':', '_') + } + + private fun createAndroidBinary( + binaryName: String, + manifestName: String, + dependencyName: String + ) { + testBazelWorkspace.rootBuildFile.writeText( + """ + android_binary( + name = "$binaryName", + manifest = "$manifestName", + deps = [ + "$dependencyName" + ], + ) + """.trimIndent() + "\n" + ) + } + private fun generateCustomJvmTestRuleBazelFile( firstFilename: String, secondFilename: String @@ -366,6 +463,10 @@ class BazelClientTest { return secondNewFile } + private fun initiazeCommandExecutorWithLongProcessWaitTime(): CommandExecutorImpl { + return CommandExecutorImpl(processTimeout = 5, processTimeoutUnit = TimeUnit.MINUTES) + } + private fun updateBuildFileToUseCustomJvmTestRule(bazelFile: File, buildFile: File) { buildFile.prependText( "load(\"//:${bazelFile.name}\", \"custom_jvm_test\")\n" diff --git a/scripts/src/javatests/org/oppia/android/scripts/maven/BUILD.bazel b/scripts/src/javatests/org/oppia/android/scripts/maven/BUILD.bazel new file mode 100644 index 00000000000..ae83bf44416 --- /dev/null +++ b/scripts/src/javatests/org/oppia/android/scripts/maven/BUILD.bazel @@ -0,0 +1,20 @@ +""" +Tests corresponding to the scripts that compile third-party dependencies and extract their +license links. +""" + +load("@io_bazel_rules_kotlin//kotlin:kotlin.bzl", "kt_jvm_test") + +kt_jvm_test( + name = "GenerateMavenDependenciesListTest", + srcs = ["GenerateMavenDependenciesListTest.kt"], + deps = [ + "//scripts/src/java/org/oppia/android/scripts/common:command_executor", + "//scripts/src/java/org/oppia/android/scripts/maven:generate_maven_dependencies_list_lib", + "//scripts/src/java/org/oppia/android/scripts/testing:test_bazel_workspace", + "//testing:assertion_helpers", + "//third_party:com_google_truth_truth", + "//third_party:org_jetbrains_kotlin_kotlin-test-junit", + "//third_party:org_mockito_kotlin_mockito-kotlin", + ], +) diff --git a/scripts/src/javatests/org/oppia/android/scripts/maven/GenerateMavenDependenciesListTest.kt b/scripts/src/javatests/org/oppia/android/scripts/maven/GenerateMavenDependenciesListTest.kt new file mode 100644 index 00000000000..cbc4396b287 --- /dev/null +++ b/scripts/src/javatests/org/oppia/android/scripts/maven/GenerateMavenDependenciesListTest.kt @@ -0,0 +1,806 @@ +package org.oppia.android.scripts.maven + +import com.google.common.truth.Truth.assertThat +import com.google.protobuf.TextFormat +import org.junit.After +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.rules.TemporaryFolder +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.eq +import org.mockito.kotlin.mock +import org.oppia.android.scripts.common.CommandExecutorImpl +import org.oppia.android.scripts.proto.DirectLinkOnly +import org.oppia.android.scripts.proto.ExtractedCopyLink +import org.oppia.android.scripts.proto.License +import org.oppia.android.scripts.proto.MavenDependency +import org.oppia.android.scripts.proto.MavenDependencyList +import org.oppia.android.scripts.proto.ScrapableLink +import org.oppia.android.scripts.testing.TestBazelWorkspace +import org.oppia.android.testing.assertThrows +import java.io.ByteArrayOutputStream +import java.io.File +import java.io.PrintStream +import java.util.concurrent.TimeUnit + +/** Tests for [GenerateMavenDependenciesList]. */ +class GenerateMavenDependenciesListTest { + + private val THIRD_PARTY_PREFIX = "//third_pary:" + private val DEP_WITH_SCRAPABLE_LICENSE = "androidx.databinding:databinding-adapters:3.4.2" + private val DEP_WITH_NO_LICENSE = "com.google.protobuf:protobuf-lite:3.0.0" + private val DEP_WITH_SCRAPABLE_AND_EXTRACTED_COPY_LICENSES = + "com.github.bumptech.glide:annotations:4.11.0" + private val DEP_WITH_DIRECT_LINK_ONLY_LICENSE = "com.google.firebase:firebase-analytics:17.5.0" + private val DEP_WITH_INVALID_LINKS = "io.fabric.sdk.android:fabric:1.4.7" + + private val DATA_BINDING_VERSION = "3.4.2" + private val PROTO_LITE_VERSION = "3.0.0" + private val GLIDE_ANNOTATIONS_VERSION = "4.11.0" + private val FIREBASE_ANALYTICS_VERSION = "17.5.0" + private val IO_FABRIC_VERSION = "1.4.7" + + private val DATA_BINDING_POM = "https://maven.google.com/androidx/databinding/databinding-" + + "adapters/$DATA_BINDING_VERSION/databinding-adapters-$DATA_BINDING_VERSION.pom" + private val PROTO_LITE_POM = "https://repo1.maven.org/maven2/com/google/protobuf/protobuf" + + "-lite/$PROTO_LITE_VERSION/protobuf-lite-$PROTO_LITE_VERSION.pom" + private val IO_FABRIC_POM = "https://maven.google.com/io/fabric/sdk/android/fabric/" + + "$IO_FABRIC_VERSION/fabric-$IO_FABRIC_VERSION.pom" + private val GLIDE_ANNOTATIONS_POM = "https://repo1.maven.org/maven2/com/github/bumptech/glide" + + "/annotations/$GLIDE_ANNOTATIONS_VERSION/annotations-$GLIDE_ANNOTATIONS_VERSION.pom" + private val FIREBASE_ANALYTICS_POM = "https://maven.google.com/com/google/firebase/firebase-" + + "analytics/$FIREBASE_ANALYTICS_VERSION/firebase-analytics-$FIREBASE_ANALYTICS_VERSION.pom" + + private val LICENSE_DETAILS_INCOMPLETE_FAILURE = "Licenses details are not completed" + private val UNAVAILABLE_OR_INVALID_LICENSE_LINKS_FAILURE = + "License links are invalid or not available for some dependencies" + private val SCRIPT_PASSED_MESSAGE = + "Script executed succesfully: maven_dependencies.textproto updated successfully." + + private val outContent: ByteArrayOutputStream = ByteArrayOutputStream() + private val originalOut: PrintStream = System.out + + private val mockLicenseFetcher by lazy { initializeLicenseFetcher() } + private val commandExecutor by lazy { initializeCommandExecutorWithLongProcessWaitTime() } + private lateinit var testBazelWorkspace: TestBazelWorkspace + + @Rule + @JvmField + var tempFolder = TemporaryFolder() + + @Before + fun setUp() { + tempFolder.newFolder("scripts", "assets") + tempFolder.newFolder("third_party") + testBazelWorkspace = TestBazelWorkspace(tempFolder) + System.setOut(PrintStream(outContent)) + } + + @After + fun restoreStreams() { + System.setOut(originalOut) + } + + @Test + fun testEmptyPbFile_scriptFailsWithException_writesTextProto() { + val textProtoFile = tempFolder.newFile("scripts/assets/maven_dependencies.textproto") + tempFolder.newFile("scripts/assets/maven_dependencies.pb") + + val coordsList = listOf(DEP_WITH_SCRAPABLE_LICENSE, DEP_WITH_DIRECT_LINK_ONLY_LICENSE) + setupBazelEnvironment(coordsList) + + val exception = assertThrows(Exception::class) { + GenerateMavenDependenciesList( + mockLicenseFetcher, + commandExecutor + ).main( + arrayOf( + "${tempFolder.root}", + "scripts/assets/maven_install.json", + "scripts/assets/maven_dependencies.textproto", + "${tempFolder.root}/scripts/assets/maven_dependencies.pb" + ) + ) + } + assertThat(exception).hasMessageThat().contains(LICENSE_DETAILS_INCOMPLETE_FAILURE) + + val outputMavenDependencyList = parseTextProto( + textProtoFile, + MavenDependencyList.getDefaultInstance() + ) + + val dependency1 = outputMavenDependencyList.mavenDependencyList[0] + assertIsDependency( + dependency = dependency1, + artifactName = DEP_WITH_SCRAPABLE_LICENSE, + artifactVersion = DATA_BINDING_VERSION, + ) + val licenseForDependency1 = dependency1.licenseList[0] + verifyLicenseHasVerifiedLinkNotSet( + license = licenseForDependency1, + originalLink = "https://www.apache.org/licenses/LICENSE-2.0.txt", + licenseName = "The Apache License, Version 2.0" + ) + val dependency2 = outputMavenDependencyList.mavenDependencyList[1] + assertIsDependency( + dependency = dependency2, + artifactName = DEP_WITH_DIRECT_LINK_ONLY_LICENSE, + artifactVersion = FIREBASE_ANALYTICS_VERSION, + ) + val licenseForDependency2 = dependency2.licenseList[0] + verifyLicenseHasVerifiedLinkNotSet( + license = licenseForDependency2, + originalLink = "https://developer.android.com/studio/terms.html", + licenseName = "Android Software Development Kit License" + ) + } + + @Test + fun testLicenseLinkNotVerified_forAtleastOneLicense_scriptFailsWithException() { + val pbFile = tempFolder.newFile("scripts/assets/maven_dependencies.pb") + val license1 = License.newBuilder().apply { + this.licenseName = "The Apache License, Version 2.0" + this.originalLink = "https://www.apache.org/licenses/LICENSE-2.0.txt" + }.build() + val license2 = License.newBuilder().apply { + this.licenseName = "Simplified BSD License" + this.originalLink = "https://www.opensource.org/licenses/bsd-license" + this.extractedCopyLink = ExtractedCopyLink.newBuilder() + .setUrl("https://local-copy/bsd-license").build() + }.build() + val mavenDependencyList = MavenDependencyList.newBuilder().apply { + this.addAllMavenDependency( + listOf( + MavenDependency.newBuilder().apply { + this.artifactName = DEP_WITH_SCRAPABLE_LICENSE + this.artifactVersion = DATA_BINDING_VERSION + this.addAllLicense(listOf(license1)) + }.build(), + MavenDependency.newBuilder().apply { + this.artifactName = DEP_WITH_SCRAPABLE_AND_EXTRACTED_COPY_LICENSES + this.artifactVersion = GLIDE_ANNOTATIONS_VERSION + this.addAllLicense(listOf(license1, license2)) + }.build() + ) + ) + }.build() + mavenDependencyList.writeTo(pbFile.outputStream()) + + val coordsList = listOf(DEP_WITH_SCRAPABLE_LICENSE, DEP_WITH_DIRECT_LINK_ONLY_LICENSE) + setupBazelEnvironment(coordsList) + + val exception = assertThrows(Exception::class) { + GenerateMavenDependenciesList( + mockLicenseFetcher, + commandExecutor + ).main( + arrayOf( + "${tempFolder.root}", + "scripts/assets/maven_install.json", + "scripts/assets/maven_dependencies.textproto", + "${tempFolder.root}/scripts/assets/maven_dependencies.pb" + ) + ) + } + assertThat(exception).hasMessageThat().contains(LICENSE_DETAILS_INCOMPLETE_FAILURE) + } + + @Test + fun testDependencyHasNonScrapableLink_scriptFailsWithException_writesTextProto() { + val textProtoFile = tempFolder.newFile("scripts/assets/maven_dependencies.textproto") + tempFolder.newFile("scripts/assets/maven_dependencies.pb") + + val coordsList = listOf(DEP_WITH_DIRECT_LINK_ONLY_LICENSE) + setupBazelEnvironment(coordsList) + + val exception = assertThrows(Exception::class) { + GenerateMavenDependenciesList( + mockLicenseFetcher, + commandExecutor + ).main( + arrayOf( + "${tempFolder.root}", + "scripts/assets/maven_install.json", + "scripts/assets/maven_dependencies.textproto", + "${tempFolder.root}/scripts/assets/maven_dependencies.pb" + ) + ) + } + assertThat(exception).hasMessageThat().contains(LICENSE_DETAILS_INCOMPLETE_FAILURE) + + val outputMavenDependencyList = parseTextProto( + textProtoFile, + MavenDependencyList.getDefaultInstance() + ) + + val dependency = outputMavenDependencyList.mavenDependencyList[0] + assertIsDependency( + dependency = dependency, + artifactName = DEP_WITH_DIRECT_LINK_ONLY_LICENSE, + artifactVersion = FIREBASE_ANALYTICS_VERSION, + ) + val licenseForDependency = dependency.licenseList[0] + verifyLicenseHasVerifiedLinkNotSet( + license = licenseForDependency, + originalLink = "https://developer.android.com/studio/terms.html", + licenseName = "Android Software Development Kit License" + ) + } + + @Test + fun testDependencyHasLocalCopyLinkAndScrapableLink_scriptFails_andWritesTextProto() { + val textProtoFile = tempFolder.newFile("scripts/assets/maven_dependencies.textproto") + tempFolder.newFile("scripts/assets/maven_dependencies.pb") + + val coordsList = listOf(DEP_WITH_SCRAPABLE_AND_EXTRACTED_COPY_LICENSES) + setupBazelEnvironment(coordsList) + + val exception = assertThrows(Exception::class) { + GenerateMavenDependenciesList( + mockLicenseFetcher, + commandExecutor + ).main( + arrayOf( + "${tempFolder.root}", + "scripts/assets/maven_install.json", + "scripts/assets/maven_dependencies.textproto", + "${tempFolder.root}/scripts/assets/maven_dependencies.pb" + ) + ) + } + assertThat(exception).hasMessageThat().contains(LICENSE_DETAILS_INCOMPLETE_FAILURE) + + val outputMavenDependencyList = parseTextProto( + textProtoFile, + MavenDependencyList.getDefaultInstance() + ) + + val dependency = outputMavenDependencyList.mavenDependencyList[0] + assertIsDependency( + dependency = dependency, + artifactName = DEP_WITH_SCRAPABLE_AND_EXTRACTED_COPY_LICENSES, + artifactVersion = GLIDE_ANNOTATIONS_VERSION, + ) + val license1ForDependency = dependency.licenseList[0] + val license2ForDependency = dependency.licenseList[1] + + verifyLicenseHasVerifiedLinkNotSet( + license = license1ForDependency, + originalLink = "https://www.opensource.org/licenses/bsd-license", + licenseName = "Simplified BSD License" + ) + verifyLicenseHasVerifiedLinkNotSet( + license = license2ForDependency, + originalLink = "https://www.apache.org/licenses/LICENSE-2.0.txt", + licenseName = "The Apache Software License, Version 2.0" + ) + } + + @Test + fun testDependencyHasInvalidLicense_scriptFailsWithException_writesTextProto() { + val textProtoFile = tempFolder.newFile("scripts/assets/maven_dependencies.textproto") + val pbFile = tempFolder.newFile("scripts/assets/maven_dependencies.pb") + + val license1 = License.newBuilder().apply { + this.licenseName = "Fabric Software and Services Agreement" + this.originalLink = "https://fabric.io/terms" + this.isOriginalLinkInvalid = true + }.build() + val mavenDependencyList = MavenDependencyList.newBuilder().apply { + this.addAllMavenDependency( + listOf( + MavenDependency.newBuilder().apply { + this.artifactName = DEP_WITH_INVALID_LINKS + this.artifactVersion = IO_FABRIC_VERSION + this.addAllLicense(listOf(license1)) + }.build() + ) + ) + }.build() + mavenDependencyList.writeTo(pbFile.outputStream()) + + val coordsList = listOf(DEP_WITH_INVALID_LINKS) + setupBazelEnvironment(coordsList) + + val exception = assertThrows(Exception::class) { + GenerateMavenDependenciesList( + mockLicenseFetcher, + commandExecutor + ).main( + arrayOf( + "${tempFolder.root}", + "scripts/assets/maven_install.json", + "scripts/assets/maven_dependencies.textproto", + "${tempFolder.root}/scripts/assets/maven_dependencies.pb" + ) + ) + } + assertThat(exception).hasMessageThat().contains(UNAVAILABLE_OR_INVALID_LICENSE_LINKS_FAILURE) + + val outputMavenDependencyList = parseTextProto( + textProtoFile, + MavenDependencyList.getDefaultInstance() + ) + + val dependency = outputMavenDependencyList.mavenDependencyList[0] + assertIsDependency( + dependency = dependency, + artifactName = DEP_WITH_INVALID_LINKS, + artifactVersion = IO_FABRIC_VERSION, + ) + val licenseForDependency = dependency.licenseList[0] + verifyLicenseHasOriginalLinkInvalid( + license = licenseForDependency, + originalLink = "https://fabric.io/terms", + licenseName = "Fabric Software and Services Agreement" + ) + } + + @Test + fun testDependencyHasNoLicense_scriptFails_writesTextProto() { + val textProtoFile = tempFolder.newFile("scripts/assets/maven_dependencies.textproto") + tempFolder.newFile("scripts/assets/maven_dependencies.pb") + + val coordsList = listOf(DEP_WITH_NO_LICENSE) + setupBazelEnvironment(coordsList) + + val exception = assertThrows(Exception::class) { + GenerateMavenDependenciesList( + mockLicenseFetcher, + commandExecutor + ).main( + arrayOf( + "${tempFolder.root}", + "scripts/assets/maven_install.json", + "scripts/assets/maven_dependencies.textproto", + "${tempFolder.root}/scripts/assets/maven_dependencies.pb" + ) + ) + } + assertThat(exception).hasMessageThat().contains(UNAVAILABLE_OR_INVALID_LICENSE_LINKS_FAILURE) + + val outputMavenDependencyList = parseTextProto( + textProtoFile, + MavenDependencyList.getDefaultInstance() + ) + + val dependency = outputMavenDependencyList.mavenDependencyList[0] + assertIsDependency( + dependency = dependency, + artifactName = DEP_WITH_NO_LICENSE, + artifactVersion = PROTO_LITE_VERSION, + ) + assertThat(dependency.licenseList).isEmpty() + } + + @Test + fun testDependenciesHaveMultipleLicense_completeLicenseDetails_scriptPasses_writesTextProto() { + val textProtoFile = tempFolder.newFile("scripts/assets/maven_dependencies.textproto") + val pbFile = tempFolder.newFile("scripts/assets/maven_dependencies.pb") + val license1 = License.newBuilder().apply { + this.licenseName = "The Apache License, Version 2.0" + this.originalLink = "https://www.apache.org/licenses/LICENSE-2.0.txt" + this.scrapableLink = ScrapableLink.newBuilder() + .setUrl("https://www.apache.org/licenses/LICENSE-2.0.txt").build() + }.build() + val license2 = License.newBuilder().apply { + this.licenseName = "Simplified BSD License" + this.originalLink = "https://www.opensource.org/licenses/bsd-license" + this.extractedCopyLink = ExtractedCopyLink.newBuilder() + .setUrl("https://local-copy/bsd-license").build() + }.build() + val mavenDependencyList = MavenDependencyList.newBuilder().apply { + this.addAllMavenDependency( + listOf( + MavenDependency.newBuilder().apply { + this.artifactName = DEP_WITH_SCRAPABLE_LICENSE + this.artifactVersion = DATA_BINDING_VERSION + this.addAllLicense(listOf(license1)) + }.build(), + MavenDependency.newBuilder().apply { + this.artifactName = DEP_WITH_SCRAPABLE_AND_EXTRACTED_COPY_LICENSES + this.artifactVersion = GLIDE_ANNOTATIONS_VERSION + this.addAllLicense(listOf(license1, license2)) + }.build() + ) + ) + }.build() + mavenDependencyList.writeTo(pbFile.outputStream()) + + val coordsList = + listOf(DEP_WITH_SCRAPABLE_LICENSE, DEP_WITH_SCRAPABLE_AND_EXTRACTED_COPY_LICENSES) + setupBazelEnvironment(coordsList) + + GenerateMavenDependenciesList( + mockLicenseFetcher, + commandExecutor + ).main( + arrayOf( + "${tempFolder.root}", + "scripts/assets/maven_install.json", + "scripts/assets/maven_dependencies.textproto", + "${tempFolder.root}/scripts/assets/maven_dependencies.pb" + ) + ) + assertThat(outContent.toString()).contains(SCRIPT_PASSED_MESSAGE) + + val outputMavenDependencyList = parseTextProto( + textProtoFile, + MavenDependencyList.getDefaultInstance() + ) + + val dependency1 = outputMavenDependencyList.mavenDependencyList[0] + assertIsDependency( + dependency = dependency1, + artifactName = DEP_WITH_SCRAPABLE_LICENSE, + artifactVersion = DATA_BINDING_VERSION, + ) + val licenseForDependency1 = dependency1.licenseList[0] + verifyLicenseHasScrapableVerifiedLink( + license = licenseForDependency1, + originalLink = "https://www.apache.org/licenses/LICENSE-2.0.txt", + licenseName = "The Apache License, Version 2.0", + verifiedLink = "https://www.apache.org/licenses/LICENSE-2.0.txt" + ) + val dependency2 = outputMavenDependencyList.mavenDependencyList[1] + assertIsDependency( + dependency = dependency2, + artifactName = DEP_WITH_SCRAPABLE_AND_EXTRACTED_COPY_LICENSES, + artifactVersion = GLIDE_ANNOTATIONS_VERSION, + ) + val license1ForDependency2 = dependency2.licenseList[0] + val license2ForDependency2 = dependency2.licenseList[1] + verifyLicenseHasScrapableVerifiedLink( + license = license1ForDependency2, + originalLink = "https://www.apache.org/licenses/LICENSE-2.0.txt", + licenseName = "The Apache License, Version 2.0", + verifiedLink = "https://www.apache.org/licenses/LICENSE-2.0.txt" + ) + verifyLicenseHasExtractedCopyVerifiedLink( + license = license2ForDependency2, + originalLink = "https://www.opensource.org/licenses/bsd-license", + licenseName = "Simplified BSD License", + verifiedLink = "https://local-copy/bsd-license" + ) + } + + @Test + fun testDependenciesHaveCompleteLicenseDetails_scriptPasses_writesTextProto() { + val textProtoFile = tempFolder.newFile("scripts/assets/maven_dependencies.textproto") + val pbFile = tempFolder.newFile("scripts/assets/maven_dependencies.pb") + + val license1 = License.newBuilder().apply { + this.licenseName = "Android Software Development Kit License" + this.originalLink = "https://developer.android.com/studio/terms.html" + this.directLinkOnly = DirectLinkOnly.newBuilder() + .setUrl("https://developer.android.com/studio/terms.html").build() + }.build() + val license2 = License.newBuilder().apply { + this.licenseName = "Simplified BSD License" + this.extractedCopyLink = ExtractedCopyLink.newBuilder() + .setUrl("https://local-copy/bsd-license").build() + }.build() + + val mavenDependencyList = MavenDependencyList.newBuilder().apply { + this.addAllMavenDependency( + listOf( + MavenDependency.newBuilder().apply { + this.artifactName = DEP_WITH_DIRECT_LINK_ONLY_LICENSE + this.artifactVersion = FIREBASE_ANALYTICS_VERSION + this.addAllLicense(listOf(license1)) + }.build(), + MavenDependency.newBuilder().apply { + this.artifactName = DEP_WITH_NO_LICENSE + this.artifactVersion = PROTO_LITE_VERSION + this.addAllLicense(listOf(license2)) + }.build() + ) + ) + }.build() + mavenDependencyList.writeTo(pbFile.outputStream()) + + val coordsList = listOf(DEP_WITH_DIRECT_LINK_ONLY_LICENSE, DEP_WITH_NO_LICENSE) + setupBazelEnvironment(coordsList) + + GenerateMavenDependenciesList( + mockLicenseFetcher, + commandExecutor + ).main( + arrayOf( + "${tempFolder.root}", + "scripts/assets/maven_install.json", + "scripts/assets/maven_dependencies.textproto", + "${tempFolder.root}/scripts/assets/maven_dependencies.pb" + ) + ) + + assertThat(outContent.toString()).contains(SCRIPT_PASSED_MESSAGE) + + val outputMavenDependencyList = parseTextProto( + textProtoFile, + MavenDependencyList.getDefaultInstance() + ) + + val dependency1 = outputMavenDependencyList.mavenDependencyList[0] + assertIsDependency( + dependency = dependency1, + artifactName = DEP_WITH_DIRECT_LINK_ONLY_LICENSE, + artifactVersion = FIREBASE_ANALYTICS_VERSION, + ) + val licenseForDependency1 = dependency1.licenseList[0] + verifyLicenseHasDirectLinkOnlyVerifiedLink( + license = licenseForDependency1, + originalLink = "https://developer.android.com/studio/terms.html", + licenseName = "Android Software Development Kit License", + verifiedLink = "https://developer.android.com/studio/terms.html" + ) + val dependency2 = outputMavenDependencyList.mavenDependencyList[1] + assertIsDependency( + dependency = dependency2, + artifactName = DEP_WITH_NO_LICENSE, + artifactVersion = PROTO_LITE_VERSION, + ) + val license1ForDependency2 = dependency2.licenseList[0] + verifyLicenseHasExtractedCopyVerifiedLink( + license = license1ForDependency2, + originalLink = "", + licenseName = "Simplified BSD License", + verifiedLink = "https://local-copy/bsd-license" + ) + } + + private fun verifyLicenseHasScrapableVerifiedLink( + license: License, + originalLink: String, + licenseName: String, + verifiedLink: String, + ) { + assertThat(license.licenseName).isEqualTo(licenseName) + assertThat(license.verifiedLinkCase).isEqualTo( + License.VerifiedLinkCase.SCRAPABLE_LINK + ) + assertThat(license.scrapableLink.url).isEqualTo(verifiedLink) + assertThat(license.originalLink).isEqualTo(originalLink) + assertThat(license.isOriginalLinkInvalid).isFalse() + } + + private fun verifyLicenseHasExtractedCopyVerifiedLink( + license: License, + originalLink: String, + licenseName: String, + verifiedLink: String, + ) { + assertThat(license.licenseName).isEqualTo(licenseName) + assertThat(license.verifiedLinkCase).isEqualTo( + License.VerifiedLinkCase.EXTRACTED_COPY_LINK + ) + assertThat(license.extractedCopyLink.url).isEqualTo(verifiedLink) + assertThat(license.originalLink).isEqualTo(originalLink) + assertThat(license.isOriginalLinkInvalid).isFalse() + } + + private fun verifyLicenseHasDirectLinkOnlyVerifiedLink( + license: License, + originalLink: String, + licenseName: String, + verifiedLink: String, + ) { + assertThat(license.licenseName).isEqualTo(licenseName) + assertThat(license.verifiedLinkCase).isEqualTo( + License.VerifiedLinkCase.DIRECT_LINK_ONLY + ) + assertThat(license.directLinkOnly.url).isEqualTo(verifiedLink) + assertThat(license.originalLink).isEqualTo(originalLink) + assertThat(license.isOriginalLinkInvalid).isFalse() + } + + private fun verifyLicenseHasVerifiedLinkNotSet( + license: License, + originalLink: String, + licenseName: String + ) { + assertThat(license.licenseName).isEqualTo(licenseName) + assertThat(license.verifiedLinkCase).isEqualTo(License.VerifiedLinkCase.VERIFIEDLINK_NOT_SET) + assertThat(license.originalLink).isEqualTo(originalLink) + assertThat(license.isOriginalLinkInvalid).isFalse() + } + + private fun verifyLicenseHasOriginalLinkInvalid( + license: License, + originalLink: String, + licenseName: String + ) { + assertThat(license.licenseName).isEqualTo(licenseName) + assertThat(license.verifiedLinkCase).isEqualTo(License.VerifiedLinkCase.VERIFIEDLINK_NOT_SET) + assertThat(license.originalLink).isEqualTo(originalLink) + assertThat(license.isOriginalLinkInvalid).isTrue() + } + + private fun assertIsDependency( + dependency: MavenDependency, + artifactName: String, + artifactVersion: String, + ) { + assertThat(dependency.artifactName).isEqualTo(artifactName) + assertThat(dependency.artifactVersion).isEqualTo(artifactVersion) + } + + private fun parseTextProto( + textProtoFile: File, + proto: MavenDependencyList + ): MavenDependencyList { + val builder = proto.newBuilderForType() + TextFormat.merge(textProtoFile.readText(), builder) + return builder.build() + } + + private fun setupBazelEnvironment(coordsList: List) { + val mavenInstallJson = tempFolder.newFile("scripts/assets/maven_install.json") + writeMavenInstallJson(mavenInstallJson) + testBazelWorkspace.setUpWorkspaceForRulesJvmExternal(coordsList) + val thirdPartyPrefixCoordList = coordsList.map { coordinate -> + "//third_party:${omitVersionAndReplaceColonsHyphensPeriods(coordinate)}" + } + createThirdPartyAndroidBinary(thirdPartyPrefixCoordList) + writeThirdPartyBuildFile(coordsList) + } + + private fun writeThirdPartyBuildFile(exportsList: List) { + val thirdPartyBuild = tempFolder.newFile("third_party/BUILD.bazel") + thirdPartyBuild.appendText( + """ + load("@rules_jvm_external//:defs.bzl", "artifact") + """.trimIndent() + "\n" + ) + for (export in exportsList) { + createThirdPartyAndroidLibrary(thirdPartyBuild, export) + } + } + + private fun createThirdPartyAndroidLibrary(thirdPartyBuild: File, artifactName: String) { + thirdPartyBuild.appendText( + """ + android_library( + name = "${omitVersionAndReplaceColonsHyphensPeriods(artifactName)}", + visibility = ["//visibility:public"], + exports = [artifact("$artifactName")], + ) + """.trimIndent() + "\n" + ) + } + + private fun omitVersionAndReplaceColonsHyphensPeriods(artifactName: String): String { + val lastColonIndex = artifactName.lastIndexOf(':') + return artifactName.substring(0, lastColonIndex).replace('.', '_').replace(':', '_') + } + + private fun createThirdPartyAndroidBinary( + dependenciesList: List + ) { + tempFolder.newFile("AndroidManifest.xml") + val build = tempFolder.newFile("BUILD.bazel") + build.appendText("depsList = [\n") + for (dep in dependenciesList) { + build.appendText("\"$dep\",") + } + build.appendText("]\n") + build.appendText( + """ + android_binary( + name = "oppia", + manifest = "AndroidManifest.xml", + deps = depsList + ) + """.trimIndent() + "\n" + ) + } + + /** Helper function to write a fake maven_install.json file. */ + private fun writeMavenInstallJson(file: File) { + file.writeText( + """ + { + "dependency_tree": { + "dependencies": [ + { + "coord": "androidx.databinding:databinding-adapters:3.4.2", + "url": "${DATA_BINDING_POM.dropLast(3)}aar" + }, + { + "coord": "com.github.bumptech.glide:annotations:4.11.0", + "url": "${GLIDE_ANNOTATIONS_POM.dropLast(3)}jar" + }, + { + "coord": "com.google.firebase:firebase-analytics:17.5.0", + "url": "${FIREBASE_ANALYTICS_POM.dropLast(3)}aar" + }, + { + "coord": "com.google.protobuf:protobuf-lite:3.0.0", + "url": "${PROTO_LITE_POM.dropLast(3)}jar" + }, + { + "coord": "io.fabric.sdk.android:fabric:1.4.7", + "url": "${IO_FABRIC_POM.dropLast(3)}aar" + } + ] + } + } + """.trimIndent() + ) + } + + private fun initializeCommandExecutorWithLongProcessWaitTime(): CommandExecutorImpl { + return CommandExecutorImpl(processTimeout = 5, processTimeoutUnit = TimeUnit.MINUTES) + } + + /** Returns a mock for the [LicenseFetcher]. */ + private fun initializeLicenseFetcher(): LicenseFetcher { + return mock { + on { scrapeText(eq(DATA_BINDING_POM)) } + .doReturn( + """ + + + + The Apache License, Version 2.0 + https://www.apache.org/licenses/LICENSE-2.0.txt + repo + + + """.trimIndent() + ) + on { scrapeText(eq(GLIDE_ANNOTATIONS_POM)) } + .doReturn( + """ + + + + Simplified BSD License + https://www.opensource.org/licenses/bsd-license + repo + + + The Apache Software License, Version 2.0 + https://www.apache.org/licenses/LICENSE-2.0.txt + repo + + + """.trimIndent() + ) + on { scrapeText(eq(FIREBASE_ANALYTICS_POM)) } + .doReturn( + """ + + + + Android Software Development Kit License + https://developer.android.com/studio/terms.html + repo + + + """.trimIndent() + ) + on { scrapeText(eq(IO_FABRIC_POM)) } + .doReturn( + """ + + + + Fabric Terms of Service + https://www.fabric.io.terms + repo + + + """.trimIndent() + ) + on { scrapeText(eq(PROTO_LITE_POM)) } + .doReturn( + """ + + Random Project + """.trimIndent() + ) + } + } +} diff --git a/scripts/src/javatests/org/oppia/android/scripts/testing/TestBazelWorkspaceTest.kt b/scripts/src/javatests/org/oppia/android/scripts/testing/TestBazelWorkspaceTest.kt index 6b10ef9c528..7f061e036f3 100644 --- a/scripts/src/javatests/org/oppia/android/scripts/testing/TestBazelWorkspaceTest.kt +++ b/scripts/src/javatests/org/oppia/android/scripts/testing/TestBazelWorkspaceTest.kt @@ -62,6 +62,117 @@ class TestBazelWorkspaceTest { assertThrows(AssertionError::class) { testBazelWorkspace.initEmptyWorkspace() } } + @Test + fun testWorkspaceFileProperty_retrieve_createsWorkspaceFile() { + val testBazelWorkspace = TestBazelWorkspace(tempFolder) + + val workspaceFile = testBazelWorkspace.workspaceFile + + // Verify that WORKSPACE file is a top-level file that exists within the root, but is empty. + assertThat(workspaceFile.exists()).isTrue() + assertThat(workspaceFile.name).isEqualTo("WORKSPACE") + assertThat(workspaceFile.isRelativeTo(tempFolder.root)).isTrue() + assertThat(workspaceFile.toRelativeString(tempFolder.root)).isEqualTo("WORKSPACE") + assertThat(workspaceFile.readLines()).isEmpty() + } + + @Test + fun testSetupWorkspaceForRulesJvmExternal_withOneDep_containsCorrectList() { + val testBazelWorkspace = TestBazelWorkspace(tempFolder) + + testBazelWorkspace.setUpWorkspaceForRulesJvmExternal( + listOf("com.android.support:support-annotations:28.0.0") + ) + + val workspaceFile = testBazelWorkspace.workspaceFile + val workspaceContent = workspaceFile.readAsJoinedString() + + assertThat(workspaceContent).contains("com.android.support:support-annotations:28.0.0") + } + + @Test + fun testSetupWorkspaceForRulesJvmExternal_withTwoDeps_containsCorrectList() { + val testBazelWorkspace = TestBazelWorkspace(tempFolder) + + testBazelWorkspace.setUpWorkspaceForRulesJvmExternal( + listOf( + "com.android.support:support-annotations:28.0.0", + "io.fabric.sdk.android:fabric:1.4.7" + ) + ) + + val workspaceFile = testBazelWorkspace.workspaceFile + val workspaceContent = workspaceFile.readAsJoinedString() + + assertThat(workspaceContent).contains("com.android.support:support-annotations:28.0.0") + assertThat(workspaceContent).contains("io.fabric.sdk.android:fabric:1.4.7") + } + + @Test + fun testSetupWorkspaceForRulesJvmExternal_withMultipleDeps_containsCorrectList() { + val testBazelWorkspace = TestBazelWorkspace(tempFolder) + + testBazelWorkspace.setUpWorkspaceForRulesJvmExternal( + listOf( + "com.android.support:support-annotations:28.0.0", + "io.fabric.sdk.android:fabric:1.4.7", + "androidx.databinding:databinding-adapters:3.4.2", + "com.google.protobuf:protobuf-lite:3.0.0" + ) + ) + + val workspaceFile = testBazelWorkspace.workspaceFile + val workspaceContent = workspaceFile.readAsJoinedString() + + assertThat(workspaceContent).contains("com.android.support:support-annotations:28.0.0") + assertThat(workspaceContent).contains("io.fabric.sdk.android:fabric:1.4.7") + assertThat(workspaceContent).contains("androidx.databinding:databinding-adapters:3.4.2") + assertThat(workspaceContent).contains("com.google.protobuf:protobuf-lite:3.0.0") + } + + @Test + fun testSetupWorkspaceForRulesJvmExternal_multipleCalls_containsOnlyFirstTimeContent() { + val testBazelWorkspace = TestBazelWorkspace(tempFolder) + + testBazelWorkspace.setUpWorkspaceForRulesJvmExternal( + listOf("com.android.support:support-annotations:28.0.0") + ) + + testBazelWorkspace.setUpWorkspaceForRulesJvmExternal( + listOf("io.fabric.sdk.android:fabric:1.4.7") + ) + + val workspaceFile = testBazelWorkspace.workspaceFile + val workspaceContent = workspaceFile.readAsJoinedString() + + assertThat(workspaceContent).contains("com.android.support:support-annotations:28.0.0") + assertThat(workspaceContent).doesNotContain("io.fabric.sdk.android:fabric:1.4.7") + } + + @Test + fun testSetupWorkspaceForRulesJvmExternal_addsMavenInstall() { + val testBazelWorkspace = TestBazelWorkspace(tempFolder) + + testBazelWorkspace.setUpWorkspaceForRulesJvmExternal( + listOf("com.android.support:support-annotations:28.0.0") + ) + + val workspaceFile = testBazelWorkspace.workspaceFile + val workspaceContent = workspaceFile.readAsJoinedString() + + assertThat(workspaceContent).contains( + """ + maven_install( + artifacts = artifactsList, + repositories = [ + "https://maven.google.com", + "https://repo1.maven.org/maven2", + ], + ) + """.trimIndent() + ) + } + @Test fun testRootBuildFileProperty_retrieve_createsBuildFile() { val testBazelWorkspace = TestBazelWorkspace(tempFolder) diff --git a/third_party/maven_install.json b/third_party/maven_install.json index 469a63ebf48..3d6cf35c655 100644 --- a/third_party/maven_install.json +++ b/third_party/maven_install.json @@ -1,8 +1,8 @@ { "dependency_tree": { "__AUTOGENERATED_FILE_DO_NOT_MODIFY_THIS_FILE_MANUALLY": "THERE_IS_NO_DATA_ONLY_ZUUL", - "__INPUT_ARTIFACTS_HASH": -1412576931, - "__RESOLVED_ARTIFACTS_HASH": -1944714209, + "__INPUT_ARTIFACTS_HASH": 400706612, + "__RESOLVED_ARTIFACTS_HASH": -1263051889, "conflict_resolution": { "androidx.appcompat:appcompat:1.0.2": "androidx.appcompat:appcompat:1.2.0", "androidx.core:core:1.0.1": "androidx.core:core:1.3.0", @@ -11,7 +11,8 @@ "org.jetbrains.kotlin:kotlin-reflect:1.3.41": "org.jetbrains.kotlin:kotlin-reflect:1.4.10", "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.72": "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.4.10", "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.2": "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.4", - "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.2.1": "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.4" + "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.2.1": "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.4", + "org.mockito:mockito-core:2.19.0": "org.mockito:mockito-core:3.9.0" }, "dependencies": [ { @@ -57,7 +58,7 @@ "dependencies": [ "junit:junit:4.12", "org.hamcrest:hamcrest-core:1.3", - "org.mockito:mockito-core:2.19.0", + "org.mockito:mockito-core:3.9.0", "com.android.support:support-annotations:28.0.0", "android.arch.core:runtime:aar:1.1.1", "android.arch.core:common:1.1.1" @@ -66,7 +67,7 @@ "android.arch.core:runtime:aar:1.1.1", "com.android.support:support-annotations:28.0.0", "junit:junit:4.12", - "org.mockito:mockito-core:2.19.0" + "org.mockito:mockito-core:3.9.0" ], "file": "v1/https/maven.google.com/android/arch/core/core-testing/1.1.1/core-testing-1.1.1.aar", "mirror_urls": [ @@ -82,9 +83,9 @@ { "coord": "android.arch.core:core-testing:jar:sources:1.1.1", "dependencies": [ - "org.mockito:mockito-core:jar:sources:2.19.0", "com.android.support:support-annotations:jar:sources:28.0.0", "android.arch.core:runtime:aar:sources:1.1.1", + "org.mockito:mockito-core:jar:sources:3.9.0", "org.hamcrest:hamcrest-core:jar:sources:1.3", "junit:junit:jar:sources:4.12", "android.arch.core:common:jar:sources:1.1.1" @@ -93,7 +94,7 @@ "android.arch.core:runtime:aar:sources:1.1.1", "com.android.support:support-annotations:jar:sources:28.0.0", "junit:junit:jar:sources:4.12", - "org.mockito:mockito-core:jar:sources:2.19.0" + "org.mockito:mockito-core:jar:sources:3.9.0" ], "file": "v1/https/maven.google.com/android/arch/core/core-testing/1.1.1/core-testing-1.1.1-sources.jar", "mirror_urls": [ @@ -460,17 +461,17 @@ "coord": "androidx.arch.core:core-testing:2.1.0", "dependencies": [ "androidx.annotation:annotation:1.1.0", + "org.objenesis:objenesis:3.2", "junit:junit:4.12", "org.hamcrest:hamcrest-core:1.3", - "org.mockito:mockito-core:2.19.0", - "org.objenesis:objenesis:2.6", + "org.mockito:mockito-core:3.9.0", "androidx.arch.core:core-runtime:aar:2.1.0" ], "directDependencies": [ "androidx.annotation:annotation:1.1.0", "androidx.arch.core:core-runtime:aar:2.1.0", "junit:junit:4.12", - "org.mockito:mockito-core:2.19.0" + "org.mockito:mockito-core:3.9.0" ], "file": "v1/https/maven.google.com/androidx/arch/core/core-testing/2.1.0/core-testing-2.1.0.aar", "mirror_urls": [ @@ -487,17 +488,17 @@ "coord": "androidx.arch.core:core-testing:jar:sources:2.1.0", "dependencies": [ "androidx.arch.core:core-runtime:aar:sources:2.1.0", - "org.mockito:mockito-core:jar:sources:2.19.0", + "org.mockito:mockito-core:jar:sources:3.9.0", "org.hamcrest:hamcrest-core:jar:sources:1.3", "androidx.annotation:annotation:jar:sources:1.1.0", - "junit:junit:jar:sources:4.12", - "org.objenesis:objenesis:jar:sources:2.6" + "org.objenesis:objenesis:jar:sources:3.2", + "junit:junit:jar:sources:4.12" ], "directDependencies": [ "androidx.annotation:annotation:jar:sources:1.1.0", "androidx.arch.core:core-runtime:aar:sources:2.1.0", "junit:junit:jar:sources:4.12", - "org.mockito:mockito-core:jar:sources:2.19.0" + "org.mockito:mockito-core:jar:sources:3.9.0" ], "file": "v1/https/maven.google.com/androidx/arch/core/core-testing/2.1.0/core-testing-2.1.0-sources.jar", "mirror_urls": [ @@ -6218,6 +6219,36 @@ "sha256": "ba4df669fec153fa4cd0ef8d02c6d3ef0702b7ac4cabe080facf3b6e490bb972", "url": "https://repo1.maven.org/maven2/com/google/j2objc/j2objc-annotations/1.3/j2objc-annotations-1.3-sources.jar" }, + { + "coord": "com.google.protobuf:protobuf-java:3.17.3", + "dependencies": [], + "directDependencies": [], + "file": "v1/https/repo1.maven.org/maven2/com/google/protobuf/protobuf-java/3.17.3/protobuf-java-3.17.3.jar", + "mirror_urls": [ + "https://maven.google.com/com/google/protobuf/protobuf-java/3.17.3/protobuf-java-3.17.3.jar", + "https://repo1.maven.org/maven2/com/google/protobuf/protobuf-java/3.17.3/protobuf-java-3.17.3.jar", + "https://maven.fabric.io/public/com/google/protobuf/protobuf-java/3.17.3/protobuf-java-3.17.3.jar", + "https://maven.google.com/com/google/protobuf/protobuf-java/3.17.3/protobuf-java-3.17.3.jar", + "https://repo1.maven.org/maven2/com/google/protobuf/protobuf-java/3.17.3/protobuf-java-3.17.3.jar" + ], + "sha256": "4ac549b192694141958049f060a1c826a33342f619e108ced8c17d9877f5e3ed", + "url": "https://repo1.maven.org/maven2/com/google/protobuf/protobuf-java/3.17.3/protobuf-java-3.17.3.jar" + }, + { + "coord": "com.google.protobuf:protobuf-java:jar:sources:3.17.3", + "dependencies": [], + "directDependencies": [], + "file": "v1/https/repo1.maven.org/maven2/com/google/protobuf/protobuf-java/3.17.3/protobuf-java-3.17.3-sources.jar", + "mirror_urls": [ + "https://maven.google.com/com/google/protobuf/protobuf-java/3.17.3/protobuf-java-3.17.3-sources.jar", + "https://repo1.maven.org/maven2/com/google/protobuf/protobuf-java/3.17.3/protobuf-java-3.17.3-sources.jar", + "https://maven.fabric.io/public/com/google/protobuf/protobuf-java/3.17.3/protobuf-java-3.17.3-sources.jar", + "https://maven.google.com/com/google/protobuf/protobuf-java/3.17.3/protobuf-java-3.17.3-sources.jar", + "https://repo1.maven.org/maven2/com/google/protobuf/protobuf-java/3.17.3/protobuf-java-3.17.3-sources.jar" + ], + "sha256": "204bad0a36827296ed0b6fdbdf1a17028f3e3d289dd20042980331b8f0ef646e", + "url": "https://repo1.maven.org/maven2/com/google/protobuf/protobuf-java/3.17.3/protobuf-java-3.17.3-sources.jar" + }, { "coord": "com.google.protobuf:protobuf-lite:3.0.0", "dependencies": [], @@ -7185,64 +7216,64 @@ "url": "https://repo1.maven.org/maven2/junit/junit/4.12/junit-4.12-sources.jar" }, { - "coord": "net.bytebuddy:byte-buddy-agent:1.8.10", + "coord": "net.bytebuddy:byte-buddy-agent:1.10.20", "dependencies": [], "directDependencies": [], - "file": "v1/https/repo1.maven.org/maven2/net/bytebuddy/byte-buddy-agent/1.8.10/byte-buddy-agent-1.8.10.jar", + "file": "v1/https/repo1.maven.org/maven2/net/bytebuddy/byte-buddy-agent/1.10.20/byte-buddy-agent-1.10.20.jar", "mirror_urls": [ - "https://maven.google.com/net/bytebuddy/byte-buddy-agent/1.8.10/byte-buddy-agent-1.8.10.jar", - "https://repo1.maven.org/maven2/net/bytebuddy/byte-buddy-agent/1.8.10/byte-buddy-agent-1.8.10.jar", - "https://maven.fabric.io/public/net/bytebuddy/byte-buddy-agent/1.8.10/byte-buddy-agent-1.8.10.jar", - "https://maven.google.com/net/bytebuddy/byte-buddy-agent/1.8.10/byte-buddy-agent-1.8.10.jar", - "https://repo1.maven.org/maven2/net/bytebuddy/byte-buddy-agent/1.8.10/byte-buddy-agent-1.8.10.jar" + "https://maven.google.com/net/bytebuddy/byte-buddy-agent/1.10.20/byte-buddy-agent-1.10.20.jar", + "https://repo1.maven.org/maven2/net/bytebuddy/byte-buddy-agent/1.10.20/byte-buddy-agent-1.10.20.jar", + "https://maven.fabric.io/public/net/bytebuddy/byte-buddy-agent/1.10.20/byte-buddy-agent-1.10.20.jar", + "https://maven.google.com/net/bytebuddy/byte-buddy-agent/1.10.20/byte-buddy-agent-1.10.20.jar", + "https://repo1.maven.org/maven2/net/bytebuddy/byte-buddy-agent/1.10.20/byte-buddy-agent-1.10.20.jar" ], - "sha256": "f7403b1126137eb68a5cc3beaf543d965bafca87fad7d7d30082617748c19e05", - "url": "https://repo1.maven.org/maven2/net/bytebuddy/byte-buddy-agent/1.8.10/byte-buddy-agent-1.8.10.jar" + "sha256": "b592a6c43e752bf41659717956c57fbb790394d2ee5f8941876659f9c5c0e7e8", + "url": "https://repo1.maven.org/maven2/net/bytebuddy/byte-buddy-agent/1.10.20/byte-buddy-agent-1.10.20.jar" }, { - "coord": "net.bytebuddy:byte-buddy-agent:jar:sources:1.8.10", + "coord": "net.bytebuddy:byte-buddy-agent:jar:sources:1.10.20", "dependencies": [], "directDependencies": [], - "file": "v1/https/repo1.maven.org/maven2/net/bytebuddy/byte-buddy-agent/1.8.10/byte-buddy-agent-1.8.10-sources.jar", + "file": "v1/https/repo1.maven.org/maven2/net/bytebuddy/byte-buddy-agent/1.10.20/byte-buddy-agent-1.10.20-sources.jar", "mirror_urls": [ - "https://maven.google.com/net/bytebuddy/byte-buddy-agent/1.8.10/byte-buddy-agent-1.8.10-sources.jar", - "https://repo1.maven.org/maven2/net/bytebuddy/byte-buddy-agent/1.8.10/byte-buddy-agent-1.8.10-sources.jar", - "https://maven.fabric.io/public/net/bytebuddy/byte-buddy-agent/1.8.10/byte-buddy-agent-1.8.10-sources.jar", - "https://maven.google.com/net/bytebuddy/byte-buddy-agent/1.8.10/byte-buddy-agent-1.8.10-sources.jar", - "https://repo1.maven.org/maven2/net/bytebuddy/byte-buddy-agent/1.8.10/byte-buddy-agent-1.8.10-sources.jar" + "https://maven.google.com/net/bytebuddy/byte-buddy-agent/1.10.20/byte-buddy-agent-1.10.20-sources.jar", + "https://repo1.maven.org/maven2/net/bytebuddy/byte-buddy-agent/1.10.20/byte-buddy-agent-1.10.20-sources.jar", + "https://maven.fabric.io/public/net/bytebuddy/byte-buddy-agent/1.10.20/byte-buddy-agent-1.10.20-sources.jar", + "https://maven.google.com/net/bytebuddy/byte-buddy-agent/1.10.20/byte-buddy-agent-1.10.20-sources.jar", + "https://repo1.maven.org/maven2/net/bytebuddy/byte-buddy-agent/1.10.20/byte-buddy-agent-1.10.20-sources.jar" ], - "sha256": "ac9ed517672119797b4e295092720370b8ae8972978b5119d54d9104f0285634", - "url": "https://repo1.maven.org/maven2/net/bytebuddy/byte-buddy-agent/1.8.10/byte-buddy-agent-1.8.10-sources.jar" + "sha256": "1c86f2ad5d84a8640292a84af3e8245851bab08a9136dbe4c084e50585bf9c8a", + "url": "https://repo1.maven.org/maven2/net/bytebuddy/byte-buddy-agent/1.10.20/byte-buddy-agent-1.10.20-sources.jar" }, { - "coord": "net.bytebuddy:byte-buddy:1.8.10", + "coord": "net.bytebuddy:byte-buddy:1.10.20", "dependencies": [], "directDependencies": [], - "file": "v1/https/repo1.maven.org/maven2/net/bytebuddy/byte-buddy/1.8.10/byte-buddy-1.8.10.jar", + "file": "v1/https/repo1.maven.org/maven2/net/bytebuddy/byte-buddy/1.10.20/byte-buddy-1.10.20.jar", "mirror_urls": [ - "https://maven.google.com/net/bytebuddy/byte-buddy/1.8.10/byte-buddy-1.8.10.jar", - "https://repo1.maven.org/maven2/net/bytebuddy/byte-buddy/1.8.10/byte-buddy-1.8.10.jar", - "https://maven.fabric.io/public/net/bytebuddy/byte-buddy/1.8.10/byte-buddy-1.8.10.jar", - "https://maven.google.com/net/bytebuddy/byte-buddy/1.8.10/byte-buddy-1.8.10.jar", - "https://repo1.maven.org/maven2/net/bytebuddy/byte-buddy/1.8.10/byte-buddy-1.8.10.jar" + "https://maven.google.com/net/bytebuddy/byte-buddy/1.10.20/byte-buddy-1.10.20.jar", + "https://repo1.maven.org/maven2/net/bytebuddy/byte-buddy/1.10.20/byte-buddy-1.10.20.jar", + "https://maven.fabric.io/public/net/bytebuddy/byte-buddy/1.10.20/byte-buddy-1.10.20.jar", + "https://maven.google.com/net/bytebuddy/byte-buddy/1.10.20/byte-buddy-1.10.20.jar", + "https://repo1.maven.org/maven2/net/bytebuddy/byte-buddy/1.10.20/byte-buddy-1.10.20.jar" ], - "sha256": "8c29e0118256acf9fbadcd75143df2d8bd9bfb07623ccf95a14646be5a92380c", - "url": "https://repo1.maven.org/maven2/net/bytebuddy/byte-buddy/1.8.10/byte-buddy-1.8.10.jar" + "sha256": "5fcad05da791e9a22811c255a4a74b7ea094b7243d9dbf3e6fc578c8c94290ac", + "url": "https://repo1.maven.org/maven2/net/bytebuddy/byte-buddy/1.10.20/byte-buddy-1.10.20.jar" }, { - "coord": "net.bytebuddy:byte-buddy:jar:sources:1.8.10", + "coord": "net.bytebuddy:byte-buddy:jar:sources:1.10.20", "dependencies": [], "directDependencies": [], - "file": "v1/https/repo1.maven.org/maven2/net/bytebuddy/byte-buddy/1.8.10/byte-buddy-1.8.10-sources.jar", + "file": "v1/https/repo1.maven.org/maven2/net/bytebuddy/byte-buddy/1.10.20/byte-buddy-1.10.20-sources.jar", "mirror_urls": [ - "https://maven.google.com/net/bytebuddy/byte-buddy/1.8.10/byte-buddy-1.8.10-sources.jar", - "https://repo1.maven.org/maven2/net/bytebuddy/byte-buddy/1.8.10/byte-buddy-1.8.10-sources.jar", - "https://maven.fabric.io/public/net/bytebuddy/byte-buddy/1.8.10/byte-buddy-1.8.10-sources.jar", - "https://maven.google.com/net/bytebuddy/byte-buddy/1.8.10/byte-buddy-1.8.10-sources.jar", - "https://repo1.maven.org/maven2/net/bytebuddy/byte-buddy/1.8.10/byte-buddy-1.8.10-sources.jar" + "https://maven.google.com/net/bytebuddy/byte-buddy/1.10.20/byte-buddy-1.10.20-sources.jar", + "https://repo1.maven.org/maven2/net/bytebuddy/byte-buddy/1.10.20/byte-buddy-1.10.20-sources.jar", + "https://maven.fabric.io/public/net/bytebuddy/byte-buddy/1.10.20/byte-buddy-1.10.20-sources.jar", + "https://maven.google.com/net/bytebuddy/byte-buddy/1.10.20/byte-buddy-1.10.20-sources.jar", + "https://repo1.maven.org/maven2/net/bytebuddy/byte-buddy/1.10.20/byte-buddy-1.10.20-sources.jar" ], - "sha256": "f486a05c24dfb5cbcea60137ed9de7cabe7de0ed9234561b460a1efe1b0b26c7", - "url": "https://repo1.maven.org/maven2/net/bytebuddy/byte-buddy/1.8.10/byte-buddy-1.8.10-sources.jar" + "sha256": "50334bed7d0c05a7e02224105ff33267c9d2acb1a07ec3a532bac28e38abdd0e", + "url": "https://repo1.maven.org/maven2/net/bytebuddy/byte-buddy/1.10.20/byte-buddy-1.10.20-sources.jar" }, { "coord": "net.ltgt.gradle.incap:incap:0.3", @@ -8165,80 +8196,124 @@ "url": "https://repo1.maven.org/maven2/org/jetbrains/annotations/13.0/annotations-13.0-sources.jar" }, { - "coord": "org.mockito:mockito-core:2.19.0", + "coord": "org.mockito.kotlin:mockito-kotlin:3.2.0", + "dependencies": [ + "net.bytebuddy:byte-buddy:1.10.20", + "net.bytebuddy:byte-buddy-agent:1.10.20", + "org.objenesis:objenesis:3.2", + "org.mockito:mockito-core:3.9.0" + ], + "directDependencies": [ + "org.mockito:mockito-core:3.9.0" + ], + "file": "v1/https/repo1.maven.org/maven2/org/mockito/kotlin/mockito-kotlin/3.2.0/mockito-kotlin-3.2.0.jar", + "mirror_urls": [ + "https://maven.google.com/org/mockito/kotlin/mockito-kotlin/3.2.0/mockito-kotlin-3.2.0.jar", + "https://repo1.maven.org/maven2/org/mockito/kotlin/mockito-kotlin/3.2.0/mockito-kotlin-3.2.0.jar", + "https://maven.fabric.io/public/org/mockito/kotlin/mockito-kotlin/3.2.0/mockito-kotlin-3.2.0.jar", + "https://maven.google.com/org/mockito/kotlin/mockito-kotlin/3.2.0/mockito-kotlin-3.2.0.jar", + "https://repo1.maven.org/maven2/org/mockito/kotlin/mockito-kotlin/3.2.0/mockito-kotlin-3.2.0.jar" + ], + "sha256": "12995c7fdb8de84cf58fca31f1cd8810eeaa39e63c00e61ffcb2b49a4305f21f", + "url": "https://repo1.maven.org/maven2/org/mockito/kotlin/mockito-kotlin/3.2.0/mockito-kotlin-3.2.0.jar" + }, + { + "coord": "org.mockito.kotlin:mockito-kotlin:jar:sources:3.2.0", + "dependencies": [ + "org.mockito:mockito-core:jar:sources:3.9.0", + "net.bytebuddy:byte-buddy-agent:jar:sources:1.10.20", + "net.bytebuddy:byte-buddy:jar:sources:1.10.20", + "org.objenesis:objenesis:jar:sources:3.2" + ], + "directDependencies": [ + "org.mockito:mockito-core:jar:sources:3.9.0" + ], + "file": "v1/https/repo1.maven.org/maven2/org/mockito/kotlin/mockito-kotlin/3.2.0/mockito-kotlin-3.2.0-sources.jar", + "mirror_urls": [ + "https://maven.google.com/org/mockito/kotlin/mockito-kotlin/3.2.0/mockito-kotlin-3.2.0-sources.jar", + "https://repo1.maven.org/maven2/org/mockito/kotlin/mockito-kotlin/3.2.0/mockito-kotlin-3.2.0-sources.jar", + "https://maven.fabric.io/public/org/mockito/kotlin/mockito-kotlin/3.2.0/mockito-kotlin-3.2.0-sources.jar", + "https://maven.google.com/org/mockito/kotlin/mockito-kotlin/3.2.0/mockito-kotlin-3.2.0-sources.jar", + "https://repo1.maven.org/maven2/org/mockito/kotlin/mockito-kotlin/3.2.0/mockito-kotlin-3.2.0-sources.jar" + ], + "sha256": "50bae6ff5fb02d2c211479df3e55799190df77d60f3e3f0be669a68ccde1e10b", + "url": "https://repo1.maven.org/maven2/org/mockito/kotlin/mockito-kotlin/3.2.0/mockito-kotlin-3.2.0-sources.jar" + }, + { + "coord": "org.mockito:mockito-core:3.9.0", "dependencies": [ - "net.bytebuddy:byte-buddy:1.8.10", - "org.objenesis:objenesis:2.6", - "net.bytebuddy:byte-buddy-agent:1.8.10" + "net.bytebuddy:byte-buddy:1.10.20", + "net.bytebuddy:byte-buddy-agent:1.10.20", + "org.objenesis:objenesis:3.2" ], "directDependencies": [ - "net.bytebuddy:byte-buddy:1.8.10", - "net.bytebuddy:byte-buddy-agent:1.8.10", - "org.objenesis:objenesis:2.6" + "net.bytebuddy:byte-buddy:1.10.20", + "net.bytebuddy:byte-buddy-agent:1.10.20", + "org.objenesis:objenesis:3.2" ], - "file": "v1/https/repo1.maven.org/maven2/org/mockito/mockito-core/2.19.0/mockito-core-2.19.0.jar", + "file": "v1/https/repo1.maven.org/maven2/org/mockito/mockito-core/3.9.0/mockito-core-3.9.0.jar", "mirror_urls": [ - "https://maven.google.com/org/mockito/mockito-core/2.19.0/mockito-core-2.19.0.jar", - "https://repo1.maven.org/maven2/org/mockito/mockito-core/2.19.0/mockito-core-2.19.0.jar", - "https://maven.fabric.io/public/org/mockito/mockito-core/2.19.0/mockito-core-2.19.0.jar", - "https://maven.google.com/org/mockito/mockito-core/2.19.0/mockito-core-2.19.0.jar", - "https://repo1.maven.org/maven2/org/mockito/mockito-core/2.19.0/mockito-core-2.19.0.jar" + "https://maven.google.com/org/mockito/mockito-core/3.9.0/mockito-core-3.9.0.jar", + "https://repo1.maven.org/maven2/org/mockito/mockito-core/3.9.0/mockito-core-3.9.0.jar", + "https://maven.fabric.io/public/org/mockito/mockito-core/3.9.0/mockito-core-3.9.0.jar", + "https://maven.google.com/org/mockito/mockito-core/3.9.0/mockito-core-3.9.0.jar", + "https://repo1.maven.org/maven2/org/mockito/mockito-core/3.9.0/mockito-core-3.9.0.jar" ], - "sha256": "d6ac2e04164c5d5c89e73838dc1c8b3856ca6582d3f2daf91816fd9d7ba3c9a9", - "url": "https://repo1.maven.org/maven2/org/mockito/mockito-core/2.19.0/mockito-core-2.19.0.jar" + "sha256": "a1f64211407b8dc4cf80b16e07cc11aa9e5228d53dc4a5357326d66825f6a4ac", + "url": "https://repo1.maven.org/maven2/org/mockito/mockito-core/3.9.0/mockito-core-3.9.0.jar" }, { - "coord": "org.mockito:mockito-core:jar:sources:2.19.0", + "coord": "org.mockito:mockito-core:jar:sources:3.9.0", "dependencies": [ - "net.bytebuddy:byte-buddy:jar:sources:1.8.10", - "org.objenesis:objenesis:jar:sources:2.6", - "net.bytebuddy:byte-buddy-agent:jar:sources:1.8.10" + "net.bytebuddy:byte-buddy-agent:jar:sources:1.10.20", + "net.bytebuddy:byte-buddy:jar:sources:1.10.20", + "org.objenesis:objenesis:jar:sources:3.2" ], "directDependencies": [ - "net.bytebuddy:byte-buddy:jar:sources:1.8.10", - "net.bytebuddy:byte-buddy-agent:jar:sources:1.8.10", - "org.objenesis:objenesis:jar:sources:2.6" + "net.bytebuddy:byte-buddy:jar:sources:1.10.20", + "net.bytebuddy:byte-buddy-agent:jar:sources:1.10.20", + "org.objenesis:objenesis:jar:sources:3.2" ], - "file": "v1/https/repo1.maven.org/maven2/org/mockito/mockito-core/2.19.0/mockito-core-2.19.0-sources.jar", + "file": "v1/https/repo1.maven.org/maven2/org/mockito/mockito-core/3.9.0/mockito-core-3.9.0-sources.jar", "mirror_urls": [ - "https://maven.google.com/org/mockito/mockito-core/2.19.0/mockito-core-2.19.0-sources.jar", - "https://repo1.maven.org/maven2/org/mockito/mockito-core/2.19.0/mockito-core-2.19.0-sources.jar", - "https://maven.fabric.io/public/org/mockito/mockito-core/2.19.0/mockito-core-2.19.0-sources.jar", - "https://maven.google.com/org/mockito/mockito-core/2.19.0/mockito-core-2.19.0-sources.jar", - "https://repo1.maven.org/maven2/org/mockito/mockito-core/2.19.0/mockito-core-2.19.0-sources.jar" + "https://maven.google.com/org/mockito/mockito-core/3.9.0/mockito-core-3.9.0-sources.jar", + "https://repo1.maven.org/maven2/org/mockito/mockito-core/3.9.0/mockito-core-3.9.0-sources.jar", + "https://maven.fabric.io/public/org/mockito/mockito-core/3.9.0/mockito-core-3.9.0-sources.jar", + "https://maven.google.com/org/mockito/mockito-core/3.9.0/mockito-core-3.9.0-sources.jar", + "https://repo1.maven.org/maven2/org/mockito/mockito-core/3.9.0/mockito-core-3.9.0-sources.jar" ], - "sha256": "63712c962eb31f6102a91274719740167cb7694d425f88883ae8f086ee12e94c", - "url": "https://repo1.maven.org/maven2/org/mockito/mockito-core/2.19.0/mockito-core-2.19.0-sources.jar" + "sha256": "6457852efe7e4ba539e5816d069401c9f2a39abc8cea43d0039174ac112a6ac3", + "url": "https://repo1.maven.org/maven2/org/mockito/mockito-core/3.9.0/mockito-core-3.9.0-sources.jar" }, { - "coord": "org.objenesis:objenesis:2.6", + "coord": "org.objenesis:objenesis:3.2", "dependencies": [], "directDependencies": [], - "file": "v1/https/repo1.maven.org/maven2/org/objenesis/objenesis/2.6/objenesis-2.6.jar", + "file": "v1/https/repo1.maven.org/maven2/org/objenesis/objenesis/3.2/objenesis-3.2.jar", "mirror_urls": [ - "https://maven.google.com/org/objenesis/objenesis/2.6/objenesis-2.6.jar", - "https://repo1.maven.org/maven2/org/objenesis/objenesis/2.6/objenesis-2.6.jar", - "https://maven.fabric.io/public/org/objenesis/objenesis/2.6/objenesis-2.6.jar", - "https://maven.google.com/org/objenesis/objenesis/2.6/objenesis-2.6.jar", - "https://repo1.maven.org/maven2/org/objenesis/objenesis/2.6/objenesis-2.6.jar" + "https://maven.google.com/org/objenesis/objenesis/3.2/objenesis-3.2.jar", + "https://repo1.maven.org/maven2/org/objenesis/objenesis/3.2/objenesis-3.2.jar", + "https://maven.fabric.io/public/org/objenesis/objenesis/3.2/objenesis-3.2.jar", + "https://maven.google.com/org/objenesis/objenesis/3.2/objenesis-3.2.jar", + "https://repo1.maven.org/maven2/org/objenesis/objenesis/3.2/objenesis-3.2.jar" ], - "sha256": "5e168368fbc250af3c79aa5fef0c3467a2d64e5a7bd74005f25d8399aeb0708d", - "url": "https://repo1.maven.org/maven2/org/objenesis/objenesis/2.6/objenesis-2.6.jar" + "sha256": "03d960bd5aef03c653eb000413ada15eb77cdd2b8e4448886edf5692805e35f3", + "url": "https://repo1.maven.org/maven2/org/objenesis/objenesis/3.2/objenesis-3.2.jar" }, { - "coord": "org.objenesis:objenesis:jar:sources:2.6", + "coord": "org.objenesis:objenesis:jar:sources:3.2", "dependencies": [], "directDependencies": [], - "file": "v1/https/repo1.maven.org/maven2/org/objenesis/objenesis/2.6/objenesis-2.6-sources.jar", + "file": "v1/https/repo1.maven.org/maven2/org/objenesis/objenesis/3.2/objenesis-3.2-sources.jar", "mirror_urls": [ - "https://maven.google.com/org/objenesis/objenesis/2.6/objenesis-2.6-sources.jar", - "https://repo1.maven.org/maven2/org/objenesis/objenesis/2.6/objenesis-2.6-sources.jar", - "https://maven.fabric.io/public/org/objenesis/objenesis/2.6/objenesis-2.6-sources.jar", - "https://maven.google.com/org/objenesis/objenesis/2.6/objenesis-2.6-sources.jar", - "https://repo1.maven.org/maven2/org/objenesis/objenesis/2.6/objenesis-2.6-sources.jar" + "https://maven.google.com/org/objenesis/objenesis/3.2/objenesis-3.2-sources.jar", + "https://repo1.maven.org/maven2/org/objenesis/objenesis/3.2/objenesis-3.2-sources.jar", + "https://maven.fabric.io/public/org/objenesis/objenesis/3.2/objenesis-3.2-sources.jar", + "https://maven.google.com/org/objenesis/objenesis/3.2/objenesis-3.2-sources.jar", + "https://repo1.maven.org/maven2/org/objenesis/objenesis/3.2/objenesis-3.2-sources.jar" ], - "sha256": "52d9f4dba531677fc074eff00ea07f22a1d42e5a97cc9e8571c4cd3d459b6be0", - "url": "https://repo1.maven.org/maven2/org/objenesis/objenesis/2.6/objenesis-2.6-sources.jar" + "sha256": "56a7987f5746bd809c363e29055c41f5472a086b0e0e32248693e064d10bc306", + "url": "https://repo1.maven.org/maven2/org/objenesis/objenesis/3.2/objenesis-3.2-sources.jar" }, { "coord": "org.ow2.asm:asm-analysis:7.2", diff --git a/third_party/versions.bzl b/third_party/versions.bzl index fa391f65998..893cff3d4e7 100644 --- a/third_party/versions.bzl +++ b/third_party/versions.bzl @@ -85,6 +85,7 @@ MAVEN_TEST_DEPENDENCY_VERSIONS = { "androidx.test:runner": "1.2.0", "androidx.work:work-testing": "2.4.0", "com.github.bumptech.glide:mocks": "4.11.0", + "com.google.protobuf:protobuf-java": "3.17.3", "com.google.truth:truth": "0.43", "com.squareup.okhttp3:mockwebserver": "4.1.0", "com.squareup.retrofit2:retrofit-mock": "2.5.0", @@ -92,6 +93,7 @@ MAVEN_TEST_DEPENDENCY_VERSIONS = { "org.jetbrains.kotlin:kotlin-reflect": "1.3.41", "org.jetbrains.kotlin:kotlin-test-junit": "1.3.72", "org.jetbrains.kotlinx:kotlinx-coroutines-test": "1.2.2", + "org.mockito.kotlin:mockito-kotlin": "3.2.0", "org.mockito:mockito-core": "2.19.0", "org.robolectric:annotations": "4.4", "org.robolectric:robolectric": "4.4",