From 00f8deb8f2dd6f0871e1ab628bf06ad420c6201a Mon Sep 17 00:00:00 2001 From: Tad Fisher Date: Fri, 6 Oct 2023 16:01:15 -0700 Subject: [PATCH] Rewrite based on code from the GitHub Dependency Graph Gradle Plugin --- COPYING | 23 +- LICENSE | 177 +++ app/build.gradle.kts | 71 +- app/gradle.lockfile | 38 - .../kotlin/org/nixos/gradle2nix/Artifact.kt | 9 + .../main/kotlin/org/nixos/gradle2nix/Env.kt | 50 - .../org/nixos/gradle2nix/GradleRunner.kt | 40 +- .../kotlin/org/nixos/gradle2nix/Logger.kt | 5 +- .../main/kotlin/org/nixos/gradle2nix/Main.kt | 201 ++- .../kotlin/org/nixos/gradle2nix/Process.kt | 207 +++ .../kotlin/org/nixos/gradle2nix/Projects.kt | 11 - .../dependency/ComponentArtifactIdentifier.kt | 31 + .../dependency/ComponentIdentifier.kt | 28 + .../ModuleComponentArtifactIdentifier.kt | 40 + .../dependency/ModuleComponentIdentifier.kt | 76 + .../gradle2nix/dependency/ModuleIdentifier.kt | 40 + .../metadata/ArtifactVerificationMetadata.kt | 6 + .../org/nixos/gradle2nix/metadata/Checksum.kt | 43 + .../nixos/gradle2nix/metadata/ChecksumKind.kt | 30 + .../metadata/ComponentVerificationMetadata.kt | 8 + .../DependencyVerificationConfiguration.kt | 98 ++ .../metadata/DependencyVerificationXmlTags.kt | 48 + .../DependencyVerificationsXmlReader.kt | 421 ++++++ .../gradle2nix/metadata/DependencyVerifier.kt | 23 + .../metadata/DependencyVerifierBuilder.kt | 162 +++ .../org/nixos/gradle2nix/BuildSrcTest.kt | 19 - .../kotlin/org/nixos/gradle2nix/GoldenTest.kt | 40 + .../kotlin/org/nixos/gradle2nix/TestUtil.kt | 152 +- assets/gradle2nix.svg | 29 +- build.gradle.kts | 36 +- .../basic-java-project/groovy/build.gradle | 1 - .../groovy/settings.gradle} | 0 .../kotlin/build.gradle.kts | 1 - .../kotlin/settings.gradle.kts} | 0 .../kotlin/build.gradle.kts | 54 +- .../kotlin/settings.gradle.kts} | 0 .../kotlin/build.gradle.kts | 4 +- .../kotlin/buildSrc/build.gradle.kts | 3 +- .../apply-plugin-publish.gradle.kts | 5 + .../com.example/custom-spotless.gradle.kts | 11 - .../classifier/groovy/settings.gradle} | 0 .../classifier/kotlin/build.gradle.kts | 2 +- .../classifier/kotlin/settings.gradle.kts | 0 .../maven-bom/kotlin/build.gradle.kts | 4 +- .../maven-bom/kotlin/settings.gradle.kts | 0 .../snapshot-dynamic/groovy/build.gradle | 4 +- .../snapshot-dynamic/groovy/settings.gradle | 0 .../snapshot-dynamic/kotlin/build.gradle.kts | 3 +- .../kotlin/settings.gradle.kts | 0 .../snapshot-redirect/groovy/build.gradle | 3 +- .../snapshot-redirect/groovy/settings.gradle | 0 .../dependency/snapshot/groovy/build.gradle | 4 +- .../snapshot/groovy/settings.gradle | 0 .../snapshot/kotlin/build.gradle.kts | 3 +- .../snapshot/kotlin/settings.gradle.kts | 0 .../basic/basic-java-project.groovy.json | 72 + .../basic/basic-java-project.kotlin.json | 72 + .../basic/basic-kotlin-project.kotlin.json | 1258 +++++++++++++++++ .../buildsrc/plugin-in-buildsrc.kotlin.json | 576 ++++++++ .../golden/dependency/classifier.groovy.json | 16 + .../golden/dependency/classifier.kotlin.json | 16 + .../golden/dependency/maven-bom.kotlin.json | 38 + .../dependency/snapshot-dynamic.groovy.json | 2 + .../dependency/snapshot-dynamic.kotlin.json | 2 + .../dependency/snapshot-redirect.groovy.json | 16 + .../golden/dependency/snapshot.groovy.json | 2 + .../golden/dependency/snapshot.kotlin.json | 2 + .../settings-buildscript.groovy.json | 128 ++ fixtures/golden/ivy/basic.kotlin.json | 30 + .../resolves-from-default-repo.groovy.json | 494 +++++++ .../resolves-from-default-repo.kotlin.json | 494 +++++++ fixtures/golden/s3/maven-snapshot.groovy.json | 2 + fixtures/golden/s3/maven-snapshot.kotlin.json | 2 + fixtures/golden/s3/maven.groovy.json | 2 + fixtures/golden/s3/maven.kotlin.json | 2 + .../golden/settings/buildscript.groovy.json | 16 + ...pendency-resolution-management.kotlin.json | 16 + .../subprojects/multi-module.groovy.json | 114 ++ .../subprojects/multi-module.kotlin.json | 114 ++ fixtures/ivy/basic/kotlin/build.gradle.kts | 6 +- fixtures/ivy/basic/kotlin/settings.gradle.kts | 0 .../groovy/build.gradle | 4 +- .../groovy/settings.gradle | 0 .../kotlin/build.gradle.kts | 5 +- .../kotlin/settings.gradle.kts | 0 .../s3/maven-snapshot/kotlin/build.gradle.kts | 3 +- fixtures/s3/maven/kotlin/build.gradle.kts | 2 +- .../buildscript/groovy/settings.gradle | 2 +- .../kotlin/build.gradle.kts | 3 +- .../kotlin/settings.gradle.kts | 4 +- .../dependent-subprojects/groovy/build.gradle | 15 - .../groovy/settings.gradle | 1 - .../multi-module/groovy/build.gradle | 4 +- .../multi-module/kotlin/build.gradle.kts | 2 +- .../kotlin/child-a/build.gradle.kts | 1 + .../kotlin/child-b/build.gradle.kts | 1 + gradle.properties | 5 +- gradle/libs.versions.toml | 43 + gradle/wrapper/gradle-wrapper.jar | Bin 59203 -> 61574 bytes gradle/wrapper/gradle-wrapper.properties | 3 +- gradlew | 285 ++-- gradlew.bat | 15 +- ivy/build.gradle.kts | 24 - ivy/gradle.lockfile | 123 -- .../main/kotlin/org/nixos/gradle2nix/S3.kt | 294 ---- .../kotlin/org/nixos/gradle2nix/S3Test.kt | 113 -- model/build.gradle.kts | 22 +- model/gradle.lockfile | 13 - .../nixos/gradle2nix/DependencyCoordinates.kt | 6 + .../org/nixos/gradle2nix/DependencySource.kt | 13 + .../main/kotlin/org/nixos/gradle2nix/Impl.kt | 139 -- .../main/kotlin/org/nixos/gradle2nix/Model.kt | 51 - .../org/nixos/gradle2nix/PluginParameters.kt | 7 + .../kotlin/org/nixos/gradle2nix/Repository.kt | 20 + .../nixos/gradle2nix/ResolvedConfiguration.kt | 19 + .../nixos/gradle2nix/ResolvedDependency.kt | 32 + plugin/.stutter/java11.lock | 5 - plugin/.stutter/java8.lock | 7 - plugin/build.gradle.kts | 101 +- plugin/buildscript-gradle.lockfile | 36 - plugin/gradle.lockfile | 170 --- .../kotlin/org/nixos/gradle2nix/BasicTest.kt | 74 - .../org/nixos/gradle2nix/DependencyTest.kt | 102 -- .../kotlin/org/nixos/gradle2nix/IvyTest.kt | 34 - .../kotlin/org/nixos/gradle2nix/PluginTest.kt | 25 - .../kotlin/org/nixos/gradle2nix/S3Test.kt | 52 - .../org/nixos/gradle2nix/SettingsTest.kt | 39 - .../org/nixos/gradle2nix/SubprojectsTest.kt | 152 -- .../kotlin/org/nixos/gradle2nix/TestUtil.kt | 300 ---- .../org/nixos/gradle2nix/WrapperTest.kt | 27 - .../java/org/nixos/gradle2nix/ApiHack.java | 15 - .../nixos/gradle2nix/ConfigurationResolver.kt | 303 ---- .../org/nixos/gradle2nix/Gradle2NixPlugin.kt | 283 +--- .../org/nixos/gradle2nix/ModelProperties.kt | 20 - .../gradle2nix/NixDependencyGraphRenderer.kt | 21 + .../org/nixos/gradle2nix/PluginResolver.kt | 31 - .../nixos/gradle2nix/RepositoriesCollector.kt | 37 - .../nixos/gradle2nix/RepositoryResolver.kt | 317 ----- .../main/kotlin/org/nixos/gradle2nix/Util.kt | 24 - .../AbstractDependencyExtractorPlugin.kt | 128 ++ .../DependencyGraphRenderer.kt | 11 + .../extractor/DependencyExtractor.kt | 342 +++++ .../DependencyExtractorBuildService.kt | 19 + .../extractor/LegacyDependencyExtractor.kt | 10 + .../extractor/ResolvedConfigurationFilter.kt | 16 + .../dependencygraph/util/GradleExtensions.kt | 19 + .../dependencygraph/util/PluginParameters.kt | 6 + .../ForceDependencyResolutionPlugin.kt | 65 + .../LegacyResolveProjectDependenciesTask.kt | 20 + .../ResolveProjectDependenciesTask.kt | 31 + settings.gradle.kts | 44 +- 151 files changed, 6425 insertions(+), 3491 deletions(-) create mode 100644 LICENSE delete mode 100644 app/gradle.lockfile create mode 100644 app/src/main/kotlin/org/nixos/gradle2nix/Artifact.kt delete mode 100644 app/src/main/kotlin/org/nixos/gradle2nix/Env.kt create mode 100644 app/src/main/kotlin/org/nixos/gradle2nix/Process.kt create mode 100644 app/src/main/kotlin/org/nixos/gradle2nix/dependency/ComponentArtifactIdentifier.kt create mode 100644 app/src/main/kotlin/org/nixos/gradle2nix/dependency/ComponentIdentifier.kt create mode 100644 app/src/main/kotlin/org/nixos/gradle2nix/dependency/ModuleComponentArtifactIdentifier.kt create mode 100644 app/src/main/kotlin/org/nixos/gradle2nix/dependency/ModuleComponentIdentifier.kt create mode 100644 app/src/main/kotlin/org/nixos/gradle2nix/dependency/ModuleIdentifier.kt create mode 100644 app/src/main/kotlin/org/nixos/gradle2nix/metadata/ArtifactVerificationMetadata.kt create mode 100644 app/src/main/kotlin/org/nixos/gradle2nix/metadata/Checksum.kt create mode 100644 app/src/main/kotlin/org/nixos/gradle2nix/metadata/ChecksumKind.kt create mode 100644 app/src/main/kotlin/org/nixos/gradle2nix/metadata/ComponentVerificationMetadata.kt create mode 100644 app/src/main/kotlin/org/nixos/gradle2nix/metadata/DependencyVerificationConfiguration.kt create mode 100644 app/src/main/kotlin/org/nixos/gradle2nix/metadata/DependencyVerificationXmlTags.kt create mode 100644 app/src/main/kotlin/org/nixos/gradle2nix/metadata/DependencyVerificationsXmlReader.kt create mode 100644 app/src/main/kotlin/org/nixos/gradle2nix/metadata/DependencyVerifier.kt create mode 100644 app/src/main/kotlin/org/nixos/gradle2nix/metadata/DependencyVerifierBuilder.kt delete mode 100644 app/src/test/kotlin/org/nixos/gradle2nix/BuildSrcTest.kt create mode 100644 app/src/test/kotlin/org/nixos/gradle2nix/GoldenTest.kt rename fixtures/{subprojects/dependent-subprojects/groovy/child-a/build.gradle => basic/basic-java-project/groovy/settings.gradle} (100%) rename fixtures/{subprojects/dependent-subprojects/groovy/child-b/build.gradle => basic/basic-java-project/kotlin/settings.gradle.kts} (100%) rename fixtures/{subprojects/dependent-subprojects/groovy/child-c/build.gradle => basic/basic-kotlin-project/kotlin/settings.gradle.kts} (100%) create mode 100644 fixtures/buildsrc/plugin-in-buildsrc/kotlin/buildSrc/src/main/kotlin/com.example/apply-plugin-publish.gradle.kts delete mode 100644 fixtures/buildsrc/plugin-in-buildsrc/kotlin/buildSrc/src/main/kotlin/com.example/custom-spotless.gradle.kts rename fixtures/{subprojects/dependent-subprojects/groovy/child-d/build.gradle => dependency/classifier/groovy/settings.gradle} (100%) create mode 100644 fixtures/dependency/classifier/kotlin/settings.gradle.kts create mode 100644 fixtures/dependency/maven-bom/kotlin/settings.gradle.kts create mode 100644 fixtures/dependency/snapshot-dynamic/groovy/settings.gradle create mode 100644 fixtures/dependency/snapshot-dynamic/kotlin/settings.gradle.kts create mode 100644 fixtures/dependency/snapshot-redirect/groovy/settings.gradle create mode 100644 fixtures/dependency/snapshot/groovy/settings.gradle create mode 100644 fixtures/dependency/snapshot/kotlin/settings.gradle.kts create mode 100644 fixtures/golden/basic/basic-java-project.groovy.json create mode 100644 fixtures/golden/basic/basic-java-project.kotlin.json create mode 100644 fixtures/golden/basic/basic-kotlin-project.kotlin.json create mode 100644 fixtures/golden/buildsrc/plugin-in-buildsrc.kotlin.json create mode 100644 fixtures/golden/dependency/classifier.groovy.json create mode 100644 fixtures/golden/dependency/classifier.kotlin.json create mode 100644 fixtures/golden/dependency/maven-bom.kotlin.json create mode 100644 fixtures/golden/dependency/snapshot-dynamic.groovy.json create mode 100644 fixtures/golden/dependency/snapshot-dynamic.kotlin.json create mode 100644 fixtures/golden/dependency/snapshot-redirect.groovy.json create mode 100644 fixtures/golden/dependency/snapshot.groovy.json create mode 100644 fixtures/golden/dependency/snapshot.kotlin.json create mode 100644 fixtures/golden/integration/settings-buildscript.groovy.json create mode 100644 fixtures/golden/ivy/basic.kotlin.json create mode 100644 fixtures/golden/plugin/resolves-from-default-repo.groovy.json create mode 100644 fixtures/golden/plugin/resolves-from-default-repo.kotlin.json create mode 100644 fixtures/golden/s3/maven-snapshot.groovy.json create mode 100644 fixtures/golden/s3/maven-snapshot.kotlin.json create mode 100644 fixtures/golden/s3/maven.groovy.json create mode 100644 fixtures/golden/s3/maven.kotlin.json create mode 100644 fixtures/golden/settings/buildscript.groovy.json create mode 100644 fixtures/golden/settings/dependency-resolution-management.kotlin.json create mode 100644 fixtures/golden/subprojects/multi-module.groovy.json create mode 100644 fixtures/golden/subprojects/multi-module.kotlin.json create mode 100644 fixtures/ivy/basic/kotlin/settings.gradle.kts create mode 100644 fixtures/plugin/resolves-from-default-repo/groovy/settings.gradle create mode 100644 fixtures/plugin/resolves-from-default-repo/kotlin/settings.gradle.kts delete mode 100644 fixtures/subprojects/dependent-subprojects/groovy/build.gradle delete mode 100644 fixtures/subprojects/dependent-subprojects/groovy/settings.gradle create mode 100644 gradle/libs.versions.toml delete mode 100644 ivy/build.gradle.kts delete mode 100644 ivy/gradle.lockfile delete mode 100644 ivy/src/main/kotlin/org/nixos/gradle2nix/S3.kt delete mode 100644 ivy/src/test/kotlin/org/nixos/gradle2nix/S3Test.kt delete mode 100644 model/gradle.lockfile create mode 100644 model/src/main/kotlin/org/nixos/gradle2nix/DependencyCoordinates.kt create mode 100644 model/src/main/kotlin/org/nixos/gradle2nix/DependencySource.kt delete mode 100644 model/src/main/kotlin/org/nixos/gradle2nix/Impl.kt delete mode 100644 model/src/main/kotlin/org/nixos/gradle2nix/Model.kt create mode 100644 model/src/main/kotlin/org/nixos/gradle2nix/PluginParameters.kt create mode 100644 model/src/main/kotlin/org/nixos/gradle2nix/Repository.kt create mode 100644 model/src/main/kotlin/org/nixos/gradle2nix/ResolvedConfiguration.kt create mode 100644 model/src/main/kotlin/org/nixos/gradle2nix/ResolvedDependency.kt delete mode 100644 plugin/.stutter/java11.lock delete mode 100644 plugin/.stutter/java8.lock delete mode 100644 plugin/buildscript-gradle.lockfile delete mode 100644 plugin/gradle.lockfile delete mode 100644 plugin/src/compatTest/kotlin/org/nixos/gradle2nix/BasicTest.kt delete mode 100644 plugin/src/compatTest/kotlin/org/nixos/gradle2nix/DependencyTest.kt delete mode 100644 plugin/src/compatTest/kotlin/org/nixos/gradle2nix/IvyTest.kt delete mode 100644 plugin/src/compatTest/kotlin/org/nixos/gradle2nix/PluginTest.kt delete mode 100644 plugin/src/compatTest/kotlin/org/nixos/gradle2nix/S3Test.kt delete mode 100644 plugin/src/compatTest/kotlin/org/nixos/gradle2nix/SettingsTest.kt delete mode 100644 plugin/src/compatTest/kotlin/org/nixos/gradle2nix/SubprojectsTest.kt delete mode 100644 plugin/src/compatTest/kotlin/org/nixos/gradle2nix/TestUtil.kt delete mode 100644 plugin/src/compatTest/kotlin/org/nixos/gradle2nix/WrapperTest.kt delete mode 100644 plugin/src/main/java/org/nixos/gradle2nix/ApiHack.java delete mode 100644 plugin/src/main/kotlin/org/nixos/gradle2nix/ConfigurationResolver.kt delete mode 100644 plugin/src/main/kotlin/org/nixos/gradle2nix/ModelProperties.kt create mode 100644 plugin/src/main/kotlin/org/nixos/gradle2nix/NixDependencyGraphRenderer.kt delete mode 100644 plugin/src/main/kotlin/org/nixos/gradle2nix/PluginResolver.kt delete mode 100644 plugin/src/main/kotlin/org/nixos/gradle2nix/RepositoriesCollector.kt delete mode 100644 plugin/src/main/kotlin/org/nixos/gradle2nix/RepositoryResolver.kt delete mode 100644 plugin/src/main/kotlin/org/nixos/gradle2nix/Util.kt create mode 100644 plugin/src/main/kotlin/org/nixos/gradle2nix/dependencygraph/AbstractDependencyExtractorPlugin.kt create mode 100644 plugin/src/main/kotlin/org/nixos/gradle2nix/dependencygraph/DependencyGraphRenderer.kt create mode 100644 plugin/src/main/kotlin/org/nixos/gradle2nix/dependencygraph/extractor/DependencyExtractor.kt create mode 100644 plugin/src/main/kotlin/org/nixos/gradle2nix/dependencygraph/extractor/DependencyExtractorBuildService.kt create mode 100644 plugin/src/main/kotlin/org/nixos/gradle2nix/dependencygraph/extractor/LegacyDependencyExtractor.kt create mode 100644 plugin/src/main/kotlin/org/nixos/gradle2nix/dependencygraph/extractor/ResolvedConfigurationFilter.kt create mode 100644 plugin/src/main/kotlin/org/nixos/gradle2nix/dependencygraph/util/GradleExtensions.kt create mode 100644 plugin/src/main/kotlin/org/nixos/gradle2nix/dependencygraph/util/PluginParameters.kt create mode 100644 plugin/src/main/kotlin/org/nixos/gradle2nix/forceresolve/ForceDependencyResolutionPlugin.kt create mode 100644 plugin/src/main/kotlin/org/nixos/gradle2nix/forceresolve/LegacyResolveProjectDependenciesTask.kt create mode 100644 plugin/src/main/kotlin/org/nixos/gradle2nix/forceresolve/ResolveProjectDependenciesTask.kt diff --git a/COPYING b/COPYING index 22a0345..3a6e355 100644 --- a/COPYING +++ b/COPYING @@ -1,20 +1,7 @@ -Copyright © Tad Fisher and the gradle2nix contributors +Copyright © Tad Fisher and the gradle2nix contributors, and licensed +under the Apache License 2.0. -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: +Based on substantial portions of the GitHub Dependency Graph Gradle Plugin, +Copyright © Gradle, Inc and licensed under the Apache License 2.0. -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file +See LICENSE for the full text of the Apache License 2.0. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f433b1a --- /dev/null +++ b/LICENSE @@ -0,0 +1,177 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 6efa4c6..3f194ea 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,27 +1,28 @@ -import org.jetbrains.kotlin.gradle.tasks.KotlinCompile - plugins { - kotlin("jvm") - kotlin("kapt") + id("org.jetbrains.kotlin.jvm") + id("org.jetbrains.kotlin.plugin.serialization") application } +configurations { + register("share") +} + dependencies { - implementation(project(":model")) implementation(kotlin("reflect")) - implementation("org.gradle:gradle-tooling-api:${gradle.gradleVersion}") - implementation("com.github.ajalt:clikt:latest.release") - implementation("org.slf4j:slf4j-api:latest.release") - runtimeOnly("org.slf4j:slf4j-simple:latest.release") - implementation("com.squareup.moshi:moshi-adapters:latest.release") - implementation("com.squareup.moshi:moshi-kotlin:latest.release") - kapt("com.squareup.moshi:moshi-kotlin-codegen:latest.release") - implementation("com.squareup.okio:okio:latest.release") + implementation(project(":model")) + implementation(libs.clikt) + implementation(libs.gradle.toolingApi) + implementation(libs.serialization.json) + implementation(libs.slf4j.api) + runtimeOnly(libs.slf4j.simple) + implementation(libs.okio) + + "share"(project(":plugin", configuration = "shadow")) testRuntimeOnly(kotlin("reflect")) - testImplementation("org.spekframework.spek2:spek-dsl-jvm:latest.release") - testRuntimeOnly("org.spekframework.spek2:spek-runner-junit5:latest.release") - testImplementation("io.strikt:strikt-core:latest.release") + testImplementation(libs.kotest.assertions) + testImplementation(libs.kotest.runner) } application { @@ -29,11 +30,15 @@ application { applicationName = "gradle2nix" applicationDefaultJvmArgs += "-Dorg.nixos.gradle2nix.share=@APP_HOME@/share" applicationDistribution - .from(tasks.getByPath(":plugin:shadowJar"), "$rootDir/gradle-env.nix") + .from(configurations.named("share")) .into("share") .rename("plugin.*\\.jar", "plugin.jar") } +kotlin { + jvmToolchain(11) +} + sourceSets { test { resources { @@ -42,6 +47,8 @@ sourceSets { } } +val updateGolden = providers.gradleProperty("update-golden") + tasks { (run) { dependsOn(installDist) @@ -52,28 +59,32 @@ tasks { startScripts { doLast { - unixScript.writeText(unixScript.readText().replace("@APP_HOME@", "\$APP_HOME")) + unixScript.writeText( + unixScript.readText() + .replace("@APP_HOME@", "\\\"\$APP_HOME\\\"") + .replace(Regex("DEFAULT_JVM_OPTS=\'(.*)\'")) { match -> + "DEFAULT_JVM_OPTS=${match.groupValues[1]}" + } + ) windowsScript.writeText(windowsScript.readText().replace("@APP_HOME@", "%APP_HOME%")) } } - test { + withType { + notCompatibleWithConfigurationCache("huh?") dependsOn(installDist) doFirst { - systemProperties("org.nixos.gradle2nix.share" to installDist.get().destinationDir.resolve("share")) - } - useJUnitPlatform { - includeEngines("spek2") + if (updateGolden.isPresent) { + systemProperty("org.nixos.gradle2nix.update-golden", "") + } + systemProperties( + "org.nixos.gradle2nix.share" to installDist.get().destinationDir.resolve("share"), + "org.nixos.gradle2nix.m2" to rootDir.resolve("fixtures/repositories/m2").toURI().toString() + ) } + useJUnitPlatform() testLogging { events("passed", "skipped", "failed") } } - - withType { - kotlinOptions { - jvmTarget = "1.8" - freeCompilerArgs = listOf("-Xopt-in=kotlin.RequiresOptIn") - } - } } diff --git a/app/gradle.lockfile b/app/gradle.lockfile deleted file mode 100644 index 968ee4b..0000000 --- a/app/gradle.lockfile +++ /dev/null @@ -1,38 +0,0 @@ -# This is a Gradle generated file for dependency locking. -# Manual edits can break the build and are not advised. -# This file is expected to be part of source control. -com.christophsturm:filepeek:0.1.2=testRuntimeClasspath -com.github.ajalt:clikt:2.8.0=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.squareup.moshi:moshi-adapters:1.11.0=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.squareup.moshi:moshi-kotlin:1.11.0=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.squareup.moshi:moshi:1.11.0=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.squareup.okio:okio:3.0.0-alpha.1=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.github.classgraph:classgraph:4.8.37=testRuntimeClasspath -io.strikt:strikt-core:0.28.2=testCompileClasspath,testRuntimeClasspath -net.swiftzer.semver:semver:1.1.1=runtimeClasspath,testRuntimeClasspath -org.apiguardian:apiguardian-api:1.1.0=testRuntimeClasspath -org.gradle:gradle-tooling-api:6.8.1=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.jetbrains.kotlin:kotlin-reflect:1.4.20=compileClasspath,runtimeClasspath,testCompileClasspath -org.jetbrains.kotlin:kotlin-reflect:1.4.21-2=testRuntimeClasspath -org.jetbrains.kotlin:kotlin-stdlib-common:1.4.20=compileClasspath,runtimeClasspath -org.jetbrains.kotlin:kotlin-stdlib-common:1.4.21-2=testCompileClasspath,testRuntimeClasspath -org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.4.20=compileClasspath,runtimeClasspath -org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.4.21-2=testCompileClasspath,testRuntimeClasspath -org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.20=compileClasspath,runtimeClasspath -org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.21-2=testCompileClasspath,testRuntimeClasspath -org.jetbrains.kotlin:kotlin-stdlib:1.4.20=compileClasspath,runtimeClasspath -org.jetbrains.kotlin:kotlin-stdlib:1.4.21-2=testCompileClasspath,testRuntimeClasspath -org.jetbrains.kotlinx:kotlinx-coroutines-core-common:1.3.3=testRuntimeClasspath -org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.4.2=testRuntimeClasspath -org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.2=testRuntimeClasspath -org.jetbrains:annotations:13.0=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.junit.platform:junit-platform-commons:1.6.0=testRuntimeClasspath -org.junit.platform:junit-platform-engine:1.6.0=testRuntimeClasspath -org.junit:junit-bom:5.6.0=testRuntimeClasspath -org.opentest4j:opentest4j:1.2.0=testCompileClasspath,testRuntimeClasspath -org.slf4j:slf4j-api:2.0.0-alpha1=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.slf4j:slf4j-simple:2.0.0-alpha1=runtimeClasspath,testRuntimeClasspath -org.spekframework.spek2:spek-dsl-jvm:2.0.15=testCompileClasspath,testRuntimeClasspath -org.spekframework.spek2:spek-runner-junit5:2.0.15=testRuntimeClasspath -org.spekframework.spek2:spek-runtime-jvm:2.0.15=testRuntimeClasspath -empty= diff --git a/app/src/main/kotlin/org/nixos/gradle2nix/Artifact.kt b/app/src/main/kotlin/org/nixos/gradle2nix/Artifact.kt new file mode 100644 index 0000000..efa0d1c --- /dev/null +++ b/app/src/main/kotlin/org/nixos/gradle2nix/Artifact.kt @@ -0,0 +1,9 @@ +package org.nixos.gradle2nix + +import kotlinx.serialization.Serializable + +@Serializable +data class Artifact( + val urls: List, + val hash: String, +) diff --git a/app/src/main/kotlin/org/nixos/gradle2nix/Env.kt b/app/src/main/kotlin/org/nixos/gradle2nix/Env.kt deleted file mode 100644 index 6c4135a..0000000 --- a/app/src/main/kotlin/org/nixos/gradle2nix/Env.kt +++ /dev/null @@ -1,50 +0,0 @@ -package org.nixos.gradle2nix - -import com.squareup.moshi.JsonClass - -@JsonClass(generateAdapter = true) -data class NixGradleEnv( - val name: String, - val version: String, - val path: String, - val gradle: DefaultGradle, - val dependencies: Map> -) - -fun buildEnv(builds: Map): Map = - builds.mapValues { (path, build) -> - NixGradleEnv( - name = build.rootProject.name, - version = build.rootProject.version, - path = path, - gradle = build.gradle, - dependencies = mapOf( - "settings" to build.settingsDependencies, - "plugin" to build.pluginDependencies, - "buildscript" to build.rootProject.collectDependencies(DefaultProject::buildscriptDependencies), - "project" to build.rootProject.collectDependencies(DefaultProject::projectDependencies) - ) - ) - } - -private fun DefaultProject.collectDependencies( - chooser: DefaultProject.() -> List -): List { - val result = mutableMapOf() - mergeRepo(result, chooser()) - for (child in children) { - mergeRepo(result, child.collectDependencies(chooser)) - } - return result.values.toList() -} - -private fun mergeRepo( - base: MutableMap, - extra: List -) { - extra.forEach { artifact -> - base.merge(artifact.id, artifact) { old, new -> - old.copy(urls = old.urls.union(new.urls).toList()) - } - } -} diff --git a/app/src/main/kotlin/org/nixos/gradle2nix/GradleRunner.kt b/app/src/main/kotlin/org/nixos/gradle2nix/GradleRunner.kt index 4978ebe..a0368e8 100644 --- a/app/src/main/kotlin/org/nixos/gradle2nix/GradleRunner.kt +++ b/app/src/main/kotlin/org/nixos/gradle2nix/GradleRunner.kt @@ -13,19 +13,31 @@ fun connect(config: Config): ProjectConnection = .forProjectDirectory(config.projectDir) .connect() -@Suppress("UnstableApiUsage") -fun ProjectConnection.getBuildModel(config: Config, path: String): DefaultBuild { - return model(Build::class.java).apply { - addArguments( - "--init-script=$shareDir/init.gradle", - "-Porg.nixos.gradle2nix.configurations=${config.configurations.joinToString(",")}", - "-Porg.nixos.gradle2nix.subprojects=${config.subprojects.joinToString(",")}" - ) - if (config.gradleArgs != null) addArguments(config.gradleArgs) - if (path.isNotEmpty()) addArguments("--project-dir=$path") - if (!config.quiet) { - setStandardOutput(System.err) - setStandardError(System.err) +fun ProjectConnection.build( + config: Config, +) { + newBuild() + .apply { + if (config.tasks.isNotEmpty()) { + forTasks(*config.tasks.toTypedArray()) + } else { + forTasks(RESOLVE_ALL_TASK) + } + addArguments(config.gradleArgs) + addArguments( + "--init-script=${config.appHome}/init.gradle", + "--write-verification-metadata", "sha256" + ) + if (config.projectFilter != null) { + addArguments("-D${PARAM_INCLUDE_PROJECTS}") + } + if (config.configurationFilter != null) { + addArguments("-D${PARAM_INCLUDE_CONFIGURATIONS}") + } + if (config.logger.verbose) { + setStandardOutput(System.err) + setStandardError(System.err) + } } - }.get().let { DefaultBuild(it) } + .run() } diff --git a/app/src/main/kotlin/org/nixos/gradle2nix/Logger.kt b/app/src/main/kotlin/org/nixos/gradle2nix/Logger.kt index 949249a..9191481 100644 --- a/app/src/main/kotlin/org/nixos/gradle2nix/Logger.kt +++ b/app/src/main/kotlin/org/nixos/gradle2nix/Logger.kt @@ -1,6 +1,7 @@ package org.nixos.gradle2nix import java.io.PrintStream +import kotlin.system.exitProcess class Logger( val out: PrintStream = System.err, @@ -9,9 +10,9 @@ class Logger( val log: (String) -> Unit = { if (verbose) out.println(it) } val warn: (String) -> Unit = { out.println("Warning: $it")} - val error: (String) -> Unit = { + val error: (String) -> Nothing = { out.println("Error: $it") - System.exit(1) + exitProcess(1) } operator fun component1() = log diff --git a/app/src/main/kotlin/org/nixos/gradle2nix/Main.kt b/app/src/main/kotlin/org/nixos/gradle2nix/Main.kt index afe4c1b..921ac1b 100644 --- a/app/src/main/kotlin/org/nixos/gradle2nix/Main.kt +++ b/app/src/main/kotlin/org/nixos/gradle2nix/Main.kt @@ -1,102 +1,97 @@ package org.nixos.gradle2nix -import com.github.ajalt.clikt.completion.CompletionCandidates import com.github.ajalt.clikt.core.CliktCommand import com.github.ajalt.clikt.core.context import com.github.ajalt.clikt.output.CliktHelpFormatter -import com.github.ajalt.clikt.parameters.arguments.ProcessedArgument import com.github.ajalt.clikt.parameters.arguments.argument -import com.github.ajalt.clikt.parameters.arguments.convert -import com.github.ajalt.clikt.parameters.arguments.default +import com.github.ajalt.clikt.parameters.arguments.multiple import com.github.ajalt.clikt.parameters.options.default import com.github.ajalt.clikt.parameters.options.flag import com.github.ajalt.clikt.parameters.options.multiple import com.github.ajalt.clikt.parameters.options.option import com.github.ajalt.clikt.parameters.options.validate import com.github.ajalt.clikt.parameters.types.file -import com.squareup.moshi.Moshi -import com.squareup.moshi.Types -import okio.buffer -import okio.sink import java.io.File - -val shareDir: String = System.getProperty("org.nixos.gradle2nix.share") +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.encodeToStream data class Config( + val appHome: File, + val gradleHome: File, val gradleVersion: String?, - val gradleArgs: String?, - val configurations: List, + val gradleArgs: List, + val projectFilter: String?, + val configurationFilter: String?, val projectDir: File, - val includes: List, - val subprojects: List, - val buildSrc: Boolean, - val quiet: Boolean -) { - val allProjects = listOf(projectDir) + includes + val tasks: List, + val logger: Logger, +) + +@OptIn(ExperimentalSerializationApi::class) +private val JsonFormat = Json { + prettyPrint = true + prettyPrintIndent = " " } -class Main : CliktCommand( +class Gradle2Nix : CliktCommand( name = "gradle2nix" ) { - private val gradleVersion: String? by option("--gradle-version", "-g", + private val gradleVersion: String? by option( + "--gradle-version", "-g", metavar = "VERSION", - help = "Use a specific Gradle version") + help = "Use a specific Gradle version" + ) - private val gradleArgs: String? by option("--gradle-args", "-a", - metavar = "ARGS", - help = "Extra arguments to pass to Gradle") + private val projectFilter: String? by option( + "--projects", "-p", + metavar = "REGEX", + help = "Regex to filter Gradle projects (default: include all projects)" + ) - private val configurations: List by option("--configuration", "-c", - metavar = "NAME", - help = "Add a configuration to resolve (default: all configurations)") - .multiple() + private val configurationFilter: String? by option( + "--configurations", "-c", + metavar = "REGEX", + help = "Regex to filter Gradle configurations (default: include all configurations)") - private val includes: List by option("--include", "-i", - metavar = "DIR", - help = "Add an additional project to include") - .file(mustExist = true, canBeFile = false, canBeDir = true, mustBeReadable = true) - .multiple() - .validate { files -> - val failures = files.filterNot { it.isProjectRoot() } - if (failures.isNotEmpty()) { - val message = failures.joinToString("\n ") - fail("Included builds are not Gradle projects:\n$message\n" + - "Gradle projects must contain a settings.gradle or settings.gradle.kts script.") - } - } - - private val subprojects: List by option("--project", "-p", - metavar = "PATH", - help = "Only resolve these subproject paths, e.g. ':', or ':sub:project' (default: all projects)") - .multiple() - .validate { paths -> - val failures = paths.filterNot { it.startsWith(":") } - if (failures.isNotEmpty()) { - val message = failures.joinToString("\n ") - fail("Subproject paths must be absolute:\n$message\n" + - "Paths are in the form ':parent:child'.") - } - } - - val outDir: File? by option("--out-dir", "-o", + val outDir: File? by option( + "--out-dir", "-o", metavar = "DIR", help = "Path to write generated files (default: PROJECT-DIR)") .file(canBeFile = false, canBeDir = true) - val envFile: String by option("--env", "-e", + val envFile: String by option( + "--env", "-e", metavar = "FILENAME", help = "Prefix for environment files (.json and .nix)") .default("gradle-env") - private val buildSrc: Boolean by option("--build-src", "-b", help = "Include buildSrc project (default: true)") - .flag("--no-build-src", "-nb", default = true) - private val quiet: Boolean by option("--quiet", "-q", help = "Disable logging") .flag(default = false) - private val projectDir: File by argument("PROJECT-DIR", help = "Path to the project root (default: .)") - .projectDir() - .default(File(".")) + private val projectDir: File by option( + "--projectDir", "-d", + metavar = "PROJECT-DIR", + help = "Path to the project root (default: .)") + .file() + .default(File("."), "Current directory") + .validate { file -> + if (!file.exists()) fail("Directory \"$file\" does not exist.") + if (file.isFile) fail("Directory \"$file\" is a file.") + if (!file.canRead()) fail("Directory \"$file\" is not readable.") + if (!file.isProjectRoot()) fail("Directory \"$file\" is not a Gradle project.") + } + + private val tasks: List by option( + "--tasks", "-t", + metavar = "TASKS", + help = "Gradle tasks to run" + ).multiple() + + private val gradleArgs: List by argument( + name = "ARGS", + help = "Extra arguments to pass to Gradle" + ).multiple() init { context { @@ -107,65 +102,59 @@ class Main : CliktCommand( // Visible for testing lateinit var config: Config + @OptIn(ExperimentalSerializationApi::class) override fun run() { + val appHome = System.getProperty("org.nixos.gradle2nix.share") + if (appHome == null) { + System.err.println("Error: could not locate the /share directory in the gradle2nix installation") + } + val gradleHome = System.getenv("GRADLE_USER_HOME")?.let(::File) ?: File("${System.getProperty("user.home")}/.gradle") + val logger = Logger(verbose = !quiet) + config = Config( + File(appHome), + gradleHome, gradleVersion, gradleArgs, - configurations, + projectFilter, + configurationFilter, projectDir, - includes, - subprojects, - buildSrc, - quiet + tasks, + logger ) - val (log, _, _) = Logger(verbose = !config.quiet) - val paths = resolveProjects(config).map { p -> - p.toRelativeString(config.projectDir) - } + val (log, _, error) = logger - val models = connect(config).use { connection -> - paths.associateWith { project -> - log("Resolving project model: ${project.takeIf { it.isNotEmpty() } ?: "root project"}...") - connection.getBuildModel(config, project) + val metadata = File("$projectDir/gradle/verification-metadata.xml") + if (metadata.exists()) { + val backup = metadata.resolveSibling("verification-metadata.xml.bak") + if (metadata.renameTo(backup)) { + Runtime.getRuntime().addShutdownHook(Thread { + metadata.delete() + backup.renameTo(metadata) + }) } } - log("Building environment...") - val nixGradleEnv = buildEnv(models) + connect(config).use { connection -> + connection.build(config) + } + + val dependencies = try { + processDependencies(config) + } catch (e: Throwable) { + error("Dependency parsing failed: ${e.message}") + } val outDir = outDir ?: projectDir - val json = outDir.resolve("$envFile.json") log("Writing environment to $json") - - json.sink().buffer().use { out -> - Moshi.Builder().build() - .adapter>( - Types.newParameterizedType(Map::class.java, String::class.java, NixGradleEnv::class.java) - ) - .indent(" ") - .toJson(out, nixGradleEnv) - out.flush() - } - - val nix = outDir.resolve("$envFile.nix") - log("Writing Nix script to $nix") - - File(shareDir).resolve("gradle-env.nix").copyTo(nix, overwrite = true) - } -} - -fun ProcessedArgument.projectDir(): ProcessedArgument { - return convert(completionCandidates = CompletionCandidates.Path) { - File(it).also { file -> - if (!file.exists()) fail("Directory \"$file\" does not exist.") - if (file.isFile) fail("Directory \"$file\" is a file.") - if (!file.canRead()) fail("Directory \"$file\" is not readable.") - if (!file.isProjectRoot()) fail("Directory \"$file\" is not a Gradle project.") + json.outputStream().buffered().use { output -> + JsonFormat.encodeToStream(dependencies, output) } } } -fun main(args: Array) = Main().main(args) - +fun main(args: Array) { + Gradle2Nix().main(args) +} diff --git a/app/src/main/kotlin/org/nixos/gradle2nix/Process.kt b/app/src/main/kotlin/org/nixos/gradle2nix/Process.kt new file mode 100644 index 0000000..1b6b1e2 --- /dev/null +++ b/app/src/main/kotlin/org/nixos/gradle2nix/Process.kt @@ -0,0 +1,207 @@ +package org.nixos.gradle2nix + +import java.io.File +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.decodeFromStream +import okio.ByteString.Companion.decodeHex +import okio.HashingSource +import okio.blackholeSink +import okio.buffer +import okio.source +import org.nixos.gradle2nix.dependency.ModuleComponentIdentifier +import org.nixos.gradle2nix.dependencygraph.model.DependencyCoordinates +import org.nixos.gradle2nix.dependencygraph.model.Repository +import org.nixos.gradle2nix.dependencygraph.model.ResolvedConfiguration +import org.nixos.gradle2nix.metadata.ArtifactVerificationMetadata +import org.nixos.gradle2nix.metadata.Checksum +import org.nixos.gradle2nix.metadata.ChecksumKind +import org.nixos.gradle2nix.metadata.ComponentVerificationMetadata +import org.nixos.gradle2nix.metadata.DependencyVerificationsXmlReader +import org.nixos.gradle2nix.metadata.DependencyVerifier + +// Local Maven repository for testing +private val m2 = System.getProperty("org.nixos.gradle2nix.m2") + +private fun shouldSkipRepository(repository: Repository): Boolean { + return repository.artifactResources.all { it.startsWith("file:") && (m2 == null || !it.startsWith(m2)) } || + repository.metadataResources.all { it.startsWith("file:") && (m2 == null || !it.startsWith(m2)) } +} + +fun processDependencies(config: Config): Map> { + val verifier = readVerificationMetadata(config) + val configurations = readDependencyGraph(config) + + val repositories = configurations + .flatMap { it.repositories } + .associateBy { it.id } + .filterNot { (id, repo) -> + if (shouldSkipRepository(repo)) { + config.logger.warn("$id: all URLs are files; skipping") + true + } else { + false + } + } + if (repositories.isEmpty()) { + config.logger.warn("no repositories found in any configuration") + return emptyMap() + } + + return configurations.asSequence() + .flatMap { it.allDependencies.asSequence() } + .groupBy { it.id } + .mapNotNull { (id, dependencies) -> + val deps = dependencies.toSet() + if (deps.isEmpty()) { + config.logger.warn("$id: no resolved dependencies in dependency graph") + return@mapNotNull null + } + val coordinates = deps.first().coordinates + val componentId = ModuleComponentIdentifier( + coordinates.group, + coordinates.module, + coordinates.version + ) + val metadata = verifier.verificationMetadata[componentId] + ?: verifyComponentFilesInCache(config, componentId) + if (metadata == null) { + config.logger.warn("$id: not present in metadata or cache; skipping") + return@mapNotNull null + } + + val repoIds = dependencies.mapNotNull { it.repository } + if (repoIds.isEmpty()) { + config.logger.warn("$id: no repository ids in dependency graph; skipping") + return@mapNotNull null + } + val repos = repoIds.mapNotNull(repositories::get) + if (repos.isEmpty()) { + config.logger.warn("$id: no repositories found for repository ids $repoIds; skipping") + return@mapNotNull null + } + + id to metadata.artifactVerifications.associate { meta -> + meta.artifactName to Artifact( + urls = repos + .flatMap { repository -> artifactUrls(coordinates, meta, repository) } + .distinct(), + hash = meta.checksums.maxBy { c -> c.kind.ordinal }.toSri() + ) + } + } + .toMap() +} + +private fun readVerificationMetadata(config: Config): DependencyVerifier { + return config.projectDir.resolve("gradle/verification-metadata.xml") + .inputStream() + .buffered() + .use { input -> DependencyVerificationsXmlReader.readFromXml(input) } +} + +@OptIn(ExperimentalSerializationApi::class) +private fun readDependencyGraph(config: Config): List { + return config.projectDir.resolve("build/reports/nix-dependency-graph/dependency-graph.json") + .inputStream() + .buffered() + .use { input -> Json.decodeFromStream(input) } +} + +private fun verifyComponentFilesInCache( + config: Config, + component: ModuleComponentIdentifier +): ComponentVerificationMetadata? { + val cacheDir = config.gradleHome.resolve("caches/modules-2/files-2.1/${component.group}/${component.module}/${component.version}") + if (!cacheDir.exists()) { + return null + } + val verifications = cacheDir.walkBottomUp().filter { it.isFile }.map { f -> + ArtifactVerificationMetadata( + f.name, + listOf(Checksum(ChecksumKind.sha256, f.sha256())) + ) + } + config.logger.log("$component: obtained artifact hashes from Gradle cache.") + return ComponentVerificationMetadata(component, verifications.toList()) +} + +private fun File.sha256(): String { + val source = HashingSource.sha256(source()) + source.buffer().readAll(blackholeSink()) + return source.hash.hex() +} + +private fun Checksum.toSri(): String { + val hash = value.decodeHex().base64() + return when (kind) { + ChecksumKind.md5 -> "md5-$hash" + ChecksumKind.sha1 -> "sha1-$hash" + ChecksumKind.sha256 -> "sha256-$hash" + ChecksumKind.sha512 -> "sha512-$hash" + } +} + +private fun artifactUrls( + coordinates: DependencyCoordinates, + metadata: ArtifactVerificationMetadata, + repository: Repository +): List { + val groupAsPath = coordinates.group.replace(".", "/") + + val attributes = mutableMapOf( + "organisation" to if (repository.m2Compatible) groupAsPath else coordinates.group, + "module" to coordinates.module, + "revision" to coordinates.version, + ) + fileAttributes(metadata.artifactName, coordinates.version) + + val resources = when (attributes["ext"]) { + "pom" -> if ("mavenPom" in repository.metadataSources) repository.metadataResources else repository.artifactResources + "xml" -> if ("ivyDescriptor" in repository.metadataSources) repository.metadataResources else repository.artifactResources + "module" -> if ("gradleMetadata" in repository.metadataSources || "ignoreGradleMetadataRedirection" !in repository.metadataSources) { + repository.metadataResources + } else { + repository.artifactResources + } + else -> repository.artifactResources + } + + val urls = mutableListOf() + + for (resource in resources) { + val location = attributes.entries.fold(fill(resource, attributes)) { acc, (key, value) -> + acc.replace("[$key]", value) + } + if (location.none { it == '[' || it == ']' }) { + urls.add(location) + } + } + + return urls +} + +private val optionalRegex = Regex("\\(([^)]+)\\)") +private val attrRegex = Regex("\\[([^]]+)]") + +private fun fill(template: String, attributes: Map): String { + return optionalRegex.replace(template) { match -> + attrRegex.find(match.value)?.groupValues?.get(1)?.let { attr -> + attributes[attr]?.takeIf { it.isNotBlank() }?.let { value -> + match.groupValues[1].replace("[$attr]", value) + } + } ?: "" + } +} + +// Gradle persists artifacts with the Maven artifact pattern, which may not match the repository's pattern. +private fun fileAttributes(file: String, version: String): Map { + val parts = Regex("(.+)-$version(-([^.]+))?(\\.(.+))?").matchEntire(file) ?: return emptyMap() + + val (artifact, _, classifier, _, ext) = parts.destructured + + return buildMap { + put("artifact", artifact) + put("classifier", classifier) + put("ext", ext) + } +} diff --git a/app/src/main/kotlin/org/nixos/gradle2nix/Projects.kt b/app/src/main/kotlin/org/nixos/gradle2nix/Projects.kt index f112991..7beba7f 100644 --- a/app/src/main/kotlin/org/nixos/gradle2nix/Projects.kt +++ b/app/src/main/kotlin/org/nixos/gradle2nix/Projects.kt @@ -2,16 +2,5 @@ package org.nixos.gradle2nix import java.io.File -fun resolveProjects(config: Config) = config.allProjects.run { - if (config.buildSrc) { - flatMap { listOfNotNull(it, it.findBuildSrc()) } - } else { - this - } -} - -fun File.findBuildSrc(): File? = - resolve("buildSrc").takeIf { it.isDirectory } - fun File.isProjectRoot(): Boolean = isDirectory && (resolve("settings.gradle").isFile || resolve("settings.gradle.kts").isFile) diff --git a/app/src/main/kotlin/org/nixos/gradle2nix/dependency/ComponentArtifactIdentifier.kt b/app/src/main/kotlin/org/nixos/gradle2nix/dependency/ComponentArtifactIdentifier.kt new file mode 100644 index 0000000..e4788ee --- /dev/null +++ b/app/src/main/kotlin/org/nixos/gradle2nix/dependency/ComponentArtifactIdentifier.kt @@ -0,0 +1,31 @@ +/* + * Copyright 2014 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.nixos.gradle2nix.dependency + +/** + * An opaque immutable identifier for an artifact that belongs to some component instance. + */ +interface ComponentArtifactIdentifier { + /** + * Returns the id of the component that this artifact belongs to. + */ + val componentIdentifier: ComponentIdentifier + + /** + * Returns some human-consumable display name for this artifact. + */ + val displayName: String +} diff --git a/app/src/main/kotlin/org/nixos/gradle2nix/dependency/ComponentIdentifier.kt b/app/src/main/kotlin/org/nixos/gradle2nix/dependency/ComponentIdentifier.kt new file mode 100644 index 0000000..83b0bfc --- /dev/null +++ b/app/src/main/kotlin/org/nixos/gradle2nix/dependency/ComponentIdentifier.kt @@ -0,0 +1,28 @@ +/* + * Copyright 2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.nixos.gradle2nix.dependency + +/** + * An opaque immutable identifier for a component instance. There are various sub-interfaces that expose specific details about the identifier. + */ +interface ComponentIdentifier { + /** + * Returns a human-consumable display name for this identifier. + * + * @return Component identifier display name + */ + val displayName: String +} diff --git a/app/src/main/kotlin/org/nixos/gradle2nix/dependency/ModuleComponentArtifactIdentifier.kt b/app/src/main/kotlin/org/nixos/gradle2nix/dependency/ModuleComponentArtifactIdentifier.kt new file mode 100644 index 0000000..298284a --- /dev/null +++ b/app/src/main/kotlin/org/nixos/gradle2nix/dependency/ModuleComponentArtifactIdentifier.kt @@ -0,0 +1,40 @@ +/* + * Copyright 2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.nixos.gradle2nix.dependency + +/** + * An immutable identifier for an artifact that belongs to some module version. + */ +interface ModuleComponentArtifactIdentifier : ComponentArtifactIdentifier { + /** + * Returns the id of the component that this artifact belongs to. + */ + override val componentIdentifier: ModuleComponentIdentifier + + /** + * Returns a file base name that can be used for this artifact. + */ + val fileName: String +} + +data class DefaultModuleComponentArtifactIdentifier( + override val componentIdentifier: ModuleComponentIdentifier, + override val fileName: String, +) : ModuleComponentArtifactIdentifier { + override val displayName: String get() = "$fileName ($componentIdentifier)" + + override fun toString(): String = displayName +} diff --git a/app/src/main/kotlin/org/nixos/gradle2nix/dependency/ModuleComponentIdentifier.kt b/app/src/main/kotlin/org/nixos/gradle2nix/dependency/ModuleComponentIdentifier.kt new file mode 100644 index 0000000..8be353b --- /dev/null +++ b/app/src/main/kotlin/org/nixos/gradle2nix/dependency/ModuleComponentIdentifier.kt @@ -0,0 +1,76 @@ +/* + * Copyright 2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.nixos.gradle2nix.dependency + +/** + * An identifier for a component instance which is available as a module version. + */ +interface ModuleComponentIdentifier : ComponentIdentifier { + /** + * The module group of the component. + * + * @return Component group + */ + val group: String + + /** + * The module name of the component. + * + * @return Component module + */ + val module: String + + /** + * The module version of the component. + * + * @return Component version + */ + val version: String + + /** + * The module identifier of the component. Returns the same information + * as [group] and [module]. + * + * @return the module identifier + */ + val moduleIdentifier: ModuleIdentifier +} + +data class DefaultModuleComponentIdentifier( + override val moduleIdentifier: ModuleIdentifier, + override val version: String, +) : ModuleComponentIdentifier { + override val group: String + get() = moduleIdentifier.group + + override val module: String + get() = moduleIdentifier.name + + override val displayName: String + get() = "$group:$module:$version" + + override fun toString(): String = displayName +} + + +fun ModuleComponentIdentifier( + group: String, + module: String, + version: String +): ModuleComponentIdentifier = DefaultModuleComponentIdentifier( + DefaultModuleIdentifier(group, module), + version +) diff --git a/app/src/main/kotlin/org/nixos/gradle2nix/dependency/ModuleIdentifier.kt b/app/src/main/kotlin/org/nixos/gradle2nix/dependency/ModuleIdentifier.kt new file mode 100644 index 0000000..5457f3f --- /dev/null +++ b/app/src/main/kotlin/org/nixos/gradle2nix/dependency/ModuleIdentifier.kt @@ -0,0 +1,40 @@ +/* + * Copyright 2012 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.nixos.gradle2nix.dependency + +/** + * The identifier of a module. + */ +interface ModuleIdentifier { + /** + * The group of the module. + * + * @return module group + */ + val group: String + + /** + * The name of the module. + * + * @return module name + */ + val name: String +} + +data class DefaultModuleIdentifier( + override val group: String, + override val name: String, +) : ModuleIdentifier diff --git a/app/src/main/kotlin/org/nixos/gradle2nix/metadata/ArtifactVerificationMetadata.kt b/app/src/main/kotlin/org/nixos/gradle2nix/metadata/ArtifactVerificationMetadata.kt new file mode 100644 index 0000000..ae67531 --- /dev/null +++ b/app/src/main/kotlin/org/nixos/gradle2nix/metadata/ArtifactVerificationMetadata.kt @@ -0,0 +1,6 @@ +package org.nixos.gradle2nix.metadata + +data class ArtifactVerificationMetadata( + val artifactName: String, + val checksums: List +) diff --git a/app/src/main/kotlin/org/nixos/gradle2nix/metadata/Checksum.kt b/app/src/main/kotlin/org/nixos/gradle2nix/metadata/Checksum.kt new file mode 100644 index 0000000..a5c84ef --- /dev/null +++ b/app/src/main/kotlin/org/nixos/gradle2nix/metadata/Checksum.kt @@ -0,0 +1,43 @@ +/* + * Copyright 2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.nixos.gradle2nix.metadata + +/** + * Internal representation of a checksum, aimed at *verification*. + * A checksum consists of a kind (md5, sha1, ...), a value, but also + * provides *alternatives*. Alternatives are checksums which are + * deemed trusted, because sometimes in a single build we can see different + * checksums for the same module, because they are sourced from different + * repositories. + * + * In theory, this shouldn't be allowed. However, it's often the case that + * an artifact, in particular _metadata artifacts_ (POM files, ...) differ + * from one repository to the other (either by end of lines, additional line + * at the end of the file, ...). Because they are different doesn't mean that + * they are compromised, so this is a facility for the user to declare "I know + * I should use a single source of truth but the infrastructure is hard or + * impossible to fix so let's trust this source". + * + * In addition to the list of alternatives, a checksum also provides a source, + * which is documentation to explain where a checksum was found. + */ +data class Checksum( + val kind: ChecksumKind, + val value: String, + val alternatives: Set = emptySet(), + val origin: String? = null, + val reason: String? = null +) diff --git a/app/src/main/kotlin/org/nixos/gradle2nix/metadata/ChecksumKind.kt b/app/src/main/kotlin/org/nixos/gradle2nix/metadata/ChecksumKind.kt new file mode 100644 index 0000000..6469cf7 --- /dev/null +++ b/app/src/main/kotlin/org/nixos/gradle2nix/metadata/ChecksumKind.kt @@ -0,0 +1,30 @@ +/* + * Copyright 2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.nixos.gradle2nix.metadata + +enum class ChecksumKind(val algorithm: String) { + md5("MD5"), + sha1("SHA1"), + sha256("SHA-256"), + sha512("SHA-512"); + + companion object { + private val SORTED_BY_SECURITY: List = listOf(sha512, sha256, sha1, md5) + fun mostSecureFirst(): List { + return SORTED_BY_SECURITY + } + } +} diff --git a/app/src/main/kotlin/org/nixos/gradle2nix/metadata/ComponentVerificationMetadata.kt b/app/src/main/kotlin/org/nixos/gradle2nix/metadata/ComponentVerificationMetadata.kt new file mode 100644 index 0000000..54e454c --- /dev/null +++ b/app/src/main/kotlin/org/nixos/gradle2nix/metadata/ComponentVerificationMetadata.kt @@ -0,0 +1,8 @@ +package org.nixos.gradle2nix.metadata + +import org.nixos.gradle2nix.dependency.ModuleComponentIdentifier + +data class ComponentVerificationMetadata( + val componentId: ModuleComponentIdentifier, + val artifactVerifications: List, +) diff --git a/app/src/main/kotlin/org/nixos/gradle2nix/metadata/DependencyVerificationConfiguration.kt b/app/src/main/kotlin/org/nixos/gradle2nix/metadata/DependencyVerificationConfiguration.kt new file mode 100644 index 0000000..8bfbf18 --- /dev/null +++ b/app/src/main/kotlin/org/nixos/gradle2nix/metadata/DependencyVerificationConfiguration.kt @@ -0,0 +1,98 @@ +/* + * Copyright 2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.nixos.gradle2nix.metadata + +import org.nixos.gradle2nix.dependency.ModuleComponentArtifactIdentifier +import org.nixos.gradle2nix.dependency.ModuleComponentIdentifier + +class DependencyVerificationConfiguration( + val trustedArtifacts: List = emptyList(), +) { + data class TrustCoordinates internal constructor( + val group: String?, + val name: String?, + val version: String?, + val fileName: String?, + val isRegex: Boolean, + val reason: String? + ) : Comparable { + + fun matches(id: ModuleComponentArtifactIdentifier): Boolean { + val moduleComponentIdentifier: ModuleComponentIdentifier = id.componentIdentifier + return (matches(group, moduleComponentIdentifier.group) + && matches(name, moduleComponentIdentifier.module) + && matches(version, moduleComponentIdentifier.version) + && matches(fileName, id.fileName)) + } + + private fun matches(value: String?, expr: String): Boolean { + if (value == null) { + return true + } + return if (!isRegex) { + expr == value + } else expr.matches(value.toRegex()) + } + + override fun compareTo(other: TrustCoordinates): Int { + val regexComparison = isRegex.compareTo(other.isRegex) + if (regexComparison != 0) { + return regexComparison + } + val groupComparison = compareNullableStrings( + group, other.group + ) + if (groupComparison != 0) { + return groupComparison + } + val nameComparison = compareNullableStrings( + name, other.name + ) + if (nameComparison != 0) { + return nameComparison + } + val versionComparison = compareNullableStrings( + version, other.version + ) + if (versionComparison != 0) { + return versionComparison + } + val fileNameComparison = compareNullableStrings( + fileName, other.fileName + ) + return if (fileNameComparison != 0) { + fileNameComparison + } else compareNullableStrings( + reason, other.reason + ) + } + } + + companion object { + private fun compareNullableStrings(first: String?, second: String?): Int { + if (first == null) { + return if (second == null) { + 0 + } else { + -1 + } + } else if (second == null) { + return 1 + } + return first.compareTo(second) + } + } +} diff --git a/app/src/main/kotlin/org/nixos/gradle2nix/metadata/DependencyVerificationXmlTags.kt b/app/src/main/kotlin/org/nixos/gradle2nix/metadata/DependencyVerificationXmlTags.kt new file mode 100644 index 0000000..b6e019a --- /dev/null +++ b/app/src/main/kotlin/org/nixos/gradle2nix/metadata/DependencyVerificationXmlTags.kt @@ -0,0 +1,48 @@ +/* + * Copyright 2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.nixos.gradle2nix.metadata + +internal object DependencyVerificationXmlTags { + const val ALSO_TRUST = "also-trust" + const val ARTIFACT = "artifact" + const val COMPONENT = "component" + const val COMPONENTS = "components" + const val CONFIG = "configuration" + const val ENABLED = "enabled" + const val FILE = "file" + const val GROUP = "group" + const val ID = "id" + const val IGNORED_KEY = "ignored-key" + const val IGNORED_KEYS = "ignored-keys" + const val KEY_SERVER = "key-server" + const val KEY_SERVERS = "key-servers" + const val NAME = "name" + const val ORIGIN = "origin" + const val PGP = "pgp" + const val REASON = "reason" + const val REGEX = "regex" + const val TRUST = "trust" + const val TRUSTED_ARTIFACTS = "trusted-artifacts" + const val TRUSTED_KEY = "trusted-key" + const val TRUSTED_KEYS = "trusted-keys" + const val TRUSTING = "trusting" + const val URI = "uri" + const val VALUE = "value" + const val VERIFICATION_METADATA = "verification-metadata" + const val VERIFY_METADATA = "verify-metadata" + const val VERIFY_SIGNATURES = "verify-signatures" + const val VERSION = "version" +} diff --git a/app/src/main/kotlin/org/nixos/gradle2nix/metadata/DependencyVerificationsXmlReader.kt b/app/src/main/kotlin/org/nixos/gradle2nix/metadata/DependencyVerificationsXmlReader.kt new file mode 100644 index 0000000..2976931 --- /dev/null +++ b/app/src/main/kotlin/org/nixos/gradle2nix/metadata/DependencyVerificationsXmlReader.kt @@ -0,0 +1,421 @@ +/* + * Copyright 2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.nixos.gradle2nix.metadata + +import java.io.IOException +import java.io.InputStream +import javax.xml.parsers.ParserConfigurationException +import javax.xml.parsers.SAXParser +import javax.xml.parsers.SAXParserFactory +import org.gradle.internal.UncheckedException +import org.nixos.gradle2nix.dependency.DefaultModuleComponentArtifactIdentifier +import org.nixos.gradle2nix.dependency.DefaultModuleComponentIdentifier +import org.nixos.gradle2nix.dependency.DefaultModuleIdentifier +import org.nixos.gradle2nix.dependency.ModuleComponentArtifactIdentifier +import org.nixos.gradle2nix.dependency.ModuleComponentIdentifier +import org.nixos.gradle2nix.dependency.ModuleIdentifier +import org.nixos.gradle2nix.metadata.DependencyVerificationXmlTags.ALSO_TRUST +import org.nixos.gradle2nix.metadata.DependencyVerificationXmlTags.ARTIFACT +import org.nixos.gradle2nix.metadata.DependencyVerificationXmlTags.COMPONENT +import org.nixos.gradle2nix.metadata.DependencyVerificationXmlTags.COMPONENTS +import org.nixos.gradle2nix.metadata.DependencyVerificationXmlTags.CONFIG +import org.nixos.gradle2nix.metadata.DependencyVerificationXmlTags.IGNORED_KEY +import org.nixos.gradle2nix.metadata.DependencyVerificationXmlTags.IGNORED_KEYS +import org.nixos.gradle2nix.metadata.DependencyVerificationXmlTags.KEY_SERVER +import org.nixos.gradle2nix.metadata.DependencyVerificationXmlTags.KEY_SERVERS +import org.nixos.gradle2nix.metadata.DependencyVerificationXmlTags.PGP +import org.nixos.gradle2nix.metadata.DependencyVerificationXmlTags.TRUST +import org.nixos.gradle2nix.metadata.DependencyVerificationXmlTags.TRUSTED_ARTIFACTS +import org.nixos.gradle2nix.metadata.DependencyVerificationXmlTags.TRUSTED_KEY +import org.nixos.gradle2nix.metadata.DependencyVerificationXmlTags.TRUSTED_KEYS +import org.nixos.gradle2nix.metadata.DependencyVerificationXmlTags.TRUSTING +import org.nixos.gradle2nix.metadata.DependencyVerificationXmlTags.VALUE +import org.nixos.gradle2nix.metadata.DependencyVerificationXmlTags.VERIFICATION_METADATA +import org.nixos.gradle2nix.metadata.DependencyVerificationXmlTags.VERIFY_METADATA +import org.nixos.gradle2nix.metadata.DependencyVerificationXmlTags.VERIFY_SIGNATURES +import org.xml.sax.Attributes +import org.xml.sax.InputSource +import org.xml.sax.SAXException +import org.xml.sax.ext.DefaultHandler2 + +object DependencyVerificationsXmlReader { + fun readFromXml( + input: InputStream, + builder: DependencyVerifierBuilder + ) { + try { + val saxParser = createSecureParser() + val xmlReader = saxParser.xmlReader + val handler = VerifiersHandler(builder) + xmlReader.setProperty("http://xml.org/sax/properties/lexical-handler", handler) + xmlReader.contentHandler = handler + xmlReader.parse(InputSource(input)) + } catch (e: Exception) { + throw IllegalStateException( + "Unable to read dependency verification metadata", + e + ) + } finally { + try { + input.close() + } catch (e: IOException) { + throw UncheckedException.throwAsUncheckedException(e) + } + } + } + + fun readFromXml(input: InputStream): DependencyVerifier { + val builder = DependencyVerifierBuilder() + readFromXml(input, builder) + return builder.build() + } + + @Throws(ParserConfigurationException::class, SAXException::class) + private fun createSecureParser(): SAXParser { + val spf = SAXParserFactory.newInstance() + spf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false) + spf.setFeature("http://xml.org/sax/features/namespaces", false) + spf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true) + return spf.newSAXParser() + } + + private class VerifiersHandler(private val builder: DependencyVerifierBuilder) : DefaultHandler2() { + private var inMetadata = false + private var inComponents = false + private var inConfiguration = false + private var inVerifyMetadata = false + private var inVerifySignatures = false + private var inTrustedArtifacts = false + private var inKeyServers = false + private var inIgnoredKeys = false + private var inTrustedKeys = false + private var inTrustedKey = false + private var currentTrustedKey: String? = null + private var currentComponent: ModuleComponentIdentifier? = null + private var currentArtifact: ModuleComponentArtifactIdentifier? = + null + private var currentChecksum: ChecksumKind? = null + + override fun startElement(uri: String, localName: String, qName: String, attributes: Attributes) { + when (qName) { + CONFIG -> inConfiguration = + true + + VERIFICATION_METADATA -> inMetadata = + true + + COMPONENTS -> { + assertInMetadata() + inComponents = true + } + + COMPONENT -> { + assertInComponents() + currentComponent = createComponentId(attributes) + } + + ARTIFACT -> { + assertValidComponent() + currentArtifact = createArtifactId(attributes) + } + + VERIFY_METADATA -> { + assertInConfiguration(VERIFY_METADATA) + inVerifyMetadata = true + } + + VERIFY_SIGNATURES -> { + assertInConfiguration(VERIFY_SIGNATURES) + inVerifySignatures = true + } + + TRUSTED_ARTIFACTS -> { + assertInConfiguration(TRUSTED_ARTIFACTS) + inTrustedArtifacts = true + } + + TRUSTED_KEY -> { + assertContext( + inTrustedKeys, + TRUSTED_KEY, + TRUSTED_KEYS + ) + inTrustedKey = true + } + + TRUSTED_KEYS -> { + assertInConfiguration(TRUSTED_KEYS) + inTrustedKeys = true + } + + TRUST -> { + assertInTrustedArtifacts() + addTrustedArtifact(attributes) + } + + TRUSTING -> { + assertContext( + inTrustedKey, + TRUSTING, + TRUSTED_KEY + ) + } + + KEY_SERVERS -> { + assertInConfiguration(KEY_SERVERS) + inKeyServers = true + } + + KEY_SERVER -> { + assertContext( + inKeyServers, + KEY_SERVER, + KEY_SERVERS + ) + } + + IGNORED_KEYS -> { + if (currentArtifact == null) { + assertInConfiguration(IGNORED_KEYS) + } + inIgnoredKeys = true + } + + IGNORED_KEY -> { + assertContext( + inIgnoredKeys, + IGNORED_KEY, + IGNORED_KEYS + ) + } + + else -> if (currentChecksum != null && ALSO_TRUST == qName) { + builder.addChecksum( + currentArtifact!!, + currentChecksum!!, + getAttribute(attributes, VALUE), + null, + null + ) + } else if (currentArtifact != null) { + if (PGP != qName) { + currentChecksum = enumValueOf(qName) + builder.addChecksum( + currentArtifact!!, + currentChecksum!!, + getAttribute( + attributes, + VALUE + ), + getNullableAttribute( + attributes, + DependencyVerificationXmlTags.ORIGIN + ), + getNullableAttribute( + attributes, + DependencyVerificationXmlTags.REASON + ) + ) + } + } + } + } + + private fun assertInTrustedArtifacts() { + assertContext( + inTrustedArtifacts, + TRUST, + TRUSTED_ARTIFACTS + ) + } + + private fun addTrustedArtifact(attributes: Attributes) { + var regex = false + val regexAttr = getNullableAttribute( + attributes, + DependencyVerificationXmlTags.REGEX + ) + if (regexAttr != null) { + regex = regexAttr.toBoolean() + } + builder.addTrustedArtifact( + getNullableAttribute( + attributes, + DependencyVerificationXmlTags.GROUP + ), + getNullableAttribute( + attributes, + DependencyVerificationXmlTags.NAME + ), + getNullableAttribute( + attributes, + DependencyVerificationXmlTags.VERSION + ), + getNullableAttribute( + attributes, + DependencyVerificationXmlTags.FILE + ), + regex, + getNullableAttribute( + attributes, + DependencyVerificationXmlTags.REASON + ) + ) + } + + private fun readBoolean(ch: CharArray, start: Int, length: Int): Boolean { + return String(ch, start, length).toBoolean() + } + + private fun assertInConfiguration(tag: String) { + assertContext( + inConfiguration, + tag, + DependencyVerificationXmlTags.CONFIG + ) + } + + private fun assertInComponents() { + assertContext( + inComponents, + DependencyVerificationXmlTags.COMPONENT, + DependencyVerificationXmlTags.COMPONENTS + ) + } + + private fun assertInMetadata() { + assertContext( + inMetadata, + DependencyVerificationXmlTags.COMPONENTS, + DependencyVerificationXmlTags.VERIFICATION_METADATA + ) + } + + private fun assertValidComponent() { + assertContext( + currentComponent != null, + ARTIFACT, + DependencyVerificationXmlTags.COMPONENT + ) + } + + override fun endElement(uri: String, localName: String, qName: String) { + when (qName) { + DependencyVerificationXmlTags.CONFIG -> inConfiguration = + false + + VERIFY_METADATA -> inVerifyMetadata = + false + + VERIFY_SIGNATURES -> inVerifySignatures = + false + + DependencyVerificationXmlTags.VERIFICATION_METADATA -> inMetadata = + false + + DependencyVerificationXmlTags.COMPONENTS -> inComponents = + false + + DependencyVerificationXmlTags.COMPONENT -> currentComponent = + null + + TRUSTED_ARTIFACTS -> inTrustedArtifacts = + false + + TRUSTED_KEYS -> inTrustedKeys = + false + + TRUSTED_KEY -> { + inTrustedKey = false + currentTrustedKey = null + } + + KEY_SERVERS -> inKeyServers = + false + + ARTIFACT -> { + currentArtifact = null + currentChecksum = null + } + + IGNORED_KEYS -> inIgnoredKeys = + false + } + } + + private fun createArtifactId(attributes: Attributes): ModuleComponentArtifactIdentifier { + return DefaultModuleComponentArtifactIdentifier( + currentComponent!!, + getAttribute( + attributes, + DependencyVerificationXmlTags.NAME + ) + ) + } + + private fun createComponentId(attributes: Attributes): ModuleComponentIdentifier { + return DefaultModuleComponentIdentifier( + createModuleId(attributes), + getAttribute( + attributes, + DependencyVerificationXmlTags.VERSION + ) + ) + } + + private fun createModuleId(attributes: Attributes): ModuleIdentifier { + return DefaultModuleIdentifier( + getAttribute( + attributes, + DependencyVerificationXmlTags.GROUP + ), + getAttribute( + attributes, + DependencyVerificationXmlTags.NAME + ) + ) + } + + private fun getAttribute(attributes: Attributes, name: String): String { + val value = attributes.getValue(name) + assertContext( + value != null, + "Missing attribute: $name" + ) + return value.intern() + } + + private fun getNullableAttribute(attributes: Attributes, name: String): String? { + val value = attributes.getValue(name) ?: return null + return value.intern() + } + + companion object { + private fun assertContext(test: Boolean, innerTag: String, outerTag: String) { + assertContext( + test, + "<$innerTag> must be found under the <$outerTag> tag" + ) + } + + private fun assertContext(test: Boolean, message: String) { + if (!test) { + throw IllegalStateException("Invalid dependency verification metadata file: $message") + } + } + } + } +} diff --git a/app/src/main/kotlin/org/nixos/gradle2nix/metadata/DependencyVerifier.kt b/app/src/main/kotlin/org/nixos/gradle2nix/metadata/DependencyVerifier.kt new file mode 100644 index 0000000..d2639c0 --- /dev/null +++ b/app/src/main/kotlin/org/nixos/gradle2nix/metadata/DependencyVerifier.kt @@ -0,0 +1,23 @@ +/* + * Copyright 2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.nixos.gradle2nix.metadata + +import org.nixos.gradle2nix.dependency.ModuleComponentIdentifier + +class DependencyVerifier internal constructor( + val verificationMetadata: Map, + val configuration: DependencyVerificationConfiguration, +) diff --git a/app/src/main/kotlin/org/nixos/gradle2nix/metadata/DependencyVerifierBuilder.kt b/app/src/main/kotlin/org/nixos/gradle2nix/metadata/DependencyVerifierBuilder.kt new file mode 100644 index 0000000..2eeaa58 --- /dev/null +++ b/app/src/main/kotlin/org/nixos/gradle2nix/metadata/DependencyVerifierBuilder.kt @@ -0,0 +1,162 @@ +/* + * Copyright 2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.nixos.gradle2nix.metadata + +import org.nixos.gradle2nix.dependency.ModuleComponentArtifactIdentifier +import org.nixos.gradle2nix.dependency.ModuleComponentIdentifier + +class DependencyVerifierBuilder { + private val byComponent: MutableMap = mutableMapOf() + private val trustedArtifacts: MutableList = mutableListOf() + + fun addChecksum( + artifact: ModuleComponentArtifactIdentifier, + kind: ChecksumKind, + value: String, + origin: String?, + reason: String? + ) { + val componentIdentifier: ModuleComponentIdentifier = artifact.componentIdentifier + byComponent.getOrPut(componentIdentifier) { + ComponentVerificationsBuilder(componentIdentifier) + }.addChecksum(artifact, kind, value, origin, reason) + } + + @JvmOverloads + fun addTrustedArtifact( + group: String?, + name: String?, + version: String?, + fileName: String?, + regex: Boolean, + reason: String? = null + ) { + validateUserInput(group, name, version, fileName) + trustedArtifacts.add(DependencyVerificationConfiguration.TrustCoordinates(group, name, version, fileName, regex, reason)) + } + + private fun validateUserInput( + group: String?, + name: String?, + version: String?, + fileName: String? + ) { + // because this can be called from parsing XML, we need to perform additional verification + if (group == null && name == null && version == null && fileName == null) { + throw IllegalStateException("A trusted artifact must have at least one of group, name, version or file name not null") + } + } + + fun build(): DependencyVerifier { + return DependencyVerifier( + byComponent + .toSortedMap( + compareBy { it.group } + .thenBy { it.module } + .thenBy { it.version } + ) + .mapValues { it.value.build() }, + DependencyVerificationConfiguration(trustedArtifacts), + ) + } + + private class ComponentVerificationsBuilder(private val component: ModuleComponentIdentifier) { + private val byArtifact: MutableMap = mutableMapOf() + + fun addChecksum( + artifact: ModuleComponentArtifactIdentifier, + kind: ChecksumKind, + value: String, + origin: String?, + reason: String? + ) { + byArtifact.computeIfAbsent(artifact.fileName) { ArtifactVerificationBuilder() } + .addChecksum(kind, value, origin, reason) + } + + fun build(): ComponentVerificationMetadata { + return ComponentVerificationMetadata( + component, + byArtifact + .map { ArtifactVerificationMetadata(it.key, it.value.buildChecksums()) } + .sortedBy { it.artifactName } + ) + } + } + + protected class ArtifactVerificationBuilder { + private val builder: MutableMap = mutableMapOf() + + fun addChecksum(kind: ChecksumKind, value: String, origin: String?, reason: String?) { + val builder = builder.getOrPut(kind) { + ChecksumBuilder(kind) + } + builder.addChecksum(value) + if (origin != null) { + builder.withOrigin(origin) + } + if (reason != null) { + builder.withReason(reason) + } + } + + fun buildChecksums(): List { + return builder.values + .map(ChecksumBuilder::build) + .sortedBy { it.kind } + } + } + + private class ChecksumBuilder(private val kind: ChecksumKind) { + private var value: String? = null + private var origin: String? = null + private var reason: String? = null + private var alternatives: MutableSet = mutableSetOf() + + /** + * Sets the origin, if not set already. This is + * mostly used for automatic generation of checksums + */ + fun withOrigin(origin: String?) { + this.origin = this.origin ?: origin + } + + /** + * Sets the reason, if not set already. + */ + fun withReason(reason: String?) { + this.reason = this.reason ?: reason + } + + fun addChecksum(checksum: String) { + if (value == null) { + value = checksum + } else if (value != checksum) { + alternatives.add(checksum) + } + } + + fun build(): Checksum { + return Checksum( + kind, + checkNotNull(value) { "Checksum is null" }, + alternatives, + origin, + reason + ) + } + } +} diff --git a/app/src/test/kotlin/org/nixos/gradle2nix/BuildSrcTest.kt b/app/src/test/kotlin/org/nixos/gradle2nix/BuildSrcTest.kt deleted file mode 100644 index cd82130..0000000 --- a/app/src/test/kotlin/org/nixos/gradle2nix/BuildSrcTest.kt +++ /dev/null @@ -1,19 +0,0 @@ -package org.nixos.gradle2nix - -import org.spekframework.spek2.Spek -import org.spekframework.spek2.style.specification.describe -import strikt.api.expectThat -import strikt.assertions.containsKey - -object BuildSrcTest : Spek({ - fixture("buildsrc/plugin-in-buildsrc/kotlin") - val fixture: Fixture by memoized() - - describe("project with plugin in buildSrc") { - fixture.run() - - it("should include buildSrc in gradle env", timeout = 0) { - expectThat(fixture.env()).containsKey("buildSrc") - } - } -}) \ No newline at end of file diff --git a/app/src/test/kotlin/org/nixos/gradle2nix/GoldenTest.kt b/app/src/test/kotlin/org/nixos/gradle2nix/GoldenTest.kt new file mode 100644 index 0000000..7beaf76 --- /dev/null +++ b/app/src/test/kotlin/org/nixos/gradle2nix/GoldenTest.kt @@ -0,0 +1,40 @@ +package org.nixos.gradle2nix + +import io.kotest.core.spec.style.FunSpec + +class GoldenTest : FunSpec({ + context("basic") { + golden("basic/basic-java-project") + golden("basic/basic-kotlin-project") + } + context("buildsrc") { + golden("buildsrc/plugin-in-buildsrc") + } + context("dependency") { + golden("dependency/classifier") + golden("dependency/maven-bom") + golden("dependency/snapshot") + golden("dependency/snapshot-dynamic") + golden("dependency/snapshot-redirect") + } + context("integration") { + golden("integration/settings-buildscript") + } + context("ivy") { + golden("ivy/basic") + } + context("plugin") { + golden("plugin/resolves-from-default-repo") + } + context("s3") { + golden("s3/maven") + golden("s3/maven-snapshot") + } + context("settings") { + golden("settings/buildscript") + golden("settings/dependency-resolution-management") + } + context("subprojects") { + golden("subprojects/multi-module") + } +}) diff --git a/app/src/test/kotlin/org/nixos/gradle2nix/TestUtil.kt b/app/src/test/kotlin/org/nixos/gradle2nix/TestUtil.kt index 99d620c..49b6b07 100644 --- a/app/src/test/kotlin/org/nixos/gradle2nix/TestUtil.kt +++ b/app/src/test/kotlin/org/nixos/gradle2nix/TestUtil.kt @@ -1,60 +1,126 @@ package org.nixos.gradle2nix -import com.squareup.moshi.Moshi -import com.squareup.moshi.Types -import okio.buffer -import okio.source -import org.spekframework.spek2.dsl.Root -import strikt.api.expectThat -import strikt.assertions.exists -import strikt.assertions.isNotNull -import strikt.assertions.toPath +import io.kotest.assertions.fail +import io.kotest.common.ExperimentalKotest +import io.kotest.common.KotestInternal +import io.kotest.core.names.TestName +import io.kotest.core.source.sourceRef +import io.kotest.core.spec.style.scopes.ContainerScope +import io.kotest.core.spec.style.scopes.RootScope +import io.kotest.core.test.NestedTest +import io.kotest.core.test.TestScope +import io.kotest.core.test.TestType +import io.kotest.extensions.system.withEnvironment +import io.kotest.matchers.equals.beEqual +import io.kotest.matchers.equals.shouldBeEqual +import io.kotest.matchers.file.shouldBeAFile +import io.kotest.matchers.paths.shouldBeAFile +import io.kotest.matchers.should +import java.io.File +import java.io.FileFilter import java.nio.file.Files import java.nio.file.Path import java.nio.file.Paths -import kotlin.io.path.ExperimentalPathApi import kotlin.io.path.createTempDirectory +import kotlin.io.path.inputStream +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.SerializationException +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.decodeFromStream +import kotlinx.serialization.json.encodeToStream +import okio.use -private val moshi = Moshi.Builder().build() +private val app = Gradle2Nix() -class Fixture(val project: Path) { - private val app = Main() +@OptIn(ExperimentalSerializationApi::class) +private val json = Json { + prettyPrint = true + prettyPrintIndent = " " +} - fun run(vararg args: String) { - app.main(args.toList() + project.toString()) +@OptIn(ExperimentalKotest::class, ExperimentalSerializationApi::class, KotestInternal::class) +suspend fun TestScope.fixture( + project: String, + vararg args: String, + test: suspend TestScope.(Map>) -> Unit +) { + val tmp = Paths.get("build/tmp/gradle2nix").apply { toFile().mkdirs() } + val baseDir = Paths.get("../fixtures", project).toFile() + val children = baseDir.listFiles(FileFilter { it.isDirectory && (it.name == "groovy" || it.name == "kotlin") }) + ?.toList() + val cases = if (children.isNullOrEmpty()) { + listOf(project to baseDir) + } else { + children.map { "$project.${it.name}" to it } } + for (case in cases) { + registerTestCase( + NestedTest( + name = TestName(case.first), + disabled = false, + config = null, + type = TestType.Dynamic, + source = sourceRef() + ) { + var dirName = case.second.toString().replace("/", ".") + while (dirName.startsWith(".")) dirName = dirName.removePrefix(".") + while (dirName.endsWith(".")) dirName = dirName.removeSuffix(".") - fun env(): Map { - val file = (app.outDir ?: project.toFile()).resolve("${app.envFile}.json") - expectThat(file).toPath().exists() - val env = file.source().buffer().use { source -> - moshi - .adapter>( - Types.newParameterizedType(Map::class.java, String::class.java, NixGradleEnv::class.java) - ).fromJson(source) - } - expectThat(env).isNotNull() - return env!! + val tempDir = File(tmp.toFile(), dirName) + tempDir.deleteRecursively() + case.second.copyRecursively(tempDir) + + if (!tempDir.resolve("settings.gradle").exists() && !tempDir.resolve("settings.gradle.kts").exists()) { + Files.createFile(tempDir.resolve("settings.gradle").toPath()) + } + app.main(listOf("-d", tempDir.toString()) + args.withM2()) + val file = tempDir.resolve("${app.envFile}.json") + file.shouldBeAFile() + val env: Map> = file.inputStream().buffered().use { input -> + Json.decodeFromStream(input) + } + test(env) + } + ) } } -@OptIn(ExperimentalPathApi::class) -fun Root.fixture(name: String) { - val fixture by memoized( - factory = { - val url = checkNotNull(Thread.currentThread().contextClassLoader.getResource(name)?.toURI()) { - "$name: No test fixture found" +val updateGolden = System.getProperty("org.nixos.gradle2nix.update-golden") != null + +@OptIn(ExperimentalSerializationApi::class) +suspend fun TestScope.golden( + project: String, + vararg args: String, +) { + fixture(project, *args) { env -> + val filename = "${testCase.name.testName}.json" + val goldenFile = File("../fixtures/golden/$filename") + if (updateGolden) { + goldenFile.parentFile.mkdirs() + goldenFile.outputStream().buffered().use { output -> + json.encodeToStream(env, output) } - val fixtureRoot = Paths.get(url) - val dest = createTempDirectory("gradle2nix") - val src = checkNotNull(fixtureRoot.takeIf { Files.exists(it) }) { - "$name: Test fixture not found: $fixtureRoot" + } else { + if (!goldenFile.exists()) { + fail("Golden file '$filename' doesn't exist. Run with --update-golden to generate.") } - src.toFile().copyRecursively(dest.toFile()) - Fixture(dest) - }, - destructor = { - it.project.toFile().deleteRecursively() + val goldenData: Map> = try { + goldenFile.inputStream().buffered().use { input -> + json.decodeFromStream(input) + } + } catch (e: SerializationException) { + fail("Failed to load golden data from '$filename'. Run with --update-golden to regenerate.") + } + env should beEqual(goldenData) } - ) -} \ No newline at end of file + } +} + +val m2 = System.getProperty("org.nixos.gradle2nix.m2") + +private fun Array.withM2(): List { + val args = toMutableList() + if (args.indexOf("--") < 0) args.add("--") + args.add("-Dorg.nixos.gradle2nix.m2=$m2") + return args +} diff --git a/assets/gradle2nix.svg b/assets/gradle2nix.svg index 0569dea..4a642c4 100644 --- a/assets/gradle2nix.svg +++ b/assets/gradle2nix.svg @@ -2,21 +2,20 @@ + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="1280" + height="640" + viewBox="0 0 338.66666 169.33334" + version="1.1" + id="svg8" + inkscape:version="0.92.4 (5da689c313, 2019-01-14)" + sodipodi:docname="gradle2nix.svg"> { - this@allprojects.withConvention(JavaPluginConvention::class) { - sourceSets.all { - configurations { - named(compileClasspathConfigurationName) { - resolutionStrategy.activateDependencyLocking() - } - named(runtimeClasspathConfigurationName) { - resolutionStrategy.activateDependencyLocking() - } - } - } - - tasks.register("lock") { - doFirst { - assert(gradle.startParameter.isWriteDependencyLocks) - file("buildscript-gradle.lockfile").delete() - file("gradle.lockfile").delete() - } - doLast { - configurations.matching { it.isCanBeResolved }.all { resolve() } - } - } - } - } -} - tasks { wrapper { - gradleVersion = "6.8.1" + gradleVersion = "8.3" distributionType = Wrapper.DistributionType.ALL } } diff --git a/fixtures/basic/basic-java-project/groovy/build.gradle b/fixtures/basic/basic-java-project/groovy/build.gradle index e0f793a..b3fef8a 100644 --- a/fixtures/basic/basic-java-project/groovy/build.gradle +++ b/fixtures/basic/basic-java-project/groovy/build.gradle @@ -3,7 +3,6 @@ plugins { } repositories { - jcenter() mavenCentral() } diff --git a/fixtures/subprojects/dependent-subprojects/groovy/child-a/build.gradle b/fixtures/basic/basic-java-project/groovy/settings.gradle similarity index 100% rename from fixtures/subprojects/dependent-subprojects/groovy/child-a/build.gradle rename to fixtures/basic/basic-java-project/groovy/settings.gradle diff --git a/fixtures/basic/basic-java-project/kotlin/build.gradle.kts b/fixtures/basic/basic-java-project/kotlin/build.gradle.kts index 59670f1..02b6753 100644 --- a/fixtures/basic/basic-java-project/kotlin/build.gradle.kts +++ b/fixtures/basic/basic-java-project/kotlin/build.gradle.kts @@ -3,7 +3,6 @@ plugins { } repositories { - jcenter() mavenCentral() } diff --git a/fixtures/subprojects/dependent-subprojects/groovy/child-b/build.gradle b/fixtures/basic/basic-java-project/kotlin/settings.gradle.kts similarity index 100% rename from fixtures/subprojects/dependent-subprojects/groovy/child-b/build.gradle rename to fixtures/basic/basic-java-project/kotlin/settings.gradle.kts diff --git a/fixtures/basic/basic-kotlin-project/kotlin/build.gradle.kts b/fixtures/basic/basic-kotlin-project/kotlin/build.gradle.kts index c776334..d47e7c4 100644 --- a/fixtures/basic/basic-kotlin-project/kotlin/build.gradle.kts +++ b/fixtures/basic/basic-kotlin-project/kotlin/build.gradle.kts @@ -1,30 +1,30 @@ - val kotlinVersion = "1.3.61" - val spekVersion = "2.0.9" +val kotlinVersion = "1.6.21" +val spekVersion = "2.0.9" - plugins { - application - kotlin("jvm") version "1.3.61" - } +plugins { + application + kotlin("jvm") version "1.6.21" +} - dependencies { - compile(kotlin("stdlib")) - implementation("com.natpryce:konfig:1.6.10.0") - implementation("com.github.pengrad:java-telegram-bot-api:4.6.0") - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.3") - implementation("org.jetbrains.exposed:exposed-core:0.21.1") - implementation("org.jetbrains.exposed", "exposed-dao", "0.21.1") - implementation("org.jetbrains.exposed", "exposed-jdbc", "0.21.1") - implementation("org.jetbrains.exposed", "exposed-jodatime", "0.21.1") - implementation("io.javalin:javalin:3.7.0") - implementation("org.slf4j:slf4j-simple:1.8.0-beta4") - implementation(group = "org.xerial", name = "sqlite-jdbc", version = "3.30.1") - implementation("org.postgresql:postgresql:42.2.2") - implementation("com.fasterxml.jackson.core:jackson-databind:2.10.1") - testImplementation("org.spekframework.spek2:spek-dsl-jvm:$spekVersion") - testRuntimeOnly("org.spekframework.spek2:spek-runner-junit5:$spekVersion") - testCompile("com.winterbe:expekt:0.5.0") - } +dependencies { + compileOnly(kotlin("stdlib")) + implementation("com.natpryce:konfig:1.6.10.0") + implementation("com.github.pengrad:java-telegram-bot-api:4.6.0") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.3") + implementation("org.jetbrains.exposed:exposed-core:0.21.1") + implementation("org.jetbrains.exposed", "exposed-dao", "0.21.1") + implementation("org.jetbrains.exposed", "exposed-jdbc", "0.21.1") + implementation("org.jetbrains.exposed", "exposed-jodatime", "0.21.1") + implementation("io.javalin:javalin:3.7.0") + implementation("org.slf4j:slf4j-simple:1.8.0-beta4") + implementation(group = "org.xerial", name = "sqlite-jdbc", version = "3.30.1") + implementation("org.postgresql:postgresql:42.2.2") + implementation("com.fasterxml.jackson.core:jackson-databind:2.10.1") + testImplementation("org.spekframework.spek2:spek-dsl-jvm:$spekVersion") + testRuntimeOnly("org.spekframework.spek2:spek-runner-junit5:$spekVersion") + testCompileOnly("com.winterbe:expekt:0.5.0") +} - repositories { - jcenter() - } +repositories { + mavenCentral() +} diff --git a/fixtures/subprojects/dependent-subprojects/groovy/child-c/build.gradle b/fixtures/basic/basic-kotlin-project/kotlin/settings.gradle.kts similarity index 100% rename from fixtures/subprojects/dependent-subprojects/groovy/child-c/build.gradle rename to fixtures/basic/basic-kotlin-project/kotlin/settings.gradle.kts diff --git a/fixtures/buildsrc/plugin-in-buildsrc/kotlin/build.gradle.kts b/fixtures/buildsrc/plugin-in-buildsrc/kotlin/build.gradle.kts index 62f05d4..c9641cd 100644 --- a/fixtures/buildsrc/plugin-in-buildsrc/kotlin/build.gradle.kts +++ b/fixtures/buildsrc/plugin-in-buildsrc/kotlin/build.gradle.kts @@ -1,4 +1,4 @@ plugins { `java-library` - id("com.example.custom-spotless") -} \ No newline at end of file + id("com.example.apply-plugin-publish") +} diff --git a/fixtures/buildsrc/plugin-in-buildsrc/kotlin/buildSrc/build.gradle.kts b/fixtures/buildsrc/plugin-in-buildsrc/kotlin/buildSrc/build.gradle.kts index cbd39da..a77804b 100644 --- a/fixtures/buildsrc/plugin-in-buildsrc/kotlin/buildSrc/build.gradle.kts +++ b/fixtures/buildsrc/plugin-in-buildsrc/kotlin/buildSrc/build.gradle.kts @@ -3,10 +3,9 @@ plugins { } repositories { - jcenter() gradlePluginPortal() } dependencies { - implementation("com.diffplug.spotless:spotless-plugin-gradle:3.28.1") + implementation("com.gradle.publish:plugin-publish-plugin:1.2.1") } diff --git a/fixtures/buildsrc/plugin-in-buildsrc/kotlin/buildSrc/src/main/kotlin/com.example/apply-plugin-publish.gradle.kts b/fixtures/buildsrc/plugin-in-buildsrc/kotlin/buildSrc/src/main/kotlin/com.example/apply-plugin-publish.gradle.kts new file mode 100644 index 0000000..7c0993e --- /dev/null +++ b/fixtures/buildsrc/plugin-in-buildsrc/kotlin/buildSrc/src/main/kotlin/com.example/apply-plugin-publish.gradle.kts @@ -0,0 +1,5 @@ +package com.example + +plugins { + id("com.gradle.plugin-publish") +} diff --git a/fixtures/buildsrc/plugin-in-buildsrc/kotlin/buildSrc/src/main/kotlin/com.example/custom-spotless.gradle.kts b/fixtures/buildsrc/plugin-in-buildsrc/kotlin/buildSrc/src/main/kotlin/com.example/custom-spotless.gradle.kts deleted file mode 100644 index 518ad01..0000000 --- a/fixtures/buildsrc/plugin-in-buildsrc/kotlin/buildSrc/src/main/kotlin/com.example/custom-spotless.gradle.kts +++ /dev/null @@ -1,11 +0,0 @@ -package com.example - -plugins { - com.diffplug.gradle.spotless -} - -spotless { - kotlin { - ktlint() - } -} \ No newline at end of file diff --git a/fixtures/subprojects/dependent-subprojects/groovy/child-d/build.gradle b/fixtures/dependency/classifier/groovy/settings.gradle similarity index 100% rename from fixtures/subprojects/dependent-subprojects/groovy/child-d/build.gradle rename to fixtures/dependency/classifier/groovy/settings.gradle diff --git a/fixtures/dependency/classifier/kotlin/build.gradle.kts b/fixtures/dependency/classifier/kotlin/build.gradle.kts index e195487..524aa43 100644 --- a/fixtures/dependency/classifier/kotlin/build.gradle.kts +++ b/fixtures/dependency/classifier/kotlin/build.gradle.kts @@ -8,4 +8,4 @@ repositories { dependencies { implementation("com.badlogicgames.gdx:gdx-platform:1.9.9:natives-desktop") -} \ No newline at end of file +} diff --git a/fixtures/dependency/classifier/kotlin/settings.gradle.kts b/fixtures/dependency/classifier/kotlin/settings.gradle.kts new file mode 100644 index 0000000..e69de29 diff --git a/fixtures/dependency/maven-bom/kotlin/build.gradle.kts b/fixtures/dependency/maven-bom/kotlin/build.gradle.kts index 9ad9517..2734242 100644 --- a/fixtures/dependency/maven-bom/kotlin/build.gradle.kts +++ b/fixtures/dependency/maven-bom/kotlin/build.gradle.kts @@ -3,10 +3,10 @@ plugins { } repositories { - maven { url = uri("http://localhost:9999") } + maven { url = uri(System.getProperty("org.nixos.gradle2nix.m2")) } } dependencies { implementation(platform("io.micrometer:micrometer-bom:1.5.1")) implementation("io.micrometer:micrometer-core") -} \ No newline at end of file +} diff --git a/fixtures/dependency/maven-bom/kotlin/settings.gradle.kts b/fixtures/dependency/maven-bom/kotlin/settings.gradle.kts new file mode 100644 index 0000000..e69de29 diff --git a/fixtures/dependency/snapshot-dynamic/groovy/build.gradle b/fixtures/dependency/snapshot-dynamic/groovy/build.gradle index 5b137e1..83d134f 100644 --- a/fixtures/dependency/snapshot-dynamic/groovy/build.gradle +++ b/fixtures/dependency/snapshot-dynamic/groovy/build.gradle @@ -3,9 +3,9 @@ plugins { } repositories { - maven { url = uri("http://localhost:9999") } + maven { url = uri(System.getProperty("org.nixos.gradle2nix.m2")) } } dependencies { implementation "org.apache:test-SNAPSHOT1:2.0.2-SNAPSHOT" -} \ No newline at end of file +} diff --git a/fixtures/dependency/snapshot-dynamic/groovy/settings.gradle b/fixtures/dependency/snapshot-dynamic/groovy/settings.gradle new file mode 100644 index 0000000..e69de29 diff --git a/fixtures/dependency/snapshot-dynamic/kotlin/build.gradle.kts b/fixtures/dependency/snapshot-dynamic/kotlin/build.gradle.kts index 805f99e..695191a 100644 --- a/fixtures/dependency/snapshot-dynamic/kotlin/build.gradle.kts +++ b/fixtures/dependency/snapshot-dynamic/kotlin/build.gradle.kts @@ -1,9 +1,10 @@ + plugins { java } repositories { - maven { url = uri("http://localhost:9999") } + maven { url = uri(System.getProperty("org.nixos.gradle2nix.m2")) } } dependencies { diff --git a/fixtures/dependency/snapshot-dynamic/kotlin/settings.gradle.kts b/fixtures/dependency/snapshot-dynamic/kotlin/settings.gradle.kts new file mode 100644 index 0000000..e69de29 diff --git a/fixtures/dependency/snapshot-redirect/groovy/build.gradle b/fixtures/dependency/snapshot-redirect/groovy/build.gradle index a2a9953..70cfd52 100644 --- a/fixtures/dependency/snapshot-redirect/groovy/build.gradle +++ b/fixtures/dependency/snapshot-redirect/groovy/build.gradle @@ -3,10 +3,9 @@ plugins { } repositories { - jcenter() maven { url 'https://jitpack.io' } } dependencies { implementation 'com.github.anuken:packr:-SNAPSHOT' -} \ No newline at end of file +} diff --git a/fixtures/dependency/snapshot-redirect/groovy/settings.gradle b/fixtures/dependency/snapshot-redirect/groovy/settings.gradle new file mode 100644 index 0000000..e69de29 diff --git a/fixtures/dependency/snapshot/groovy/build.gradle b/fixtures/dependency/snapshot/groovy/build.gradle index e0a1b63..a752a63 100644 --- a/fixtures/dependency/snapshot/groovy/build.gradle +++ b/fixtures/dependency/snapshot/groovy/build.gradle @@ -3,9 +3,9 @@ plugins { } repositories { - maven { url = uri("http://localhost:9999") } + maven { url = uri("file:/../../../../") } } dependencies { implementation "org.apache:test-SNAPSHOT2:2.0.2-SNAPSHOT" -} \ No newline at end of file +} diff --git a/fixtures/dependency/snapshot/groovy/settings.gradle b/fixtures/dependency/snapshot/groovy/settings.gradle new file mode 100644 index 0000000..e69de29 diff --git a/fixtures/dependency/snapshot/kotlin/build.gradle.kts b/fixtures/dependency/snapshot/kotlin/build.gradle.kts index ed66d2a..5fdce81 100644 --- a/fixtures/dependency/snapshot/kotlin/build.gradle.kts +++ b/fixtures/dependency/snapshot/kotlin/build.gradle.kts @@ -1,9 +1,10 @@ + plugins { java } repositories { - maven { url = uri("http://localhost:9999") } + maven { url = uri(System.getProperty("org.nixos.gradle2nix.m2")) } } dependencies { diff --git a/fixtures/dependency/snapshot/kotlin/settings.gradle.kts b/fixtures/dependency/snapshot/kotlin/settings.gradle.kts new file mode 100644 index 0000000..e69de29 diff --git a/fixtures/golden/basic/basic-java-project.groovy.json b/fixtures/golden/basic/basic-java-project.groovy.json new file mode 100644 index 0000000..83d14c1 --- /dev/null +++ b/fixtures/golden/basic/basic-java-project.groovy.json @@ -0,0 +1,72 @@ +{ + "com.squareup.okio:okio:2.2.2": { + "okio-2.2.2.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/com/squareup/okio/okio/2.2.2/okio-2.2.2.jar" + ], + "hash": "sha256-5YyXQGprsROIk3UCmaxjxqoEs4trSerhv8rRpj75uhs=" + }, + "okio-2.2.2.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/com/squareup/okio/okio/2.2.2/okio-2.2.2.pom" + ], + "hash": "sha256-/WIZiPf2lXAlc13G3QkLAKIPOju413ynkDYHf2KbFAs=" + } + }, + "com.squareup.moshi:moshi:1.8.0": { + "moshi-1.8.0.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/com/squareup/moshi/moshi/1.8.0/moshi-1.8.0.jar" + ], + "hash": "sha256-Qv50bSaU6hH+agK+zZ2iyj2v6Xye/VCg+a9cRZbnSmo=" + }, + "moshi-1.8.0.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/com/squareup/moshi/moshi/1.8.0/moshi-1.8.0.pom" + ], + "hash": "sha256-FLuAWbnddiACWSkN+IfjfmaaB0qsnImUAePIEC/lII8=" + } + }, + "org.jetbrains.kotlin:kotlin-stdlib:1.2.60": { + "kotlin-stdlib-1.2.60.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib/1.2.60/kotlin-stdlib-1.2.60.jar" + ], + "hash": "sha256-ahMCmPUXGsUqHiSW9+rnhbb1ZBbqPMuZ5DRNBNg/8HE=" + }, + "kotlin-stdlib-1.2.60.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib/1.2.60/kotlin-stdlib-1.2.60.pom" + ], + "hash": "sha256-5jKJkgnmtMqrlA/YLk7GOjLjJkP4Fff6cJdkeJDXnxg=" + } + }, + "org.jetbrains.kotlin:kotlin-stdlib-common:1.2.60": { + "kotlin-stdlib-common-1.2.60.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib-common/1.2.60/kotlin-stdlib-common-1.2.60.jar" + ], + "hash": "sha256-CbQ3WgZc8SeryZjF3PIrFmTEWvQrSJSZ16j0+Kt5P7E=" + }, + "kotlin-stdlib-common-1.2.60.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib-common/1.2.60/kotlin-stdlib-common-1.2.60.pom" + ], + "hash": "sha256-gwwnrx4c8k8PUm6kV5AcQ/OMGbtvfl03Y8PSU98bjaE=" + } + }, + "org.jetbrains:annotations:13.0": { + "annotations-13.0.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/annotations/13.0/annotations-13.0.jar" + ], + "hash": "sha256-rOKhDcji1f00kl7KwD5JiLLA+FFlDJS4zvSbob0RFHg=" + }, + "annotations-13.0.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/annotations/13.0/annotations-13.0.pom" + ], + "hash": "sha256-llrrK+3/NpgZvd4b96CzuJuCR91pyIuGN112Fju4w5c=" + } + } +} \ No newline at end of file diff --git a/fixtures/golden/basic/basic-java-project.kotlin.json b/fixtures/golden/basic/basic-java-project.kotlin.json new file mode 100644 index 0000000..83d14c1 --- /dev/null +++ b/fixtures/golden/basic/basic-java-project.kotlin.json @@ -0,0 +1,72 @@ +{ + "com.squareup.okio:okio:2.2.2": { + "okio-2.2.2.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/com/squareup/okio/okio/2.2.2/okio-2.2.2.jar" + ], + "hash": "sha256-5YyXQGprsROIk3UCmaxjxqoEs4trSerhv8rRpj75uhs=" + }, + "okio-2.2.2.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/com/squareup/okio/okio/2.2.2/okio-2.2.2.pom" + ], + "hash": "sha256-/WIZiPf2lXAlc13G3QkLAKIPOju413ynkDYHf2KbFAs=" + } + }, + "com.squareup.moshi:moshi:1.8.0": { + "moshi-1.8.0.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/com/squareup/moshi/moshi/1.8.0/moshi-1.8.0.jar" + ], + "hash": "sha256-Qv50bSaU6hH+agK+zZ2iyj2v6Xye/VCg+a9cRZbnSmo=" + }, + "moshi-1.8.0.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/com/squareup/moshi/moshi/1.8.0/moshi-1.8.0.pom" + ], + "hash": "sha256-FLuAWbnddiACWSkN+IfjfmaaB0qsnImUAePIEC/lII8=" + } + }, + "org.jetbrains.kotlin:kotlin-stdlib:1.2.60": { + "kotlin-stdlib-1.2.60.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib/1.2.60/kotlin-stdlib-1.2.60.jar" + ], + "hash": "sha256-ahMCmPUXGsUqHiSW9+rnhbb1ZBbqPMuZ5DRNBNg/8HE=" + }, + "kotlin-stdlib-1.2.60.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib/1.2.60/kotlin-stdlib-1.2.60.pom" + ], + "hash": "sha256-5jKJkgnmtMqrlA/YLk7GOjLjJkP4Fff6cJdkeJDXnxg=" + } + }, + "org.jetbrains.kotlin:kotlin-stdlib-common:1.2.60": { + "kotlin-stdlib-common-1.2.60.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib-common/1.2.60/kotlin-stdlib-common-1.2.60.jar" + ], + "hash": "sha256-CbQ3WgZc8SeryZjF3PIrFmTEWvQrSJSZ16j0+Kt5P7E=" + }, + "kotlin-stdlib-common-1.2.60.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib-common/1.2.60/kotlin-stdlib-common-1.2.60.pom" + ], + "hash": "sha256-gwwnrx4c8k8PUm6kV5AcQ/OMGbtvfl03Y8PSU98bjaE=" + } + }, + "org.jetbrains:annotations:13.0": { + "annotations-13.0.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/annotations/13.0/annotations-13.0.jar" + ], + "hash": "sha256-rOKhDcji1f00kl7KwD5JiLLA+FFlDJS4zvSbob0RFHg=" + }, + "annotations-13.0.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/annotations/13.0/annotations-13.0.pom" + ], + "hash": "sha256-llrrK+3/NpgZvd4b96CzuJuCR91pyIuGN112Fju4w5c=" + } + } +} \ No newline at end of file diff --git a/fixtures/golden/basic/basic-kotlin-project.kotlin.json b/fixtures/golden/basic/basic-kotlin-project.kotlin.json new file mode 100644 index 0000000..cf9a936 --- /dev/null +++ b/fixtures/golden/basic/basic-kotlin-project.kotlin.json @@ -0,0 +1,1258 @@ +{ + "org.jetbrains.kotlin.jvm:org.jetbrains.kotlin.jvm.gradle.plugin:1.6.21": { + "org.jetbrains.kotlin.jvm.gradle.plugin-1.6.21.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/jvm/org.jetbrains.kotlin.jvm.gradle.plugin/1.6.21/org.jetbrains.kotlin.jvm.gradle.plugin-1.6.21.pom" + ], + "hash": "sha256-hKJnm90W1DkKJmp58Gzaix+iq38XlowYk0l84ZWOHEQ=" + } + }, + "org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.21": { + "kotlin-gradle-plugin-1.6.21.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-gradle-plugin/1.6.21/kotlin-gradle-plugin-1.6.21.jar" + ], + "hash": "sha256-Z1Oi4RJtP5k6lRryrcBrHsTKJxdulsj2Mnd5kBBNFa0=" + }, + "kotlin-gradle-plugin-1.6.21.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-gradle-plugin/1.6.21/kotlin-gradle-plugin-1.6.21.pom" + ], + "hash": "sha256-7RX0N/j1aW6NU7mszIYS6cas9Wfbau0E/ymq3F4DpC4=" + } + }, + "org.jetbrains.kotlin:kotlin-gradle-plugin-api:1.6.21": { + "kotlin-gradle-plugin-api-1.6.21.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-gradle-plugin-api/1.6.21/kotlin-gradle-plugin-api-1.6.21.jar" + ], + "hash": "sha256-x0wfF5FsrG1ygGJkmI0V4yGa8kYJB5E3Tq6cua8ufLM=" + }, + "kotlin-gradle-plugin-api-1.6.21.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-gradle-plugin-api/1.6.21/kotlin-gradle-plugin-api-1.6.21.pom" + ], + "hash": "sha256-JL0R1cjnNGMHSBUXnPuyYCAJIxyEE5aTr3ydVtzU3z8=" + } + }, + "org.jetbrains.kotlin:kotlin-native-utils:1.6.21": { + "kotlin-native-utils-1.6.21.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-native-utils/1.6.21/kotlin-native-utils-1.6.21.jar" + ], + "hash": "sha256-a9hyJOVq4V/+IQTTx2M9cq9sezWCRa08SnbG1f0NNr8=" + }, + "kotlin-native-utils-1.6.21.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-native-utils/1.6.21/kotlin-native-utils-1.6.21.pom" + ], + "hash": "sha256-92q9t+TzvxouyTxiqybO/lKg7UlBAY53w/74BrCXvq8=" + } + }, + "org.jetbrains.kotlin:kotlin-util-io:1.6.21": { + "kotlin-util-io-1.6.21.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-util-io/1.6.21/kotlin-util-io-1.6.21.jar" + ], + "hash": "sha256-wCxUcFYyGLcDvh5xbi0M6leH01y+tryUbfAMAM1CrNI=" + }, + "kotlin-util-io-1.6.21.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-util-io/1.6.21/kotlin-util-io-1.6.21.pom" + ], + "hash": "sha256-52nyybmRQIYlhKLAoRLIQ3P0fkryrxbQHOSThRMZMhk=" + } + }, + "org.jetbrains.kotlin:kotlin-project-model:1.6.21": { + "kotlin-project-model-1.6.21.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-project-model/1.6.21/kotlin-project-model-1.6.21.jar" + ], + "hash": "sha256-FLaE+Kc+IpZ4ASS/OB3eAT9YLqIzZ6zgGEWAo4Sa6Ng=" + }, + "kotlin-project-model-1.6.21.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-project-model/1.6.21/kotlin-project-model-1.6.21.pom" + ], + "hash": "sha256-dgKHUWgMZJAf76wyN5AmtNC9I3rdfw873ujtXH51LG4=" + } + }, + "org.jetbrains.kotlin:kotlin-gradle-plugin-model:1.6.21": { + "kotlin-gradle-plugin-model-1.6.21.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-gradle-plugin-model/1.6.21/kotlin-gradle-plugin-model-1.6.21.jar" + ], + "hash": "sha256-YFmxxhMI+4WaAedYsrj9Ctr/dBs9+AI1+t6VrWq4loc=" + }, + "kotlin-gradle-plugin-model-1.6.21.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-gradle-plugin-model/1.6.21/kotlin-gradle-plugin-model-1.6.21.pom" + ], + "hash": "sha256-QkLJzsOQLX21n4OMupPDDnMeC10yzDnQ5Ft1gKZUBOo=" + } + }, + "org.jetbrains.kotlin:kotlin-util-klib:1.6.21": { + "kotlin-util-klib-1.6.21.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-util-klib/1.6.21/kotlin-util-klib-1.6.21.jar" + ], + "hash": "sha256-7iGKnoGAwbNIwawc+K1xj2qseLp+JrUIEyNIT2Q8YbI=" + }, + "kotlin-util-klib-1.6.21.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-util-klib/1.6.21/kotlin-util-klib-1.6.21.pom" + ], + "hash": "sha256-EsegqvZnLbHXgxxHGWV/+b9rEXGbD8h9iNcnXzl/lKs=" + } + }, + "org.jetbrains.kotlin:kotlin-klib-commonizer-api:1.6.21": { + "kotlin-klib-commonizer-api-1.6.21.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-klib-commonizer-api/1.6.21/kotlin-klib-commonizer-api-1.6.21.jar" + ], + "hash": "sha256-0s9pUu7ziUqs+KnYzx6MZ78hW075AmioyQMYNFMKOHQ=" + }, + "kotlin-klib-commonizer-api-1.6.21.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-klib-commonizer-api/1.6.21/kotlin-klib-commonizer-api-1.6.21.pom" + ], + "hash": "sha256-FICJ7qPCUPClDqxomfFFq5D1mJM8GrT5qsldYXKHJfQ=" + } + }, + "org.jetbrains.kotlin:kotlin-tooling-metadata:1.6.21": { + "kotlin-tooling-metadata-1.6.21.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-tooling-metadata/1.6.21/kotlin-tooling-metadata-1.6.21.jar" + ], + "hash": "sha256-Tsk9BRYGawtki6mHxtPWX2+wZ9wLu6lcHs5h4EKEiOU=" + }, + "kotlin-tooling-metadata-1.6.21.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-tooling-metadata/1.6.21/kotlin-tooling-metadata-1.6.21.pom" + ], + "hash": "sha256-uwYH34aI9gLBwvTQmWMz/YsDtpMoaGpud15S9/sNFxg=" + } + }, + "com.google.code.gson:gson:2.8.9": { + "gson-2.8.9.jar": { + "urls": [ + "https://plugins.gradle.org/m2/com/google/code/gson/gson/2.8.9/gson-2.8.9.jar" + ], + "hash": "sha256-05mSkYVd5JXJTHQ3YbirUXbP6r4oGlqw2OjUUyb9cD4=" + }, + "gson-2.8.9.pom": { + "urls": [ + "https://plugins.gradle.org/m2/com/google/code/gson/gson/2.8.9/gson-2.8.9.pom" + ], + "hash": "sha256-r97W5qaQ+/OtSuZa2jl/CpCl9jCzA9G3QbnJeSb91N4=" + } + }, + "com.google.guava:guava:29.0-jre": { + "guava-29.0-jre.jar": { + "urls": [ + "https://plugins.gradle.org/m2/com/google/guava/guava/29.0-jre/guava-29.0-jre.jar" + ], + "hash": "sha256-sixftm1h57lSJTHQSy+RW1FY6AqgtA7nKCyL+wew2iU=" + }, + "guava-29.0-jre.pom": { + "urls": [ + "https://plugins.gradle.org/m2/com/google/guava/guava/29.0-jre/guava-29.0-jre.pom" + ], + "hash": "sha256-kCfpNAmJA9KH8bphyLZfAdHR4dp6b7zAS/PeBUQBRCY=" + } + }, + "com.google.guava:failureaccess:1.0.1": { + "failureaccess-1.0.1.jar": { + "urls": [ + "https://plugins.gradle.org/m2/com/google/guava/failureaccess/1.0.1/failureaccess-1.0.1.jar" + ], + "hash": "sha256-oXHuTHNN0tqDfksWvp30Zhr6typBra8x64Tf2vk2yiY=" + }, + "failureaccess-1.0.1.pom": { + "urls": [ + "https://plugins.gradle.org/m2/com/google/guava/failureaccess/1.0.1/failureaccess-1.0.1.pom" + ], + "hash": "sha256-6WBCznj+y6DaK+lkUilHyHtAopG1/TzWcqQ0kkEDxLk=" + } + }, + "com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava": { + "listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar": { + "urls": [ + "https://plugins.gradle.org/m2/com/google/guava/listenablefuture/9999.0-empty-to-avoid-conflict-with-guava/listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar" + ], + "hash": "sha256-s3KgN9QjCqV/vv/e8w/WEj+cDC24XQrO0AyRuXTzP5k=" + }, + "listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.pom": { + "urls": [ + "https://plugins.gradle.org/m2/com/google/guava/listenablefuture/9999.0-empty-to-avoid-conflict-with-guava/listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.pom" + ], + "hash": "sha256-GNSx2yYVPU5VB5zh92ux/gXNuGLvmVSojLzE/zi4Z5s=" + } + }, + "com.google.code.findbugs:jsr305:3.0.2": { + "jsr305-3.0.2.jar": { + "urls": [ + "https://plugins.gradle.org/m2/com/google/code/findbugs/jsr305/3.0.2/jsr305-3.0.2.jar" + ], + "hash": "sha256-dmrSoHg/JoeWLIrXTO7MOKKLn3Ki0IXuQ4t4E+ko0Mc=" + }, + "jsr305-3.0.2.pom": { + "urls": [ + "https://plugins.gradle.org/m2/com/google/code/findbugs/jsr305/3.0.2/jsr305-3.0.2.pom" + ], + "hash": "sha256-GYidvfGyVLJgGl7mRbgUepdGRIgil2hMeYr+XWPXjf4=" + } + }, + "org.checkerframework:checker-qual:2.11.1": { + "checker-qual-2.11.1.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/checkerframework/checker-qual/2.11.1/checker-qual-2.11.1.jar" + ], + "hash": "sha256-AVIkpLHcbebaBTJz1Np9Oc/qIOYwOBafxFrA0dycWTg=" + }, + "checker-qual-2.11.1.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/checkerframework/checker-qual/2.11.1/checker-qual-2.11.1.pom" + ], + "hash": "sha256-zy4MkNj3V0VfSiWOpglzkFNmO9XaannZvVP5NaR955w=" + } + }, + "com.google.errorprone:error_prone_annotations:2.3.4": { + "error_prone_annotations-2.3.4.jar": { + "urls": [ + "https://plugins.gradle.org/m2/com/google/errorprone/error_prone_annotations/2.3.4/error_prone_annotations-2.3.4.jar" + ], + "hash": "sha256-uvfW6pfOYGxT4RtoVLpfLOfvXCTd3wr6GNEmC9JbACw=" + }, + "error_prone_annotations-2.3.4.pom": { + "urls": [ + "https://plugins.gradle.org/m2/com/google/errorprone/error_prone_annotations/2.3.4/error_prone_annotations-2.3.4.pom" + ], + "hash": "sha256-EyZziktPfMrPYHuGahH7hRk+9g9qWUYRh85yZfm+W+0=" + } + }, + "com.google.j2objc:j2objc-annotations:1.3": { + "j2objc-annotations-1.3.jar": { + "urls": [ + "https://plugins.gradle.org/m2/com/google/j2objc/j2objc-annotations/1.3/j2objc-annotations-1.3.jar" + ], + "hash": "sha256-Ia8wySJnvWEiwOC00gzMtmQaN+r5VsZUDsRx1YTmSns=" + }, + "j2objc-annotations-1.3.pom": { + "urls": [ + "https://plugins.gradle.org/m2/com/google/j2objc/j2objc-annotations/1.3/j2objc-annotations-1.3.pom" + ], + "hash": "sha256-X6yoJLoRW+5FhzAzff2y/OpGui/XdNQwTtvzD6aj8FU=" + } + }, + "de.undercouch:gradle-download-task:4.1.1": { + "gradle-download-task-4.1.1.jar": { + "urls": [ + "https://plugins.gradle.org/m2/de/undercouch/gradle-download-task/4.1.1/gradle-download-task-4.1.1.jar" + ], + "hash": "sha256-6wi1cOQI1GRnBecKlJYU1DnqKxFFXxZSqwMw3olU2rk=" + }, + "gradle-download-task-4.1.1.pom": { + "urls": [ + "https://plugins.gradle.org/m2/de/undercouch/gradle-download-task/4.1.1/gradle-download-task-4.1.1.pom" + ], + "hash": "sha256-EQnx9xpUJU1ZAzfYudRD+d/AhyjJwdgzVlXMHcyIwLk=" + } + }, + "com.github.gundy:semver4j:0.16.4": { + "semver4j-0.16.4-nodeps.jar": { + "urls": [ + "https://plugins.gradle.org/m2/com/github/gundy/semver4j/0.16.4/semver4j-0.16.4-nodeps.jar" + ], + "hash": "sha256-P1nspRY3TM1P01UWJb9Q+KSxkfcAUI985IZkYKYSivA=" + }, + "semver4j-0.16.4.pom": { + "urls": [ + "https://plugins.gradle.org/m2/com/github/gundy/semver4j/0.16.4/semver4j-0.16.4.pom" + ], + "hash": "sha256-MgAdskQ7M53SH1t5/ynRreci0boIDCFL3oGfD3LRYE0=" + } + }, + "org.jetbrains.kotlin:kotlin-compiler-embeddable:1.6.21": { + "kotlin-compiler-embeddable-1.6.21.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-compiler-embeddable/1.6.21/kotlin-compiler-embeddable-1.6.21.jar", + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-compiler-embeddable/1.6.21/kotlin-compiler-embeddable-1.6.21.jar" + ], + "hash": "sha256-X5ZQPNgiqmwg7abHFqVTxBTYAO0Mbn1lX6Gx+/1P7Cs=" + }, + "kotlin-compiler-embeddable-1.6.21.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-compiler-embeddable/1.6.21/kotlin-compiler-embeddable-1.6.21.pom", + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-compiler-embeddable/1.6.21/kotlin-compiler-embeddable-1.6.21.pom" + ], + "hash": "sha256-wpULrWEPTie9iidbgcDoPIUfGD1gTuH7iRJV9DRa9EE=" + } + }, + "org.jetbrains.kotlin:kotlin-daemon-embeddable:1.6.21": { + "kotlin-daemon-embeddable-1.6.21.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-daemon-embeddable/1.6.21/kotlin-daemon-embeddable-1.6.21.jar", + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-daemon-embeddable/1.6.21/kotlin-daemon-embeddable-1.6.21.jar" + ], + "hash": "sha256-UcPsHDLDbWVw2DFC6v4qJCk08WXwt4w4YTdpBfkPLhI=" + }, + "kotlin-daemon-embeddable-1.6.21.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-daemon-embeddable/1.6.21/kotlin-daemon-embeddable-1.6.21.pom", + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-daemon-embeddable/1.6.21/kotlin-daemon-embeddable-1.6.21.pom" + ], + "hash": "sha256-tFZZFoP6YHGHAFr0sx0x1DYE4CHWBFUf8PIubdpWK5o=" + } + }, + "org.jetbrains.intellij.deps:trove4j:1.0.20200330": { + "trove4j-1.0.20200330.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/intellij/deps/trove4j/1.0.20200330/trove4j-1.0.20200330.jar", + "https://repo.maven.apache.org/maven2/org/jetbrains/intellij/deps/trove4j/1.0.20200330/trove4j-1.0.20200330.jar" + ], + "hash": "sha256-xf1yW/+rUYRr88d9sTg8YKquv+G3/i8A0j/ht98KQ50=" + }, + "trove4j-1.0.20200330.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/intellij/deps/trove4j/1.0.20200330/trove4j-1.0.20200330.pom", + "https://repo.maven.apache.org/maven2/org/jetbrains/intellij/deps/trove4j/1.0.20200330/trove4j-1.0.20200330.pom" + ], + "hash": "sha256-h3IcuqZaPJfYsbqdIHhA8WTJ/jh1n8nqEP/iZWX40+k=" + } + }, + "net.java.dev.jna:jna:5.6.0": { + "jna-5.6.0.jar": { + "urls": [ + "https://plugins.gradle.org/m2/net/java/dev/jna/jna/5.6.0/jna-5.6.0.jar", + "https://repo.maven.apache.org/maven2/net/java/dev/jna/jna/5.6.0/jna-5.6.0.jar" + ], + "hash": "sha256-VVfiNaiqL5dm1dxgnWeUjyqIMsLXls6p7x1svgs7fq8=" + }, + "jna-5.6.0.pom": { + "urls": [ + "https://plugins.gradle.org/m2/net/java/dev/jna/jna/5.6.0/jna-5.6.0.pom", + "https://repo.maven.apache.org/maven2/net/java/dev/jna/jna/5.6.0/jna-5.6.0.pom" + ], + "hash": "sha256-X+gbAlWXjyRhbTexBgi3lJil8wc+HZsgONhzaoMfJgg=" + } + }, + "org.jetbrains.kotlin:kotlin-annotation-processing-gradle:1.6.21": { + "kotlin-annotation-processing-gradle-1.6.21.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-annotation-processing-gradle/1.6.21/kotlin-annotation-processing-gradle-1.6.21.jar" + ], + "hash": "sha256-tA86gSFVnlAjnFXvh2Z6IYBRG7GTQfzIYZh+T4TOYog=" + }, + "kotlin-annotation-processing-gradle-1.6.21.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-annotation-processing-gradle/1.6.21/kotlin-annotation-processing-gradle-1.6.21.pom" + ], + "hash": "sha256-MImLOrD3X6VZjABz0qoqV9yctWnJ6Mb/O6UXUopMEHE=" + } + }, + "org.jetbrains.kotlin:kotlin-android-extensions:1.6.21": { + "kotlin-android-extensions-1.6.21.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-android-extensions/1.6.21/kotlin-android-extensions-1.6.21.jar" + ], + "hash": "sha256-QY25MO/hevs27AnooGI1615PYAsrXKFIeEIsn5lEbPs=" + }, + "kotlin-android-extensions-1.6.21.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-android-extensions/1.6.21/kotlin-android-extensions-1.6.21.pom" + ], + "hash": "sha256-oWU+E091vwu2aNklZdd/Qy3lGijcUcNK9eOBS53tCsQ=" + } + }, + "org.jetbrains.kotlin:kotlin-compiler-runner:1.6.21": { + "kotlin-compiler-runner-1.6.21.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-compiler-runner/1.6.21/kotlin-compiler-runner-1.6.21.jar" + ], + "hash": "sha256-IGGw47e3Uwv2cg2LBBC+Eyb7Fs1NrcN+d8Aqz+/loLM=" + }, + "kotlin-compiler-runner-1.6.21.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-compiler-runner/1.6.21/kotlin-compiler-runner-1.6.21.pom" + ], + "hash": "sha256-b0Ofb0jeG+QltfdQlLbqpICL6hG8LjzmtUTG4zOQtSU=" + } + }, + "org.jetbrains.kotlin:kotlin-build-common:1.6.21": { + "kotlin-build-common-1.6.21.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-build-common/1.6.21/kotlin-build-common-1.6.21.jar" + ], + "hash": "sha256-Y+6kBdNeNOggJcL0FW49R1fLjyWUmWIzVspma9IQAZ0=" + }, + "kotlin-build-common-1.6.21.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-build-common/1.6.21/kotlin-build-common-1.6.21.pom" + ], + "hash": "sha256-LRDfBINfB7h6qBoOf+xAbSwawRxU5+CPCOtRGv5btI8=" + } + }, + "org.jetbrains.kotlin:kotlin-daemon-client:1.6.21": { + "kotlin-daemon-client-1.6.21.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-daemon-client/1.6.21/kotlin-daemon-client-1.6.21.jar" + ], + "hash": "sha256-ZHawBzZPVFzmd6ObkzG8IbVqvXtWbwOfUfIVCKOQL6c=" + }, + "kotlin-daemon-client-1.6.21.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-daemon-client/1.6.21/kotlin-daemon-client-1.6.21.pom" + ], + "hash": "sha256-Q8EnIKTydrNdwEOWEo6bf7Goq9B6FstAnGwNZwaiMWs=" + } + }, + "org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.5.0": { + "kotlinx-coroutines-core-jvm-1.5.0.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.5.0/kotlinx-coroutines-core-jvm-1.5.0.jar" + ], + "hash": "sha256-eNbMcTX4TWkv83Uvz9H6G74JQNffcGUuTx6u7Ax4r7s=" + }, + "kotlinx-coroutines-core-jvm-1.5.0.module": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.5.0/kotlinx-coroutines-core-jvm-1.5.0.module" + ], + "hash": "sha256-yIXdAoEHbFhDgm3jF+PLzcPYhZ2+71OuHPrNG5xg+W4=" + } + }, + "org.jetbrains.kotlin:kotlin-scripting-compiler-embeddable:1.6.21": { + "kotlin-scripting-compiler-embeddable-1.6.21.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-scripting-compiler-embeddable/1.6.21/kotlin-scripting-compiler-embeddable-1.6.21.jar", + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-scripting-compiler-embeddable/1.6.21/kotlin-scripting-compiler-embeddable-1.6.21.jar" + ], + "hash": "sha256-XJNzrzrkC3PW12JLJOjOEXIUSV2GLebSz7YYpxGRBrE=" + }, + "kotlin-scripting-compiler-embeddable-1.6.21.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-scripting-compiler-embeddable/1.6.21/kotlin-scripting-compiler-embeddable-1.6.21.pom", + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-scripting-compiler-embeddable/1.6.21/kotlin-scripting-compiler-embeddable-1.6.21.pom" + ], + "hash": "sha256-C32qtju7PFTd0+NF6wzLI3aAv9TDh7Zfzllt/0uEe9s=" + } + }, + "org.jetbrains.kotlin:kotlin-scripting-compiler-impl-embeddable:1.6.21": { + "kotlin-scripting-compiler-impl-embeddable-1.6.21.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-scripting-compiler-impl-embeddable/1.6.21/kotlin-scripting-compiler-impl-embeddable-1.6.21.jar", + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-scripting-compiler-impl-embeddable/1.6.21/kotlin-scripting-compiler-impl-embeddable-1.6.21.jar" + ], + "hash": "sha256-5ARLjeAehGM5I3BvQ82oDTfYu++M6ahm+dlZYt3SBIA=" + }, + "kotlin-scripting-compiler-impl-embeddable-1.6.21.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-scripting-compiler-impl-embeddable/1.6.21/kotlin-scripting-compiler-impl-embeddable-1.6.21.pom", + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-scripting-compiler-impl-embeddable/1.6.21/kotlin-scripting-compiler-impl-embeddable-1.6.21.pom" + ], + "hash": "sha256-gIxqOEi7Xk9sYWmKxYkxIVc8Q9s4FCNW6D3q0EyzhjQ=" + } + }, + "org.jetbrains.kotlin:kotlin-scripting-common:1.6.21": { + "kotlin-scripting-common-1.6.21.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-scripting-common/1.6.21/kotlin-scripting-common-1.6.21.jar", + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-scripting-common/1.6.21/kotlin-scripting-common-1.6.21.jar" + ], + "hash": "sha256-v79fA2I3zTPCX7oz1IlI2ZXbgYbOPwnDGvnCnIDOnK4=" + }, + "kotlin-scripting-common-1.6.21.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-scripting-common/1.6.21/kotlin-scripting-common-1.6.21.pom", + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-scripting-common/1.6.21/kotlin-scripting-common-1.6.21.pom" + ], + "hash": "sha256-qgWvDyJWUokIeXiduzo6UY4XdWqFsT1UCo3P3wPL+5w=" + } + }, + "org.jetbrains.kotlin:kotlin-scripting-jvm:1.6.21": { + "kotlin-scripting-jvm-1.6.21.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-scripting-jvm/1.6.21/kotlin-scripting-jvm-1.6.21.jar", + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-scripting-jvm/1.6.21/kotlin-scripting-jvm-1.6.21.jar" + ], + "hash": "sha256-ksA/6r9L3ZLVoixRp0i9NAJ0Z8PY9MZftbV0uGsH0QQ=" + }, + "kotlin-scripting-jvm-1.6.21.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-scripting-jvm/1.6.21/kotlin-scripting-jvm-1.6.21.pom", + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-scripting-jvm/1.6.21/kotlin-scripting-jvm-1.6.21.pom" + ], + "hash": "sha256-i5q1hXoYheSL2uAPqosix0sNPkCmNPyeCadG+op1fTI=" + } + }, + "org.jetbrains.kotlin:kotlin-stdlib:1.6.21": { + "kotlin-stdlib-1.6.21.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib/1.6.21/kotlin-stdlib-1.6.21.jar" + ], + "hash": "sha256-c5xSZnK7M3Vzso9jr6gwbrCIsMOgln9W1sifSjASpJI=" + }, + "kotlin-stdlib-1.6.21.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib/1.6.21/kotlin-stdlib-1.6.21.pom" + ], + "hash": "sha256-zkJyW6Ab2DbNqmZ9l032hL9vjxXng5JjMgraf/quHzQ=" + } + }, + "org.jetbrains.kotlin:kotlin-stdlib-common:1.6.21": { + "kotlin-stdlib-common-1.6.21.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib-common/1.6.21/kotlin-stdlib-common-1.6.21.jar" + ], + "hash": "sha256-GDvsWc2fOhSVexkOjIec8RlL0fEGsKe24cu4eQ0kI2M=" + }, + "kotlin-stdlib-common-1.6.21.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib-common/1.6.21/kotlin-stdlib-common-1.6.21.pom" + ], + "hash": "sha256-W8FW7nP9PC2sil7FSNWBtjMzNUfC/r7Zz2VH//FSa6I=" + } + }, + "org.jetbrains:annotations:13.0": { + "annotations-13.0.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/annotations/13.0/annotations-13.0.jar" + ], + "hash": "sha256-rOKhDcji1f00kl7KwD5JiLLA+FFlDJS4zvSbob0RFHg=" + }, + "annotations-13.0.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/annotations/13.0/annotations-13.0.pom" + ], + "hash": "sha256-llrrK+3/NpgZvd4b96CzuJuCR91pyIuGN112Fju4w5c=" + } + }, + "com.natpryce:konfig:1.6.10.0": { + "konfig-1.6.10.0.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/com/natpryce/konfig/1.6.10.0/konfig-1.6.10.0.jar" + ], + "hash": "sha256-1Va6vANYRVP1/TzEaJTF6jMxCSv7qufqYm1bjUznk7s=" + }, + "konfig-1.6.10.0.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/com/natpryce/konfig/1.6.10.0/konfig-1.6.10.0.pom" + ], + "hash": "sha256-1NTlAHxEbyBlnbIqc2WXwLCFOLy6FL1HEND4VNxxFYg=" + } + }, + "com.github.pengrad:java-telegram-bot-api:4.6.0": { + "java-telegram-bot-api-4.6.0.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/com/github/pengrad/java-telegram-bot-api/4.6.0/java-telegram-bot-api-4.6.0.jar" + ], + "hash": "sha256-w4H/cErewM/mZbrnUYtwiT5Czf83Smb0qYxGfeG/TdU=" + }, + "java-telegram-bot-api-4.6.0.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/com/github/pengrad/java-telegram-bot-api/4.6.0/java-telegram-bot-api-4.6.0.pom" + ], + "hash": "sha256-nZxF//5qwbIbZffUK0k2T/gnnX5pLU9wF0BLLhC+YsE=" + } + }, + "com.google.code.gson:gson:2.8.5": { + "gson-2.8.5.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/com/google/code/gson/gson/2.8.5/gson-2.8.5.jar" + ], + "hash": "sha256-IzoBSfw2XJ9u29aDz+JmsZvcdzvpjqva9rPJJLSOfYE=" + }, + "gson-2.8.5.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/com/google/code/gson/gson/2.8.5/gson-2.8.5.pom" + ], + "hash": "sha256-uDCFV6f8zJLZ/nyM0FmSWLNhKF0uzedontqYhDJVoJI=" + } + }, + "com.squareup.okhttp3:okhttp:3.12.3": { + "okhttp-3.12.3.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/com/squareup/okhttp3/okhttp/3.12.3/okhttp-3.12.3.jar" + ], + "hash": "sha256-gUWW1U7f2Ut9nYcSvzeYZ9ObCQQo3TjFEG0N7V2jVv8=" + }, + "okhttp-3.12.3.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/com/squareup/okhttp3/okhttp/3.12.3/okhttp-3.12.3.pom" + ], + "hash": "sha256-xXZHCTgwkLDEfEiizwh2OTvt1Ihmv83hk0NJf/oXuEQ=" + } + }, + "com.squareup.okio:okio:1.15.0": { + "okio-1.15.0.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/com/squareup/okio/okio/1.15.0/okio-1.15.0.jar" + ], + "hash": "sha256-aT+jGafohDMAYCsgQCO3Z08Qbry1d/LdWAchK2YRi9I=" + }, + "okio-1.15.0.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/com/squareup/okio/okio/1.15.0/okio-1.15.0.pom" + ], + "hash": "sha256-8cELFIDRq3X7BRoHsnPjfNolJel+Fgfug+aDO3Dhv84=" + } + }, + "com.squareup.okhttp3:logging-interceptor:3.12.3": { + "logging-interceptor-3.12.3.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/com/squareup/okhttp3/logging-interceptor/3.12.3/logging-interceptor-3.12.3.jar" + ], + "hash": "sha256-NNEihOBDYkI+VFe03a74xNhLyNgN1K0ZQ+Y8hQf4FXY=" + }, + "logging-interceptor-3.12.3.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/com/squareup/okhttp3/logging-interceptor/3.12.3/logging-interceptor-3.12.3.pom" + ], + "hash": "sha256-H/YmwXE+Itb1bpLtvctOnBRMszSoT6gAsHAfmW+xp/I=" + } + }, + "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.3": { + "kotlinx-coroutines-core-1.3.3.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlinx/kotlinx-coroutines-core/1.3.3/kotlinx-coroutines-core-1.3.3.jar" + ], + "hash": "sha256-91+LDzJgcX1FNZ+elv9mYT+gY+QGUSCwZ8yQcleghmo=" + }, + "kotlinx-coroutines-core-1.3.3.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlinx/kotlinx-coroutines-core/1.3.3/kotlinx-coroutines-core-1.3.3.pom" + ], + "hash": "sha256-KZmeUobJiKm3K3xt/rmE2fohxRcY9bb5P1Yh5wClN/4=" + } + }, + "io.javalin:javalin:3.7.0": { + "javalin-3.7.0.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/io/javalin/javalin/3.7.0/javalin-3.7.0.jar" + ], + "hash": "sha256-YGYQPPI2In7IacUllknrErvlwFyH8MHp9y86RGYOZ3I=" + }, + "javalin-3.7.0.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/io/javalin/javalin/3.7.0/javalin-3.7.0.pom" + ], + "hash": "sha256-11U3Www5qZiAEH3sDAzdMgDx7qi2npxtFkYdyuR050M=" + } + }, + "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.61": { + "kotlin-stdlib-jdk8-1.3.61.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.3.61/kotlin-stdlib-jdk8-1.3.61.jar" + ], + "hash": "sha256-ODm6fet5g3XaGAe8Rp0c8xXbemJ1WZ9zMYQ3R3LsOyE=" + }, + "kotlin-stdlib-jdk8-1.3.61.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.3.61/kotlin-stdlib-jdk8-1.3.61.pom" + ], + "hash": "sha256-4wGH5XIMpkC45oaG8g3QJQ3O8Bk9VuVWnDxKYSdzErY=" + } + }, + "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.61": { + "kotlin-stdlib-jdk7-1.3.61.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.3.61/kotlin-stdlib-jdk7-1.3.61.jar" + ], + "hash": "sha256-EfSlfj59gfPxUtXc7+Ob13YUtalBJf87EVJrChmsOYk=" + }, + "kotlin-stdlib-jdk7-1.3.61.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.3.61/kotlin-stdlib-jdk7-1.3.61.pom" + ], + "hash": "sha256-xBYICuq9uRGKCO54wo4oVgOM2FhYQipx98Rr8nb2Z6c=" + } + }, + "org.slf4j:slf4j-api:1.8.0-beta4": { + "slf4j-api-1.8.0-beta4.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/slf4j/slf4j-api/1.8.0-beta4/slf4j-api-1.8.0-beta4.jar" + ], + "hash": "sha256-YCtxIynIS0qDxARk9P39D+QjjFPvOXE5qGcGRznb9OA=" + }, + "slf4j-api-1.8.0-beta4.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/slf4j/slf4j-api/1.8.0-beta4/slf4j-api-1.8.0-beta4.pom" + ], + "hash": "sha256-+DFtKKzyUrIbHp6O7ZqEwq+9yOBA9p06ELq4E9PYWoU=" + } + }, + "org.eclipse.jetty:jetty-server:9.4.25.v20191220": { + "jetty-server-9.4.25.v20191220.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/eclipse/jetty/jetty-server/9.4.25.v20191220/jetty-server-9.4.25.v20191220.jar" + ], + "hash": "sha256-z89tvOS+zuXwZw2bx6do0bc87wyEZj6CrMC5EN8lZfQ=" + }, + "jetty-server-9.4.25.v20191220.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/eclipse/jetty/jetty-server/9.4.25.v20191220/jetty-server-9.4.25.v20191220.pom" + ], + "hash": "sha256-uWOogVUMUf5hOTgJbqfwWZLoGzBBy8faXgb6+8/YZWk=" + } + }, + "javax.servlet:javax.servlet-api:3.1.0": { + "javax.servlet-api-3.1.0.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/javax/servlet/javax.servlet-api/3.1.0/javax.servlet-api-3.1.0.jar" + ], + "hash": "sha256-r0VrLdQcToLPVPPnQ7xniXPZ/jW9TTBx+gXH5TM7hII=" + }, + "javax.servlet-api-3.1.0.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/javax/servlet/javax.servlet-api/3.1.0/javax.servlet-api-3.1.0.pom" + ], + "hash": "sha256-sxEJ4i6j8t8a15VUMucYo13vUK5sGWmANK+ooM+ekGk=" + } + }, + "org.eclipse.jetty:jetty-http:9.4.25.v20191220": { + "jetty-http-9.4.25.v20191220.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/eclipse/jetty/jetty-http/9.4.25.v20191220/jetty-http-9.4.25.v20191220.jar" + ], + "hash": "sha256-3JxGbw/kvzf9X02iPQFoSwZ63poWAsTzxbaUvMIIR7o=" + }, + "jetty-http-9.4.25.v20191220.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/eclipse/jetty/jetty-http/9.4.25.v20191220/jetty-http-9.4.25.v20191220.pom" + ], + "hash": "sha256-upIlnnZtESJEah+zuPZAXnroxcQS8i6XbLCEyoxhm4c=" + } + }, + "org.eclipse.jetty:jetty-util:9.4.25.v20191220": { + "jetty-util-9.4.25.v20191220.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/eclipse/jetty/jetty-util/9.4.25.v20191220/jetty-util-9.4.25.v20191220.jar" + ], + "hash": "sha256-IjeA1yTBx0Y8MjOoLAdobz/XigIvVg0BAlfb5AKODRQ=" + }, + "jetty-util-9.4.25.v20191220.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/eclipse/jetty/jetty-util/9.4.25.v20191220/jetty-util-9.4.25.v20191220.pom" + ], + "hash": "sha256-iEWSOTxmB1pqb6Z9iY532crIf1lIy10xDPyN5Z7wE8Y=" + } + }, + "org.eclipse.jetty:jetty-io:9.4.25.v20191220": { + "jetty-io-9.4.25.v20191220.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/eclipse/jetty/jetty-io/9.4.25.v20191220/jetty-io-9.4.25.v20191220.jar" + ], + "hash": "sha256-6cMdtQO2B1/UPxVTGQMAfB6Cn8JF2jQEtuQyfyTvlWk=" + }, + "jetty-io-9.4.25.v20191220.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/eclipse/jetty/jetty-io/9.4.25.v20191220/jetty-io-9.4.25.v20191220.pom" + ], + "hash": "sha256-q1nyQDwSIWhSvBzyhOVhETo9LgsZUmMwmLBfOQW9kYE=" + } + }, + "org.eclipse.jetty:jetty-webapp:9.4.25.v20191220": { + "jetty-webapp-9.4.25.v20191220.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/eclipse/jetty/jetty-webapp/9.4.25.v20191220/jetty-webapp-9.4.25.v20191220.jar" + ], + "hash": "sha256-qnWB2sNcrVdLA82aFEuibc9DeZ0k7P9enzoGiKJzLvE=" + }, + "jetty-webapp-9.4.25.v20191220.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/eclipse/jetty/jetty-webapp/9.4.25.v20191220/jetty-webapp-9.4.25.v20191220.pom" + ], + "hash": "sha256-HRXbctxeN+O+7iEjd1PsYkXn/sXj/ehUK5Yf/ZB9Ufw=" + } + }, + "org.eclipse.jetty:jetty-xml:9.4.25.v20191220": { + "jetty-xml-9.4.25.v20191220.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/eclipse/jetty/jetty-xml/9.4.25.v20191220/jetty-xml-9.4.25.v20191220.jar" + ], + "hash": "sha256-+xNsUWNTj8WHQuqlbfRIdlu4FvJd43Hasf6u5612tN8=" + }, + "jetty-xml-9.4.25.v20191220.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/eclipse/jetty/jetty-xml/9.4.25.v20191220/jetty-xml-9.4.25.v20191220.pom" + ], + "hash": "sha256-05fGZk1fr9j7VX7NJU4EZP16bakwG60B4C248SAFvrM=" + } + }, + "org.eclipse.jetty:jetty-servlet:9.4.25.v20191220": { + "jetty-servlet-9.4.25.v20191220.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/eclipse/jetty/jetty-servlet/9.4.25.v20191220/jetty-servlet-9.4.25.v20191220.jar" + ], + "hash": "sha256-LU0t1OZdCWL0/xHTycytMKYmN3fgVpwbVzE4aLNHchw=" + }, + "jetty-servlet-9.4.25.v20191220.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/eclipse/jetty/jetty-servlet/9.4.25.v20191220/jetty-servlet-9.4.25.v20191220.pom" + ], + "hash": "sha256-tw95bbbqAGKLv7ph5aLgMRLjJ10OaIHJN/ARwn/wXos=" + } + }, + "org.eclipse.jetty:jetty-security:9.4.25.v20191220": { + "jetty-security-9.4.25.v20191220.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/eclipse/jetty/jetty-security/9.4.25.v20191220/jetty-security-9.4.25.v20191220.jar" + ], + "hash": "sha256-Y3HBY7kqyNb6sIyx2rkdDmkOclZoc9j3g706d5Oj2iY=" + }, + "jetty-security-9.4.25.v20191220.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/eclipse/jetty/jetty-security/9.4.25.v20191220/jetty-security-9.4.25.v20191220.pom" + ], + "hash": "sha256-2j1qeYtoSPOXIPxweDTha+S8pC31qiXCWSK5Mvz+rus=" + } + }, + "org.eclipse.jetty.websocket:websocket-server:9.4.25.v20191220": { + "websocket-server-9.4.25.v20191220.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/eclipse/jetty/websocket/websocket-server/9.4.25.v20191220/websocket-server-9.4.25.v20191220.jar" + ], + "hash": "sha256-PjO/DwuuXX+/Rx16JWroB4UCkGoxIaCgANkU39F21bo=" + }, + "websocket-server-9.4.25.v20191220.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/eclipse/jetty/websocket/websocket-server/9.4.25.v20191220/websocket-server-9.4.25.v20191220.pom" + ], + "hash": "sha256-o9WaXSoxrXskMRuXh2Eog5sNeO+oH4blG6yqelb5MKQ=" + } + }, + "org.eclipse.jetty.websocket:websocket-common:9.4.25.v20191220": { + "websocket-common-9.4.25.v20191220.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/eclipse/jetty/websocket/websocket-common/9.4.25.v20191220/websocket-common-9.4.25.v20191220.jar" + ], + "hash": "sha256-1bvWkUvEIbKOB6MXkc2ZKgBnQX1rX9Bapkq1hDrydzw=" + }, + "websocket-common-9.4.25.v20191220.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/eclipse/jetty/websocket/websocket-common/9.4.25.v20191220/websocket-common-9.4.25.v20191220.pom" + ], + "hash": "sha256-ukxD7w1zKeye8StLaAa+D3rHPCQRm8vkvj1m7ebbmlQ=" + } + }, + "org.eclipse.jetty.websocket:websocket-api:9.4.25.v20191220": { + "websocket-api-9.4.25.v20191220.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/eclipse/jetty/websocket/websocket-api/9.4.25.v20191220/websocket-api-9.4.25.v20191220.jar" + ], + "hash": "sha256-sRCCel9HyDUQMj7hm+h+N7FUDhVTfNAhqTVJ4El0f68=" + }, + "websocket-api-9.4.25.v20191220.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/eclipse/jetty/websocket/websocket-api/9.4.25.v20191220/websocket-api-9.4.25.v20191220.pom" + ], + "hash": "sha256-xn/BVBQDGiWCGJri17IMVhDTUvWkf4fSi8+1lJQTWcs=" + } + }, + "org.eclipse.jetty.websocket:websocket-client:9.4.25.v20191220": { + "websocket-client-9.4.25.v20191220.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/eclipse/jetty/websocket/websocket-client/9.4.25.v20191220/websocket-client-9.4.25.v20191220.jar" + ], + "hash": "sha256-dIMBH4pyWNlP62P+SjZSCYG8HYXsPdGxSJlX1AcRFOw=" + }, + "websocket-client-9.4.25.v20191220.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/eclipse/jetty/websocket/websocket-client/9.4.25.v20191220/websocket-client-9.4.25.v20191220.pom" + ], + "hash": "sha256-AzAQdDEZ7xKiB2CXLdHAu6BwLuiVU5LtP/QdF2RMOs4=" + } + }, + "org.eclipse.jetty:jetty-client:9.4.25.v20191220": { + "jetty-client-9.4.25.v20191220.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/eclipse/jetty/jetty-client/9.4.25.v20191220/jetty-client-9.4.25.v20191220.jar" + ], + "hash": "sha256-qwUsaY1GjdLLjZ1bcH5VqUQDDZFfT59BrDzc+Cs9xuA=" + }, + "jetty-client-9.4.25.v20191220.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/eclipse/jetty/jetty-client/9.4.25.v20191220/jetty-client-9.4.25.v20191220.pom" + ], + "hash": "sha256-ixNwOlizo3BtJIiemlXDSRu+XY+DZdOoKkvvqcbp+eM=" + } + }, + "org.eclipse.jetty.websocket:websocket-servlet:9.4.25.v20191220": { + "websocket-servlet-9.4.25.v20191220.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/eclipse/jetty/websocket/websocket-servlet/9.4.25.v20191220/websocket-servlet-9.4.25.v20191220.jar" + ], + "hash": "sha256-nYoe6bmGzp/JJM3ai9fvzxwLZ0X3qWa1B8x3WU/vIms=" + }, + "websocket-servlet-9.4.25.v20191220.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/eclipse/jetty/websocket/websocket-servlet/9.4.25.v20191220/websocket-servlet-9.4.25.v20191220.pom" + ], + "hash": "sha256-WgVNHE2/17gfAqoOIR+pm7ZHZEn6T48swQGjvPtT7wY=" + } + }, + "org.slf4j:slf4j-simple:1.8.0-beta4": { + "slf4j-simple-1.8.0-beta4.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/slf4j/slf4j-simple/1.8.0-beta4/slf4j-simple-1.8.0-beta4.jar" + ], + "hash": "sha256-usZqvFtEYt/2Lh4ZqzsKZcFg681WTPJZENsAOo5WnP0=" + }, + "slf4j-simple-1.8.0-beta4.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/slf4j/slf4j-simple/1.8.0-beta4/slf4j-simple-1.8.0-beta4.pom" + ], + "hash": "sha256-oXrS6OU00OgZ6o0UIT3nSNRlD/8qJX0+kqE9oxAoe/c=" + } + }, + "org.xerial:sqlite-jdbc:3.30.1": { + "sqlite-jdbc-3.30.1.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/xerial/sqlite-jdbc/3.30.1/sqlite-jdbc-3.30.1.jar" + ], + "hash": "sha256-KAA0qJkwABBMWza8XhE5sOgt8d6c/ZUfUpva3q9vRW0=" + }, + "sqlite-jdbc-3.30.1.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/xerial/sqlite-jdbc/3.30.1/sqlite-jdbc-3.30.1.pom" + ], + "hash": "sha256-eGpZKh7AtwPJJVOlE37gAxGb5UmlGTM05t44WrKGb3I=" + } + }, + "org.postgresql:postgresql:42.2.2": { + "postgresql-42.2.2.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/postgresql/postgresql/42.2.2/postgresql-42.2.2.jar" + ], + "hash": "sha256-GZZSQCajAnhT85MuhjnvgTgH0bY/4Ugy9BD/+kJ0+nA=" + }, + "postgresql-42.2.2.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/postgresql/postgresql/42.2.2/postgresql-42.2.2.pom" + ], + "hash": "sha256-NbNCwrBu1Cf6VP/xw3GNQ2HvrcNC7DJM7DnBeKm481Y=" + } + }, + "com.fasterxml.jackson.core:jackson-databind:2.10.1": { + "jackson-databind-2.10.1.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/com/fasterxml/jackson/core/jackson-databind/2.10.1/jackson-databind-2.10.1.jar" + ], + "hash": "sha256-LSP0cAFJIjNWWt9aNPIl8q6JVkzuCAJIc+w2t4Qu3kY=" + }, + "jackson-databind-2.10.1.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/com/fasterxml/jackson/core/jackson-databind/2.10.1/jackson-databind-2.10.1.pom" + ], + "hash": "sha256-OGlQZeP1ILBbvY6lmC5ba1vZ+FYpZ7g9rLfQerCMauc=" + } + }, + "com.fasterxml.jackson.core:jackson-annotations:2.10.1": { + "jackson-annotations-2.10.1.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/com/fasterxml/jackson/core/jackson-annotations/2.10.1/jackson-annotations-2.10.1.jar" + ], + "hash": "sha256-Zz+K4Wvs6k+pN0BLOoUUF/r0LfO7xZICi74r/gzJ2Ms=" + }, + "jackson-annotations-2.10.1.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/com/fasterxml/jackson/core/jackson-annotations/2.10.1/jackson-annotations-2.10.1.pom" + ], + "hash": "sha256-7OURA2Z+iBHw/3RYmGryFxhi5UuYE8FwjPk3kESH+Vw=" + } + }, + "com.fasterxml.jackson.core:jackson-core:2.10.1": { + "jackson-core-2.10.1.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/com/fasterxml/jackson/core/jackson-core/2.10.1/jackson-core-2.10.1.jar" + ], + "hash": "sha256-eb/73NNJ9ppawlLitAlhMXBDhq9PoU2VOV6poOQjzzM=" + }, + "jackson-core-2.10.1.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/com/fasterxml/jackson/core/jackson-core/2.10.1/jackson-core-2.10.1.pom" + ], + "hash": "sha256-EXkJC3ILJankJmQwLwM0oiQLMMcoC0IkJeT0UZ5bLP4=" + } + }, + "org.jetbrains.kotlin:kotlin-stdlib:1.3.61": { + "kotlin-stdlib-1.3.61.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib/1.3.61/kotlin-stdlib-1.3.61.jar" + ], + "hash": "sha256-5R5RJhmn52UKMOtOs+nAPmkJx7XjwCZATgdiVMCYuTI=" + }, + "kotlin-stdlib-1.3.61.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib/1.3.61/kotlin-stdlib-1.3.61.pom" + ], + "hash": "sha256-2+W6vNjUPpsIwoRWgLU/wbs+BRxIBYAt3Q7T6OLFCoQ=" + } + }, + "org.jetbrains.kotlin:kotlin-stdlib-common:1.3.61": { + "kotlin-stdlib-common-1.3.61.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib-common/1.3.61/kotlin-stdlib-common-1.3.61.jar" + ], + "hash": "sha256-oufzQc8wR7XwChkX73d9MjzasqVzd0aLjtYqoxRpz38=" + }, + "kotlin-stdlib-common-1.3.61.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib-common/1.3.61/kotlin-stdlib-common-1.3.61.pom" + ], + "hash": "sha256-4i2wCbsaYWNtlCVjWYlzbbXj/KSUgJq/JERo3EdM/AQ=" + } + }, + "org.jetbrains.kotlin:kotlin-script-runtime:1.6.21": { + "kotlin-script-runtime-1.6.21.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-script-runtime/1.6.21/kotlin-script-runtime-1.6.21.jar" + ], + "hash": "sha256-YGw0p+bo5DnpIIdl59dbHbz4Dzg1Pz4puydFbXs3EXE=" + }, + "kotlin-script-runtime-1.6.21.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-script-runtime/1.6.21/kotlin-script-runtime-1.6.21.pom" + ], + "hash": "sha256-jVeQOOsdLK0DMFKOKdyMy4rozQ1WClRMXBswqT7O/t4=" + } + }, + "org.jetbrains.kotlin:kotlin-reflect:1.6.21": { + "kotlin-reflect-1.6.21.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-reflect/1.6.21/kotlin-reflect-1.6.21.jar" + ], + "hash": "sha256-Hh9XIJ9yOMP9FzWhuTOaVlZVB9yiSfg3G/WdkfYBrqo=" + }, + "kotlin-reflect-1.6.21.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-reflect/1.6.21/kotlin-reflect-1.6.21.pom" + ], + "hash": "sha256-2nHh493COI1nVkFnLi8DFtucnSEvlG8CbUoOahM2p/M=" + } + }, + "org.jetbrains.kotlin:kotlin-klib-commonizer-embeddable:1.6.21": { + "kotlin-klib-commonizer-embeddable-1.6.21.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-klib-commonizer-embeddable/1.6.21/kotlin-klib-commonizer-embeddable-1.6.21.jar" + ], + "hash": "sha256-1jgafq67fkj8p2v1u55fQ/pW3eb9UQXK6N4TXmMoD3A=" + }, + "kotlin-klib-commonizer-embeddable-1.6.21.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-klib-commonizer-embeddable/1.6.21/kotlin-klib-commonizer-embeddable-1.6.21.pom" + ], + "hash": "sha256-bz7nSBQNsbaG5nqJehadbeQv1nQkHVVA3FK7o2Re/i4=" + } + }, + "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.21": { + "kotlin-stdlib-jdk8-1.6.21.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.6.21/kotlin-stdlib-jdk8-1.6.21.jar" + ], + "hash": "sha256-2rRUibR3NtWfzkS4BnbxlHqba8qxD9YOh4qDvYKmlUw=" + }, + "kotlin-stdlib-jdk8-1.6.21.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.6.21/kotlin-stdlib-jdk8-1.6.21.pom" + ], + "hash": "sha256-g2oReaCNJJFGl9JhLgO4SKCHyAy0sMoj+c+rJH86dcQ=" + } + }, + "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.6.21": { + "kotlin-stdlib-jdk7-1.6.21.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.6.21/kotlin-stdlib-jdk7-1.6.21.jar" + ], + "hash": "sha256-8bBjTbuUFyA4RjAguy3UXKJoSfjOKdYlrLDxVp0R2+4=" + }, + "kotlin-stdlib-jdk7-1.6.21.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.6.21/kotlin-stdlib-jdk7-1.6.21.pom" + ], + "hash": "sha256-ARzSjruf3oFrA1nVrhCjZ07A/yxTEMBBLCDv6Oo9oG4=" + } + }, + "com.winterbe:expekt:0.5.0": { + "expekt-0.5.0.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/com/winterbe/expekt/0.5.0/expekt-0.5.0.jar" + ], + "hash": "sha256-mKJnQqgnRs1u5m7/u8PK/TInA+onhhf734u5tU3O8Xw=" + }, + "expekt-0.5.0.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/com/winterbe/expekt/0.5.0/expekt-0.5.0.pom" + ], + "hash": "sha256-We03cwfzVZIPORBAQ6YBDDrnbKWfKULGxk3Ttg0pEsc=" + } + }, + "org.spekframework.spek2:spek-dsl-jvm:2.0.9": { + "spek-dsl-jvm-2.0.9.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/spekframework/spek2/spek-dsl-jvm/2.0.9/spek-dsl-jvm-2.0.9.jar" + ], + "hash": "sha256-gohf+MCcJfvntBQS/IoIyCAn8kuE6gH3ZL5jm8CYGeg=" + }, + "spek-dsl-jvm-2.0.9.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/spekframework/spek2/spek-dsl-jvm/2.0.9/spek-dsl-jvm-2.0.9.pom" + ], + "hash": "sha256-q3b2C4rYViJC615qA1SLpiL6xHDFpE6pzckZ34VzgQQ=" + } + }, + "org.jetbrains.kotlin:kotlin-stdlib:1.0.3": { + "kotlin-stdlib-1.0.3.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib/1.0.3/kotlin-stdlib-1.0.3.jar" + ], + "hash": "sha256-ZyHVKFgAZF7WgZP35t0H0srOCd6fO3XN8fMFD0Y1l4E=" + }, + "kotlin-stdlib-1.0.3.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib/1.0.3/kotlin-stdlib-1.0.3.pom" + ], + "hash": "sha256-QK5yi+5hvXgSHcWjdko/vH1jRYaHuRmI2qXKMhFQNx0=" + } + }, + "org.jetbrains.kotlin:kotlin-runtime:1.0.3": { + "kotlin-runtime-1.0.3.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-runtime/1.0.3/kotlin-runtime-1.0.3.jar" + ], + "hash": "sha256-jBkPOBLPoR0I6wFBuMh36jJC8N3Q6r/llSV0Pq5ifo4=" + }, + "kotlin-runtime-1.0.3.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-runtime/1.0.3/kotlin-runtime-1.0.3.pom" + ], + "hash": "sha256-LaX+tSEGymCnZiDUaRgktUkbyi7ojMJVcwALCX3lRRc=" + } + }, + "org.spekframework.spek2:spek-runner-junit5:2.0.9": { + "spek-runner-junit5-2.0.9.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/spekframework/spek2/spek-runner-junit5/2.0.9/spek-runner-junit5-2.0.9.jar" + ], + "hash": "sha256-K0ZmWbdh12OKtc2CX8yC3CrA1FPJ6yAKGUAHG4KkpYY=" + }, + "spek-runner-junit5-2.0.9.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/spekframework/spek2/spek-runner-junit5/2.0.9/spek-runner-junit5-2.0.9.pom" + ], + "hash": "sha256-7GcxgitATmAvUWoOpzJFBWgHoMWg2Kb4SkTjqnfrSjg=" + } + }, + "org.spekframework.spek2:spek-runtime-jvm:2.0.9": { + "spek-runtime-jvm-2.0.9.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/spekframework/spek2/spek-runtime-jvm/2.0.9/spek-runtime-jvm-2.0.9.jar" + ], + "hash": "sha256-dZ18fuF78XJYwySioaGwhusrz2QnnBfh+av1Xsph+nQ=" + }, + "spek-runtime-jvm-2.0.9.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/spekframework/spek2/spek-runtime-jvm/2.0.9/spek-runtime-jvm-2.0.9.pom" + ], + "hash": "sha256-9o3lUgIMgh6TRueEI5uGtT5rR1+2DQRoWsdGILeiKeU=" + } + }, + "org.jetbrains.kotlin:kotlin-reflect:1.3.50": { + "kotlin-reflect-1.3.50.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-reflect/1.3.50/kotlin-reflect-1.3.50.jar" + ], + "hash": "sha256-ZFgxmepaVK79G9FZUoiSX3hCJu5WLR3SeQEcYHWz16Q=" + }, + "kotlin-reflect-1.3.50.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-reflect/1.3.50/kotlin-reflect-1.3.50.pom" + ], + "hash": "sha256-h0UYHlo+C6/v1GMJxrgQ33JT83n+uYUTHq+NTZwwJjU=" + } + }, + "io.github.classgraph:classgraph:4.8.37": { + "classgraph-4.8.37.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/io/github/classgraph/classgraph/4.8.37/classgraph-4.8.37.jar" + ], + "hash": "sha256-fR0+iCjB7vVJ1B7x7Oc9LFxYz7lRs/Igzwzx3SVVgXM=" + }, + "classgraph-4.8.37.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/io/github/classgraph/classgraph/4.8.37/classgraph-4.8.37.pom" + ], + "hash": "sha256-pJBV0GEleGZQvjPu13rphQCROLhNOWA7wesu08gIWzQ=" + } + }, + "org.jetbrains.kotlinx:kotlinx-coroutines-core-common:1.3.0": { + "kotlinx-coroutines-core-common-1.3.0.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlinx/kotlinx-coroutines-core-common/1.3.0/kotlinx-coroutines-core-common-1.3.0.jar" + ], + "hash": "sha256-Evof8J9dtFxlGJKP9HmjZPOqEXSRW6JgWkIF7GCwGMM=" + }, + "kotlinx-coroutines-core-common-1.3.0.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlinx/kotlinx-coroutines-core-common/1.3.0/kotlinx-coroutines-core-common-1.3.0.pom" + ], + "hash": "sha256-V2kqeg3vYTmMQz4s87C0p0l4ZpQuBLFFshG1t57CoYM=" + } + }, + "org.junit.platform:junit-platform-engine:1.5.2": { + "junit-platform-engine-1.5.2.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/junit/platform/junit-platform-engine/1.5.2/junit-platform-engine-1.5.2.jar" + ], + "hash": "sha256-/yC6StjADvF7rvnFVRL5wC2aaHQPfxrAGppqoCOZMfg=" + }, + "junit-platform-engine-1.5.2.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/junit/platform/junit-platform-engine/1.5.2/junit-platform-engine-1.5.2.pom" + ], + "hash": "sha256-LUuVVVwh4IXrwd299C156x1VZA3Bk7G35hACQP0vGJ8=" + } + }, + "org.apiguardian:apiguardian-api:1.1.0": { + "apiguardian-api-1.1.0.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/apiguardian/apiguardian-api/1.1.0/apiguardian-api-1.1.0.jar" + ], + "hash": "sha256-qarp/4rj4XoqGPeRdegrFiZ8JG+708qd+7spCwjc/dQ=" + }, + "apiguardian-api-1.1.0.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/apiguardian/apiguardian-api/1.1.0/apiguardian-api-1.1.0.pom" + ], + "hash": "sha256-qUW5y1zZt3sscRhE5lnEPsBw71nZ9Qn6n0wYYbSGJxE=" + } + }, + "org.opentest4j:opentest4j:1.2.0": { + "opentest4j-1.2.0.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/opentest4j/opentest4j/1.2.0/opentest4j-1.2.0.jar" + ], + "hash": "sha256-WIEt5giY2Xb7ge87YtoFxmBMGP1KJJ9QRCgkefwoavI=" + }, + "opentest4j-1.2.0.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/opentest4j/opentest4j/1.2.0/opentest4j-1.2.0.pom" + ], + "hash": "sha256-qW5nGBbB/4gDvex0ySQfAlvfsnfaXStO4CJmQFk2+ZQ=" + } + }, + "org.junit.platform:junit-platform-commons:1.5.2": { + "junit-platform-commons-1.5.2.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/junit/platform/junit-platform-commons/1.5.2/junit-platform-commons-1.5.2.jar" + ], + "hash": "sha256-/ESv38DyDIXnGmbnlDKBrvO8Hg/WLS1po2y2kB5oLBA=" + }, + "junit-platform-commons-1.5.2.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/junit/platform/junit-platform-commons/1.5.2/junit-platform-commons-1.5.2.pom" + ], + "hash": "sha256-O9DU3tYyqK+MpYf7Z2QBnedxsda8uJrNViQ1oQCfqto=" + } + } +} \ No newline at end of file diff --git a/fixtures/golden/buildsrc/plugin-in-buildsrc.kotlin.json b/fixtures/golden/buildsrc/plugin-in-buildsrc.kotlin.json new file mode 100644 index 0000000..5be4cf3 --- /dev/null +++ b/fixtures/golden/buildsrc/plugin-in-buildsrc.kotlin.json @@ -0,0 +1,576 @@ +{ + "org.gradle.kotlin.kotlin-dsl:org.gradle.kotlin.kotlin-dsl.gradle.plugin:4.1.0": { + "org.gradle.kotlin.kotlin-dsl.gradle.plugin-4.1.0.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/gradle/kotlin/kotlin-dsl/org.gradle.kotlin.kotlin-dsl.gradle.plugin/4.1.0/org.gradle.kotlin.kotlin-dsl.gradle.plugin-4.1.0.pom" + ], + "hash": "sha256-PKFoMBmO4Pc//RRgknW3A5zR/seULCInsEX6JpbjV2c=" + } + }, + "org.gradle.kotlin:gradle-kotlin-dsl-plugins:4.1.0": { + "gradle-kotlin-dsl-plugins-4.1.0.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/gradle/kotlin/gradle-kotlin-dsl-plugins/4.1.0/gradle-kotlin-dsl-plugins-4.1.0.jar" + ], + "hash": "sha256-jpBY+e9xNsVaJKXPKgoZKNb9oYJ2JZ0KktaxAr8m1Cc=" + }, + "gradle-kotlin-dsl-plugins-4.1.0.module": { + "urls": [ + "https://plugins.gradle.org/m2/org/gradle/kotlin/gradle-kotlin-dsl-plugins/4.1.0/gradle-kotlin-dsl-plugins-4.1.0.module" + ], + "hash": "sha256-0KuS4r6L+a8iIhAL4VOLP2R7SvA0+ZA1RJGKO4dxrq8=" + } + }, + "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.0": { + "kotlin-stdlib-jdk8-1.9.0.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.9.0/kotlin-stdlib-jdk8-1.9.0.jar" + ], + "hash": "sha256-pZ+iT98f+1lLrs2/D9EAEPl3zqECNtSH/jRkl3pzd/o=" + }, + "kotlin-stdlib-jdk8-1.9.0.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.9.0/kotlin-stdlib-jdk8-1.9.0.pom" + ], + "hash": "sha256-ZNWY3YjiUEZnMeIDBKtvBsu7urfuMitHA7a1n4gcT5I=" + } + }, + "org.jetbrains.kotlin:kotlin-stdlib:1.9.0": { + "kotlin-stdlib-1.9.0.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-stdlib/1.9.0/kotlin-stdlib-1.9.0.jar" + ], + "hash": "sha256-Na7/vi21qkRgcs7lD87ki3+p4vxRyjfAzH19C8OdlS4=" + }, + "kotlin-stdlib-1.9.0.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-stdlib/1.9.0/kotlin-stdlib-1.9.0.pom" + ], + "hash": "sha256-N3UiY/Ysw+MlCFbiiO5Kc9QQLXJqd2JwNPlIBsjBCso=" + } + }, + "org.jetbrains.kotlin:kotlin-stdlib-common:1.9.0": { + "kotlin-stdlib-common-1.9.0.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-stdlib-common/1.9.0/kotlin-stdlib-common-1.9.0.jar" + ], + "hash": "sha256-KDJ0IEvXwCB4nsRvj45yr0JE1/VQszkqV+XKAGrXqiw=" + }, + "kotlin-stdlib-common-1.9.0.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-stdlib-common/1.9.0/kotlin-stdlib-common-1.9.0.pom" + ], + "hash": "sha256-NmDTanD+s6vknxG5BjPkHTYnNXbwcbDhCdqbOg3wgqU=" + } + }, + "org.jetbrains:annotations:13.0": { + "annotations-13.0.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/annotations/13.0/annotations-13.0.jar" + ], + "hash": "sha256-rOKhDcji1f00kl7KwD5JiLLA+FFlDJS4zvSbob0RFHg=" + }, + "annotations-13.0.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/annotations/13.0/annotations-13.0.pom" + ], + "hash": "sha256-llrrK+3/NpgZvd4b96CzuJuCR91pyIuGN112Fju4w5c=" + } + }, + "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.0": { + "kotlin-stdlib-jdk7-1.9.0.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.9.0/kotlin-stdlib-jdk7-1.9.0.jar" + ], + "hash": "sha256-t5eaeqyUBV8Nnx/TtHzl/+HLYDKoQrqfvnGG8IUokXg=" + }, + "kotlin-stdlib-jdk7-1.9.0.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.9.0/kotlin-stdlib-jdk7-1.9.0.pom" + ], + "hash": "sha256-wRB08MiYqYuGPGFEcdQ409+Soewzgqbjf5NdfXGVS1o=" + } + }, + "org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.0": { + "kotlin-gradle-plugin-1.9.0-gradle81.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-gradle-plugin/1.9.0/kotlin-gradle-plugin-1.9.0-gradle81.jar" + ], + "hash": "sha256-2UOg4ntHRuAfg0BaSfUmoi9HGx2x1GOdb84m1xHv+/4=" + }, + "kotlin-gradle-plugin-1.9.0.module": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-gradle-plugin/1.9.0/kotlin-gradle-plugin-1.9.0.module" + ], + "hash": "sha256-/mOWRe2PvcW8Et588lRopNsx52O6ea26ogahTeYP0xQ=" + } + }, + "org.jetbrains.kotlin:kotlin-gradle-plugin-api:1.9.0": { + "kotlin-gradle-plugin-api-1.9.0-gradle81.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-gradle-plugin-api/1.9.0/kotlin-gradle-plugin-api-1.9.0-gradle81.jar" + ], + "hash": "sha256-AhbssMeA+V47EBoOlc/DZ/LvVrBvLeqqqZJaDxF8INk=" + }, + "kotlin-gradle-plugin-api-1.9.0.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-gradle-plugin-api/1.9.0/kotlin-gradle-plugin-api-1.9.0.jar" + ], + "hash": "sha256-AhbssMeA+V47EBoOlc/DZ/LvVrBvLeqqqZJaDxF8INk=" + }, + "kotlin-gradle-plugin-api-1.9.0.module": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-gradle-plugin-api/1.9.0/kotlin-gradle-plugin-api-1.9.0.module" + ], + "hash": "sha256-Gu1WWijZE0KsWkuhRsKFal67xjkoXFKVUhefr8dhTds=" + } + }, + "org.jetbrains.kotlin:kotlin-gradle-plugins-bom:1.9.0": { + "kotlin-gradle-plugins-bom-1.9.0.module": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-gradle-plugins-bom/1.9.0/kotlin-gradle-plugins-bom-1.9.0.module" + ], + "hash": "sha256-/SufabguQ8tQnwhBsYvzxd99q18eC+oDT8CypZnW4pY=" + }, + "kotlin-gradle-plugins-bom-1.9.0.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-gradle-plugins-bom/1.9.0/kotlin-gradle-plugins-bom-1.9.0.pom" + ], + "hash": "sha256-XTmtkDDf0zsLrCn3UAAJzmKHYulP5h8BF3rhNL24D6E=" + } + }, + "org.jetbrains.kotlin:kotlin-gradle-plugin-model:1.9.0": { + "kotlin-gradle-plugin-model-1.9.0.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-gradle-plugin-model/1.9.0/kotlin-gradle-plugin-model-1.9.0.jar" + ], + "hash": "sha256-/wWqtoizSnmSp+NfncTewlhD8Tqelwrv5W3T4SKghBU=" + }, + "kotlin-gradle-plugin-model-1.9.0.module": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-gradle-plugin-model/1.9.0/kotlin-gradle-plugin-model-1.9.0.module" + ], + "hash": "sha256-ZvS5FEcM80bgXQP4otWenHRvpu3PZMcS8XagiBqsPkk=" + } + }, + "org.jetbrains.kotlin:kotlin-tooling-core:1.9.0": { + "kotlin-tooling-core-1.9.0.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-tooling-core/1.9.0/kotlin-tooling-core-1.9.0.jar" + ], + "hash": "sha256-5TmiJCi5ysZ/dbLWHytuKnGDUNe7MYI8fcqJB/kJma4=" + }, + "kotlin-tooling-core-1.9.0.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-tooling-core/1.9.0/kotlin-tooling-core-1.9.0.pom" + ], + "hash": "sha256-KSbD/CFJWXAcjxYNjdyGn3s/pFKv3o3RdcjnwrvHBSI=" + } + }, + "org.jetbrains.kotlin:kotlin-sam-with-receiver:1.9.0": { + "kotlin-sam-with-receiver-1.9.0-gradle81.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-sam-with-receiver/1.9.0/kotlin-sam-with-receiver-1.9.0-gradle81.jar" + ], + "hash": "sha256-mK1U0XMRxg6FyGlDPV3TdpMTIDABJwCb5+nrsyH8xCs=" + }, + "kotlin-sam-with-receiver-1.9.0.module": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-sam-with-receiver/1.9.0/kotlin-sam-with-receiver-1.9.0.module" + ], + "hash": "sha256-GTPz8nsCrr/6LnvBmWN8TKg90xgHqjgwDLHh+jGsoiU=" + } + }, + "org.jetbrains.kotlin:kotlin-assignment:1.9.0": { + "kotlin-assignment-1.9.0-gradle81.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-assignment/1.9.0/kotlin-assignment-1.9.0-gradle81.jar" + ], + "hash": "sha256-mBdB+GlpwM4YQ1pcEJePJ3VGMxbOgqp/gmfsf2qfFNk=" + }, + "kotlin-assignment-1.9.0.module": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-assignment/1.9.0/kotlin-assignment-1.9.0.module" + ], + "hash": "sha256-kqBzm0y4NgSm3jddsKP+gTUDhZy8+aLtqZ++CXs8e9Y=" + } + }, + "org.jetbrains.kotlin:kotlin-gradle-plugin-annotations:1.9.0": { + "kotlin-gradle-plugin-annotations-1.9.0.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-gradle-plugin-annotations/1.9.0/kotlin-gradle-plugin-annotations-1.9.0.jar" + ], + "hash": "sha256-KiZbNbx8VYIUJukbajzU9LkWOKC8fIffz+TgX36IRzg=" + }, + "kotlin-gradle-plugin-annotations-1.9.0.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-gradle-plugin-annotations/1.9.0/kotlin-gradle-plugin-annotations-1.9.0.pom" + ], + "hash": "sha256-qjXuWp0dpZMV92nH/tsshwBWD1PFaAupKwsniBpKQnE=" + } + }, + "org.jetbrains.kotlin:kotlin-native-utils:1.9.0": { + "kotlin-native-utils-1.9.0.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-native-utils/1.9.0/kotlin-native-utils-1.9.0.jar" + ], + "hash": "sha256-D0gF93S/tcG20UXf5g09ILGu4ACJbOjs8+7/hM5EAjs=" + }, + "kotlin-native-utils-1.9.0.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-native-utils/1.9.0/kotlin-native-utils-1.9.0.pom" + ], + "hash": "sha256-Kazy3vkmve3Z6yBOx3Tnhq3Ek0EGqSlJAK81FVbn52o=" + } + }, + "org.jetbrains.kotlin:kotlin-util-io:1.9.0": { + "kotlin-util-io-1.9.0.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-util-io/1.9.0/kotlin-util-io-1.9.0.jar" + ], + "hash": "sha256-gdNtxt4/C+rwTDl6BrVoITujc6vO9Tlyt7gvYX1a9ws=" + }, + "kotlin-util-io-1.9.0.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-util-io/1.9.0/kotlin-util-io-1.9.0.pom" + ], + "hash": "sha256-BUBa+xn3tgbFKr6haJqi1ew9qQL+4teoV2lDOeSgQJQ=" + } + }, + "org.jetbrains.kotlin:kotlin-util-klib:1.9.0": { + "kotlin-util-klib-1.9.0.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-util-klib/1.9.0/kotlin-util-klib-1.9.0.jar" + ], + "hash": "sha256-wtxr820zabdzdVq4k3uX5Tmk1F04Fb7xm+36FUsS0xM=" + }, + "kotlin-util-klib-1.9.0.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-util-klib/1.9.0/kotlin-util-klib-1.9.0.pom" + ], + "hash": "sha256-acZHGaDgRjb2whMZ/lGKC1gieEu66P7YXmUfv6b4pFs=" + } + }, + "org.jetbrains.kotlin:kotlin-project-model:1.9.0": { + "kotlin-project-model-1.9.0.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-project-model/1.9.0/kotlin-project-model-1.9.0.jar" + ], + "hash": "sha256-rgk26bTGkh8D73eTYcrLu3XtKyIQR5+S9l8cushuNAw=" + }, + "kotlin-project-model-1.9.0.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-project-model/1.9.0/kotlin-project-model-1.9.0.pom" + ], + "hash": "sha256-6sHGTGAMYAJ7jmjfrTogPEFbn6IkWB1oz6Xnc+hWa3g=" + } + }, + "org.jetbrains.kotlin:kotlin-gradle-plugin-idea:1.9.0": { + "kotlin-gradle-plugin-idea-1.9.0.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-gradle-plugin-idea/1.9.0/kotlin-gradle-plugin-idea-1.9.0.jar" + ], + "hash": "sha256-C7JszuNyr7m1hPKFfofMLz9TK2Kk9/pFdVjzr8Or6ZU=" + }, + "kotlin-gradle-plugin-idea-1.9.0.module": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-gradle-plugin-idea/1.9.0/kotlin-gradle-plugin-idea-1.9.0.module" + ], + "hash": "sha256-pVg82bC0AS6VWtlhn7kBe1saosJRsFltr1M0kExcpls=" + } + }, + "org.jetbrains.kotlin:kotlin-gradle-plugin-idea-proto:1.9.0": { + "kotlin-gradle-plugin-idea-proto-1.9.0.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-gradle-plugin-idea-proto/1.9.0/kotlin-gradle-plugin-idea-proto-1.9.0.jar" + ], + "hash": "sha256-54mY6iK81fVtE5gszBJMnHM3vRUHVMvcivKhSNdslwA=" + }, + "kotlin-gradle-plugin-idea-proto-1.9.0.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-gradle-plugin-idea-proto/1.9.0/kotlin-gradle-plugin-idea-proto-1.9.0.pom" + ], + "hash": "sha256-QeElaFOKOjQHBMlww6T2D/WAH/3OQlymjKLAtHIhclA=" + } + }, + "org.jetbrains.kotlin:kotlin-klib-commonizer-api:1.9.0": { + "kotlin-klib-commonizer-api-1.9.0.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-klib-commonizer-api/1.9.0/kotlin-klib-commonizer-api-1.9.0.jar" + ], + "hash": "sha256-hd4ulysLtvRSjB90j0PEhmMUX8hTnsqWwT0bwNo/k10=" + }, + "kotlin-klib-commonizer-api-1.9.0.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-klib-commonizer-api/1.9.0/kotlin-klib-commonizer-api-1.9.0.pom" + ], + "hash": "sha256-hE+SCI/0j4v64lYh4yStw1d5CM9lD0/bJWlhC4+vaz4=" + } + }, + "org.jetbrains.kotlin:kotlin-build-tools-api:1.9.0": { + "kotlin-build-tools-api-1.9.0.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-build-tools-api/1.9.0/kotlin-build-tools-api-1.9.0.jar" + ], + "hash": "sha256-0ZyavegKnPQtJezJLLEUjtOhXu2yZy2qHkZubaWQ7sY=" + }, + "kotlin-build-tools-api-1.9.0.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-build-tools-api/1.9.0/kotlin-build-tools-api-1.9.0.pom" + ], + "hash": "sha256-iIFxmCZbm8BrPzSTWyv2qpj0gA7OvBzOQREN70m3pCE=" + } + }, + "org.jetbrains.kotlin:kotlin-compiler-embeddable:1.9.0": { + "kotlin-compiler-embeddable-1.9.0.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-compiler-embeddable/1.9.0/kotlin-compiler-embeddable-1.9.0.jar" + ], + "hash": "sha256-lTtQaR103SGf8/ZkRnDD5UnYfN/AphVPFgKmoKXf43M=" + }, + "kotlin-compiler-embeddable-1.9.0.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-compiler-embeddable/1.9.0/kotlin-compiler-embeddable-1.9.0.pom" + ], + "hash": "sha256-7jyHHO39bM9A0ESUQJbihlXeL5NF2ET+7GPMqcXkt8w=" + } + }, + "org.jetbrains.kotlin:kotlin-daemon-embeddable:1.9.0": { + "kotlin-daemon-embeddable-1.9.0.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-daemon-embeddable/1.9.0/kotlin-daemon-embeddable-1.9.0.jar" + ], + "hash": "sha256-lDGCoTfs1A5ikFjaUtszbO6bZm+AmWtlWELyCmNz35o=" + }, + "kotlin-daemon-embeddable-1.9.0.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-daemon-embeddable/1.9.0/kotlin-daemon-embeddable-1.9.0.pom" + ], + "hash": "sha256-OOMsu5GFenIGy/3cON1O4L5mX0CgEV/21NF348f4kq8=" + } + }, + "org.jetbrains.intellij.deps:trove4j:1.0.20200330": { + "trove4j-1.0.20200330.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/intellij/deps/trove4j/1.0.20200330/trove4j-1.0.20200330.jar" + ], + "hash": "sha256-xf1yW/+rUYRr88d9sTg8YKquv+G3/i8A0j/ht98KQ50=" + }, + "trove4j-1.0.20200330.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/intellij/deps/trove4j/1.0.20200330/trove4j-1.0.20200330.pom" + ], + "hash": "sha256-h3IcuqZaPJfYsbqdIHhA8WTJ/jh1n8nqEP/iZWX40+k=" + } + }, + "org.jetbrains.kotlin:kotlin-android-extensions:1.9.0": { + "kotlin-android-extensions-1.9.0.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-android-extensions/1.9.0/kotlin-android-extensions-1.9.0.jar" + ], + "hash": "sha256-IGdbuyp7aYKnFEUYpRi5qJZfkK6JuMmHiJgBIkSh6jM=" + }, + "kotlin-android-extensions-1.9.0.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-android-extensions/1.9.0/kotlin-android-extensions-1.9.0.pom" + ], + "hash": "sha256-WOHIg3JukUaVIDV95u0Xw/mSnI4IsRdKJ6OtbsDrzVI=" + } + }, + "org.jetbrains.kotlin:kotlin-compiler-runner:1.9.0": { + "kotlin-compiler-runner-1.9.0.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-compiler-runner/1.9.0/kotlin-compiler-runner-1.9.0.jar" + ], + "hash": "sha256-BbWlXUlJFhpxT838ZKXwTYejZMm73IZBs7t6VVfkPqo=" + }, + "kotlin-compiler-runner-1.9.0.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-compiler-runner/1.9.0/kotlin-compiler-runner-1.9.0.pom" + ], + "hash": "sha256-GIN9BlVntn5bzy4wTYWm4rbSRDz4En9AdnNKTo3yo2E=" + } + }, + "org.jetbrains.kotlin:kotlin-daemon-client:1.9.0": { + "kotlin-daemon-client-1.9.0.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-daemon-client/1.9.0/kotlin-daemon-client-1.9.0.jar" + ], + "hash": "sha256-WoyXfn3H8N4tka5Yva3CBBXe9M9dST5aGD+WxZeZAyA=" + }, + "kotlin-daemon-client-1.9.0.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-daemon-client/1.9.0/kotlin-daemon-client-1.9.0.pom" + ], + "hash": "sha256-sv50dBgjzM6JMXCWkg3LnOXBa460Z5jTn9jmdwPFidc=" + } + }, + "org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.5.0": { + "kotlinx-coroutines-core-jvm-1.5.0.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.5.0/kotlinx-coroutines-core-jvm-1.5.0.jar" + ], + "hash": "sha256-eNbMcTX4TWkv83Uvz9H6G74JQNffcGUuTx6u7Ax4r7s=" + }, + "kotlinx-coroutines-core-jvm-1.5.0.module": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.5.0/kotlinx-coroutines-core-jvm-1.5.0.module" + ], + "hash": "sha256-yIXdAoEHbFhDgm3jF+PLzcPYhZ2+71OuHPrNG5xg+W4=" + } + }, + "org.jetbrains.kotlin:kotlin-scripting-compiler-embeddable:1.9.0": { + "kotlin-scripting-compiler-embeddable-1.9.0.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-scripting-compiler-embeddable/1.9.0/kotlin-scripting-compiler-embeddable-1.9.0.jar" + ], + "hash": "sha256-Rp3RIGiVipevzhGIoBRFVM3tZ95aGqC/XKUwOfKdizY=" + }, + "kotlin-scripting-compiler-embeddable-1.9.0.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-scripting-compiler-embeddable/1.9.0/kotlin-scripting-compiler-embeddable-1.9.0.pom" + ], + "hash": "sha256-UJ5ikUwwvT43GcDgLk5Df8vxkRa4ouZYSPbUbFTsXJ4=" + } + }, + "org.jetbrains.kotlin:kotlin-scripting-compiler-impl-embeddable:1.9.0": { + "kotlin-scripting-compiler-impl-embeddable-1.9.0.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-scripting-compiler-impl-embeddable/1.9.0/kotlin-scripting-compiler-impl-embeddable-1.9.0.jar" + ], + "hash": "sha256-ZcUlbiyPvdpCbAMobmomgmfXEu+V7C05yVKSlXN4sE0=" + }, + "kotlin-scripting-compiler-impl-embeddable-1.9.0.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-scripting-compiler-impl-embeddable/1.9.0/kotlin-scripting-compiler-impl-embeddable-1.9.0.pom" + ], + "hash": "sha256-zaYp8KgbqcAKM95QJ/D4/7+fC45E5BlwNHdmu4WLTmM=" + } + }, + "org.jetbrains.kotlin:kotlin-scripting-common:1.9.0": { + "kotlin-scripting-common-1.9.0.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-scripting-common/1.9.0/kotlin-scripting-common-1.9.0.jar" + ], + "hash": "sha256-KFxBwO7tWaDn3LGIuJ+BVBR/vLvVNUcc9piWzNonP4Q=" + }, + "kotlin-scripting-common-1.9.0.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-scripting-common/1.9.0/kotlin-scripting-common-1.9.0.pom" + ], + "hash": "sha256-C/MG5PRe6xrDAk4pnUybcL4J7OmsAOcLL/mLPtQA8wc=" + } + }, + "org.jetbrains.kotlin:kotlin-scripting-jvm:1.9.0": { + "kotlin-scripting-jvm-1.9.0.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-scripting-jvm/1.9.0/kotlin-scripting-jvm-1.9.0.jar" + ], + "hash": "sha256-Zp3SvGBBjgdRU/2K2NllvlaetlwvMIOIyxEDud8TUVk=" + }, + "kotlin-scripting-jvm-1.9.0.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-scripting-jvm/1.9.0/kotlin-scripting-jvm-1.9.0.pom" + ], + "hash": "sha256-Ymj4BVjTX9qSRruneTKWkrlNWJWWVQfL6KhIkSxQ5yo=" + } + }, + "org.jetbrains.kotlin:kotlin-reflect:1.9.0": { + "kotlin-reflect-1.9.0.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-reflect/1.9.0/kotlin-reflect-1.9.0.jar" + ], + "hash": "sha256-IHAVm+UU6o6jziyLHueZPm/2CpNWWimfIAYpqn339YE=" + }, + "kotlin-reflect-1.9.0.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-reflect/1.9.0/kotlin-reflect-1.9.0.pom" + ], + "hash": "sha256-hXFWVnbRiXIEcTBs7ppV2RbAgDgvNabaaBk6x7EvTNs=" + } + }, + "com.gradle.publish:plugin-publish-plugin:1.2.1": { + "plugin-publish-plugin-1.2.1.jar": { + "urls": [ + "https://plugins.gradle.org/m2/com/gradle/publish/plugin-publish-plugin/1.2.1/plugin-publish-plugin-1.2.1.jar" + ], + "hash": "sha256-KY8MLpeVMhcaBaQWAyY3M7ZfiRE9ToCczQ4mmQFJ3hg=" + }, + "plugin-publish-plugin-1.2.1.module": { + "urls": [ + "https://plugins.gradle.org/m2/com/gradle/publish/plugin-publish-plugin/1.2.1/plugin-publish-plugin-1.2.1.module" + ], + "hash": "sha256-w98uuag1ZdO2MVDYa0344o9mG1XOzdRJJ+RpMxA2yxk=" + } + }, + "org.apache.maven:maven-model:3.6.3": { + "maven-model-3.6.3.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/apache/maven/maven-model/3.6.3/maven-model-3.6.3.jar" + ], + "hash": "sha256-F87x9Y4UbvDX2elrO5LZih1v19KzKIulOOj/Hg2RYM8=" + }, + "maven-model-3.6.3.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/apache/maven/maven-model/3.6.3/maven-model-3.6.3.pom" + ], + "hash": "sha256-fHIOjLA9KFxxzW4zTZyeWWBivdMQ7grRX1xHmpkxVPA=" + } + }, + "org.jetbrains.kotlin:kotlin-script-runtime:1.9.0": { + "kotlin-script-runtime-1.9.0.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-script-runtime/1.9.0/kotlin-script-runtime-1.9.0.jar" + ], + "hash": "sha256-2E0MykEFD6wqlS0RBEc8Mpr/BMULNxMuV1SiCYCa3Z8=" + }, + "kotlin-script-runtime-1.9.0.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-script-runtime/1.9.0/kotlin-script-runtime-1.9.0.pom" + ], + "hash": "sha256-FNwHhdGUFP7sEgI91z64DIPQeIC3PlS48IqHj0I9seY=" + } + }, + "org.jetbrains.kotlin:kotlin-reflect:1.6.10": { + "kotlin-reflect-1.6.10.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-reflect/1.6.10/kotlin-reflect-1.6.10.jar" + ], + "hash": "sha256-MnesECrheq0QpVq+x1/1aWyNEJeQOWQ0tJbnUIeFQgM=" + }, + "kotlin-reflect-1.6.10.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-reflect/1.6.10/kotlin-reflect-1.6.10.pom" + ], + "hash": "sha256-V5BVJCdKAK4CiqzMJyg/a8WSWpNKBGwcxdBsjuTW1ak=" + } + }, + "org.jetbrains.kotlin:kotlin-sam-with-receiver-compiler-plugin-embeddable:1.9.0": { + "kotlin-sam-with-receiver-compiler-plugin-embeddable-1.9.0.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-sam-with-receiver-compiler-plugin-embeddable/1.9.0/kotlin-sam-with-receiver-compiler-plugin-embeddable-1.9.0.jar" + ], + "hash": "sha256-o7OlA4sNEybfGz/lvyCmaQnt1C3qa2P3BgCm8k/0VYw=" + }, + "kotlin-sam-with-receiver-compiler-plugin-embeddable-1.9.0.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-sam-with-receiver-compiler-plugin-embeddable/1.9.0/kotlin-sam-with-receiver-compiler-plugin-embeddable-1.9.0.pom" + ], + "hash": "sha256-WdgiTXgRi2RAwdRsCRzwLOWSnQaTU0hZnxP9wrmoU88=" + } + }, + "org.jetbrains.kotlin:kotlin-assignment-compiler-plugin-embeddable:1.9.0": { + "kotlin-assignment-compiler-plugin-embeddable-1.9.0.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-assignment-compiler-plugin-embeddable/1.9.0/kotlin-assignment-compiler-plugin-embeddable-1.9.0.jar" + ], + "hash": "sha256-MBY6DUXnN9gHW0kI0PCv8DXvqGa+iFIvQ37Jbg43KZQ=" + }, + "kotlin-assignment-compiler-plugin-embeddable-1.9.0.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-assignment-compiler-plugin-embeddable/1.9.0/kotlin-assignment-compiler-plugin-embeddable-1.9.0.pom" + ], + "hash": "sha256-HDfsoLIO1JYF6lVsonWq8eyZUaKZge4TNxZYP2pD4J8=" + } + } +} \ No newline at end of file diff --git a/fixtures/golden/dependency/classifier.groovy.json b/fixtures/golden/dependency/classifier.groovy.json new file mode 100644 index 0000000..c4ee874 --- /dev/null +++ b/fixtures/golden/dependency/classifier.groovy.json @@ -0,0 +1,16 @@ +{ + "com.badlogicgames.gdx:gdx-platform:1.9.9": { + "gdx-platform-1.9.9-natives-desktop.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/com/badlogicgames/gdx/gdx-platform/1.9.9/gdx-platform-1.9.9-natives-desktop.jar" + ], + "hash": "sha256-e8c9VPpFH+LeJU6PgmCkOb/jutOxFnO6LPMaTxL2hU8=" + }, + "gdx-platform-1.9.9.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/com/badlogicgames/gdx/gdx-platform/1.9.9/gdx-platform-1.9.9.pom" + ], + "hash": "sha256-SWnDZyJaErav4Z4sA+D1WA3U1aQOSR64sd8+cQzofSY=" + } + } +} \ No newline at end of file diff --git a/fixtures/golden/dependency/classifier.kotlin.json b/fixtures/golden/dependency/classifier.kotlin.json new file mode 100644 index 0000000..c4ee874 --- /dev/null +++ b/fixtures/golden/dependency/classifier.kotlin.json @@ -0,0 +1,16 @@ +{ + "com.badlogicgames.gdx:gdx-platform:1.9.9": { + "gdx-platform-1.9.9-natives-desktop.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/com/badlogicgames/gdx/gdx-platform/1.9.9/gdx-platform-1.9.9-natives-desktop.jar" + ], + "hash": "sha256-e8c9VPpFH+LeJU6PgmCkOb/jutOxFnO6LPMaTxL2hU8=" + }, + "gdx-platform-1.9.9.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/com/badlogicgames/gdx/gdx-platform/1.9.9/gdx-platform-1.9.9.pom" + ], + "hash": "sha256-SWnDZyJaErav4Z4sA+D1WA3U1aQOSR64sd8+cQzofSY=" + } + } +} \ No newline at end of file diff --git a/fixtures/golden/dependency/maven-bom.kotlin.json b/fixtures/golden/dependency/maven-bom.kotlin.json new file mode 100644 index 0000000..9521858 --- /dev/null +++ b/fixtures/golden/dependency/maven-bom.kotlin.json @@ -0,0 +1,38 @@ +{ + "io.micrometer:micrometer-bom:1.5.1": { + "micrometer-bom-1.5.1.pom": { + "urls": [ + "file:/home/tad/proj/gradle2nix/fixtures/repositories/m2/io/micrometer/micrometer-bom/1.5.1/micrometer-bom-1.5.1.pom" + ], + "hash": "sha256-K/qF6ds8ck5sWvelJBYk+w+K04oQpT/4BtY57WVLRUI=" + } + }, + "io.micrometer:micrometer-core:1.5.1": { + "micrometer-core-1.5.1.jar": { + "urls": [ + "file:/home/tad/proj/gradle2nix/fixtures/repositories/m2/io/micrometer/micrometer-core/1.5.1/micrometer-core-1.5.1.jar" + ], + "hash": "sha256-DtgVYBDVGDBWMwSfeKC6O+fwqd+N2q4eTizJgQ1wfI8=" + }, + "micrometer-core-1.5.1.pom": { + "urls": [ + "file:/home/tad/proj/gradle2nix/fixtures/repositories/m2/io/micrometer/micrometer-core/1.5.1/micrometer-core-1.5.1.pom" + ], + "hash": "sha256-Cb4KaUHaOvdOz7VpDax6kJKuT2KWY5Ci73foX2xl6xw=" + } + }, + "org.hdrhistogram:HdrHistogram:2.1.12": { + "HdrHistogram-2.1.12.jar": { + "urls": [ + "file:/home/tad/proj/gradle2nix/fixtures/repositories/m2/org/hdrhistogram/HdrHistogram/2.1.12/HdrHistogram-2.1.12.jar" + ], + "hash": "sha256-47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=" + }, + "HdrHistogram-2.1.12.pom": { + "urls": [ + "file:/home/tad/proj/gradle2nix/fixtures/repositories/m2/org/hdrhistogram/HdrHistogram/2.1.12/HdrHistogram-2.1.12.pom" + ], + "hash": "sha256-f7PnkMFU0bXiMXC7jL9/cO8ICa8XIp8dywENd5llEIA=" + } + } +} \ No newline at end of file diff --git a/fixtures/golden/dependency/snapshot-dynamic.groovy.json b/fixtures/golden/dependency/snapshot-dynamic.groovy.json new file mode 100644 index 0000000..7a73a41 --- /dev/null +++ b/fixtures/golden/dependency/snapshot-dynamic.groovy.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/fixtures/golden/dependency/snapshot-dynamic.kotlin.json b/fixtures/golden/dependency/snapshot-dynamic.kotlin.json new file mode 100644 index 0000000..7a73a41 --- /dev/null +++ b/fixtures/golden/dependency/snapshot-dynamic.kotlin.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/fixtures/golden/dependency/snapshot-redirect.groovy.json b/fixtures/golden/dependency/snapshot-redirect.groovy.json new file mode 100644 index 0000000..cf94179 --- /dev/null +++ b/fixtures/golden/dependency/snapshot-redirect.groovy.json @@ -0,0 +1,16 @@ +{ + "com.github.anuken:packr:-SNAPSHOT:packr-1.2-g034efe5-114": { + "packr--SNAPSHOT.pom": { + "urls": [ + "https://jitpack.io/com/github/anuken/packr/-SNAPSHOT/packr--SNAPSHOT.pom" + ], + "hash": "sha256-xP28J7blX1IzuJxD4u/wy1ZbwAT5RAajBcpBWs1fAxU=" + }, + "packr--SNAPSHOT.jar": { + "urls": [ + "https://jitpack.io/com/github/anuken/packr/-SNAPSHOT/packr--SNAPSHOT.jar" + ], + "hash": "sha256-XrfVZLc7efr2n3Bz6mOw8DkRI0T8rU8B/MKUMVDl71w=" + } + } +} \ No newline at end of file diff --git a/fixtures/golden/dependency/snapshot.groovy.json b/fixtures/golden/dependency/snapshot.groovy.json new file mode 100644 index 0000000..7a73a41 --- /dev/null +++ b/fixtures/golden/dependency/snapshot.groovy.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/fixtures/golden/dependency/snapshot.kotlin.json b/fixtures/golden/dependency/snapshot.kotlin.json new file mode 100644 index 0000000..7a73a41 --- /dev/null +++ b/fixtures/golden/dependency/snapshot.kotlin.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/fixtures/golden/integration/settings-buildscript.groovy.json b/fixtures/golden/integration/settings-buildscript.groovy.json new file mode 100644 index 0000000..61a1815 --- /dev/null +++ b/fixtures/golden/integration/settings-buildscript.groovy.json @@ -0,0 +1,128 @@ +{ + "gradle.plugin.net.vivin:gradle-semantic-build-versioning:4.0.0": { + "gradle-semantic-build-versioning-4.0.0.jar": { + "urls": [ + "https://plugins.gradle.org/m2/gradle/plugin/net/vivin/gradle-semantic-build-versioning/4.0.0/gradle-semantic-build-versioning-4.0.0.jar" + ], + "hash": "sha256-UTjmfOjgGUN4ALk8n2+dD8vr763Jb7xOvAl1yZomHvg=" + }, + "gradle-semantic-build-versioning-4.0.0.pom": { + "urls": [ + "https://plugins.gradle.org/m2/gradle/plugin/net/vivin/gradle-semantic-build-versioning/4.0.0/gradle-semantic-build-versioning-4.0.0.pom" + ], + "hash": "sha256-TygodBYH7RAtletfGJ1JbHhA7UY6zqifHlGmBWdxTvc=" + } + }, + "org.eclipse.jgit:org.eclipse.jgit:4.8.0.201706111038-r": { + "org.eclipse.jgit-4.8.0.201706111038-r.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/eclipse/jgit/org.eclipse.jgit/4.8.0.201706111038-r/org.eclipse.jgit-4.8.0.201706111038-r.jar" + ], + "hash": "sha256-SdkS6NXM4N0I3KPTkBiduGkqj34zY8274YJYFGIACro=" + }, + "org.eclipse.jgit-4.8.0.201706111038-r.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/eclipse/jgit/org.eclipse.jgit/4.8.0.201706111038-r/org.eclipse.jgit-4.8.0.201706111038-r.pom" + ], + "hash": "sha256-pVap9a38avSbKhLnLcPNfkPbj9whbA81iFlyovWton0=" + } + }, + "com.jcraft:jsch:0.1.54": { + "jsch-0.1.54.jar": { + "urls": [ + "https://plugins.gradle.org/m2/com/jcraft/jsch/0.1.54/jsch-0.1.54.jar" + ], + "hash": "sha256-kusnOjMWdiR4/dT+A6DOGELFb0lsnBL+EjXbgEUOH9s=" + }, + "jsch-0.1.54.pom": { + "urls": [ + "https://plugins.gradle.org/m2/com/jcraft/jsch/0.1.54/jsch-0.1.54.pom" + ], + "hash": "sha256-q49RIDm+f2riDhjnQ7Sp2KIJWElEMZF9pYrlqu+KNHg=" + } + }, + "com.googlecode.javaewah:JavaEWAH:1.1.6": { + "JavaEWAH-1.1.6.jar": { + "urls": [ + "https://plugins.gradle.org/m2/com/googlecode/javaewah/JavaEWAH/1.1.6/JavaEWAH-1.1.6.jar" + ], + "hash": "sha256-941EoeOHfxznSLSoXfUXHl6Omlw8b2O7kAPbb4TM6VI=" + }, + "JavaEWAH-1.1.6.pom": { + "urls": [ + "https://plugins.gradle.org/m2/com/googlecode/javaewah/JavaEWAH/1.1.6/JavaEWAH-1.1.6.pom" + ], + "hash": "sha256-f0/5GbHuF783duBYo/IOYXPbI6XkTPLRB+x1cMGGq/A=" + } + }, + "org.apache.httpcomponents:httpclient:4.3.6": { + "httpclient-4.3.6.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/apache/httpcomponents/httpclient/4.3.6/httpclient-4.3.6.jar" + ], + "hash": "sha256-eYONnq73PU+FLGOkgIMMOi1LWQ8Ks66BWkiUY+RxQAQ=" + }, + "httpclient-4.3.6.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/apache/httpcomponents/httpclient/4.3.6/httpclient-4.3.6.pom" + ], + "hash": "sha256-0CY09hMekUlhwCqoNnEeuscnBLJ+JsW9Iju62JsbZMM=" + } + }, + "org.apache.httpcomponents:httpcore:4.3.3": { + "httpcore-4.3.3.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/apache/httpcomponents/httpcore/4.3.3/httpcore-4.3.3.jar" + ], + "hash": "sha256-UoXegK8WUcSJMTuRqfQMZaTNy2s73nFvzAKNFoaaWpM=" + }, + "httpcore-4.3.3.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/apache/httpcomponents/httpcore/4.3.3/httpcore-4.3.3.pom" + ], + "hash": "sha256-tCf3z2fHWk4/niEI01v0UwNXPBRex3j8rc/6zvF6EmQ=" + } + }, + "commons-logging:commons-logging:1.1.3": { + "commons-logging-1.1.3.jar": { + "urls": [ + "https://plugins.gradle.org/m2/commons-logging/commons-logging/1.1.3/commons-logging-1.1.3.jar" + ], + "hash": "sha256-cJA/b8gumQjI2p8gRD9h2Q8IcKMSZCmR/oRioLk5F4Q=" + }, + "commons-logging-1.1.3.pom": { + "urls": [ + "https://plugins.gradle.org/m2/commons-logging/commons-logging/1.1.3/commons-logging-1.1.3.pom" + ], + "hash": "sha256-MlCsOsa9YO0GMfXNAzUDKymT1j5AWmrgVV0np+SGWEk=" + } + }, + "commons-codec:commons-codec:1.6": { + "commons-codec-1.6.jar": { + "urls": [ + "https://plugins.gradle.org/m2/commons-codec/commons-codec/1.6/commons-codec-1.6.jar" + ], + "hash": "sha256-VLNOlBuOFBS9PkDXNu/TSBdy3CbbMpb2qkXOyfYgPYY=" + }, + "commons-codec-1.6.pom": { + "urls": [ + "https://plugins.gradle.org/m2/commons-codec/commons-codec/1.6/commons-codec-1.6.pom" + ], + "hash": "sha256-oG410//zprgT2UiU6/PkmPlUDIZMWzmueDkH46bHKIk=" + } + }, + "org.slf4j:slf4j-api:1.7.2": { + "slf4j-api-1.7.2.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/slf4j/slf4j-api/1.7.2/slf4j-api-1.7.2.jar" + ], + "hash": "sha256-O654m0ATM7Kh0WA7f6Vz4ZkIYoGRcHID9utwjN7iwFI=" + }, + "slf4j-api-1.7.2.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/slf4j/slf4j-api/1.7.2/slf4j-api-1.7.2.pom" + ], + "hash": "sha256-LqynGv4KFRb0q9jp/5B4ONJo84yBw6VCzOjX87h8XUw=" + } + } +} \ No newline at end of file diff --git a/fixtures/golden/ivy/basic.kotlin.json b/fixtures/golden/ivy/basic.kotlin.json new file mode 100644 index 0000000..4e61434 --- /dev/null +++ b/fixtures/golden/ivy/basic.kotlin.json @@ -0,0 +1,30 @@ +{ + "org.opendof.core-java:dof-cipher-sms4:1.0": { + "dof-cipher-sms4-1.0.jar": { + "urls": [ + "https://asset.opendof.org/artifact/org.opendof.core-java/dof-cipher-sms4/1.0/dof-cipher-sms4-1.0.jar" + ], + "hash": "sha256-/Joo51NA6nBPEwFuFcnDc10JQZDE8P3jF3P4gl0vpMA=" + }, + "ivy-1.0.xml": { + "urls": [ + "https://asset.opendof.org/ivy2/org.opendof.core-java/dof-cipher-sms4/1.0/ivy.xml" + ], + "hash": "sha256-rh+pQpXqPP/cmBD8slvwMrKlWCUb3JNzW3l58hd7oJ8=" + } + }, + "org.opendof.core-java:dof-oal:7.0.2": { + "dof-oal-7.0.2.jar": { + "urls": [ + "https://asset.opendof.org/artifact/org.opendof.core-java/dof-oal/7.0.2/dof-oal-7.0.2.jar" + ], + "hash": "sha256-u+FUhQGBA8MRl28mXMTSnZ2HY2ysPHq7h9lANmHBK40=" + }, + "ivy-7.0.2.xml": { + "urls": [ + "https://asset.opendof.org/ivy2/org.opendof.core-java/dof-oal/7.0.2/ivy.xml" + ], + "hash": "sha256-KZoUVyoDcfH/B/9V1SVqNiA/XIb3zlwoJkjb/jD+xig=" + } + } +} \ No newline at end of file diff --git a/fixtures/golden/plugin/resolves-from-default-repo.groovy.json b/fixtures/golden/plugin/resolves-from-default-repo.groovy.json new file mode 100644 index 0000000..58789dd --- /dev/null +++ b/fixtures/golden/plugin/resolves-from-default-repo.groovy.json @@ -0,0 +1,494 @@ +{ + "org.jetbrains.kotlin.jvm:org.jetbrains.kotlin.jvm.gradle.plugin:1.7.21": { + "org.jetbrains.kotlin.jvm.gradle.plugin-1.7.21.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/jvm/org.jetbrains.kotlin.jvm.gradle.plugin/1.7.21/org.jetbrains.kotlin.jvm.gradle.plugin-1.7.21.pom" + ], + "hash": "sha256-18S+c5nTziimR77ivh3nCwUdpLqoz9X4KYNDJ2UKD30=" + } + }, + "org.jetbrains.kotlin:kotlin-gradle-plugin:1.7.21": { + "kotlin-gradle-plugin-1.7.21-gradle71.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-gradle-plugin/1.7.21/kotlin-gradle-plugin-1.7.21-gradle71.jar" + ], + "hash": "sha256-P12cqfSxiGOZjcVpNIk9m1ICRRzucJ+uuXbI+rI2cyI=" + }, + "kotlin-gradle-plugin-1.7.21.module": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-gradle-plugin/1.7.21/kotlin-gradle-plugin-1.7.21.module" + ], + "hash": "sha256-j6I2KYtJBynes0XjG8ZPKSj3wbXxwjH8ZtvINlnBZ+E=" + } + }, + "org.jetbrains.kotlin:kotlin-gradle-plugin-api:1.7.21": { + "kotlin-gradle-plugin-api-1.7.21-gradle71.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-gradle-plugin-api/1.7.21/kotlin-gradle-plugin-api-1.7.21-gradle71.jar" + ], + "hash": "sha256-rflytT2LY7s2jZA88y6bB+pTZW6PnaXxDfuv03gxviE=" + }, + "kotlin-gradle-plugin-api-1.7.21.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-gradle-plugin-api/1.7.21/kotlin-gradle-plugin-api-1.7.21.jar" + ], + "hash": "sha256-rflytT2LY7s2jZA88y6bB+pTZW6PnaXxDfuv03gxviE=" + }, + "kotlin-gradle-plugin-api-1.7.21.module": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-gradle-plugin-api/1.7.21/kotlin-gradle-plugin-api-1.7.21.module" + ], + "hash": "sha256-zGXnGhweng0JaG9cpJGORShIY1q7VCl15HwYlnw6A10=" + } + }, + "org.jetbrains.kotlin:kotlin-native-utils:1.7.21": { + "kotlin-native-utils-1.7.21.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-native-utils/1.7.21/kotlin-native-utils-1.7.21.jar" + ], + "hash": "sha256-k1KYF/2Nj9hlItZqqtyr0UKhcueMz+uUnNKJ40xw+Qs=" + }, + "kotlin-native-utils-1.7.21.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-native-utils/1.7.21/kotlin-native-utils-1.7.21.pom" + ], + "hash": "sha256-CEYFdUhagoAZC0g8H3fyCk063IegIXTzDuxVdNm65FY=" + } + }, + "org.jetbrains.kotlin:kotlin-util-io:1.7.21": { + "kotlin-util-io-1.7.21.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-util-io/1.7.21/kotlin-util-io-1.7.21.jar" + ], + "hash": "sha256-7MKI4AQqAUdgOeILbOXgaRj+8fic+J9V39KO8Xwm800=" + }, + "kotlin-util-io-1.7.21.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-util-io/1.7.21/kotlin-util-io-1.7.21.pom" + ], + "hash": "sha256-ziTM1kPWW+8Cey9uINCnkhdq29ug2eVVmS5CR6Y3Ne8=" + } + }, + "org.jetbrains.kotlin:kotlin-project-model:1.7.21": { + "kotlin-project-model-1.7.21.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-project-model/1.7.21/kotlin-project-model-1.7.21.jar" + ], + "hash": "sha256-4htTvrj3SxM6Y4mClPSlfcSiKJvoVfZrD5rosVYjFT8=" + }, + "kotlin-project-model-1.7.21.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-project-model/1.7.21/kotlin-project-model-1.7.21.pom" + ], + "hash": "sha256-JQfT7SKoHyssNSxMUOY1MivHEQClFQJN0NtQRifJ8Bs=" + } + }, + "org.jetbrains.kotlin:kotlin-tooling-core:1.7.21": { + "kotlin-tooling-core-1.7.21.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-tooling-core/1.7.21/kotlin-tooling-core-1.7.21.jar" + ], + "hash": "sha256-N5fxg1NC+8EuycHU+YMyugKCkaMyUakHySJ9j9lK7kg=" + }, + "kotlin-tooling-core-1.7.21.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-tooling-core/1.7.21/kotlin-tooling-core-1.7.21.pom" + ], + "hash": "sha256-tw2g1Eorhw7Lz85ZcMMOOOLs3htfQqHdRC0TA5gSKUY=" + } + }, + "org.jetbrains.kotlin:kotlin-gradle-plugin-model:1.7.21": { + "kotlin-gradle-plugin-model-1.7.21.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-gradle-plugin-model/1.7.21/kotlin-gradle-plugin-model-1.7.21.jar" + ], + "hash": "sha256-FNP/F7o8tMi+uK3297QFB4gTS4kbsTyr5yPIwQ0dDhg=" + }, + "kotlin-gradle-plugin-model-1.7.21.module": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-gradle-plugin-model/1.7.21/kotlin-gradle-plugin-model-1.7.21.module" + ], + "hash": "sha256-kCJoZCp1guVF4xgQnjdIw3WxOLCKFVuBX2yAi7vuR7U=" + } + }, + "org.jetbrains.kotlin:kotlin-gradle-plugin-idea:1.7.21": { + "kotlin-gradle-plugin-idea-1.7.21.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-gradle-plugin-idea/1.7.21/kotlin-gradle-plugin-idea-1.7.21.jar" + ], + "hash": "sha256-C1dqyzeBqctWEKphxbev3zKQ/C2digzUv+FTe4dcReY=" + }, + "kotlin-gradle-plugin-idea-1.7.21.module": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-gradle-plugin-idea/1.7.21/kotlin-gradle-plugin-idea-1.7.21.module" + ], + "hash": "sha256-ygHy2JJMcpaXMax+oVbwi7GP60LDEAClIj2dwW1ZuTg=" + } + }, + "org.jetbrains.kotlin:kotlin-gradle-plugin-idea-proto:1.7.21": { + "kotlin-gradle-plugin-idea-proto-1.7.21.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-gradle-plugin-idea-proto/1.7.21/kotlin-gradle-plugin-idea-proto-1.7.21.jar" + ], + "hash": "sha256-NZhwZybLzo0oE05oiZw9WAv3LH6/kJcUHY28rtgnmHg=" + }, + "kotlin-gradle-plugin-idea-proto-1.7.21.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-gradle-plugin-idea-proto/1.7.21/kotlin-gradle-plugin-idea-proto-1.7.21.pom" + ], + "hash": "sha256-PRwDYK9odF8qAyoMAYR//Pnriq1wa/ZZydhAoYTsXyM=" + } + }, + "org.jetbrains.kotlin:kotlin-util-klib:1.7.21": { + "kotlin-util-klib-1.7.21.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-util-klib/1.7.21/kotlin-util-klib-1.7.21.jar" + ], + "hash": "sha256-UgkkU0RkIN+7h4BN6s6yGfVI53fm3xK35wRKOmaHEgs=" + }, + "kotlin-util-klib-1.7.21.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-util-klib/1.7.21/kotlin-util-klib-1.7.21.pom" + ], + "hash": "sha256-D8d7J3Rc+kzuX+AA5tEpmtSUT3rMB4A7u8ws0rAT3oU=" + } + }, + "org.jetbrains.kotlin:kotlin-klib-commonizer-api:1.7.21": { + "kotlin-klib-commonizer-api-1.7.21.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-klib-commonizer-api/1.7.21/kotlin-klib-commonizer-api-1.7.21.jar" + ], + "hash": "sha256-MOGWrbAAH9F7ZgNe2QcNPw5Hui0ycTt1wwPGt7e3FeI=" + }, + "kotlin-klib-commonizer-api-1.7.21.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-klib-commonizer-api/1.7.21/kotlin-klib-commonizer-api-1.7.21.pom" + ], + "hash": "sha256-so6g3vy5lNH7U6e7olh9J0DG0mAXk2UglP1ox0Ul0CA=" + } + }, + "org.jetbrains.kotlin:kotlin-compiler-embeddable:1.7.21": { + "kotlin-compiler-embeddable-1.7.21.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-compiler-embeddable/1.7.21/kotlin-compiler-embeddable-1.7.21.jar", + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-compiler-embeddable/1.7.21/kotlin-compiler-embeddable-1.7.21.jar" + ], + "hash": "sha256-Ty5JK8x5XgaA4/h67qGtrp8wbK9SBAuUpvoPiP2skvk=" + }, + "kotlin-compiler-embeddable-1.7.21.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-compiler-embeddable/1.7.21/kotlin-compiler-embeddable-1.7.21.pom", + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-compiler-embeddable/1.7.21/kotlin-compiler-embeddable-1.7.21.pom" + ], + "hash": "sha256-CwIzMip2MO/eEzUmjkYSPw1tNjg5gg/TfE7Lbv+njjs=" + } + }, + "org.jetbrains.kotlin:kotlin-daemon-embeddable:1.7.21": { + "kotlin-daemon-embeddable-1.7.21.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-daemon-embeddable/1.7.21/kotlin-daemon-embeddable-1.7.21.jar", + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-daemon-embeddable/1.7.21/kotlin-daemon-embeddable-1.7.21.jar" + ], + "hash": "sha256-A+bwJUNSJIlOSe5e2EfLCwtKh540z6uQ1wzakmKnV00=" + }, + "kotlin-daemon-embeddable-1.7.21.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-daemon-embeddable/1.7.21/kotlin-daemon-embeddable-1.7.21.pom", + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-daemon-embeddable/1.7.21/kotlin-daemon-embeddable-1.7.21.pom" + ], + "hash": "sha256-vB3pwgh7ouTlQQF6i66PQF7IAKGK5MJH6R8rVedh5kk=" + } + }, + "org.jetbrains.intellij.deps:trove4j:1.0.20200330": { + "trove4j-1.0.20200330.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/intellij/deps/trove4j/1.0.20200330/trove4j-1.0.20200330.jar", + "https://repo.maven.apache.org/maven2/org/jetbrains/intellij/deps/trove4j/1.0.20200330/trove4j-1.0.20200330.jar" + ], + "hash": "sha256-xf1yW/+rUYRr88d9sTg8YKquv+G3/i8A0j/ht98KQ50=" + }, + "trove4j-1.0.20200330.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/intellij/deps/trove4j/1.0.20200330/trove4j-1.0.20200330.pom", + "https://repo.maven.apache.org/maven2/org/jetbrains/intellij/deps/trove4j/1.0.20200330/trove4j-1.0.20200330.pom" + ], + "hash": "sha256-h3IcuqZaPJfYsbqdIHhA8WTJ/jh1n8nqEP/iZWX40+k=" + } + }, + "net.java.dev.jna:jna:5.6.0": { + "jna-5.6.0.jar": { + "urls": [ + "https://plugins.gradle.org/m2/net/java/dev/jna/jna/5.6.0/jna-5.6.0.jar", + "https://repo.maven.apache.org/maven2/net/java/dev/jna/jna/5.6.0/jna-5.6.0.jar" + ], + "hash": "sha256-VVfiNaiqL5dm1dxgnWeUjyqIMsLXls6p7x1svgs7fq8=" + }, + "jna-5.6.0.pom": { + "urls": [ + "https://plugins.gradle.org/m2/net/java/dev/jna/jna/5.6.0/jna-5.6.0.pom", + "https://repo.maven.apache.org/maven2/net/java/dev/jna/jna/5.6.0/jna-5.6.0.pom" + ], + "hash": "sha256-X+gbAlWXjyRhbTexBgi3lJil8wc+HZsgONhzaoMfJgg=" + } + }, + "org.jetbrains.kotlin:kotlin-annotation-processing-gradle:1.7.21": { + "kotlin-annotation-processing-gradle-1.7.21.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-annotation-processing-gradle/1.7.21/kotlin-annotation-processing-gradle-1.7.21.jar" + ], + "hash": "sha256-RhyKdFvNVeRyXykBIVnUdOEor/G4KlPR80UkYFK5cwk=" + }, + "kotlin-annotation-processing-gradle-1.7.21.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-annotation-processing-gradle/1.7.21/kotlin-annotation-processing-gradle-1.7.21.pom" + ], + "hash": "sha256-r2JZxfjfTezw8FXmZcTLaP8TtK9c1HfuHTO/7gAaFr4=" + } + }, + "org.jetbrains.kotlin:kotlin-android-extensions:1.7.21": { + "kotlin-android-extensions-1.7.21.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-android-extensions/1.7.21/kotlin-android-extensions-1.7.21.jar" + ], + "hash": "sha256-JVeliP7QxmbRVq1uDfXjFOqz1p5m6aJyJ5uaRiQ0xq8=" + }, + "kotlin-android-extensions-1.7.21.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-android-extensions/1.7.21/kotlin-android-extensions-1.7.21.pom" + ], + "hash": "sha256-8pic3UN0A8hyZc/K8GHSFOaGlVyX40ntFWa6FqouDI0=" + } + }, + "org.jetbrains.kotlin:kotlin-compiler-runner:1.7.21": { + "kotlin-compiler-runner-1.7.21.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-compiler-runner/1.7.21/kotlin-compiler-runner-1.7.21.jar" + ], + "hash": "sha256-LdVae/7udr97ASbFtx0FuJmBK6a0Cjc1n50T+uIC8yc=" + }, + "kotlin-compiler-runner-1.7.21.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-compiler-runner/1.7.21/kotlin-compiler-runner-1.7.21.pom" + ], + "hash": "sha256-+JDieVykDuyu+jpdjkOND3C7YCo5SUe7rOp2Quqy+Tw=" + } + }, + "org.jetbrains.kotlin:kotlin-build-common:1.7.21": { + "kotlin-build-common-1.7.21.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-build-common/1.7.21/kotlin-build-common-1.7.21.jar" + ], + "hash": "sha256-Y3O9HhUPfcsnL1KvvBWZBkCSqddbKM7WvroA/qy6u/8=" + }, + "kotlin-build-common-1.7.21.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-build-common/1.7.21/kotlin-build-common-1.7.21.pom" + ], + "hash": "sha256-msmBVHbIUfFKH3QeG46CJRxyepVGgMdXT4owUn2z718=" + } + }, + "org.jetbrains.kotlin:kotlin-daemon-client:1.7.21": { + "kotlin-daemon-client-1.7.21.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-daemon-client/1.7.21/kotlin-daemon-client-1.7.21.jar" + ], + "hash": "sha256-tyPlHq8syE/a+sqHJnk/7I1SFyUNiAv0eDA/JE3UGoU=" + }, + "kotlin-daemon-client-1.7.21.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-daemon-client/1.7.21/kotlin-daemon-client-1.7.21.pom" + ], + "hash": "sha256-Be4Gj7v3IvWRSlqiWO6KKLZChF9B1/+bVGhtXKJbvxk=" + } + }, + "org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.5.0": { + "kotlinx-coroutines-core-jvm-1.5.0.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.5.0/kotlinx-coroutines-core-jvm-1.5.0.jar" + ], + "hash": "sha256-eNbMcTX4TWkv83Uvz9H6G74JQNffcGUuTx6u7Ax4r7s=" + }, + "kotlinx-coroutines-core-jvm-1.5.0.module": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.5.0/kotlinx-coroutines-core-jvm-1.5.0.module" + ], + "hash": "sha256-yIXdAoEHbFhDgm3jF+PLzcPYhZ2+71OuHPrNG5xg+W4=" + } + }, + "org.jetbrains.kotlin:kotlin-scripting-compiler-embeddable:1.7.21": { + "kotlin-scripting-compiler-embeddable-1.7.21.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-scripting-compiler-embeddable/1.7.21/kotlin-scripting-compiler-embeddable-1.7.21.jar", + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-scripting-compiler-embeddable/1.7.21/kotlin-scripting-compiler-embeddable-1.7.21.jar" + ], + "hash": "sha256-qu9jHwICEl2ZHZgjRxn4ZK1anW40m/DtRGsTd9gXGKE=" + }, + "kotlin-scripting-compiler-embeddable-1.7.21.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-scripting-compiler-embeddable/1.7.21/kotlin-scripting-compiler-embeddable-1.7.21.pom", + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-scripting-compiler-embeddable/1.7.21/kotlin-scripting-compiler-embeddable-1.7.21.pom" + ], + "hash": "sha256-xHXL2+0BepcMD9y46qu1UNc9E6T+a4e3efxM9S148JM=" + } + }, + "org.jetbrains.kotlin:kotlin-scripting-compiler-impl-embeddable:1.7.21": { + "kotlin-scripting-compiler-impl-embeddable-1.7.21.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-scripting-compiler-impl-embeddable/1.7.21/kotlin-scripting-compiler-impl-embeddable-1.7.21.jar", + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-scripting-compiler-impl-embeddable/1.7.21/kotlin-scripting-compiler-impl-embeddable-1.7.21.jar" + ], + "hash": "sha256-ZOK9uuvzgJSzwh5nCX5Qe4NoTaQTi6h6CwmhMgOXVCg=" + }, + "kotlin-scripting-compiler-impl-embeddable-1.7.21.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-scripting-compiler-impl-embeddable/1.7.21/kotlin-scripting-compiler-impl-embeddable-1.7.21.pom", + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-scripting-compiler-impl-embeddable/1.7.21/kotlin-scripting-compiler-impl-embeddable-1.7.21.pom" + ], + "hash": "sha256-5c0+HEj+qhC1YVqidOFh5/dcFijcJhZ1ALZ0b4gfweM=" + } + }, + "org.jetbrains.kotlin:kotlin-scripting-common:1.7.21": { + "kotlin-scripting-common-1.7.21.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-scripting-common/1.7.21/kotlin-scripting-common-1.7.21.jar", + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-scripting-common/1.7.21/kotlin-scripting-common-1.7.21.jar" + ], + "hash": "sha256-0ZLMLNlDFecijrkTZqNpdmpoIrPOvKwUwR1MSXM2y6Q=" + }, + "kotlin-scripting-common-1.7.21.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-scripting-common/1.7.21/kotlin-scripting-common-1.7.21.pom", + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-scripting-common/1.7.21/kotlin-scripting-common-1.7.21.pom" + ], + "hash": "sha256-2xzYRWGPDLQXOK3H72jZ+NIjZ1sFg+NbsMCEA30AWe4=" + } + }, + "org.jetbrains.kotlin:kotlin-scripting-jvm:1.7.21": { + "kotlin-scripting-jvm-1.7.21.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-scripting-jvm/1.7.21/kotlin-scripting-jvm-1.7.21.jar", + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-scripting-jvm/1.7.21/kotlin-scripting-jvm-1.7.21.jar" + ], + "hash": "sha256-Uz441a1oFCoFE0HyK8cO113IUGSxk3rPBRN1XMPwSF4=" + }, + "kotlin-scripting-jvm-1.7.21.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-scripting-jvm/1.7.21/kotlin-scripting-jvm-1.7.21.pom", + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-scripting-jvm/1.7.21/kotlin-scripting-jvm-1.7.21.pom" + ], + "hash": "sha256-cnwtOnluoiOWPu7P7kHvKygsVbZ+V8O0mgFwpMSbfGE=" + } + }, + "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.21": { + "kotlin-stdlib-jdk8-1.7.21.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.7.21/kotlin-stdlib-jdk8-1.7.21.jar" + ], + "hash": "sha256-sy5K5+uwVycz/kOThb8DT1+u6LbFhdQW/s+TPpSR044=" + }, + "kotlin-stdlib-jdk8-1.7.21.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.7.21/kotlin-stdlib-jdk8-1.7.21.pom" + ], + "hash": "sha256-bzuTQ8QS1q5ApMePuKcJhklkUKlSjNusdimojhqlg4k=" + } + }, + "org.jetbrains.kotlin:kotlin-stdlib:1.7.21": { + "kotlin-stdlib-1.7.21.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib/1.7.21/kotlin-stdlib-1.7.21.jar" + ], + "hash": "sha256-1Gqddz/7ne5P8adIrIRdyOUABcWJMClRdgorUYe93Rk=" + }, + "kotlin-stdlib-1.7.21.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib/1.7.21/kotlin-stdlib-1.7.21.pom" + ], + "hash": "sha256-mzkq1D4vQhJp9jxiBz+ulCN9LjHe7o9msZzBkbTaBqw=" + } + }, + "org.jetbrains.kotlin:kotlin-stdlib-common:1.7.21": { + "kotlin-stdlib-common-1.7.21.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib-common/1.7.21/kotlin-stdlib-common-1.7.21.jar" + ], + "hash": "sha256-5iv+yiNhA6EBciS4oiqEHbXcTbSdgKOb1E27IkaEpmo=" + }, + "kotlin-stdlib-common-1.7.21.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib-common/1.7.21/kotlin-stdlib-common-1.7.21.pom" + ], + "hash": "sha256-LuberkeOGLGvushzHFvxoUe1dWiT1Z7b+nEWBcNDX4Q=" + } + }, + "org.jetbrains:annotations:13.0": { + "annotations-13.0.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/annotations/13.0/annotations-13.0.jar" + ], + "hash": "sha256-rOKhDcji1f00kl7KwD5JiLLA+FFlDJS4zvSbob0RFHg=" + }, + "annotations-13.0.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/annotations/13.0/annotations-13.0.pom" + ], + "hash": "sha256-llrrK+3/NpgZvd4b96CzuJuCR91pyIuGN112Fju4w5c=" + } + }, + "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.7.21": { + "kotlin-stdlib-jdk7-1.7.21.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.7.21/kotlin-stdlib-jdk7-1.7.21.jar" + ], + "hash": "sha256-uMqg+XFaIYf0+pmQba5Xy6EM7vmn+Ajb7o6vNjWVWKU=" + }, + "kotlin-stdlib-jdk7-1.7.21.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.7.21/kotlin-stdlib-jdk7-1.7.21.pom" + ], + "hash": "sha256-vy6yU9onofKT0RRpMpRBeF26xRceWB8v7Z1aKm2YaZw=" + } + }, + "org.jetbrains.kotlin:kotlin-script-runtime:1.7.21": { + "kotlin-script-runtime-1.7.21.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-script-runtime/1.7.21/kotlin-script-runtime-1.7.21.jar" + ], + "hash": "sha256-LEmLbZiWTK3dS1hLe0mPmxCPaf8akVOrxlt02uQJJ/Y=" + }, + "kotlin-script-runtime-1.7.21.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-script-runtime/1.7.21/kotlin-script-runtime-1.7.21.pom" + ], + "hash": "sha256-LuSdd/3Dw6l0akiYCbfGQ3fh2NnEXCDZI+MXI5sicwQ=" + } + }, + "org.jetbrains.kotlin:kotlin-reflect:1.7.21": { + "kotlin-reflect-1.7.21.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-reflect/1.7.21/kotlin-reflect-1.7.21.jar" + ], + "hash": "sha256-wbF65MSTF+7Sb3ecM8lpBEbFZp6zx+Jsibbg1s8sogQ=" + }, + "kotlin-reflect-1.7.21.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-reflect/1.7.21/kotlin-reflect-1.7.21.pom" + ], + "hash": "sha256-Xn69/iAG9vHksPORwbqBhTmKj2NF2xpStYTx40Cz8EM=" + } + }, + "org.jetbrains.kotlin:kotlin-klib-commonizer-embeddable:1.7.21": { + "kotlin-klib-commonizer-embeddable-1.7.21.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-klib-commonizer-embeddable/1.7.21/kotlin-klib-commonizer-embeddable-1.7.21.jar" + ], + "hash": "sha256-nTpktCC+2+20HV5tsJ28h2FKffCBR5PACQqDYJBp+1Y=" + }, + "kotlin-klib-commonizer-embeddable-1.7.21.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-klib-commonizer-embeddable/1.7.21/kotlin-klib-commonizer-embeddable-1.7.21.pom" + ], + "hash": "sha256-bOmRoyzYOdq3wbf88+1xbr6XgbRgg3ViDC9fH8RwjrA=" + } + } +} \ No newline at end of file diff --git a/fixtures/golden/plugin/resolves-from-default-repo.kotlin.json b/fixtures/golden/plugin/resolves-from-default-repo.kotlin.json new file mode 100644 index 0000000..58789dd --- /dev/null +++ b/fixtures/golden/plugin/resolves-from-default-repo.kotlin.json @@ -0,0 +1,494 @@ +{ + "org.jetbrains.kotlin.jvm:org.jetbrains.kotlin.jvm.gradle.plugin:1.7.21": { + "org.jetbrains.kotlin.jvm.gradle.plugin-1.7.21.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/jvm/org.jetbrains.kotlin.jvm.gradle.plugin/1.7.21/org.jetbrains.kotlin.jvm.gradle.plugin-1.7.21.pom" + ], + "hash": "sha256-18S+c5nTziimR77ivh3nCwUdpLqoz9X4KYNDJ2UKD30=" + } + }, + "org.jetbrains.kotlin:kotlin-gradle-plugin:1.7.21": { + "kotlin-gradle-plugin-1.7.21-gradle71.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-gradle-plugin/1.7.21/kotlin-gradle-plugin-1.7.21-gradle71.jar" + ], + "hash": "sha256-P12cqfSxiGOZjcVpNIk9m1ICRRzucJ+uuXbI+rI2cyI=" + }, + "kotlin-gradle-plugin-1.7.21.module": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-gradle-plugin/1.7.21/kotlin-gradle-plugin-1.7.21.module" + ], + "hash": "sha256-j6I2KYtJBynes0XjG8ZPKSj3wbXxwjH8ZtvINlnBZ+E=" + } + }, + "org.jetbrains.kotlin:kotlin-gradle-plugin-api:1.7.21": { + "kotlin-gradle-plugin-api-1.7.21-gradle71.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-gradle-plugin-api/1.7.21/kotlin-gradle-plugin-api-1.7.21-gradle71.jar" + ], + "hash": "sha256-rflytT2LY7s2jZA88y6bB+pTZW6PnaXxDfuv03gxviE=" + }, + "kotlin-gradle-plugin-api-1.7.21.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-gradle-plugin-api/1.7.21/kotlin-gradle-plugin-api-1.7.21.jar" + ], + "hash": "sha256-rflytT2LY7s2jZA88y6bB+pTZW6PnaXxDfuv03gxviE=" + }, + "kotlin-gradle-plugin-api-1.7.21.module": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-gradle-plugin-api/1.7.21/kotlin-gradle-plugin-api-1.7.21.module" + ], + "hash": "sha256-zGXnGhweng0JaG9cpJGORShIY1q7VCl15HwYlnw6A10=" + } + }, + "org.jetbrains.kotlin:kotlin-native-utils:1.7.21": { + "kotlin-native-utils-1.7.21.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-native-utils/1.7.21/kotlin-native-utils-1.7.21.jar" + ], + "hash": "sha256-k1KYF/2Nj9hlItZqqtyr0UKhcueMz+uUnNKJ40xw+Qs=" + }, + "kotlin-native-utils-1.7.21.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-native-utils/1.7.21/kotlin-native-utils-1.7.21.pom" + ], + "hash": "sha256-CEYFdUhagoAZC0g8H3fyCk063IegIXTzDuxVdNm65FY=" + } + }, + "org.jetbrains.kotlin:kotlin-util-io:1.7.21": { + "kotlin-util-io-1.7.21.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-util-io/1.7.21/kotlin-util-io-1.7.21.jar" + ], + "hash": "sha256-7MKI4AQqAUdgOeILbOXgaRj+8fic+J9V39KO8Xwm800=" + }, + "kotlin-util-io-1.7.21.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-util-io/1.7.21/kotlin-util-io-1.7.21.pom" + ], + "hash": "sha256-ziTM1kPWW+8Cey9uINCnkhdq29ug2eVVmS5CR6Y3Ne8=" + } + }, + "org.jetbrains.kotlin:kotlin-project-model:1.7.21": { + "kotlin-project-model-1.7.21.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-project-model/1.7.21/kotlin-project-model-1.7.21.jar" + ], + "hash": "sha256-4htTvrj3SxM6Y4mClPSlfcSiKJvoVfZrD5rosVYjFT8=" + }, + "kotlin-project-model-1.7.21.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-project-model/1.7.21/kotlin-project-model-1.7.21.pom" + ], + "hash": "sha256-JQfT7SKoHyssNSxMUOY1MivHEQClFQJN0NtQRifJ8Bs=" + } + }, + "org.jetbrains.kotlin:kotlin-tooling-core:1.7.21": { + "kotlin-tooling-core-1.7.21.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-tooling-core/1.7.21/kotlin-tooling-core-1.7.21.jar" + ], + "hash": "sha256-N5fxg1NC+8EuycHU+YMyugKCkaMyUakHySJ9j9lK7kg=" + }, + "kotlin-tooling-core-1.7.21.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-tooling-core/1.7.21/kotlin-tooling-core-1.7.21.pom" + ], + "hash": "sha256-tw2g1Eorhw7Lz85ZcMMOOOLs3htfQqHdRC0TA5gSKUY=" + } + }, + "org.jetbrains.kotlin:kotlin-gradle-plugin-model:1.7.21": { + "kotlin-gradle-plugin-model-1.7.21.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-gradle-plugin-model/1.7.21/kotlin-gradle-plugin-model-1.7.21.jar" + ], + "hash": "sha256-FNP/F7o8tMi+uK3297QFB4gTS4kbsTyr5yPIwQ0dDhg=" + }, + "kotlin-gradle-plugin-model-1.7.21.module": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-gradle-plugin-model/1.7.21/kotlin-gradle-plugin-model-1.7.21.module" + ], + "hash": "sha256-kCJoZCp1guVF4xgQnjdIw3WxOLCKFVuBX2yAi7vuR7U=" + } + }, + "org.jetbrains.kotlin:kotlin-gradle-plugin-idea:1.7.21": { + "kotlin-gradle-plugin-idea-1.7.21.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-gradle-plugin-idea/1.7.21/kotlin-gradle-plugin-idea-1.7.21.jar" + ], + "hash": "sha256-C1dqyzeBqctWEKphxbev3zKQ/C2digzUv+FTe4dcReY=" + }, + "kotlin-gradle-plugin-idea-1.7.21.module": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-gradle-plugin-idea/1.7.21/kotlin-gradle-plugin-idea-1.7.21.module" + ], + "hash": "sha256-ygHy2JJMcpaXMax+oVbwi7GP60LDEAClIj2dwW1ZuTg=" + } + }, + "org.jetbrains.kotlin:kotlin-gradle-plugin-idea-proto:1.7.21": { + "kotlin-gradle-plugin-idea-proto-1.7.21.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-gradle-plugin-idea-proto/1.7.21/kotlin-gradle-plugin-idea-proto-1.7.21.jar" + ], + "hash": "sha256-NZhwZybLzo0oE05oiZw9WAv3LH6/kJcUHY28rtgnmHg=" + }, + "kotlin-gradle-plugin-idea-proto-1.7.21.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-gradle-plugin-idea-proto/1.7.21/kotlin-gradle-plugin-idea-proto-1.7.21.pom" + ], + "hash": "sha256-PRwDYK9odF8qAyoMAYR//Pnriq1wa/ZZydhAoYTsXyM=" + } + }, + "org.jetbrains.kotlin:kotlin-util-klib:1.7.21": { + "kotlin-util-klib-1.7.21.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-util-klib/1.7.21/kotlin-util-klib-1.7.21.jar" + ], + "hash": "sha256-UgkkU0RkIN+7h4BN6s6yGfVI53fm3xK35wRKOmaHEgs=" + }, + "kotlin-util-klib-1.7.21.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-util-klib/1.7.21/kotlin-util-klib-1.7.21.pom" + ], + "hash": "sha256-D8d7J3Rc+kzuX+AA5tEpmtSUT3rMB4A7u8ws0rAT3oU=" + } + }, + "org.jetbrains.kotlin:kotlin-klib-commonizer-api:1.7.21": { + "kotlin-klib-commonizer-api-1.7.21.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-klib-commonizer-api/1.7.21/kotlin-klib-commonizer-api-1.7.21.jar" + ], + "hash": "sha256-MOGWrbAAH9F7ZgNe2QcNPw5Hui0ycTt1wwPGt7e3FeI=" + }, + "kotlin-klib-commonizer-api-1.7.21.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-klib-commonizer-api/1.7.21/kotlin-klib-commonizer-api-1.7.21.pom" + ], + "hash": "sha256-so6g3vy5lNH7U6e7olh9J0DG0mAXk2UglP1ox0Ul0CA=" + } + }, + "org.jetbrains.kotlin:kotlin-compiler-embeddable:1.7.21": { + "kotlin-compiler-embeddable-1.7.21.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-compiler-embeddable/1.7.21/kotlin-compiler-embeddable-1.7.21.jar", + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-compiler-embeddable/1.7.21/kotlin-compiler-embeddable-1.7.21.jar" + ], + "hash": "sha256-Ty5JK8x5XgaA4/h67qGtrp8wbK9SBAuUpvoPiP2skvk=" + }, + "kotlin-compiler-embeddable-1.7.21.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-compiler-embeddable/1.7.21/kotlin-compiler-embeddable-1.7.21.pom", + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-compiler-embeddable/1.7.21/kotlin-compiler-embeddable-1.7.21.pom" + ], + "hash": "sha256-CwIzMip2MO/eEzUmjkYSPw1tNjg5gg/TfE7Lbv+njjs=" + } + }, + "org.jetbrains.kotlin:kotlin-daemon-embeddable:1.7.21": { + "kotlin-daemon-embeddable-1.7.21.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-daemon-embeddable/1.7.21/kotlin-daemon-embeddable-1.7.21.jar", + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-daemon-embeddable/1.7.21/kotlin-daemon-embeddable-1.7.21.jar" + ], + "hash": "sha256-A+bwJUNSJIlOSe5e2EfLCwtKh540z6uQ1wzakmKnV00=" + }, + "kotlin-daemon-embeddable-1.7.21.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-daemon-embeddable/1.7.21/kotlin-daemon-embeddable-1.7.21.pom", + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-daemon-embeddable/1.7.21/kotlin-daemon-embeddable-1.7.21.pom" + ], + "hash": "sha256-vB3pwgh7ouTlQQF6i66PQF7IAKGK5MJH6R8rVedh5kk=" + } + }, + "org.jetbrains.intellij.deps:trove4j:1.0.20200330": { + "trove4j-1.0.20200330.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/intellij/deps/trove4j/1.0.20200330/trove4j-1.0.20200330.jar", + "https://repo.maven.apache.org/maven2/org/jetbrains/intellij/deps/trove4j/1.0.20200330/trove4j-1.0.20200330.jar" + ], + "hash": "sha256-xf1yW/+rUYRr88d9sTg8YKquv+G3/i8A0j/ht98KQ50=" + }, + "trove4j-1.0.20200330.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/intellij/deps/trove4j/1.0.20200330/trove4j-1.0.20200330.pom", + "https://repo.maven.apache.org/maven2/org/jetbrains/intellij/deps/trove4j/1.0.20200330/trove4j-1.0.20200330.pom" + ], + "hash": "sha256-h3IcuqZaPJfYsbqdIHhA8WTJ/jh1n8nqEP/iZWX40+k=" + } + }, + "net.java.dev.jna:jna:5.6.0": { + "jna-5.6.0.jar": { + "urls": [ + "https://plugins.gradle.org/m2/net/java/dev/jna/jna/5.6.0/jna-5.6.0.jar", + "https://repo.maven.apache.org/maven2/net/java/dev/jna/jna/5.6.0/jna-5.6.0.jar" + ], + "hash": "sha256-VVfiNaiqL5dm1dxgnWeUjyqIMsLXls6p7x1svgs7fq8=" + }, + "jna-5.6.0.pom": { + "urls": [ + "https://plugins.gradle.org/m2/net/java/dev/jna/jna/5.6.0/jna-5.6.0.pom", + "https://repo.maven.apache.org/maven2/net/java/dev/jna/jna/5.6.0/jna-5.6.0.pom" + ], + "hash": "sha256-X+gbAlWXjyRhbTexBgi3lJil8wc+HZsgONhzaoMfJgg=" + } + }, + "org.jetbrains.kotlin:kotlin-annotation-processing-gradle:1.7.21": { + "kotlin-annotation-processing-gradle-1.7.21.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-annotation-processing-gradle/1.7.21/kotlin-annotation-processing-gradle-1.7.21.jar" + ], + "hash": "sha256-RhyKdFvNVeRyXykBIVnUdOEor/G4KlPR80UkYFK5cwk=" + }, + "kotlin-annotation-processing-gradle-1.7.21.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-annotation-processing-gradle/1.7.21/kotlin-annotation-processing-gradle-1.7.21.pom" + ], + "hash": "sha256-r2JZxfjfTezw8FXmZcTLaP8TtK9c1HfuHTO/7gAaFr4=" + } + }, + "org.jetbrains.kotlin:kotlin-android-extensions:1.7.21": { + "kotlin-android-extensions-1.7.21.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-android-extensions/1.7.21/kotlin-android-extensions-1.7.21.jar" + ], + "hash": "sha256-JVeliP7QxmbRVq1uDfXjFOqz1p5m6aJyJ5uaRiQ0xq8=" + }, + "kotlin-android-extensions-1.7.21.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-android-extensions/1.7.21/kotlin-android-extensions-1.7.21.pom" + ], + "hash": "sha256-8pic3UN0A8hyZc/K8GHSFOaGlVyX40ntFWa6FqouDI0=" + } + }, + "org.jetbrains.kotlin:kotlin-compiler-runner:1.7.21": { + "kotlin-compiler-runner-1.7.21.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-compiler-runner/1.7.21/kotlin-compiler-runner-1.7.21.jar" + ], + "hash": "sha256-LdVae/7udr97ASbFtx0FuJmBK6a0Cjc1n50T+uIC8yc=" + }, + "kotlin-compiler-runner-1.7.21.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-compiler-runner/1.7.21/kotlin-compiler-runner-1.7.21.pom" + ], + "hash": "sha256-+JDieVykDuyu+jpdjkOND3C7YCo5SUe7rOp2Quqy+Tw=" + } + }, + "org.jetbrains.kotlin:kotlin-build-common:1.7.21": { + "kotlin-build-common-1.7.21.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-build-common/1.7.21/kotlin-build-common-1.7.21.jar" + ], + "hash": "sha256-Y3O9HhUPfcsnL1KvvBWZBkCSqddbKM7WvroA/qy6u/8=" + }, + "kotlin-build-common-1.7.21.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-build-common/1.7.21/kotlin-build-common-1.7.21.pom" + ], + "hash": "sha256-msmBVHbIUfFKH3QeG46CJRxyepVGgMdXT4owUn2z718=" + } + }, + "org.jetbrains.kotlin:kotlin-daemon-client:1.7.21": { + "kotlin-daemon-client-1.7.21.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-daemon-client/1.7.21/kotlin-daemon-client-1.7.21.jar" + ], + "hash": "sha256-tyPlHq8syE/a+sqHJnk/7I1SFyUNiAv0eDA/JE3UGoU=" + }, + "kotlin-daemon-client-1.7.21.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-daemon-client/1.7.21/kotlin-daemon-client-1.7.21.pom" + ], + "hash": "sha256-Be4Gj7v3IvWRSlqiWO6KKLZChF9B1/+bVGhtXKJbvxk=" + } + }, + "org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.5.0": { + "kotlinx-coroutines-core-jvm-1.5.0.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.5.0/kotlinx-coroutines-core-jvm-1.5.0.jar" + ], + "hash": "sha256-eNbMcTX4TWkv83Uvz9H6G74JQNffcGUuTx6u7Ax4r7s=" + }, + "kotlinx-coroutines-core-jvm-1.5.0.module": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.5.0/kotlinx-coroutines-core-jvm-1.5.0.module" + ], + "hash": "sha256-yIXdAoEHbFhDgm3jF+PLzcPYhZ2+71OuHPrNG5xg+W4=" + } + }, + "org.jetbrains.kotlin:kotlin-scripting-compiler-embeddable:1.7.21": { + "kotlin-scripting-compiler-embeddable-1.7.21.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-scripting-compiler-embeddable/1.7.21/kotlin-scripting-compiler-embeddable-1.7.21.jar", + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-scripting-compiler-embeddable/1.7.21/kotlin-scripting-compiler-embeddable-1.7.21.jar" + ], + "hash": "sha256-qu9jHwICEl2ZHZgjRxn4ZK1anW40m/DtRGsTd9gXGKE=" + }, + "kotlin-scripting-compiler-embeddable-1.7.21.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-scripting-compiler-embeddable/1.7.21/kotlin-scripting-compiler-embeddable-1.7.21.pom", + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-scripting-compiler-embeddable/1.7.21/kotlin-scripting-compiler-embeddable-1.7.21.pom" + ], + "hash": "sha256-xHXL2+0BepcMD9y46qu1UNc9E6T+a4e3efxM9S148JM=" + } + }, + "org.jetbrains.kotlin:kotlin-scripting-compiler-impl-embeddable:1.7.21": { + "kotlin-scripting-compiler-impl-embeddable-1.7.21.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-scripting-compiler-impl-embeddable/1.7.21/kotlin-scripting-compiler-impl-embeddable-1.7.21.jar", + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-scripting-compiler-impl-embeddable/1.7.21/kotlin-scripting-compiler-impl-embeddable-1.7.21.jar" + ], + "hash": "sha256-ZOK9uuvzgJSzwh5nCX5Qe4NoTaQTi6h6CwmhMgOXVCg=" + }, + "kotlin-scripting-compiler-impl-embeddable-1.7.21.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-scripting-compiler-impl-embeddable/1.7.21/kotlin-scripting-compiler-impl-embeddable-1.7.21.pom", + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-scripting-compiler-impl-embeddable/1.7.21/kotlin-scripting-compiler-impl-embeddable-1.7.21.pom" + ], + "hash": "sha256-5c0+HEj+qhC1YVqidOFh5/dcFijcJhZ1ALZ0b4gfweM=" + } + }, + "org.jetbrains.kotlin:kotlin-scripting-common:1.7.21": { + "kotlin-scripting-common-1.7.21.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-scripting-common/1.7.21/kotlin-scripting-common-1.7.21.jar", + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-scripting-common/1.7.21/kotlin-scripting-common-1.7.21.jar" + ], + "hash": "sha256-0ZLMLNlDFecijrkTZqNpdmpoIrPOvKwUwR1MSXM2y6Q=" + }, + "kotlin-scripting-common-1.7.21.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-scripting-common/1.7.21/kotlin-scripting-common-1.7.21.pom", + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-scripting-common/1.7.21/kotlin-scripting-common-1.7.21.pom" + ], + "hash": "sha256-2xzYRWGPDLQXOK3H72jZ+NIjZ1sFg+NbsMCEA30AWe4=" + } + }, + "org.jetbrains.kotlin:kotlin-scripting-jvm:1.7.21": { + "kotlin-scripting-jvm-1.7.21.jar": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-scripting-jvm/1.7.21/kotlin-scripting-jvm-1.7.21.jar", + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-scripting-jvm/1.7.21/kotlin-scripting-jvm-1.7.21.jar" + ], + "hash": "sha256-Uz441a1oFCoFE0HyK8cO113IUGSxk3rPBRN1XMPwSF4=" + }, + "kotlin-scripting-jvm-1.7.21.pom": { + "urls": [ + "https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-scripting-jvm/1.7.21/kotlin-scripting-jvm-1.7.21.pom", + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-scripting-jvm/1.7.21/kotlin-scripting-jvm-1.7.21.pom" + ], + "hash": "sha256-cnwtOnluoiOWPu7P7kHvKygsVbZ+V8O0mgFwpMSbfGE=" + } + }, + "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.21": { + "kotlin-stdlib-jdk8-1.7.21.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.7.21/kotlin-stdlib-jdk8-1.7.21.jar" + ], + "hash": "sha256-sy5K5+uwVycz/kOThb8DT1+u6LbFhdQW/s+TPpSR044=" + }, + "kotlin-stdlib-jdk8-1.7.21.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.7.21/kotlin-stdlib-jdk8-1.7.21.pom" + ], + "hash": "sha256-bzuTQ8QS1q5ApMePuKcJhklkUKlSjNusdimojhqlg4k=" + } + }, + "org.jetbrains.kotlin:kotlin-stdlib:1.7.21": { + "kotlin-stdlib-1.7.21.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib/1.7.21/kotlin-stdlib-1.7.21.jar" + ], + "hash": "sha256-1Gqddz/7ne5P8adIrIRdyOUABcWJMClRdgorUYe93Rk=" + }, + "kotlin-stdlib-1.7.21.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib/1.7.21/kotlin-stdlib-1.7.21.pom" + ], + "hash": "sha256-mzkq1D4vQhJp9jxiBz+ulCN9LjHe7o9msZzBkbTaBqw=" + } + }, + "org.jetbrains.kotlin:kotlin-stdlib-common:1.7.21": { + "kotlin-stdlib-common-1.7.21.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib-common/1.7.21/kotlin-stdlib-common-1.7.21.jar" + ], + "hash": "sha256-5iv+yiNhA6EBciS4oiqEHbXcTbSdgKOb1E27IkaEpmo=" + }, + "kotlin-stdlib-common-1.7.21.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib-common/1.7.21/kotlin-stdlib-common-1.7.21.pom" + ], + "hash": "sha256-LuberkeOGLGvushzHFvxoUe1dWiT1Z7b+nEWBcNDX4Q=" + } + }, + "org.jetbrains:annotations:13.0": { + "annotations-13.0.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/annotations/13.0/annotations-13.0.jar" + ], + "hash": "sha256-rOKhDcji1f00kl7KwD5JiLLA+FFlDJS4zvSbob0RFHg=" + }, + "annotations-13.0.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/annotations/13.0/annotations-13.0.pom" + ], + "hash": "sha256-llrrK+3/NpgZvd4b96CzuJuCR91pyIuGN112Fju4w5c=" + } + }, + "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.7.21": { + "kotlin-stdlib-jdk7-1.7.21.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.7.21/kotlin-stdlib-jdk7-1.7.21.jar" + ], + "hash": "sha256-uMqg+XFaIYf0+pmQba5Xy6EM7vmn+Ajb7o6vNjWVWKU=" + }, + "kotlin-stdlib-jdk7-1.7.21.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.7.21/kotlin-stdlib-jdk7-1.7.21.pom" + ], + "hash": "sha256-vy6yU9onofKT0RRpMpRBeF26xRceWB8v7Z1aKm2YaZw=" + } + }, + "org.jetbrains.kotlin:kotlin-script-runtime:1.7.21": { + "kotlin-script-runtime-1.7.21.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-script-runtime/1.7.21/kotlin-script-runtime-1.7.21.jar" + ], + "hash": "sha256-LEmLbZiWTK3dS1hLe0mPmxCPaf8akVOrxlt02uQJJ/Y=" + }, + "kotlin-script-runtime-1.7.21.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-script-runtime/1.7.21/kotlin-script-runtime-1.7.21.pom" + ], + "hash": "sha256-LuSdd/3Dw6l0akiYCbfGQ3fh2NnEXCDZI+MXI5sicwQ=" + } + }, + "org.jetbrains.kotlin:kotlin-reflect:1.7.21": { + "kotlin-reflect-1.7.21.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-reflect/1.7.21/kotlin-reflect-1.7.21.jar" + ], + "hash": "sha256-wbF65MSTF+7Sb3ecM8lpBEbFZp6zx+Jsibbg1s8sogQ=" + }, + "kotlin-reflect-1.7.21.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-reflect/1.7.21/kotlin-reflect-1.7.21.pom" + ], + "hash": "sha256-Xn69/iAG9vHksPORwbqBhTmKj2NF2xpStYTx40Cz8EM=" + } + }, + "org.jetbrains.kotlin:kotlin-klib-commonizer-embeddable:1.7.21": { + "kotlin-klib-commonizer-embeddable-1.7.21.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-klib-commonizer-embeddable/1.7.21/kotlin-klib-commonizer-embeddable-1.7.21.jar" + ], + "hash": "sha256-nTpktCC+2+20HV5tsJ28h2FKffCBR5PACQqDYJBp+1Y=" + }, + "kotlin-klib-commonizer-embeddable-1.7.21.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-klib-commonizer-embeddable/1.7.21/kotlin-klib-commonizer-embeddable-1.7.21.pom" + ], + "hash": "sha256-bOmRoyzYOdq3wbf88+1xbr6XgbRgg3ViDC9fH8RwjrA=" + } + } +} \ No newline at end of file diff --git a/fixtures/golden/s3/maven-snapshot.groovy.json b/fixtures/golden/s3/maven-snapshot.groovy.json new file mode 100644 index 0000000..7a73a41 --- /dev/null +++ b/fixtures/golden/s3/maven-snapshot.groovy.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/fixtures/golden/s3/maven-snapshot.kotlin.json b/fixtures/golden/s3/maven-snapshot.kotlin.json new file mode 100644 index 0000000..7a73a41 --- /dev/null +++ b/fixtures/golden/s3/maven-snapshot.kotlin.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/fixtures/golden/s3/maven.groovy.json b/fixtures/golden/s3/maven.groovy.json new file mode 100644 index 0000000..7a73a41 --- /dev/null +++ b/fixtures/golden/s3/maven.groovy.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/fixtures/golden/s3/maven.kotlin.json b/fixtures/golden/s3/maven.kotlin.json new file mode 100644 index 0000000..7a73a41 --- /dev/null +++ b/fixtures/golden/s3/maven.kotlin.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/fixtures/golden/settings/buildscript.groovy.json b/fixtures/golden/settings/buildscript.groovy.json new file mode 100644 index 0000000..09eb001 --- /dev/null +++ b/fixtures/golden/settings/buildscript.groovy.json @@ -0,0 +1,16 @@ +{ + "org.apache:test:1.0.0": { + "test-1.0.0.jar": { + "urls": [ + "file:/home/tad/proj/gradle2nix/fixtures/repositories/m2/org/apache/test/1.0.0/test-1.0.0.jar" + ], + "hash": "sha256-M95zEuAwVCam7c2rKIET5qs4Q60sA84RyTA3a9jdQd8=" + }, + "test-1.0.0.pom": { + "urls": [ + "file:/home/tad/proj/gradle2nix/fixtures/repositories/m2/org/apache/test/1.0.0/test-1.0.0.pom" + ], + "hash": "sha256-sYk8m4+T+hRJ+43tpPkthrE/JftrsMnmuzORCLCK1To=" + } + } +} \ No newline at end of file diff --git a/fixtures/golden/settings/dependency-resolution-management.kotlin.json b/fixtures/golden/settings/dependency-resolution-management.kotlin.json new file mode 100644 index 0000000..09eb001 --- /dev/null +++ b/fixtures/golden/settings/dependency-resolution-management.kotlin.json @@ -0,0 +1,16 @@ +{ + "org.apache:test:1.0.0": { + "test-1.0.0.jar": { + "urls": [ + "file:/home/tad/proj/gradle2nix/fixtures/repositories/m2/org/apache/test/1.0.0/test-1.0.0.jar" + ], + "hash": "sha256-M95zEuAwVCam7c2rKIET5qs4Q60sA84RyTA3a9jdQd8=" + }, + "test-1.0.0.pom": { + "urls": [ + "file:/home/tad/proj/gradle2nix/fixtures/repositories/m2/org/apache/test/1.0.0/test-1.0.0.pom" + ], + "hash": "sha256-sYk8m4+T+hRJ+43tpPkthrE/JftrsMnmuzORCLCK1To=" + } + } +} \ No newline at end of file diff --git a/fixtures/golden/subprojects/multi-module.groovy.json b/fixtures/golden/subprojects/multi-module.groovy.json new file mode 100644 index 0000000..1503a9e --- /dev/null +++ b/fixtures/golden/subprojects/multi-module.groovy.json @@ -0,0 +1,114 @@ +{ + "junit:junit:4.12": { + "junit-4.12.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/junit/junit/4.12/junit-4.12.jar" + ], + "hash": "sha256-WXIfCAXiI9hLkGd4h9n/Vn3FNNfFAsqQPAwrF/BcEWo=" + }, + "junit-4.12.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/junit/junit/4.12/junit-4.12.pom" + ], + "hash": "sha256-kPFj944/+28cetl96efrpO6iWAcUG4XW0SvmfKJUScQ=" + } + }, + "org.hamcrest:hamcrest-core:1.3": { + "hamcrest-core-1.3.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar" + ], + "hash": "sha256-Zv3vkelzk0jfeglqo4SlaF9Oh1WEzOiThqekclHE2Ok=" + }, + "hamcrest-core-1.3.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.pom" + ], + "hash": "sha256-/eOGp5BRc6GxA95quCBydYS1DQ4yKC4nl3h8IKZP+pM=" + } + }, + "com.squareup.okio:okio:2.2.2": { + "okio-2.2.2.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/com/squareup/okio/okio/2.2.2/okio-2.2.2.jar" + ], + "hash": "sha256-5YyXQGprsROIk3UCmaxjxqoEs4trSerhv8rRpj75uhs=" + }, + "okio-2.2.2.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/com/squareup/okio/okio/2.2.2/okio-2.2.2.pom" + ], + "hash": "sha256-/WIZiPf2lXAlc13G3QkLAKIPOju413ynkDYHf2KbFAs=" + } + }, + "org.jetbrains.kotlin:kotlin-stdlib:1.2.60": { + "kotlin-stdlib-1.2.60.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib/1.2.60/kotlin-stdlib-1.2.60.jar" + ], + "hash": "sha256-ahMCmPUXGsUqHiSW9+rnhbb1ZBbqPMuZ5DRNBNg/8HE=" + }, + "kotlin-stdlib-1.2.60.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib/1.2.60/kotlin-stdlib-1.2.60.pom" + ], + "hash": "sha256-5jKJkgnmtMqrlA/YLk7GOjLjJkP4Fff6cJdkeJDXnxg=" + } + }, + "org.jetbrains.kotlin:kotlin-stdlib-common:1.2.60": { + "kotlin-stdlib-common-1.2.60.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib-common/1.2.60/kotlin-stdlib-common-1.2.60.jar" + ], + "hash": "sha256-CbQ3WgZc8SeryZjF3PIrFmTEWvQrSJSZ16j0+Kt5P7E=" + }, + "kotlin-stdlib-common-1.2.60.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib-common/1.2.60/kotlin-stdlib-common-1.2.60.pom" + ], + "hash": "sha256-gwwnrx4c8k8PUm6kV5AcQ/OMGbtvfl03Y8PSU98bjaE=" + } + }, + "org.jetbrains:annotations:13.0": { + "annotations-13.0.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/annotations/13.0/annotations-13.0.jar" + ], + "hash": "sha256-rOKhDcji1f00kl7KwD5JiLLA+FFlDJS4zvSbob0RFHg=" + }, + "annotations-13.0.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/annotations/13.0/annotations-13.0.pom" + ], + "hash": "sha256-llrrK+3/NpgZvd4b96CzuJuCR91pyIuGN112Fju4w5c=" + } + }, + "com.squareup.moshi:moshi:1.8.0": { + "moshi-1.8.0.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/com/squareup/moshi/moshi/1.8.0/moshi-1.8.0.jar" + ], + "hash": "sha256-Qv50bSaU6hH+agK+zZ2iyj2v6Xye/VCg+a9cRZbnSmo=" + }, + "moshi-1.8.0.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/com/squareup/moshi/moshi/1.8.0/moshi-1.8.0.pom" + ], + "hash": "sha256-FLuAWbnddiACWSkN+IfjfmaaB0qsnImUAePIEC/lII8=" + } + }, + "com.squareup.okio:okio:1.16.0": { + "okio-1.16.0.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/com/squareup/okio/okio/1.16.0/okio-1.16.0.jar" + ], + "hash": "sha256-7ASE/xkDZA44RcKxCruZ7/LTIwj/40WeX5IwmkUbnH4=" + }, + "okio-1.16.0.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/com/squareup/okio/okio/1.16.0/okio-1.16.0.pom" + ], + "hash": "sha256-HSUhYhwIdRI6qRMRsv6O3v0O2T9mvm3+oYzGG8XJnjY=" + } + } +} \ No newline at end of file diff --git a/fixtures/golden/subprojects/multi-module.kotlin.json b/fixtures/golden/subprojects/multi-module.kotlin.json new file mode 100644 index 0000000..1503a9e --- /dev/null +++ b/fixtures/golden/subprojects/multi-module.kotlin.json @@ -0,0 +1,114 @@ +{ + "junit:junit:4.12": { + "junit-4.12.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/junit/junit/4.12/junit-4.12.jar" + ], + "hash": "sha256-WXIfCAXiI9hLkGd4h9n/Vn3FNNfFAsqQPAwrF/BcEWo=" + }, + "junit-4.12.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/junit/junit/4.12/junit-4.12.pom" + ], + "hash": "sha256-kPFj944/+28cetl96efrpO6iWAcUG4XW0SvmfKJUScQ=" + } + }, + "org.hamcrest:hamcrest-core:1.3": { + "hamcrest-core-1.3.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar" + ], + "hash": "sha256-Zv3vkelzk0jfeglqo4SlaF9Oh1WEzOiThqekclHE2Ok=" + }, + "hamcrest-core-1.3.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.pom" + ], + "hash": "sha256-/eOGp5BRc6GxA95quCBydYS1DQ4yKC4nl3h8IKZP+pM=" + } + }, + "com.squareup.okio:okio:2.2.2": { + "okio-2.2.2.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/com/squareup/okio/okio/2.2.2/okio-2.2.2.jar" + ], + "hash": "sha256-5YyXQGprsROIk3UCmaxjxqoEs4trSerhv8rRpj75uhs=" + }, + "okio-2.2.2.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/com/squareup/okio/okio/2.2.2/okio-2.2.2.pom" + ], + "hash": "sha256-/WIZiPf2lXAlc13G3QkLAKIPOju413ynkDYHf2KbFAs=" + } + }, + "org.jetbrains.kotlin:kotlin-stdlib:1.2.60": { + "kotlin-stdlib-1.2.60.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib/1.2.60/kotlin-stdlib-1.2.60.jar" + ], + "hash": "sha256-ahMCmPUXGsUqHiSW9+rnhbb1ZBbqPMuZ5DRNBNg/8HE=" + }, + "kotlin-stdlib-1.2.60.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib/1.2.60/kotlin-stdlib-1.2.60.pom" + ], + "hash": "sha256-5jKJkgnmtMqrlA/YLk7GOjLjJkP4Fff6cJdkeJDXnxg=" + } + }, + "org.jetbrains.kotlin:kotlin-stdlib-common:1.2.60": { + "kotlin-stdlib-common-1.2.60.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib-common/1.2.60/kotlin-stdlib-common-1.2.60.jar" + ], + "hash": "sha256-CbQ3WgZc8SeryZjF3PIrFmTEWvQrSJSZ16j0+Kt5P7E=" + }, + "kotlin-stdlib-common-1.2.60.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib-common/1.2.60/kotlin-stdlib-common-1.2.60.pom" + ], + "hash": "sha256-gwwnrx4c8k8PUm6kV5AcQ/OMGbtvfl03Y8PSU98bjaE=" + } + }, + "org.jetbrains:annotations:13.0": { + "annotations-13.0.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/annotations/13.0/annotations-13.0.jar" + ], + "hash": "sha256-rOKhDcji1f00kl7KwD5JiLLA+FFlDJS4zvSbob0RFHg=" + }, + "annotations-13.0.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/org/jetbrains/annotations/13.0/annotations-13.0.pom" + ], + "hash": "sha256-llrrK+3/NpgZvd4b96CzuJuCR91pyIuGN112Fju4w5c=" + } + }, + "com.squareup.moshi:moshi:1.8.0": { + "moshi-1.8.0.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/com/squareup/moshi/moshi/1.8.0/moshi-1.8.0.jar" + ], + "hash": "sha256-Qv50bSaU6hH+agK+zZ2iyj2v6Xye/VCg+a9cRZbnSmo=" + }, + "moshi-1.8.0.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/com/squareup/moshi/moshi/1.8.0/moshi-1.8.0.pom" + ], + "hash": "sha256-FLuAWbnddiACWSkN+IfjfmaaB0qsnImUAePIEC/lII8=" + } + }, + "com.squareup.okio:okio:1.16.0": { + "okio-1.16.0.jar": { + "urls": [ + "https://repo.maven.apache.org/maven2/com/squareup/okio/okio/1.16.0/okio-1.16.0.jar" + ], + "hash": "sha256-7ASE/xkDZA44RcKxCruZ7/LTIwj/40WeX5IwmkUbnH4=" + }, + "okio-1.16.0.pom": { + "urls": [ + "https://repo.maven.apache.org/maven2/com/squareup/okio/okio/1.16.0/okio-1.16.0.pom" + ], + "hash": "sha256-HSUhYhwIdRI6qRMRsv6O3v0O2T9mvm3+oYzGG8XJnjY=" + } + } +} \ No newline at end of file diff --git a/fixtures/ivy/basic/kotlin/build.gradle.kts b/fixtures/ivy/basic/kotlin/build.gradle.kts index 871283a..e3f4b8a 100644 --- a/fixtures/ivy/basic/kotlin/build.gradle.kts +++ b/fixtures/ivy/basic/kotlin/build.gradle.kts @@ -5,10 +5,10 @@ plugins { repositories { ivy { url = uri("https://asset.opendof.org") - layout("pattern") { - this as IvyPatternRepositoryLayout + patternLayout { ivy("ivy2/[organisation]/[module]/[revision]/ivy(.[platform]).xml") artifact("artifact/[organisation]/[module]/[revision](/[platform])(/[type]s)/[artifact]-[revision](-[classifier]).[ext]") + } } } @@ -17,4 +17,4 @@ dependencies { dependencies { implementation("org.opendof.core-java:dof-cipher-sms4:1.0") } -} \ No newline at end of file +} diff --git a/fixtures/ivy/basic/kotlin/settings.gradle.kts b/fixtures/ivy/basic/kotlin/settings.gradle.kts new file mode 100644 index 0000000..e69de29 diff --git a/fixtures/plugin/resolves-from-default-repo/groovy/build.gradle b/fixtures/plugin/resolves-from-default-repo/groovy/build.gradle index 029a04c..45976dc 100644 --- a/fixtures/plugin/resolves-from-default-repo/groovy/build.gradle +++ b/fixtures/plugin/resolves-from-default-repo/groovy/build.gradle @@ -1,7 +1,7 @@ plugins { - id "org.jetbrains.kotlin.jvm" version "1.3.50" + id "org.jetbrains.kotlin.jvm" version "1.7.21" } repositories { - jcenter() + mavenCentral() } diff --git a/fixtures/plugin/resolves-from-default-repo/groovy/settings.gradle b/fixtures/plugin/resolves-from-default-repo/groovy/settings.gradle new file mode 100644 index 0000000..e69de29 diff --git a/fixtures/plugin/resolves-from-default-repo/kotlin/build.gradle.kts b/fixtures/plugin/resolves-from-default-repo/kotlin/build.gradle.kts index b4bfb93..864d671 100644 --- a/fixtures/plugin/resolves-from-default-repo/kotlin/build.gradle.kts +++ b/fixtures/plugin/resolves-from-default-repo/kotlin/build.gradle.kts @@ -1,7 +1,8 @@ + plugins { - kotlin("jvm") version "1.3.50" + kotlin("jvm") version "1.7.21" } repositories { - jcenter() + mavenCentral() } diff --git a/fixtures/plugin/resolves-from-default-repo/kotlin/settings.gradle.kts b/fixtures/plugin/resolves-from-default-repo/kotlin/settings.gradle.kts new file mode 100644 index 0000000..e69de29 diff --git a/fixtures/s3/maven-snapshot/kotlin/build.gradle.kts b/fixtures/s3/maven-snapshot/kotlin/build.gradle.kts index 5eb0cbe..e28300a 100644 --- a/fixtures/s3/maven-snapshot/kotlin/build.gradle.kts +++ b/fixtures/s3/maven-snapshot/kotlin/build.gradle.kts @@ -1,3 +1,4 @@ + plugins { java } @@ -14,4 +15,4 @@ repositories { dependencies { implementation("org.apache:test-SNAPSHOT1:2.0.0-SNAPSHOT") -} \ No newline at end of file +} diff --git a/fixtures/s3/maven/kotlin/build.gradle.kts b/fixtures/s3/maven/kotlin/build.gradle.kts index 9802565..bc21a60 100644 --- a/fixtures/s3/maven/kotlin/build.gradle.kts +++ b/fixtures/s3/maven/kotlin/build.gradle.kts @@ -14,4 +14,4 @@ repositories { dependencies { implementation("org.apache:test:1.0.0") -} \ No newline at end of file +} diff --git a/fixtures/settings/buildscript/groovy/settings.gradle b/fixtures/settings/buildscript/groovy/settings.gradle index 972356a..e4b1866 100644 --- a/fixtures/settings/buildscript/groovy/settings.gradle +++ b/fixtures/settings/buildscript/groovy/settings.gradle @@ -1,7 +1,7 @@ buildscript { repositories { maven { - url 'http://localhost:9999/' + url System.getProperty("org.nixos.gradle2nix.m2") } } dependencies { diff --git a/fixtures/settings/dependency-resolution-management/kotlin/build.gradle.kts b/fixtures/settings/dependency-resolution-management/kotlin/build.gradle.kts index 393d521..8ae3037 100644 --- a/fixtures/settings/dependency-resolution-management/kotlin/build.gradle.kts +++ b/fixtures/settings/dependency-resolution-management/kotlin/build.gradle.kts @@ -1,7 +1,8 @@ + plugins { java } dependencies { implementation("org.apache:test:1.0.0") -} \ No newline at end of file +} diff --git a/fixtures/settings/dependency-resolution-management/kotlin/settings.gradle.kts b/fixtures/settings/dependency-resolution-management/kotlin/settings.gradle.kts index 87bd9db..8292657 100644 --- a/fixtures/settings/dependency-resolution-management/kotlin/settings.gradle.kts +++ b/fixtures/settings/dependency-resolution-management/kotlin/settings.gradle.kts @@ -1,6 +1,6 @@ dependencyResolutionManagement { repositories { - maven { url = uri("http://localhost:9999") } + maven { url = uri(System.getProperty("org.nixos.gradle2nix.m2")) } } repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS) -} \ No newline at end of file +} diff --git a/fixtures/subprojects/dependent-subprojects/groovy/build.gradle b/fixtures/subprojects/dependent-subprojects/groovy/build.gradle deleted file mode 100644 index f924346..0000000 --- a/fixtures/subprojects/dependent-subprojects/groovy/build.gradle +++ /dev/null @@ -1,15 +0,0 @@ -subprojects { - apply plugin: 'java' -} - -project(':child-a') { - dependencies { - implementation project(':child-b') - } -} - -project(':child-b') { - dependencies { - implementation project(':child-c') - } -} \ No newline at end of file diff --git a/fixtures/subprojects/dependent-subprojects/groovy/settings.gradle b/fixtures/subprojects/dependent-subprojects/groovy/settings.gradle deleted file mode 100644 index e7cf411..0000000 --- a/fixtures/subprojects/dependent-subprojects/groovy/settings.gradle +++ /dev/null @@ -1 +0,0 @@ -include ':child-a', ':child-b', ':child-c', ':child-d' \ No newline at end of file diff --git a/fixtures/subprojects/multi-module/groovy/build.gradle b/fixtures/subprojects/multi-module/groovy/build.gradle index 91d2f34..30ab58d 100644 --- a/fixtures/subprojects/multi-module/groovy/build.gradle +++ b/fixtures/subprojects/multi-module/groovy/build.gradle @@ -4,10 +4,10 @@ plugins { allprojects { repositories { - jcenter() + mavenCentral() } } dependencies { testImplementation 'junit:junit:4.12' -} \ No newline at end of file +} diff --git a/fixtures/subprojects/multi-module/kotlin/build.gradle.kts b/fixtures/subprojects/multi-module/kotlin/build.gradle.kts index ba82c83..0611ab5 100644 --- a/fixtures/subprojects/multi-module/kotlin/build.gradle.kts +++ b/fixtures/subprojects/multi-module/kotlin/build.gradle.kts @@ -4,7 +4,7 @@ plugins { allprojects { repositories { - jcenter() + mavenCentral() } } diff --git a/fixtures/subprojects/multi-module/kotlin/child-a/build.gradle.kts b/fixtures/subprojects/multi-module/kotlin/child-a/build.gradle.kts index b98236a..16eb66b 100644 --- a/fixtures/subprojects/multi-module/kotlin/child-a/build.gradle.kts +++ b/fixtures/subprojects/multi-module/kotlin/child-a/build.gradle.kts @@ -1,3 +1,4 @@ + plugins { java } diff --git a/fixtures/subprojects/multi-module/kotlin/child-b/build.gradle.kts b/fixtures/subprojects/multi-module/kotlin/child-b/build.gradle.kts index 7b27bde..5468047 100644 --- a/fixtures/subprojects/multi-module/kotlin/child-b/build.gradle.kts +++ b/fixtures/subprojects/multi-module/kotlin/child-b/build.gradle.kts @@ -1,3 +1,4 @@ + plugins { java } diff --git a/gradle.properties b/gradle.properties index 527730c..db26ac0 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,5 @@ org.gradle.jvmargs='-Dfile.encoding=UTF-8' +org.gradle.caching=true +org.gradle.configuration-cache=true VERSION=1.0.0-rc2 - -shadowVersion=6.1.0 -stutterVersion=0.6.0 \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml new file mode 100644 index 0000000..ffd526f --- /dev/null +++ b/gradle/libs.versions.toml @@ -0,0 +1,43 @@ +[versions] +gradle = "8.3" +kotlin = "1.9.0" # Current embedded Gradle kotlin version +ksp = "1.9.0-1.0.13" + +[libraries] +clikt = "com.github.ajalt:clikt:+" +gradle-dependencyGraph = "org.gradle:github-dependency-graph-gradle-plugin:+" +gradle-toolingApi = { module = "org.gradle:gradle-tooling-api", version.ref = "gradle" } +ivy = "org.apache.ivy:ivy:+" +javalin = "io.javalin:javalin:+" +junit-jupiter-api = "org.junit.jupiter:junit-jupiter-api:+" +junit-jupiter-engine = "org.junit.jupiter:junit-jupiter-engine:+" +junit-jupiter-params = "org.junit.jupiter:junit-jupiter-params:+" +junit-platformLauncher = "org.junit.platform:junit-platform-launcher:+" +kotest-runner = "io.kotest:kotest-runner-junit5:+" +kotest-assertions = "io.kotest:kotest-assertions-core:+" +maven-repositoryMetadata = "org.apache.maven:maven-repository-metadata:+" +minutest = "dev.minutest:minutest:+" +moshi = "com.squareup.moshi:moshi:+" +moshi-adapters = "com.squareup.moshi:moshi-adapters:+" +moshi-kotlin = "com.squareup.moshi:moshi-kotlin:+" +moshi-kotlinCodegen = "com.squareup.moshi:moshi-kotlin-codegen:+" +okio = "com.squareup.okio:okio:+" +s3 = "com.amazonaws:aws-java-sdk-s3:+" +s3mock = "com.adobe.testing:s3mock-junit5:+" +semver = "net.swiftzer.semver:semver:+" +serialization-json = "org.jetbrains.kotlinx:kotlinx-serialization-json:+" +slf4j-api = "org.slf4j:slf4j-api:+" +slf4j-simple = "org.slf4j:slf4j-simple:+" +spek-dsl = "org.spekframework.spek2:spek-dsl-jvm:+" +spek-runner = "org.spekframework.spek2:spek-runner-junit5:+" +strikt = "io.strikt:strikt-core:+" + +[plugins] +pluginPublish = { id = "com.gradle.plugin-publish", version = "1.2.1" } +kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } +kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" } +ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } +shadow = { id = "com.github.johnrengelman.shadow", version = "8.1.1" } +stutter = { id = "org.ajoberstar.stutter", version = "0.7.2" } + +[bundles] diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index e708b1c023ec8b20f512888fe07c5bd3ff77bb8f..943f0cbfa754578e88a3dae77fce6e3dea56edbf 100644 GIT binary patch delta 39316 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 abuL2{i;J+bfn>}X%o&u085n%;f_MO}UN6)D delta 36846 zcmZpB$b9%5^9F6k&G++yIe3>cD|uS8FfiO;XJBxge4$!?vchZC`lX?FgQY`7{@=|p z<37z6M9k)@u3_vz*;;9;Gq=mHzc0IR4inf4}&e zmpvR;XMXcd|0Vw8>mp;T#gCiiA0O{A?t310$bEUkWy6_ss=v>vKKH)d?%T72_VV|c zKk(c$54!<38gYGd zW;XtsaUwCK>oeOio)`mza{`5XiXwmT$X1@$Uw+z7-%@Gslh3WJ50`&FH6w9_HmCcP zN(G6&yz+t@(M7WT)AfGE-4vhdb#EQZq0n{J;x!vHs;{0pCizgjRoY;K%)|LxVy){f z&KB71`>?i!eTk+R`2zN|2y24dQQpO=5(MrD>b8UcWPi#hUc^OpIqma zxlY(;GVydz->Eq~aat!CczXF~OD=LfzjT3PT>Xxx34L1DrxMn&PZwO=60pbYn0S_? zx=MKYUeP@|TyfKao2rUo0|R~Kql z9`QZ8w9K3(?!esASsQ)3UTbZB5PDKQ+k4u@uNHrWE*z?h*>a(4uh^{%d5Q8?Ix?>I z8(*1!=xXJx7txV9CfI#%b~>+5)Q%QS=F68pX-zGazQyx<%C&`;maDJIOE}-nyVzxp z;fGnR*9&hRlHQT?z_;zp{})!OdpphTszXIqTJ7yOyvGw2RJNk_`1aPgRa@T~$*ks` z*SpK8z zU`KLMhWngtYpz{=8S@YNlx}ScUod%3C3j#@+bm`IgEOW*^B0`#tuOh0ksi}Yui!O&hZKJD#(ujm zbgg{K-R~`5n16jx_IsdyRL^w|XKwRdg?Uav8>LgJ4@uinHt)}ASO#`dM9j@!K>(qiHMcIHXNPVudtTaw$~FH&rI8RE2D>z>=? z1^+k~8NdC-CH4M(-~(ot3#*oXWO?QB&&Ty|tJG)hzK7D;+MCqSKrv&^^D&vN9?#Awv{`<^=}1hKUBCKOd;M6u6TxlI_6K+~ zvv4smaBwg%FgP+?+~xO1hna!l0NdoV)i(8agM|V`{>QqAUw7>c2>z^W${Z5#`;tkp z;VCE8fXi=ETV?MYijA6frG7>FYsR^AUM^gwU;TnVYuU>?qLMD|z3tWKY@e5(`#X2@ z^EkeLb$?hAIP_gBE~qb>r!V&@__aG2!~9 zltT9}Epy@~@lJiu?Y4X8y0nwxreD^+JvnFRx{PJBCivb>n2=`TYv`0|IXjfYSm0uB zh(YR!lUs!Dm^|@uGiwt>T`tx&EOp&(l#_66U<2-hw9_6@vX=WVU5-@1GIcvEMlFROj{0MaNc! z9x!oU{{4&+n|mdDZZtE`+vg0DM|R9J7V?UCT3aHwlWETU7B&;FiVk-sIqw+o+B@=sEUmdbmWD-mL8{sYube(BHCoa?H2qfgc&am3K@Co9P~!B7f+Vp3L6L zkln6PpH|##Ho5(scVE!-&ljGqyX$=Sj$Mp)?cd6nTdmdYKVt4}d{JbV`=x?8Uh$Fj zoga+yb&KTLIUb8Y`jPwbi|}-VIXl|?x{8v8VwV^m`gPHFRtdiTBx8d^civ=7SPae*i?)M{z|!roy+P` zGm{F#$-2k<3=9zplMmLb*RPG@tdKrlTG!9A(Yv71QAqVgORB60|Ak4VlcwcNT%_W$ z$7RA4&B#gN2PUVwmS}oN1nauljReeOh?cWB2fQ0D z_P8bWp)c+q`J8+4&+d$Sf@kY1UU+|*FZWBo{#X9RKmL{>{~eafSG{mIS!}TKjNGjw zs;Mgft_Xw{XzsT8;;I*X^KD{lsKGJdflsSi%{habPxWeHbTn_=?e_lj|}rnH@Osg!Sc}C->^7*rpwMn-Q_yS70@J zHQ)Cya&Ik7uiTX9`u441?Q6NeYwihMe!N&$@pjiS_uDhHtj|t#YH# zE&T89-@jR}C1TSi$nM?ws&nP4_4~g&)Te8dta_}FGQF$g;I6)V^X8rFW6ZEH6?pUR zjjW8tRoOSMxZ11?H|w_?m9Ed8QS*0h^>@+KgrehTE(UJw7mZzJddn#5<-{2mjS^pW zwj^#XIq4a(D&fxJerX@MFKs;9lBSP+Ogk(8W$39rcYD{I@;&!r#g%)9uf*+dVEw$; zeEz1)j+0jt-4aqt!%gzOEIH;ceXXI+Dvjg+>_l6$CDDO~WgU9eI$a9uqfVsM&&bfr zxY@t??)P22QxlEtgk*gqkKK7QDNlc`xX)C@X#V*xuL+;ayV9{(tTS-_L}~Lk(+>Vy zuvj%~+nfGlJS!b9Dhnn`1+AVSswrVOsm$cvi&a`N-7GI`#La|~gu1ggSv42l-tef3 zhdapg@Ga{si^yJ%`F_1U9al6g@9`b|c%{B#zhcjt4ZhL+rIH^5t9AS*37(wvLqKWf z*$sTgsv!y)A9&wHP0KG6=hDn;X${;kVd0l$H(ySWv`fChe6jGb&rQq62gADzgJbiW z_i9>C+$6~6EPuSjQzUEJleFfKe>_!J_DeK}<@EjEbxY*l`VWk){8}<0KPDV%FFq5v z>}oMn{pQSdpTk83oF*4W+lMCCCezDvsrGu)|*efGJ9hvtQa@%c)FyrVMg}JGmY`aD#g>n zPx7pFTJy%XmS3!Kx7USjU!&&k67A+%r&<~FQ*Kg^b^RxayGE-oM9$joq*OC~j>Cyv zGqM=@ABOmB?>#t2{hC;R*u!m0pFa|kzRJ?#H$$xKv95sn>Bh{$+CWY@M`@!+f`ug^ zDQtWPuATFHnvrKbv++@p@l5|a*{y*l^CryPbf`@u%)v#hlgX*it^We+MVo~e*KBvx`Im4b~pJ&pQUPh<~gt&%dr;p;CLc5JA8-xehRmuveS)SnB>!b(38_nZ5TGm_H0pEZEGm_@>z%r>!!$+Ex9ftec3rhO__mw6;;JAdpFyxK4`yC z{cj~V^Px8_hrVa!6b2>F77PpevF4cYoC`nZbex%0&ggpO#GOAm{;tWJsv-hEX7f3u=v>fajCy?h=VFgDkvCjh&pKN4>VG&sr{#S7ZoTb`w3v4@r>WiPn^*H8 z^ap3}{3rc~>zkQ>&o0mUBVr$WV0!GX6&y;&^*>p^#yq%Fp;KTb)z9+n<+eP3H69uF z4{K~LXA27?r2Awazj}C9iq*vV`L2IveerjByOH&gnD4L0%ij7g6XF!kQmVV4#@P4I z+4=5=-R8l&7?+nASA>Lnyea+=qw>>Os@{pAk>$mKNcMH6=}!(6l%`qOc?X_JQ7E>m zxS*}@TBv?Q_!|FhYy5w`zOH`z$$j6-*k1dS^O@#|I?j5q?@n(1RVRgAlHV6bZ&H2X zAm_Izsa!5+|Bq5d`JP9WZ+@2E4Hfv;Xn1~ zb!tLeCw)??=SfYSeW~8^+?!aT)TvJp2Bo+k2Tmxy3)`a{qMtvudujkCd3-GMRcGkKz(L-9b6yPs$mD{QQsE<}# zQ?|I_OGJ^MUCP}Oo#Ztxc7j)G_IoBpFEis>aM67J*~-wy$u8?G9$nk-t+1J^C3*Au z$a0a$;+lK2YBWE`-I;bbgYR6&vGcRi1?u<5y3Q%Ad1!KK?<>yst^T(j{f>^^DBE?O zL;JP0n3v;3(arA*-w9rpy8hNOO!Vd--pRXr^A=q#>6zi9r7nMSiqn4`!KoKmbe9*K z%#^<0{_*#xWABgu__yaHyFuN8gg2t=lV4OHm7ZB+_r~U9Q0sE`J^dHjFV?XvcGOZ9 zpZAovxBku*8`m1ouF4RjX$x`{r{*m@ej)pW!KKGv0)Nik9;20XQ+SnEiob^TR4qLV zt$8h6A5$xw4~tASF^OAtsqNY6Cs`kM>IFw0Hrd%OJ$sqQP9H6aQ|VvVtGU*Qw_l4^ zEY}XbXcpyj`{5qhE%VB}W^YSVd7g7?!sg88J9(XNY*ljWg=`b&a)<1RvbuIxbFFIB z%dasDyRNT1wEAJ?+*Qj~7R^|`;uTjY_iV4FzM(;W(pN9!MLBNwmfmuv>Uf*_$L2Pv zhtK*i2Va<4SiCYO^6r#nvrMkaW-C7ZH- zvD6A_+pA7Boa^-e=9*3Qmo`6{Hoqi#XM6N%sr45Ynds(~`d^;(Fmsi3+oPqyTh<(Z z?l*b;)vFg?6#HH*`cdb)`cZLZ_^&A@Yd`JGGCy>|^xWn5#YsLnv z)y3Tp3=G$tKecpf_sYyYe`cPa)TVj*7|l(8U%90E zG&8F*@mZ$N)T(^X%W3g9OM_}tGjCn0nOimca>;A%k`1APT+1_We-O$2{?6mF-^TN? z_rkthy7DY|*^6!2Yp)({)o|)q|Lc}&#lEh4^^3Nr-r9TA?#IvEIK}xhU4D4p>iDK$ zQ^%oMArW)exH(~aAj7R zUoz~;aC=dCrl!>`bJnpJyjIN8LSJNeP2cA&R?Dxwub-uE_OX|;U596}tY2JRDI6)I zFnfDISN-mnn`YN*%C+Ub|M`pQ6B0RJ{NPM*v-~n|>G?&^8^5W1 z`(hKCpyz1c8mId06YsCgUy>;|tV@h*`6h`>482+7a{i?I7o(pMhpb6 z9Naf7|5;iT`$;aV#W&OHk4)w!ja>mEzW3hcuzp>TCsknZCG8da1%vK3$(A~iU2@IU z^6jeP8JZie@^hCl|4sAnwY3zuJ(+o8jUdi$`-guRj zc1GU&^2cjN^G}DjolHKwwD;cbOC?M^$M?Ib>nAGhXglTV&cRB76IQ~H?T z&HA^!3*Kh#^2=_RqdlkQ;LmNF*&?z{(my>_+LZkyNiX@;lge5_#yu>zIa)WUvpn)v!u9Q#GmX>bqg6M)!9zh6g^_UL@o2Zr_Vmee5eow_SZ|t1Op#Z$`zAy^l>-@fdu%FDg}5|1eNY%57Fl+g-aaBJ-_H)|j=m zW#945nDXp`XjlB4V*g6XvbfgP%R4gmHhJG&y`t%Qfk({bYU>$>CvMJ`WlXwSK8prB2v9w@%vD+@jt+>tC!4SIg;m%?D9`7Vqxb zdDTMY$bZ%dUIX^;fAm4)1C#%}mfHMy(rPZ=naO76%Q+bsc*PhPv?gDu7MtwHC{bxv$EgR&wNmOC4#oq6)zT9&bI)2iA_wvhASzn(1zHsMU^Zb9|&-Y5oRsMOX%@Eef z$~A9clJUHiPDeLC`H*9IXYB(^EiILuOy@&C)Z4J0|NfY@Zcl*Fxz!bdac_=$|EXBn zR2_1~>sVCGgl_%dk41Z&4r+&*?qaQ2-L!pWgbw4oE<)?Sg z5BTU=vx;l}v2+m|B};p4MbCdScKYr5D}E^LTk`1ikFKWmt8VnI=REW3#_daDnf9C3 zFRy(+@!Y$)Z{2s5e=jS2FT~;E_hR|iWjp4{WW3tAHliq@`M{O>Fyonqu@k=bq9k{f+xa)Y&?()tyHs z9a~rOXh!1KSH~Z*QvP+_#;obeX=b_IM|szU@kgqO zznw90k_h8bBj4S+Zuhzr?|Mo8iI}lI-9#=s%T$=t_NIRC-M1eERv2Ze`3rmGMMUpe z_&ny6nb7XyMV}a=j&BomHf(<*@%XvorHPm4Z|{F|ImmTG$;9mVTYX1oAA5PpuUQlD43Dn8OFiuav%sqR|GvfRmky7&Cx7b)`GzI}eL)H+M9ediv$ z`1|*0p1=;x7>NmoX7;Ro!4~-%8tUa<)5z<%^2q z4w|!swW{}Jb*_q5+xpT?F{Jv;7E#kXU*<&??F;j0^kL@jF#GIdl=ted)VD?MhlM9z zOHf|=P~*k*3ogr@4x4|}{k8u`fKj_z@drt+O@75Ur!D8)Q8OWO!Nc1>`kLNn{gB(| zKQEZ+>GDhUhKI!>{?^M_^V%QaH~qt5ANE0*xBt=dGmAbtUs<`FP11$e{_uIxKTe1B zKWg(D+PEGT@XNGlT(0$FYuo(?+nI`O&b*rR)I#q0!!q%60Z*r1TDV{P%L9r3d3WV< zJeT&cY(6%rX8-iV@+_}xXKddTwlv3Y$^IDy>m2Kk%xBs1$7WHz=eu*FkDvM-`WJaH z-sF#H{pAnkl6UGK%xbHDBz5}m+%w$S6CFYiPOE=-zvzB4*T2ON3;%>KR8Tx3;yNYs zkoqanz=fQyKW6%yTR48XQsMW;SN(0op%c17@gZ4GQ=@+L1y}lpaQY?`ub$^Kt8B&n z1rs^j3`C|r&+GWdC2PJm;99-9;< z)>wMxr%vZ%qfar5pIZA}J6mzbX0v{!(aFO%Y_G|SwMwg9o}zU|=Y`ezYn99$_3vU< z&hBW}G+MDdYE5IwA&*Gu?jo;k$GLOnx`du{PGr4#A;3g@#yeHFd)AlAy4OfF78tVBZYX&4 z&Re=dfls8|X}iE0&CG?9v!1uIiGQ7ODWRhN{^hLh)X8N(+786AmafsRTqicg-@PHp zBJ9E!=eW$rnupfjoR=M^dVOMX_w&$YH&}b+CLGOOHCa+*diZ9mWzUY3FS~T4C@5-s zh0NU<;m@A=?ArK6Z;z^W%FU&xt*50)Iv2*?6JI0p^Lo3wXsrI3Ung^yhG!jH>9@Aj zclkxL($&$z<(t~IZwEa+P=l>hDP zwTPb`A}3>2i^YAnSGb#rh<)bZo{?+iy0;@Z&N}_M_3H8qM(0mdFMcd5xo_T!1rK9x z_XcO$`mvuq@x1)^&9=7Ylm{36U+~;{D|gTS$;RW_MaDMYyy|`L^i559cJkO88M9SE zW(gLh`!6gpQgu7@8n@n6E_&`()_D%^LFI}8|TIGUB18;lB}7QDETbe@>&9`)y7Hp_Dt=IIQ#r;l*A6+^o?@< z&c^SKxy?!M(M#Z7x8Q2w-3hPdWDd>stAFG6)5glDJWQ!s`OhpT&gmT`5~))@E0oA6 zUzbx6bhVGIn-as;XDq`u*RH`)V@fIe`o4C_#5*}%*6d=m zEKmK+#kYmn*#E3bJjJpiQP%&W&AbH$hj(_~Dh)XA#}@hO-iZTO3+`vmdAO#}`uroA zt?!qFrhinaJDa>}=W73F@wzqDqHC&orgFXGD%BKOb;>>c+5*P(kvuA?z?AapbeLeMJOH^eh3LffcS!|TDq_U{TeS7-{rFZdXwDli8TY5+4 z`7=IA&OJVE=5goF&iz~<9(Qo@?$kX(>ysvhpRVA2$N6xp&#zqqapyPPQ+V84A-sOi zPMPnIbbWq=&D*zDCUDPQ8N)rzvo8v~d;GNgRE79G-Q)>tJs#NJ*%N-~w^KzuhkQN9 z!)C=A4>w`EGt%;jnd&MvA;+xl=wIfvS9|za$j|FXi^EaDs}YG2fmbCiOg4YnrnX_h z;|*0Vy)VLAo=dZynyI#I`5d#mVi8kj-~5$u>!R-F>t@Sl_?+$hQnp?Aa;lHm)-%pk zHo2>P)B|oO-u&osCoH*5OmgGZl%UPZwQ=`n)%$z>d6;Bs>-=if*4I^+I2*nEXD*!e z(Su)bMqv_%m2*|rQLo0QOSf#9t?6#cv+VYg*}FEoK3F&FCI|Q2fUIfOuT|x|iS9wl%J+xe9&ZG|v zk=ChIUWYOpPZysnQQlSjVS`xe-Op8DcC56@oP8yCuIGewhE#Wx+}pEWxQnq&AC7P1N)!AgOh(uZnA%@?-Ii_qh&>ycJ7f? zzNc6E&EEd{WR};0tmBLPOgLM1nVP1~)K&SqzUIOHhwr)O9a?hWQs(>8<-YFz8Np6B z!%JG`zIpO=v#NHc{;{3|Q#UucJ^1DAb1~*Z{pO|Jr^*to0xr&s%P2R|ikUX|^Q7#+ zdDExG#b_=x%gSE$_}RJxZIh;MOX-~xIKRd;M&d_3M=M=T4i7UniV0 zy41JLyR_{@yQR*L19_F5BEaBi&BU=~u|Z)vHe4KQHRCXky!ykm+SpvS!~e**%BRd;?=|v0=i3 zlAn5^vU{s0{g*pced%n=^{V=9sozy^OzQvPtyyWwDjjj~*#F;>Z@U&Tmrl|#_2Ue) zi(T^UmFqs`J=)8?r5~z>T%PX7eZr1KTkD4=OTd2B+n1Y9ynX5W#RT2Jjz>sk>Qj=V0~=tUkR7k!xE=nNgaQ%n)~@yG*gU4ze>7Wt)MdfFpF5fl1uE>m6s!& z{)$Srbk=3gSKso<&f}l(hu>zG@7On{#-6TsH?&#Jd3w1|#>e)*k%37};c1@Q>y?_0 z@vKu@#ItJe+3bj#8})4GKPtZYz9jbc^vxAa+EI7zKIhuPa;VXGhhKuo?8=h83!bO0 zP>oo!q-{z|bBWLSS!)&*6fkYARsUx_bE?dV1$xa@*WB%I<($ZyQkf;~|6toRhlwvZ zI=|?oJglFpy?3R)b*^5(jN{w7bd_3HFXuSaI7im*le;Zz3;*-Bl$F`Ky{pwWv_0Cm z?fl$1R^6q4ZFVKenEN<&G%G1i`eOLL+_2PqUE%-sz~-wh?@e2lZ+)?l|97p4w9202 zocEPmK0R)EZ_RmEo%8;3&b$3W`{b3+*m|z0lX{!PnJ4zTzOeg78{bCR*hdOw-7McG zEz3(>?0%=;_K{50QRN%z=N8A?G%|h^`PNJZMM>v3~0Y&m@%^CI$vcHU@^tipw49b?X!s)p$p1MRUy( zI4a@Uqvon_;QJvjqHWWLzbEom|5MK2alvKL#UtMOe*%vgZ}yJ%;x+L-f9B@g@0Rax z&b(`1U;l?G;E*zN;gi$|zW|LB9~_oxTXhx1m`-r;JTBE7`1Qv=?Krcnro=5ZNA4G& z&WzBP3Ab6s&3>ow_PiTn`|8iH-l8PDrl4|f*4r&M%j1@YR_*9ISQGj^`cCO~zSi0` zmv(*F7W3}+q4XPP;zFLE;$5G`s&`!oYd8e+oXPnq^x;!<>>3OT>pyv zpYvA7nC83-+wuH_&7Kw4&-uKryS+^_@y3aH?+a$U`l#-AH`6wC#`|WTj8#r)(e>?o zp)$*w9{v!0XvyApBfTT7_`O7swQin#{^ztCQx1lzbFV(CB$gZ= z#uxTd=?hFbZ^=lN{9{o)e=DfA)F}1Rjm#~!(I+~+)1WESHw|0Cs*|D?a1_cHY})O){yGiLct$u);~j~b**Zsw>oYcMG76WGyt zg3bAmUTUD!@fCkO>VAqu?JMYdF`ZA%S@poOg&QSRZs@q(oa1zk#mMIo--0xub60%K zq!-HD*8kjardaCf-jILVTkc=j>VIL8dWqt#$E6#OXKS(CUtG^K@rm&?xq~L5ycJ<5 zx+lK9;TeDavF#j*G}ZPa#WVIRs2Ts|&)HDH*v)+8;_A-#-MCiR>Vy1&PeGj_{b(XDIOMrBv?;owd@9qA6r}+FQ`{(njpSSe@+3g;(moO$UC*QXr2 z;8_I|o`1NbAp2nRl9|g33nwi5P_r<8L7``ZcXNsO^voE!B`;$Hm+amdR3&yQdg>nC zOBIC^4#{wR5{i>rVil)0|Mb#G#j~eAmh_zG`=HgkI<=y7!oAbkOFqwGSU>goPRBa- z>nuC!!`xoE{3>;dkYAz{C;#B{lbw!_+Z5J5>6MxD~loi)&9H@RGIW;YHLb42hfzpY) zH76~$S!inAWb`}vIY@AY`{R2GrJbR>cjVjbJy*T7a{aOQ+(A9FmsIW-sB?IH+GL-N z$FvJ^hvcVl$_u61%PcWJ!W(;O&%yAFKZ3pMQ!YtA^1R*Q_IlTuZMLtB^P)-vm1mkv z_E}i^^>y|(^Ss>!vGvZMx2@&fdV9NRPTsBp@3Y%@eOP%UfoonW(Kfx61EyM*pl(+i4f?=0=6~W(gQGzwMk? zVUoDT_-vVSPQ-M%>7l!LcduqsIu^C~Q02PYExJ2RW|-W4liR&)(nRmG^Oe*o*fHQRu?@Es6WJ+@H}3))BVj>pZA)T+Frys3M6l2knJ_Jj?(qOFz1Ec2p`CV8)n{$gdV{KyUliXPhP8W@A`{B7E9GeKJGX4 z-Fsz=l5Fixu{*Oo7$TqdU#ePjX1wyvcx->1oL_yUY1?Gk ze-iy+rfyRtoA_Syc1rM=m!=%}QkveRW|Q@3*2JXsv1>mbZ@=o(zFtUM^IUA4QP{@G zT74(3CG}l?z_R$PpJX8G)WVZ4r?QMg9eT{Gcb_%8ofEFyxT4u}g;d$`;B~L;9JxKz z-^zKex~Lqn#PX74z4@Cdtw+V@wp^FUX3+4wyzV{gw;aO8wsgCdg zyX5WB^FA=Vllm*A=XdnG+k%fX*A-5C%{X6j``UeLgr_tbZsm$x6X&L~_8{|38O4K> z5;s{ck2hMpp^IZvSJ8~C$Ab-x>u2Ax_0q}Tt@*oqoB3p|?qloTIZU!&_)(ql>Nc11 zc)w*yQ4x=39lVoTynteW{7x#xOGXIk3H z4I-yqxW4n6E4RMZx@o0(IAfthd3K0obb z{d=3%lgnpkey*C%w|Zfozglw9$MDjBQuVRVoom|9 z-G7kpRlNLM`%kyI?-z;B)U)zGDSb{?^bmK~WbQ+6IgKUz)ZZ9rh28Era@cJTe~Q?) z>n%$*w?6#!C9Yn2oA5)vlusLOcIR5&5|5j+^mTNHR&vCSvuni{M?c^FQ*Lg5!Txj1 zHSCvJ)kT!{mI-;Q9NM^Dz44s;&$!X()kRa;zqXl8mR@xacv#a)k&eiUoCJ+)pf?Mv(K9`CaS zt22)s-X{2Znv%jD;f=e+#ExHmd7!KHt=?g`%h%ktl!?oIQowDY*9V`zHB!xq5YU~=-l(@VrmAYkVXq?z^0&QIj|%%4ZVM3M zelE5n@k;vDhmTSUZ6*gC4vIZBq5ksRuc^_y{MLlUG0raN)%tdf=fxk{eS%-l8tmJ) z>`|FYTH0#?&Ry*RqCDyMrA{0DQel1lC$=i^`TLKDt?J|N$u%;+WbN!s$X1!xmz?;4 zIfPMt)dIH#*2P*Mww4|h(x|l2p00oWXM2ZfL1oDE;}Q>lw=mSLs!lyFQgn0AT*JBb z^+)`<7B64&^CQR7O;hKe^Y3=uljPsIX@1HHx3B`M{~;&R_LmAS|9HNnuC0FE=gpI> zyN^Aav4!*0E1{JcySrqj?Cdz`GgtbZoL$Eh zZ;`sbdDQt%v^Y&%OhiOwzl?MGgp|f(G{qftKyoa|tw5sy1 zllb)5=4xiSX3ptZN!FHJ=QUPJd(Yo%vC3j&S>6*y>xmPcyH#E%-!=1F`;Tv{@`}lq zCDL{ukGc{1C+F43(#Gizjw1*4SVs^DrSnCaJT9-Y;JEsZ-;# zZBcYVRru+-q00h-mfr~Gx^gBo`bEoik)p4YUdTjxe_L2TtIu*~zW1(%^jY;@-s{d4 z6qRM%jCi6~P+k1ZeZ`!{t6H;7)v6|3<2_kF)3hiv=K2MJ#T9Xs_ufr<|ba6YosfaOT$v$>wMQ?#GYQ*3bPX^LBm?)5Qe6=Uf-%Z$FMHYHyop z`s~wct?=uD*Q?c=Hq@_YzuoOwoS{EeXkx$97sg#ZGxUy!%hgWcdUB5OJ!=u;!g=AB z?7r^lT6gR-&xJT=dDq{DHL-kraW>DV@?O}n$&qWDNYDA@?ra|RPQs^7^-l;3D{T3b zI@LYnQR3D^$1nP++uzzbLvopBiCD@E_suIM@;|?f>X~9`^HTVWr_xuodZUe1!Ye;r znSJ7^A`&C6ZeNCg}$ip4NX$|fAL4f`%hf|n&hXSP>(%oz5c`JgA3JDcK$4PI^kJj zHkX_6{8Fi}Uoua24C#N)<`pRX z^@884rD3lY=P_*QzsK#cPkzJe)=vF!wf7C4+zgZTFVu@C&Jn1n>)gi^T=w{{0?*ou zdQpDy2UqOKtG{OY`{il_lS5p#@r&1M-(0`Mxr99>Z_(!$(hnXTeU<%kc8U5A&WPy~ z%~-b8^?u6=S#mK}>h4*oyUt(K&c?03IPpNehWvf0)}IzYNTt4RLcNtkFqhY!@4=bf%NPCBm@2#UleJ+`$rH2!n*6r28$ zvz=2~Gq3AmZ}S$ez;NBn%&&}5&Bte{W$fJ&G^aABRk){h-|fskmPbPB9Oo?^s$4yG zPrvqTqHv9VSWnrz>j(b}@YJ!3{AKfHZV+cav|1rb_ntXJi{x{?Wi_H~QX11Qet#*b zQO|I4Q&*mkwN1m!N6+@GDV{s;_eEu{>66nrJbg~H95d{#Yp`$YOyj;_dz0~LlIQaN zNB@(*?AV-pprKCnm-7v?bG8om4mXEf35xsB$GGa|5#Q#J+mH6oe-&lJ`uf4wrm7z@ z>*DX*I6SwktYQ9SQ50~PG0NUUI-2d}AF-c#G7Eg`nXiQO?%5}~?H-f#uir1{>RxSZ zU#YP~G^aF4RV3rx@@I2T^d~*a^<~docDRf^Zey^_((S}niEmgD!`{<)teG~`%V_WNFK zh}2EkDr~Teqdwv$bAF7Q{eeH~2VVxVyh#3Va`J<377c$`zV*r{##eA1_-mN<&!uvU zbiv)uyDvC!1-~HsX+$Wh+ z$I2RozDsueE1vN^^1xrq)Si`m?OUX*e3wiQIn#B5(Wz>ln$VMz@3mis_8$_M^ge9b zYJ=zw_vdQoZ$*mU;Cntr)1f}>Ocav~--PgO*Jhu1ZduaGuJ}X9V>)lQN%+z4-W&d$ zj`$&ZFNA)MB)wiyFCmQ8cf5I)na7EnF8RAUmf^Sb@FMihi>AF|r)JXd^OYUpf zcQ6T`_?$m$#l`%}YqJw3sqYtG>fN$@!w#2AUz@L}9(6q8`041T!*3?asx?j5V6HDS zR8M=vzQSY5>IwZUS6(mAYdLhexrFo4i7ZJ&Gl!Fmn*w!P_gtFF^^^TYQ0K~d%_avs z*Ug*o?ZCl>{wEjhy(AfszmVb6#_4(o&gDB=b?1ELIiw!s^X{c7W&`M;ppZid}g=f5zms4>dwu}{|Ztht`vTWkB*bnoR@eo2-u*B_o< z$gQOCpKs-!Q=)8{|CgPV+S_Mg?H4L_v~5DzR+%}cId+*=H3#U(R8Btjl=tB4eL9_7 zfs#^9&ubRfrzLfsvD+k@^oLok;G5vhEY@{Lf8Ho!_Iy0tVz@%4A@Gt~t}H@x3FzwuLiexGq(^Qrth^^BlV&dCYyq$Xdk7M?u&9nWTk z{k2TIUS8|fdl?xR-ZL>USbzsR4;(bC|0wppoK-$cl=-N+>jW`J4K0nPeYv-n=Jk5s zT-H^;G(oWD1M?q^!m{Wis}%N~IWu#nadF!Bw|}3CIxwD&Tw*Y{u~{-VE%warh=hXd zO;ck|sU0rZc`(0H;qe29wxs3ZTAoKI+?zVl=bOZ|;=jIsGycvwsCmrvph3{Ii56M+ zLt5&s9#5TMRA;&F(}y|QD+~2ss)TPkYLJ$t8nrxS>D&_*NgwzYd#p%05v}W}d;4j` z)#hD~wg#}2cbU(2VpX@>lX+%Uf!{G z)gtac`&6T6Jy&dYDUsvteaq#wRo1D%c zJ6f76!Val_@Z_AI)Ea0ber&m_P5*h16Aw4*+!gs~InQ1{kwgE(heaLw9Y2yJ;wKe| z#PdDYUh^kZZr?G7`1F_3N^x9|XY15y_vE*Kl;ymC^!tn>YYq0vALIY=L03?}p4-A8 z`N`CvsLeUSr!BXoy3GI%=Kbk{EX_7s;rfDt9EB-wNBcV zv+|ki;xkfmJ!wniE_!J>wy2rJZa&YM(cinSo`b_jk~Qe{%!?(Z*EFAp6nmbW8nW&D zlW>vX$6NPHZn$omWNVZ>D*5K3JGi#j= zJd8E-{<`SiMYRvSTFip>A_{`ir{oKXXb7 zS9lzgoFn>CSu*oa#Iea0UdObarP_4q+K&1=07;Bwf1b#d>wh$KY!} zCF}egJx~5%U9a}RTXKI={maljN)N95)~`DDV{6C#6F+q8)E|8}dS>7+Za2Fw;D~(6 zA3;&c^$-1x{`FjE{j&4Tsg{|uu5?M>4eR!K%NsVsVQS`87MYwtMkY4t-Q0b7*{P-1 z=Q{_t1#7vK9`0Kge9+Cb_VC8VaVL9>zvULqZrT69SMv6c)jj(+-oBlq_babHkNZXL zySYa94#}<(-miISZs`jCK5>)aBllDPGz*(H)(71zYMs*=B?Y4fR0@mn&DScgM|y0 zd_9}*;&X4W+MWNGckWp?sc`><%0)Fr{~6xi{&nb%{hoK?9lb^WxhXrT^1i|$P^HhMGYr9Y2b7nb+$F?(L^yxJ+Qv)1TN*SL%GA5EyTvSVp0}<4h|uLE zp-i9OGx8^^PJA}|ql@TUQVXdm(pYvGmQwuf0wlTX?~YZyg^CyOZgc&{y|(lw6W|{sg>R$RO|5 zv}nQ+wPP{o^{VE)o_BZ2jrtgceya~Uf6HeDR6S5|4zixBdwKgS6W6q^FY=2ySUBB} z@pvj;TJnB|Ja=~(<1GIpoe95lzl0aAXgj=_H{Rw~vfN*Juk}`9ACvDxm-EfPPLeODTnU}WpaYt>Z zzA@`t=jNsne*E2z6YiQX0}o}lytnC{{6b}WwdORIQgkq=l)^)5E;L8a!gHaCr9A!?+X`B7xW1c zdg~l|C%o`IcX8K_dA4qyvlX=?OD5ju-(9czdfwXT*N0WF&+?F({*fi})7yXSv0rQ+ zvv=fwue3TjqgiCR@J*HF&+Zi;PO*vfQ4(6ORLK4C$LoyM zDoUQ^-d9g=S2ik>er&o#YmV*yHA?aeii)ydKRLJ6qI7xZ58;$Ao=P7@mS*uD?+klf zxBT^y%K@)DmGtiZ{qkRQyGM7$1>N9Z2QKwiG06UB-n{#i94pr<+m-wqm>3xB*(bj* zkeu{Es=jw}uD`IO!145jUv542@}AF9!z7x?|8NNxmlCH(sE?>KtI+)kx^td-?3}cF zwOXB)uZr5f56nLn_86CQPf49UL(O=z@#pz}-@f~L{(k&^#sy5Lt(F|P^dOPx*`x#q z*JgLl3ZI8IGg*!NPuQq#UU6^|+j$8I_CEOvzs159xzi8U2fXd5e>N?f{o1!{Y}btM zel>k}n`cv?fA?}GT`z{mJouXq=Qp5H%>UMUNZ@>JO zoY&pQ{rz&z-|GF-z3q4X$;>hvKb?-*dAAP!S~@x1EA{7_%=kPhz1Vk(*XEdLSw;QJ zx$`vp)X!DVoL*+F&nhVi|J0*lt@O1l_D9G4%}W-)dVI6^McsF$RW65JU3Ff)?c1Ke zzvRU`_fT;g@8yOIIIc_dXv?)eTk~sQZO{4V53laO6&1Ta^!v8gSJ&>nK0CjDHCsx; zjDs_rc0K&Xn7>tG$vLAhQ-89hB;IJSnaMO$(L;K|V$IwEK;LG8o( zNr(Q2%lyhva;t3-`?cnS<-^a1#GU3dxjlEcZdx6C#`uf*+4%>~awg}G$QA>? zmcmcf8uG5uEsR?#I}(>Xp1_mVZO~@#u%Ny`Af{dVg{0f|BWhw*t9WPFJNz%R)6ruQ zf7Dp~F;_zTk?5Oui}Q+qiG1M}u|4Cr;F+JrLW$B&PLYdyFB}uTQ7EdS*6gF@`0kLc zsD}@m=Hg>-7@hc?rTw)WO(av?IE&(W7oAinUa|5E!%40QnyM!j@EEOU(Ol`smO8=p ziq6*nhLfBVHVLUHHrs?Z&B&V2!n3Js)>b)%N=Bo#ES{$nLRC*Nn|cJX-E=rRTTX$M z(MUKe?LWA!k7zr(|5?E=%E`bWBf`L-0ctx=`oKOp<+4Ei_j=*ylK}5$_lfV{ttfw)#`h~<@p~9+tg2L_iz0mbAJA_ zhsH*B(ubZ$G;*K$SIJubn0w75@yf5NakZ8$>o@#5Z+w{BXjx_5yp#SXcGRy(u-Rx_?$*gNrTGn1H`fyvZZ0@yS)?IE1jef?yvvl7WZmNn;EDG7q zwl%Q+n(3Y7Wn0cDo;9j?FSqILx|mOzFCQ|gY4OaRzh&!{&N$uY_g3FbPT{Ry-||&7 z>eueP!-||jv8%tUxL$ubWnQtZzy@`mqMFllXYMdbe`$I4(s$`fuQeB$nt3AIT3j#k z_BE`@+s@BhQta?4?V#6Gr_egd(r2 zb(c-Jc=u+^!Lq*c(?8mJrl;)jc_)88cYW0o>A6!_U)&+&slV#)H#$yRb&$$rb$nbWa@$ z7v-98xa`6lmD$fH*8jef5Rkj3``(&6Z1%OG+mn|6GI6{4u=u^-#7naYn z{@-18f-8TcS4Y?f>)U4KyEk5W7sB&sw#u#byJmjVm1_&Bk-oq1SnQ_C`UMlV_ImD( z$P$W*ovWn!;e>>yaO8fo{dSYgGH08{=pa*IPtirsC|lOsto7Zc>y1l zpZzcte!cg|lNLAqz)c1#AF1;l%M`7*Pc{3hs62g%#*4m|`G#Mgr?RmB5cm0^D*UQu zmulsP%Q`8S4td{Mq_n(7CTr&8Lkp#E2Ja6l?8*IW%KV>gf9OZ+xW97lD+8CFo|Vxi zswO|bwN^RuPM_!c<>uu!wf(Mt9FNL>R2Qv(<|A=DP_jPoWB4E6WA{Va4I^zP!iK+26fq&3aAqqUK47t>Fuwgj~DkSN$jG`Q^!{Yb-1h zXD*-fef5#s?;RsTBG(9A`>`)S^=?yDTIrPIQWLjSZd|%+-NNT3L7V?pw{G8U)%o^* z*{i=+vL~ZvtglHBb3Yw#@od%ViZ+YSE4mi%TlDHvUHzG<0&^Cdf3{kFTDziqtHY8% zx}IEjt&*O|6nfp;U8upg!@JOIx}d7Iclv_a+foU*-Lue zFZk;EZOP=>n!6kld2TIR(R_5OeA=IkBGpM#1Ml-QKjTb?tIo zZryo#{h+Vek5ae&_36{ToVtBzm)QOOE-vSdv;HZbSzPb5HRC|s9rxYWFRbI5ow5J? z=6JW?eCO*}-&))L4&8G9{Y~@B`%Z6;?>5+;9o4=wxqD`n#o5oEW&Bm0oBPY=u2?8~ z!GJxpf-m&&#)7XpOvN00=NF}kecICSdilk~n0A)lRmGh70vEPQXfXBFJBw|(c7w<1 ze9}*utoFlR4^+=cuj^A??=bi1pVc}#VS0kT)#=+B=Vl?czh{?4X0e?AvFP;)o^ON#(*o_AmnKQZ z9lOjk?fjxdw%&6$xt_1_oU^v%Tv)wjdilh5CF6*~?1n z|Iu3ZcgOvFlEmeX=Iz=)UEtdTn|DXKMgF%GBsP5WF63x$^NzY| zK2p18#YyHzJW4DNKP;2X*~h0UcF&|+xBYp+_YL(Cd2^3$J9Xxjs_3r(cdjW$*;`jP z&5kUUo5@xGVu#UuHKpiHMSQp0Pcnc1ax6N1Lvx+AzQfDMJAGNUOiQ^c{&d}R+sU*7r%wHD#re=$C00>n#j=wy-SrsB{iFulVT436J8AFS8cyO8&A~ zpM5*u_6ujOwbsuGxf2?B{J4!A$4ak0hTSDF{Z2hHp zbDqMzo+GP+x2zJHu{`$7^9*s3tuB&2t(z;u7bq6+n({Wjyq3+e&2PO;|E-8QLRC>( zs)fdhjRqnC+bJ>8{h;GfRo(Te9*!pLE8F zDO{`KT_cXoOwcWyt6OF4Xp?zn`z#mb-R3XMHa%px{;zji+{5X|ZRPT`UR`?fO6j3) z)BlJ6nNY{!uB3Kz%QG-A$WC6&ZOijSOY5xm$uquNpE5B_klHN9b6$`|MKrB?@=Mu( zdQIC6x&Z+K?>j^n>TY2b+|b}OwOfSC(euOoZLzPNZmQmPtzn&iDExp+vscoe`PqP96_wI}+-^{RYXs~dS* zEz~!1T5nU*k&{(1S3`;?C2qTZ^G`mv_Z;1g-QU+5?hvjDOUw{#S~_>_mL!|Wmn$c2 zQT^I8Cw?+-=$me*?F&v96uw%zQo?vuph0-~C07r&`+JsYx+&?4b8$uXS`_hw99n3x zcV_ZwuJB;FdQRub>(y={Vwlct|}wrEdoEmz-hp4rbQ zv7Xy_Y>iLh-p(#XwKm5#2i@lqOG0appU^N~<*NogmO_>5a!4JFl0i~+W3EF; z)Y53jY)>A|wukkrHby0Fvh{m=s;$UqSytD<&EmXI_Z5rHoyD?E@afDsCpB7~5=^`z ztwNj=zuZjSmAvXFN9fcQ8|sCbw4TpNGWs&5tS6+(`Q(iDbZ=Lg(^t0pwS3zawBEA4 zNGI27tJr%d-`e$ITjbAuuU4w}XVx>@@xRpY;pPR4y6ax(etDf6a-jC^rJ=haP_Jvnl+Dz6n}Z$1`U$8fEy{ukd# ziFK3BrN7GVRk&l~&tiYzE9bip=~s4t;GMVh-}?+}hIsyo%M?$1ySSsAA@eW8y{`c? zw(a}p?0R{gYuJSh;e;H)wK<$`by^vsH?CmGRd)|jE8xEC;~HitV!-rqUzZ}^#~WROr5}yD6K}me#B|nk`xkc9 z47gO%W%1-*Mc?`@D-`U)SsJwq8oHE@xqMJm5%rzmR*|@AOS=aB{NgYPHI%Ti15Ij_}`^bK~mL+WPsdZC7O7E&j!} zP25=V{iBg{(aF_9Hp_XmZ-4cTwtGBVFVb(y96;c7ul!IuGsP-m{(f%gly7L@r%d8p1*JXnQpW3 zQK$XYlz*oZHU{T@{J3ahNlbEt-67fI$>~jY7s{4hb%-^unWD>cY}IyM?QhQRUynM> zK0jrZK{wB)tR3@Q&3R!@W+vaPx{Ale!F-KTZ?dZgAe9at&L`42yI1 zH;vE8?Kge#xz|3l?cz3R+c*2T3hQ{}txSK_XUxcu|ChZhxA5iXr&nJ!%f^2)D&PLr zIj+Ho>r2eCm%K~e9lnZq#$E`SKXGaM7WXBYCh0!g*w5pgvCWYO0Msosw91*o5MNV&}7pbK8a=vaU;3Q zd|iEa%2lp+KTB5$X^WlD$Dqx);X+9+W3gg@=X1|X9{U=+6{`%_+zhz#D16D8+Eoj~ zZirnv)43#T%L%v1bHZEW`6|DkbU*4p^Qi5HyUdd7T#bCTT4?gH9$&UZTY}F>inA1-ySUFvgnj@f;(a{hdC$*=Fmg%h7OF+I8bg*_vQ=~zB$dhO}<75+21QN^pi=yr&} zzrM9kc5J$2waimm;Q`xn&zjES?AbRmy(h(WN{ioNn^QDd^y-R@OZ#*#?_=se5Ise5 zhKSLHf6WhND?}u|dE_Ns_j_V6_u=`SZ*Fe>{dT$S>c5}o$Ja3%w6`1iC4Xe$^)_nT zaAM+Q#l%QAvFV3*G_97A?>ftMd{w}1HRt+crG3(hcb@D}eQaho$*`fcZ%>bgnFW_j zx#X|8`8`!NZHYShH>dg>4corD%5AmOOw-k2!Drv@Nqkivx_jMi%S&-)K8JT6Dk#1b z|F@Jke9zrQ(`VmGnQ(1Q?%an{1aeHWQt$3tK5ey1=QCa3wRx+yDSq=^sPe#wXYDI~ z-RS*^H|k55-_S76HPYDb8@&Dcs;s^1Cf{15f1)DXStK*UI^eC7U%Czt^R&p4klUIz zWjv>!{AfwM5M90R#RbLa=*d6bUv1A?wrbW?pNkuLH&1#!#pkE<`whMlO{-!Buaw?c zoS(k)m&>H>ANVq&Gj69GJWwn@?VH9OBrzt-jd3_Dx+(pVHxT(kHe5 z3#8q>!)-HH@bDUmXA+P0-btR&9d`JdM!Jg=U-=6k?suYdj&?L9&Uv`w_yjw#<&Qqq zvd;c7<*_lVYL4NNKR-e~Fw8gB`B}9q|J{sh!kfL*=GngSIB40!84_=(!e722DO}3* zZpk|Pt6$RO=48+6F)dc9k5%0}V|v5J&X2#2m^eRCh~$3izUx^*%~}W7$cifxdOzE? z)Ok1@zRTYdt0gA>Kf3S*>xAb;Pdl?-zir^No?-njd%63w`Rm-(`j#Kra4*>P)z`q2 ztCqd|T2)inCU?Dt|9$1D&sPh2vf8>DHF$EFR%MCaO|-brb@}v^@bYL^c8>aKEKMKd z1ab-m=07wm+yDC5DQOMcwXD0t{gl@$FD&v~85!I;ZK`I#WQw{tTq!cxLrS40@qw;NwnS}!GD^E}GW-;y#oOW0u+N~?rtuJ=zwt}mz`yMpjYG0Sc zG?}G7a?P>~QHL3IL7YoYJkwaG*qfQd56lc zCpwd^@SW!fo+#ile-GP5*7QR5Mf1-+O4e-9&-fQK>AwssYQeBYYju<_69dD076t~l z$%&$}llkS?Cr3S2tzQ}(>o4pmVq5-uW5D*H)9qbK9ibVHViktVT2}b_%(~LQq~-fU z*;fzBrk}~{tUvnyK)3#k9|<+z~|NlYofSMWi zhCt1PGdh-cOi$ZNd#mluS)7)9^q7|7{-rgdxxz-Luf{rU6D(0pT(q>l=4Z`?`;)8O z-&D@g+}_mpp{zCR*&Maa7qvx&o=GcB@wJ~)w?e5@<@Us|y7huz_*=yS7jXJr{=PJ? zLWxIJ+$NKC*Yj^%UQ}IrTX8BiC;mt5<*!RGEW9#Ra7t;@-A;d-W0#6vJIHWe7p^<+ zJK^g?pRFfN4<;^Qsz3Zfl4(wC{-*j(>u%jw7q~8Rd*X{db!vRU7oYFid&7Fd+!c0n zR+{DB=|8BvH1A69=WDY|ou0;DX!*G9)lVfUiI36sv(f}K&;NaT-5U^Dhxgg&P?oS&D@w)GA+3>^()KFw>!6=I4e85_S=ljGiR*K5%?ln zY316u@8Cjt!))D^K9Ohhq>Kxf8?F0v?vbQS%;C~!Gjn{^te!r)l>Dw<`R)IP_eL_^ zTc`S;So`Tc%g@LAJ2ceZdL^(w3!2m}v-S$(t{aUrSqjXu`L-LJ|1PO>%`TTV|L)6FCo~k>liy6zkceuN0noSXQd7P@OUl=E}=S9yhPbZ@$M$Wb#*Bd&E znWBG8=}OqZq^9F0H7T&W`sd|o^$$+1I99--Y?O3ED_Q#8?^2$@#h1+t<7}Q*E&V_D(wP+RgmUT4(U~*aZmSh6sB4SUez)>b z-^`gGd25*#@@YF>n|oBl_T|CeUA3#X8M;nbrB|=LZs{+R+YjC|NEEU26or13x-4Z_ zyH>68|EEV>B5JDVPfoj;r+&G4*GsOL==tyOwasiZTa@}U^yy~d8Lk~4b?@E^NVuER z*P6E5_E*^S5W$Ej`{bFMC-16me)dF1b8Y^P!^V9Y?Lk)6E6;dao{Bjwd*SU?$CVn+ zaTdn&D_>3)uYaqct+GcUvAso{weI)-=P`wEQsd&L<;0#kZ4?((8F}tB*Sf=*1q$uo zqcW}6#pf3!to~du%TiV(rgQ3^HzG4;uYOl{``Wg`&d*o9x%7^vaxKaTyU?_hYudul zgGDim9=NR$?3f!h*GFj0Evf6xEA;r+%?>JA|0_0VZS=RE%R?>~g@)f=w0hRc zv`O1%Mg{9tUeCL;=D6qP=^wdPZ}&d9`iAa?O?N}S9#kxkp5`NaQKt07wzl4uc>(5; z+obGUU!-xa*Sygj$Qt`1WP^CN$eun^p?d;5maN#moA>y_m`|TuefXjd?rAExRw-Zj zcH=phK-S+Mm$hsc+0$DtvZuTLE*IYeTh{l*Dz|oB*1Wzz>hz|vD}r~gEkFB5!7ku~ zV)TDOyLnGa_s59T9OC;gGT-w@jDv2N4Hw_T4F{H{-wp}~2Gg@EbU94U zEm_8zwfy?36}-<^t+{+P_w3djsUJtVqZZ^ztv&l*=1M^(Z}1v+(^vbai_cJby?a@` zm8 z!{hGrtM(+;uA1n{FL``Ne`=7%ucq(up|#HcxHr_RNw4-Po++@oW8&F|uRM>;yraf; zso~j&>L!<2_sYs@o$I1DTo&%Uv)EB_(+b!oFn9MfrtX_YPWwRP%$B}F7Yaiw; z)wPVhVf61+v)6tLsTaq6Bv(}&xwx`x`JC;S7kn<8ReH~_SRll|MDL&|$7Qd_lNG`R zQ`G~1(;wSRK4L09DQfV=18?4DmpQBB+Y^HFg~w{yTz z@p%bH(?vMq-ah1-+VG`>bII2FOXoZ$I^0sfd80+ruc@d7R4*G4# zcMmjo481?`xmbxxH+TKhNVlNuT{W9nzs!?5TzN0i`GvzV*5~O>n>G{#96R~?MV^fM zZqGM1RxuhMY{kAFnRTq-;=%g=pKj{NEuVAh;oR8=SHDec<=Fb}!T$P78*QFn3ENaJ z^T^^^SH6Jo{os)L1GbOcdphQC@cz>i_>p_>q`Jd}AFbK}v`9{LvQzF<$hq^vjr_MaFlQGAPhjO1=$rdQ{rXw;K)r|4_6HUVb(LG}7YfPP&i3$@ zi;w0_1^47$-_OqOihTZ@H?-$5<=TarJx#eI7A?}-vu{C0${Ox2w_|VPE`C@kUp}En zXwCN@_d3%=!OQ_FE zFgO@4e(lVxcVhY0i$3<|f4Tg_NvQRRcpZcNl#h3|oniE5J3Z;$oZH29&o{oEzkh!n z^NgdD`dEsrCY@0C)Y2%8a_2I?db4LzSK*Y$Gg2?i(LG<}BC}nhE&j(RZ@t+1;B{4Z zj3s-PJI&aXReaqvwr%GuW$B}BPfBm=MU>YzDLq#bLXtHoP8(m@ui@u3W?A6qNcn%QGB{o_aeuH z)0+gtwqCIAUN?@jZrWO3UNc)fS5N9_sQ;wfL3wi(Po+9PEq|hR zc!4sX!nNHByBvPMt~({D-FJh3s+GZg_Pa&zWuGN9Y*DuIk?XX&I`8b-qlU*nEL-3C z{hQ|RXu+QWLDP*6KPiln_!7EY)%@)7X}2a_R5mkSCCSz{A#D0>wvstU2K|Q2qB;^g zI=tE5CDhyWxwifMH?1S_m&)JN8Mc+ol4@1kn-gD29OjgoyW!4X)=Pgg^KUNW3O+Tj zBD!S1=-OqPHf=uo=_xPXPF?a{%GNVWN+q*n1?R<+2ATdI-52V$&VLg)DPE_v%Q>s% zjN`MwPj?P6RP)dEoU?HA#RD?wo=y6iI*adH_OxC&DzNHlJ@@X8+U(CJyZE==J$Uh! z4Da*8gU>HaYnhWSGT-Px!aw#!yCpPy-aGxSsfeeeZhTa?spcpegDq zx=?}DRV!cm_+%9`-TEVYErd?2ykMYJem$hBXXT0HnASc{*2!8TKH3^9I+DX20}qzA zwLCPLeP`WNo!coVrpW~AUtwKudZy{(1}^`d1!r%)l0PHle5E>b=gygOFZg19FPQKm z_4Kwq);#>54}M7a`H{;xRXg_eI@&EO&Hk>20&4S1-+$Y(CaLbJidJG@{%GB?rDpT2-vV9k@GZ9apTN2dp`JV zOyj(+y?s^PjKmGoqgUNcn_(sQyl(G`#;E0*mKook`QHd`J6XSSZPRX^u1lNy`U>+du;C%;oV>3 z-@Px)uHSrbX<5;_90R>c3(qfglfNXu_Oyn1<&-%UpIQVLDoQaNmfYmk!f|=lCH1m{ zCEr99Ym3Am`u~f(5ZSd^C&a8t<#b|4O=j0c$*A+pZ|7d&5PZ(c^ZQJ9ig!rJ{2xsx z+MV}l96k6+*5;z~JPo}^LPsU_lplY7vtWI%)~oq~YOCz(qi&kJ+*jPoo*Eow{M4KbGaMlE0m=Z5n#=z%qg4%d)$UI^SKizIy66E~mx)B3f}AH_I+5Tez?G%T#$$ zX(qI2MxVCe&;J(DvT$epe{4m3?`AX0UMAirY0fj2GcqtNW(F-qhjoZS3V&HUGIQ0+ zY__v$V&q+P?7Qn~CI*IWtPBjs@FE?g%;6c^+@gr21}cluJ%52k8J5P>2lItYOwgRln(D^D-)4c4Q|8fhjJfUuYb6%t(u2Z?wZ7= z)3^U9Z?}1Oea+T-i;};$n4JADblg3#{OqFFyFUsY4%v5i{oSC5^Z;$gtBDd3Wwq(4 z%lEx($^WtJd52Hq*}bK5yPWbRuU4GymFNkbxq;L975Dw}LtV!gpI8{2ZOM@xru!uH z{KlN~TeTebp8LDXP_jVm)8iSDn64aO7Bd5@p#)?m%w#4_TCSt{;}MB zabrQP?hVe|=R39A4DPfUC_C}QI2>~jYj(0RF=*g1)St?ulQ1#yN&TJW*6YGcotXPK zq&%=XCLv)Ne{e?Iah|2cAJb0!sXrWl_fFZHA5&hQ$X-;<|8m`P*WX_?o+ZlcEq<)q zwvcbK?w;k^=cRd%uNS)O$CtY-?Ch0GAM;!c{Lbt9`iCzp3)0V=E~>lCR5Q1ELATc} zwoJ|__A|~ZXI|E>NGp`j@o(chX&{>LrD{e~+%$(-D<#x^R~Yjs@8CHx*W>ULKb4^R z4FAg+OFNhF>CC#nAZ;@Hi=Ry!<>87=8-+iMyIlE-cT@JgfR+P#+)z%TqH`&r7C1^+MO0%?(mwE0ll+%QTq8#H+ zF}Xdh5s~^T8`I=xyf9wvt2k;yCEc-b)0D~k9fIpyQ@KSKYDGD$xe%11sn9Y-z(aVF zTBuK!#}~6HzL7CyC*Mu`vHrlx+0*0pI4tVCxVnC_;l7*eG{n|^yijTXZ1(qk-%Hc> z*Hr%(JfIX-@+H}=T~D01l6SLJ$eEK*C$?4Y^u2av!-9oJCd6!ywZ4-O@ToPHZR?}A z(LBzkx3|vQ)>r>c=l{+d#Tv#jpQR4n6y2x$*kWZHd)a{rH!b;c&#!IY_ts4N-o=d{ zTwna;al2ZoyZ=##!Um4*VW0mjyZPnlzT}y_$IUpaRqoW@d^7U{^Xc=k@hi_PFkRF! z>)ey<_D3!=`U+;Ju}8i;C$g&KSof-_D!=s(JpHEBDIil(=V9^flY70x%@A9W-f;gT zg*I=e9<#1)4^!4DPv;8d?H1hee8bXBQ|9cCOE~eDd)C7JdFHW#+m`UXu1~tmm3#b; z=Dn)#Z=^%K)>rH9Pt3htw8bZUcV*>?hQ=w4syf*fXExlNob1XHc9mDhe1r4vBd_)e z3fl^7dc34DFh%0h{?BOW=v3;tfM@8#$JYccfR8SYdvQ^;kxuhT9zR%p+#r%rz7q5&I@fe zgxa((+Sj;jU+b!zGk>MluDBWeV(}|Up*?eLC%@Aa%KFWGMf?p@r_#d7xmt@{dXK-X z-^@7gfP%l$X;Yuic29Qp3CSN2@^@{SX6>8obZK|%ZNZbecNF`DcdGV0)Vq2{slEEi z$|sF}>b<_-MA=@isjBKeCfR>HQgx4$nQ8PNmfXI&=P$DVyG{;icZxbTxp1QCCL6IS zvHBUyn0Gvxf1uXq;=U$!LA&<$iF1$nZc3i=-d&OL@DpE;ie3H7G#7Mie2@d*2WhW{Fmck0f*tKc|m%X9brpXa`xbNk=F&!5E`n78Dn1a6+_tlNLw>{Ufl z;&G?u3Dplh^{YHjn9MrPvRS$(=1|u8$?qQiOnI=1Kj}g7vieKhZHMf|PF?95xK>H1y^x@K_xmL!q1VL|Uwk@UrXP@;zUIh@^0^D&Ti#7D5-eI`ty20mq_RC= zR);?GbY)T5+MYcQLf54C8h`Y(*f$}0xxA)-pY>T`r+eJ`3X%(tMLp%|+aPp8WOl3C z!Z|;}OXi=+G+O@lQDIvBj!0+43GI!01b$cDv+;hfIYIM;wB@D^-ZwIJo>gAE=$>0s z=e6QL|Ez@X&BdlA_p5$gF;6qdwe=Mg`^2*PqgKwl?a^XUu5T9RB+lQk{Y;L{t^IFh zc+cHQbg_u2d)spB(Zh8n-hEB!GdeOVR_(v{N{Marsr_MmzcwYj5N#-ER<3W?-FWuh zzlM*!vdm`o#a5oGA~*Ea7`Cl$IO!G8*Qu2wJc%z{a@MZUlQr!>ze=5r^4UDmL1@;E z-L0kq!s{OU`P7Kdo}+$ecgXdw_%J(n%?z{foX2Lj6?J*_pQ5jgC^#FzdzXoUA)B3n!5&^S8@PpY zPL+=5HqX2BLVxoFx0Ou10*h_g#e^6bax_7knIJViTRmhm)7;3+gqWif|9Q=tXJMVcN`Kr@lNQ**-eQzn{69-6>}JlAFum+Z}!DAFC;tv1eE)ihn9wxUcG+R zf4yw=H-U!8`cENpRv$NJ{R=nUe|@9a{EQ#XeEN15KWhG4QT)r8_y6*T{y%^4+Ff7x zm|M2;N6_I!S>=z1&@?_qcS)E?~-S4N=*yvBb@nD78W<4z=^VH*215X_D(t2JP1g zF4MifF*Iz&#mG9#=*s6Zv9D5>>ODWDwcjK_cwW27t4&eYemu3=x+83|T4SJm=ZuZ1 z4-;(ij8=nU$?G1E-3o~>!;xQ;&RT=p*aO18VfB`-$rv)eMxoSAh!CHWL%sdclV zaq@lNrawKMXYUx?jczhreSOX9_`5r{Zd{jmwEmiD>FaB2BW`POta-%~C$#3BU(2DZ zhMVu+3EitUV?o%&pG|>*(KnacdP*+7v_iTmcFDN>QpWCg~ zxs0E~U6bEleY-Is`^K6)&h}NZx5HYMmaTpB$90OOl?0!Axmiou%(Z>HSx)^jyjF9p zvM}|GiKwh${p;(W0&I5Z=3MIB$GXr%W247}=%7=@Uv#>+&GM>$cjd<3l!Y2CGL^U5{by?a^Gr{zfH0WE<%cuHeJI|jb zmU>AYp4w$wXPmrnQ)x!@B)8D>pM$#i^Q{jD-T1gB;oAf^KWUNr=TjaTE3fUzmGiM} zR+!ae@-#sFtE$R{oJE&UN|$~+$E(GeeAg#Yq$E}QeUVZg+bglflS@OLXHU<(vExj` zioSl;NZ%>c}`?Qgz3(J4Outd9JV@gl3CNKqku^xPF&JsMy6^r%g)yI zd7M#a|71(&@$BxDygO;`$@=)Yc7EHIFKXfv=Mxe<&oW2N*s;ynC|!()J;>zE#gx@u zJW*3D?B#NvSF&C5U$Sm>H)F24;KM^5X%5eCWURT`qgs4yUg)XPqi4)sb4)U3%{;Z1 z>*&;dJ8z$rTXaPAV1c>$*I5%?eE!BIes`TY!Sd{^u$YVQTDn}rd1n^vQ7DV3x0tK> zXw4IoOt&-*-=}*Lf3?)U>wBPg@Jf#TF1JrxbejDf%C-nEmRMD9g)f4nj?<^4h9 z&eu>FBE?LpGJ?89$>%G~f%Xe6t-puoeD3cJHxI!_u$WT!A_XnO%&cg=FH(i?0 zQo^m(e#da{hY;ylryFb6+$ni@NcU;mn<+aKcP}|U!TYI^v)*)V4q4Ui$4mK+9$IDO zyiYM)Yf-*b(bO2GSYrzl8HX_3tS&g6hS)S*t~ij8(L{c=t5d8>DQH(QiM<7|?vYTODn;$(P^pGm0C_Ak_af2-qW z)Y2_KHWqzWZu{f1)amV}1Qj7)5%%cl?!}pV96z~DGS)TowGr%5?J92*l09myl^!Db zLu}pkC(GMxwE8nY8BgU4Jh&{;OQ+B6knK_3iylf6T>%%@^(C}CJL05!R9Dt!qgY$S zoR2HNZQ8Zgc-20mgtv#UKPhznR&yo;N}L z`Y@;LlB}G2ZF5UhP zt7!c5N+X#K=WD)9pFiWC>Q^rIhJ#g$BA(kRE%r+>d=oY~Z9=4gxzXkVpQKX^8HUC6 zwFh_XHM3ppRoJ!e(!rx!&TB0`X}NI87njnA2P~lrR=qyH+kW=WO|=rKr!MesUMN3% zdGu~kzH3dXei~0d7fL=;J?~^Y-Ky!MTXFcIU+WH5ofh6$*Q2XhsvX5$#!|g)<@5rb zi8WJ1r|y{2EPb{5-=lX47V}&u*l-G6;V-%npSQ36*zN60o#!UU?WvBw(|L8#q^j#e zWi4FKO@z&M&G<0gCOUS1c29{}u-~?Q`DbK~SzEmpZW8O-GHdsh=3CB_UtKM{wtM!n z=m*J57bdK{eX-{5-p_MyYt*f)D*M$G)+S^glRAF^$6|?krCC*frtMf|x9P6WhIx_! z@!PW(Zdh4xO5Cho`E4hkw$ICkKN(95r99;l8eM0aB)4AD-yZ9DIx}zMbFmvc_so>E zdAl*A{CeAjTgwe>UtgWhB${sA=9i_ewAn$q@^xgkU0#?tUp?z41KSd*300Z>=RD$n z?{58cy~y~U5Vxv*UFk~oe=*{d+h4NWFWY?F>B&UqZeGWyJnOs6>v!GFug%?%@#jw6 z-oLzujxUsz{TANR*k1gLlb`X#%XlfZU8?UKbSiHjy8E&3$8l z%UP0}*F88cd~NL}gFN@3sH^inI@+*C8@>*-R>+X_-m7+Kxn!oHaZSo*^BX>&V^z#m zp7wf8UiR;%V?>!!!=1(!UOp7pl;XSQIG<)fmTj1{MTJ@Fj6e!uCDu+*+IVADR)BpL*bS_r~2%H#ThFutNNC z+2RHAk7tX%ZaG$4B)^<_uSKT0pHk`p??;oX4L4q^7yt0}k%&-?Uz4YpW9WhC4I9KK zXV;je3Tk$Vt`)n$^ylf$6OU}C`lL_J>^fSq%Jua}t0f}RAASXuOrC$_imisySCgRk zj&=6SK5O_@H|nUY`l`8^d7s^x6AO1A(~PuBclWm5^Yp~Jt2$n17w%cLNnTYXBr4)s zcadw%y>70n{%h*Lvj5q;;!f|O2K!9C(1X5NyH;I{o*X`9c}&<}g=d$v{sonZUVk-H zsnmVRJfX;|6}j`?SpR!ur?X6N(z#cDKKBBr$J$%=Be}WO$PDuC-@A^Aw%_mcH=9+K_+SOhhip7`JGzQ%af8 zVXg7VPh2qE&}`mD*N*Aivgh1h^Whmon&}ktFu7G9pIocx-s3uteda0Gdy!lBb*1>t zo7il3=G?)y?7cm|?}vU#>|3z$5}RVr`VxZ`Zi@B6uKMThl>ceo)EF`EP%zWqhw9IK zAFVu_5`TDyMgQ|XjnDT@Okeh8uJ2j(g%>Q(e*SUYo9By$ZE(acNp@YGug1c^KIrKB z&0A=D`MhLlTku26a{*s6UtIYxr|tPouDc&um&pch{>`7PG?VWk>uuGY=L@V4ZTPcF zzIgU&{sg(g?bGTjZ1&Imu=4?fm|57ehMoM~FS_e=mc5^LrAkY;{EE;Ynf5*F-1^*W zw#~3oP18IYA>g$lVw%XNIZj~$LMG8(aSG=rZi?%b$+<52E2LWPz?6pjymNjPh_${w z{G{>eqJyW8e`ovCnl69D@=mMP7441s%J(PjWsiF3R)c1ELmh;{G{&KUBzQJ^Cjc|_dNf=IPduJIhJP< zEZgRH_r(i%$4_C}qq6zqCY^)Yn|RiHS=VV=f1Guv?fO&IJ&Nle`Bu)YC|^GHhO%b0 zbXtRb#|F9xp`XVL!ta(Cy(CUF&)$E zJDX>5T}z28_~O0iSirf-;vdZ`=0*xkSpHG_)AOWhce>3ZtSVQ0{9G|P^7+Z;(>>%0<`GF$!1_AX6aH#K2@VpLOt==~&OXW|lX|v3SyjilyruQ{lh|5`Vv$>~UI}js zo>zP9-toFr`&p7k($Y5Fr{Wve7d&0H$u7rh{cMA~D`#izUH--~j`eWn=QgfV@%+HW zcU*t>?LYiuX92_ERU#G_>g?}5nwKAZv7AGH|5M$+{zo>Kcc$+@EOc+1_ecFL|Nn~2 z3*8{~$Nf^})VTU3wbz|CPVHIUu5%{JYu|Sr>uD1{Py1qha#8*Ky`O%tRP-fnX-l(Y zI$OjvciT7bSBdwH{(IHj@2)TYV0&b9@H~r%HR5RQEk8T<@$-f#cSKkkM{mu zkr{UN6Jv{%md*|D{Z?wB%N&j*zkIsVBk6o~nCX=4%KdGwwjUIaJyO1>Ccio0zpd?e z&g=#L7u)O~6|IZywKqI%m)=+J_~hrL%4&;JuH=h}ceUcy^=LI^ZZ=OKmR||xr_b29X>So=l*|ej#T(P* zZFc%6W+nbmD6?TL{t&&g!Tr+?RlVa8N^|~PQ2NOc$eryj=%2VU;&^(D`1}I#&HWA= z=byJbXLoSl>_>Wu`zQa^`(`UQchB4%J^PPu-aqHB=$rkHCzWr;pZqub&H7JIK5Vfr zt!qPUhqt+;Tim zQFiR`sWLDyAgR#ck)LdQPik}KT}j5tcV9|Po^wxpGT$pVCS~Ty7pp}l^Su(8T>r{| zsh4B&##g2?OPQ5CtyvftZm=^jI5Hr>lEwng$rr2TCM&!)mASaf?~M*K1H%C}1_l#^ z8ipl}Tzr!YU%SbuFr2J=%+J6Op}@ePh@zlFaPq}!*~uSX8!)X>ocwT-@#OLc0+UnU zNHNV)n%wv%o@t>b*javzY?J@KQISEpofE~RR2`^7@yVy|2~1XeE5#II1{Ml>D=!1T z14@X2K?y~lojF+1zm@EhSH6{yxq)<7gD#4~EB0`O_REDQ^Sl#~K{-bsMe%GWsAAs9 zQSY>vYQn+X+3#dzP>!QQ(W4OsR&?N?>SW&c0y0~W&qG2{$Q=V!$U8auy#Z5OGMM}3 zl0chui#dlEPY>#$)|L(1dsM)>H9L1`|k5GO)Z@~@4hwDk=4duxf9*WDTh^AG0Gcz#k=VV~8hN)m+U|7^Zz|6o<%FV!FgQ93(FIdrsN7}65HO-S19*Z)G zOq%>q!((#&V*weI^`01udUw%Y-1WbwJVie%|DR z7Mhb+K2eiFpKFGxThjPt;pBrBmS8tLL7rQNsQ`xs%hJggt*s{Se`te|EVYw z<8rX^pb6!tPsLcllXH`vi8B3N2^Ck90Z)Z6F)-|4VPG&qG4ahBum^5C3xZ6QK^YZC zQRuK9tkA$s4y;fBc~l%lVb!L|4>gn~7d|&&x^iUlt7whMr=M#vJv=k{RkYG%$rol! ztQRH+zDQ%bd2#al3~NyG(LgT-VdgDqTyq(v$YZ;2C<7`55eojHDhR(XE5gbKk~U*7 OV|XRRz~FoZ!~+0_cabXq diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 1c4bcc2..03bc515 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-all.zip +networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 4f906e0..65dcd68 100755 --- a/gradlew +++ b/gradlew @@ -1,7 +1,7 @@ -#!/usr/bin/env sh +#!/bin/sh # -# Copyright 2015 the original author or authors. +# Copyright © 2015-2021 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,67 +17,101 @@ # ############################################################################## -## -## Gradle start up script for UN*X -## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# ############################################################################## # Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" +MAX_FD=maximum warn () { echo "$*" -} +} >&2 die () { echo echo "$*" echo exit 1 -} +} >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar @@ -87,9 +121,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACMD=$JAVA_HOME/jre/sh/java else - JAVACMD="$JAVA_HOME/bin/java" + JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -98,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD="java" + JAVACMD=java which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the @@ -106,80 +140,105 @@ location of your Java installation." fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi -fi - -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi - -# For Cygwin or MSYS, switch paths to Windows format before running java -if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi - # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" - fi - i=`expr $i + 1` - done - case $i in - 0) set -- ;; - 1) set -- "$args0" ;; - 2) set -- "$args0" "$args1" ;; - 3) set -- "$args0" "$args1" "$args2" ;; - 4) set -- "$args0" "$args1" "$args2" "$args3" ;; - 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" esac fi -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=`save "$@"` +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index ac1b06f..6689b85 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -14,7 +14,7 @@ @rem limitations under the License. @rem -@if "%DEBUG%" == "" @echo off +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -25,7 +25,8 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @@ -40,7 +41,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute +if %ERRORLEVEL% equ 0 goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -75,13 +76,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal diff --git a/ivy/build.gradle.kts b/ivy/build.gradle.kts deleted file mode 100644 index 1cc1168..0000000 --- a/ivy/build.gradle.kts +++ /dev/null @@ -1,24 +0,0 @@ -plugins { - kotlin("jvm") -} - -dependencies { - api("org.apache.ivy:ivy:latest.release") - api("com.amazonaws:aws-java-sdk-s3:latest.release") - - testImplementation("com.adobe.testing:s3mock-junit5:latest.release") - testImplementation("io.strikt:strikt-core:latest.release") - testImplementation("org.junit.jupiter:junit-jupiter-api:latest.release") - testImplementation("org.junit.jupiter:junit-jupiter-params:latest.release") - testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:latest.release") - testRuntimeOnly("org.junit.platform:junit-platform-launcher:latest.release") -} - -tasks { - test { - useJUnitPlatform { - includeEngines("junit-jupiter") - } - systemProperty("fixtures", "$rootDir/fixtures") - } -} \ No newline at end of file diff --git a/ivy/gradle.lockfile b/ivy/gradle.lockfile deleted file mode 100644 index 32ae72f..0000000 --- a/ivy/gradle.lockfile +++ /dev/null @@ -1,123 +0,0 @@ -# This is a Gradle generated file for dependency locking. -# Manual edits can break the build and are not advised. -# This file is expected to be part of source control. -ch.qos.logback:logback-classic:1.2.3=testCompileClasspath,testRuntimeClasspath -ch.qos.logback:logback-core:1.2.3=testCompileClasspath,testRuntimeClasspath -com.adobe.testing:s3mock-junit5:2.1.28=testCompileClasspath,testRuntimeClasspath -com.adobe.testing:s3mock-testsupport-common:2.1.28=testCompileClasspath,testRuntimeClasspath -com.adobe.testing:s3mock:2.1.28=testCompileClasspath,testRuntimeClasspath -com.amazonaws:aws-java-sdk-core:1.11.946=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.amazonaws:aws-java-sdk-kms:1.11.946=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.amazonaws:aws-java-sdk-s3:1.11.946=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.amazonaws:jmespath-java:1.11.946=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.christophsturm:filepeek:0.1.2=testRuntimeClasspath -com.fasterxml.jackson.core:jackson-annotations:2.6.0=compileClasspath,runtimeClasspath -com.fasterxml.jackson.core:jackson-annotations:2.9.0=testCompileClasspath,testRuntimeClasspath -com.fasterxml.jackson.core:jackson-core:2.6.7=compileClasspath,runtimeClasspath -com.fasterxml.jackson.core:jackson-core:2.9.9=testCompileClasspath,testRuntimeClasspath -com.fasterxml.jackson.core:jackson-databind:2.6.7.4=compileClasspath,runtimeClasspath -com.fasterxml.jackson.core:jackson-databind:2.9.9.3=testCompileClasspath,testRuntimeClasspath -com.fasterxml.jackson.dataformat:jackson-dataformat-cbor:2.6.7=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.9.9=testCompileClasspath,testRuntimeClasspath -com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.9.9=testCompileClasspath,testRuntimeClasspath -com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.9.9=testCompileClasspath,testRuntimeClasspath -com.fasterxml.jackson.module:jackson-module-jaxb-annotations:2.9.9=testCompileClasspath,testRuntimeClasspath -com.fasterxml.jackson.module:jackson-module-parameter-names:2.9.9=testCompileClasspath,testRuntimeClasspath -com.fasterxml.woodstox:woodstox-core:5.1.0=testCompileClasspath,testRuntimeClasspath -com.fasterxml:classmate:1.3.4=testCompileClasspath,testRuntimeClasspath -com.typesafe.netty:netty-reactive-streams-http:2.0.0=testRuntimeClasspath -com.typesafe.netty:netty-reactive-streams:2.0.0=testRuntimeClasspath -commons-codec:commons-codec:1.11=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -commons-io:commons-io:2.6=testCompileClasspath,testRuntimeClasspath -commons-logging:commons-logging:1.2=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-buffer:4.1.33.Final=testRuntimeClasspath -io.netty:netty-codec-http2:4.1.33.Final=testRuntimeClasspath -io.netty:netty-codec-http:4.1.33.Final=testRuntimeClasspath -io.netty:netty-codec:4.1.33.Final=testRuntimeClasspath -io.netty:netty-common:4.1.33.Final=testRuntimeClasspath -io.netty:netty-handler:4.1.33.Final=testRuntimeClasspath -io.netty:netty-resolver:4.1.33.Final=testRuntimeClasspath -io.netty:netty-transport-native-epoll:4.1.33.Final=testRuntimeClasspath -io.netty:netty-transport-native-unix-common:4.1.33.Final=testRuntimeClasspath -io.netty:netty-transport:4.1.33.Final=testRuntimeClasspath -io.strikt:strikt-core:0.28.2=testCompileClasspath,testRuntimeClasspath -javax.annotation:javax.annotation-api:1.3.2=testCompileClasspath,testRuntimeClasspath -javax.servlet:javax.servlet-api:3.1.0=testCompileClasspath,testRuntimeClasspath -javax.validation:validation-api:2.0.1.Final=testCompileClasspath,testRuntimeClasspath -joda-time:joda-time:2.8.1=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.apache.commons:commons-lang3:3.8.1=testCompileClasspath,testRuntimeClasspath -org.apache.httpcomponents:httpclient:4.5.13=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.apache.httpcomponents:httpcore:4.4.13=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.apache.ivy:ivy:2.5.0=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.apache.logging.log4j:log4j-api:2.11.2=testCompileClasspath,testRuntimeClasspath -org.apache.logging.log4j:log4j-to-slf4j:2.11.2=testCompileClasspath,testRuntimeClasspath -org.apiguardian:apiguardian-api:1.1.0=testCompileClasspath,testRuntimeClasspath -org.codehaus.woodstox:stax2-api:4.1=testCompileClasspath,testRuntimeClasspath -org.eclipse.jetty:jetty-continuation:9.4.19.v20190610=testCompileClasspath,testRuntimeClasspath -org.eclipse.jetty:jetty-http:9.4.19.v20190610=testCompileClasspath,testRuntimeClasspath -org.eclipse.jetty:jetty-io:9.4.19.v20190610=testCompileClasspath,testRuntimeClasspath -org.eclipse.jetty:jetty-security:9.4.19.v20190610=testCompileClasspath,testRuntimeClasspath -org.eclipse.jetty:jetty-server:9.4.19.v20190610=testCompileClasspath,testRuntimeClasspath -org.eclipse.jetty:jetty-servlet:9.4.19.v20190610=testCompileClasspath,testRuntimeClasspath -org.eclipse.jetty:jetty-servlets:9.4.19.v20190610=testCompileClasspath,testRuntimeClasspath -org.eclipse.jetty:jetty-util:9.4.19.v20190610=testCompileClasspath,testRuntimeClasspath -org.eclipse.jetty:jetty-webapp:9.4.19.v20190610=testCompileClasspath,testRuntimeClasspath -org.eclipse.jetty:jetty-xml:9.4.19.v20190610=testCompileClasspath,testRuntimeClasspath -org.hibernate.validator:hibernate-validator:6.0.17.Final=testCompileClasspath,testRuntimeClasspath -org.jboss.logging:jboss-logging:3.3.2.Final=testCompileClasspath,testRuntimeClasspath -org.jetbrains.kotlin:kotlin-reflect:1.4.21-2=testRuntimeClasspath -org.jetbrains.kotlin:kotlin-stdlib-common:1.4.20=compileClasspath,runtimeClasspath -org.jetbrains.kotlin:kotlin-stdlib-common:1.4.21-2=testCompileClasspath,testRuntimeClasspath -org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.4.21-2=testCompileClasspath,testRuntimeClasspath -org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.21-2=testCompileClasspath,testRuntimeClasspath -org.jetbrains.kotlin:kotlin-stdlib:1.4.20=compileClasspath,runtimeClasspath -org.jetbrains.kotlin:kotlin-stdlib:1.4.21-2=testCompileClasspath,testRuntimeClasspath -org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.4.2=testRuntimeClasspath -org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.2=testRuntimeClasspath -org.jetbrains:annotations:13.0=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.junit.jupiter:junit-jupiter-api:5.7.0=testCompileClasspath,testRuntimeClasspath -org.junit.jupiter:junit-jupiter-engine:5.7.0=testRuntimeClasspath -org.junit.jupiter:junit-jupiter-params:5.7.0=testCompileClasspath,testRuntimeClasspath -org.junit.platform:junit-platform-commons:1.7.0=testCompileClasspath,testRuntimeClasspath -org.junit.platform:junit-platform-engine:1.7.0=testRuntimeClasspath -org.junit.platform:junit-platform-launcher:1.7.0=testRuntimeClasspath -org.junit:junit-bom:5.7.0=testCompileClasspath,testRuntimeClasspath -org.mortbay.jasper:apache-el:8.5.40=testCompileClasspath,testRuntimeClasspath -org.opentest4j:opentest4j:1.2.0=testCompileClasspath,testRuntimeClasspath -org.reactivestreams:reactive-streams:1.0.2=testCompileClasspath,testRuntimeClasspath -org.slf4j:jul-to-slf4j:1.7.28=testCompileClasspath,testRuntimeClasspath -org.slf4j:slf4j-api:1.7.28=testCompileClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-autoconfigure:2.1.9.RELEASE=testCompileClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-starter-jetty:2.1.9.RELEASE=testCompileClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-starter-json:2.1.9.RELEASE=testCompileClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-starter-logging:2.1.9.RELEASE=testCompileClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-starter-web:2.1.9.RELEASE=testCompileClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-starter:2.1.9.RELEASE=testCompileClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot:2.1.9.RELEASE=testCompileClasspath,testRuntimeClasspath -org.springframework:spring-aop:5.1.10.RELEASE=testCompileClasspath,testRuntimeClasspath -org.springframework:spring-beans:5.1.10.RELEASE=testCompileClasspath,testRuntimeClasspath -org.springframework:spring-context:5.1.10.RELEASE=testCompileClasspath,testRuntimeClasspath -org.springframework:spring-core:5.1.10.RELEASE=testCompileClasspath,testRuntimeClasspath -org.springframework:spring-expression:5.1.10.RELEASE=testCompileClasspath,testRuntimeClasspath -org.springframework:spring-jcl:5.1.10.RELEASE=testCompileClasspath,testRuntimeClasspath -org.springframework:spring-web:5.1.10.RELEASE=testCompileClasspath,testRuntimeClasspath -org.springframework:spring-webmvc:5.1.10.RELEASE=testCompileClasspath,testRuntimeClasspath -org.yaml:snakeyaml:1.23=testRuntimeClasspath -software.amazon.awssdk:annotations:2.7.19=testCompileClasspath,testRuntimeClasspath -software.amazon.awssdk:apache-client:2.7.19=testRuntimeClasspath -software.amazon.awssdk:auth:2.7.19=testCompileClasspath,testRuntimeClasspath -software.amazon.awssdk:aws-core:2.7.19=testCompileClasspath,testRuntimeClasspath -software.amazon.awssdk:aws-query-protocol:2.7.19=testCompileClasspath,testRuntimeClasspath -software.amazon.awssdk:aws-xml-protocol:2.7.19=testCompileClasspath,testRuntimeClasspath -software.amazon.awssdk:http-client-spi:2.7.19=testCompileClasspath,testRuntimeClasspath -software.amazon.awssdk:netty-nio-client:2.7.19=testRuntimeClasspath -software.amazon.awssdk:profiles:2.7.19=testCompileClasspath,testRuntimeClasspath -software.amazon.awssdk:protocol-core:2.7.19=testCompileClasspath,testRuntimeClasspath -software.amazon.awssdk:regions:2.7.19=testCompileClasspath,testRuntimeClasspath -software.amazon.awssdk:s3:2.7.19=testCompileClasspath,testRuntimeClasspath -software.amazon.awssdk:sdk-core:2.7.19=testCompileClasspath,testRuntimeClasspath -software.amazon.awssdk:url-connection-client:2.7.19=testCompileClasspath,testRuntimeClasspath -software.amazon.awssdk:utils:2.7.19=testCompileClasspath,testRuntimeClasspath -software.amazon.eventstream:eventstream:1.0.1=testCompileClasspath,testRuntimeClasspath -software.amazon.ion:ion-java:1.0.2=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -empty= diff --git a/ivy/src/main/kotlin/org/nixos/gradle2nix/S3.kt b/ivy/src/main/kotlin/org/nixos/gradle2nix/S3.kt deleted file mode 100644 index 845100d..0000000 --- a/ivy/src/main/kotlin/org/nixos/gradle2nix/S3.kt +++ /dev/null @@ -1,294 +0,0 @@ -package org.nixos.gradle2nix - -import com.amazonaws.AmazonServiceException -import com.amazonaws.ClientConfiguration -import com.amazonaws.auth.AWSCredentials -import com.amazonaws.auth.AWSStaticCredentialsProvider -import com.amazonaws.client.builder.AwsClientBuilder -import com.amazonaws.regions.Region -import com.amazonaws.regions.RegionUtils -import com.amazonaws.regions.Regions -import com.amazonaws.services.s3.AmazonS3 -import com.amazonaws.services.s3.AmazonS3ClientBuilder -import com.amazonaws.services.s3.model.GetObjectRequest -import com.amazonaws.services.s3.model.ListObjectsRequest -import com.amazonaws.services.s3.model.ObjectMetadata -import com.amazonaws.services.s3.model.S3Object -import com.amazonaws.services.s3.model.S3ObjectInputStream -import com.amazonaws.util.AwsHostNameUtils -import org.apache.http.conn.ssl.NoopHostnameVerifier -import org.apache.http.conn.ssl.SSLConnectionSocketFactory -import org.apache.ivy.core.settings.TimeoutConstraint -import org.apache.ivy.plugins.repository.AbstractRepository -import org.apache.ivy.plugins.repository.RepositoryCopyProgressListener -import org.apache.ivy.plugins.repository.Resource -import org.apache.ivy.plugins.repository.TransferEvent -import org.apache.ivy.util.FileUtil -import java.io.File -import java.io.FileOutputStream -import java.io.IOException -import java.io.InputStream -import java.net.Socket -import java.net.URI -import java.security.KeyManagementException -import java.security.NoSuchAlgorithmException -import java.security.SecureRandom -import java.security.cert.X509Certificate -import javax.net.ssl.SSLContext -import javax.net.ssl.SSLEngine -import javax.net.ssl.TrustManager -import javax.net.ssl.X509ExtendedTrustManager - - -class S3Repository( - private val client: AmazonS3, - timeoutConstraint: TimeoutConstraint? = null -) : AbstractRepository(timeoutConstraint) { - - constructor( - credentials: AWSCredentials?, - endpoint: URI?, - timeoutConstraint: TimeoutConstraint? - ) : this( - AmazonS3ClientBuilder.standard().apply { - credentials?.let { setCredentials(AWSStaticCredentialsProvider(it)) } - - if (endpoint != null) { - setEndpointConfiguration( - AwsClientBuilder.EndpointConfiguration( - endpoint.toString(), - AwsHostNameUtils.parseRegion(endpoint.host, null) ?: Regions.US_EAST_1.name - ) - ) - isChunkedEncodingDisabled = true - isPathStyleAccessEnabled = true - } else { - region = Regions.US_EAST_1.name - } - - if (System.getProperty("org.nixos.gradle2nix.s3test") != null) { - clientConfiguration = ClientConfiguration().apply { - apacheHttpClientConfig.sslSocketFactory = SSLConnectionSocketFactory( - createBlindlyTrustingSslContext(), - NoopHostnameVerifier.INSTANCE - ) - } - } - }.build(), - timeoutConstraint - ) - - private val cache = mutableMapOf() - - private val progress = RepositoryCopyProgressListener(this) - - override fun getResource(source: String): Resource = - cache.getOrPut(source) { S3Resource(this, URI(source)) } - - override fun get(source: String, destination: File) { - fireTransferInitiated(getResource(source), TransferEvent.REQUEST_GET) - try { - val res = getResource(source) - val totalLength = res.contentLength - if (totalLength > 0) { - progress.totalLength = totalLength - } - destination.parentFile?.mkdirs() - FileUtil.copy( - res.openStream(), - FileOutputStream(destination), - progress, - true - ) - fireTransferCompleted(res.contentLength) - } catch (e: Exception) { - fireTransferError(e) - throw e - } finally { - progress.totalLength = null - } - } - - override fun list(parent: String): List = - S3Resource(this, URI(parent)) - .let { resource -> - generateSequence({ - try { - withClient(resource) { - listObjects( - ListObjectsRequest() - .withBucketName(resource.bucket) - .withPrefix(resource.key) - .withDelimiter("/") - ) - } - } catch (e: AmazonServiceException) { - throw S3RepositoryException(e) - } - }) { prev -> - if (!prev.isTruncated) { - null - } else { - try { - withClient(resource) { - listNextBatchOfObjects(prev) - } - } catch (e: AmazonServiceException) { - throw S3RepositoryException(e) - } - } - } - } - .flatMap { listing -> - listing.commonPrefixes.asSequence() + - listing.objectSummaries.asSequence().map { it.key } - } - .toList() - - internal fun withClient( - resource: S3Resource, - block: AmazonS3.() -> T - ): T = client.apply { - resource.region?.let { setRegion(it) } - }.block() -} - -class S3Resource( - private val repository: S3Repository, - private val url: URI - ) : Resource { - - private val source: Source by lazy { - REGIONAL_ENDPOINT_PATTERN.find(url.normalize().toString()) - ?.let { - val (bucket, region, _, key) = it.destructured - Source( - bucket = bucket, - key = key, - region = when (region) { - "external-1" -> Region.getRegion(Regions.US_EAST_1) - else -> RegionUtils.getRegion(region) - } - ) - } - ?: Source( - bucket = url.bucket(), - key = url.key(), - region = null - ) - } - - private val metadata: Metadata by lazy { - try { - getMetadata() - } catch (e: AmazonServiceException) { - null - }?.let { meta -> - Metadata( - exists = true, - contentLength = meta.contentLength, - lastModified = meta.lastModified.time - ) - } ?: Metadata( - exists = false, - contentLength = 0, - lastModified = 0 - ) - } - - val bucket: String get() = source.bucket - - val key: String get() = source.key - - val region: Region? get() = source.region - - override fun getName(): String = url.toString() - - override fun getLastModified(): Long = metadata.lastModified - - override fun getContentLength(): Long = metadata.contentLength - - override fun exists(): Boolean = metadata.exists - - override fun isLocal(): Boolean = false - - override fun clone(cloneName: String): Resource = S3Resource(repository, URI(cloneName)) - - override fun openStream(): InputStream = - try { getContent() } - catch (e: AmazonServiceException) { throw S3RepositoryException(e) } - ?: throw S3RepositoryException() - - private fun getMetadata(): ObjectMetadata? = - getObject(withContent = false)?.objectMetadata - - private fun getContent(): S3ObjectInputStream? = - getObject(withContent = true)?.objectContent - - private fun getObject(withContent: Boolean = true): S3Object? { - val request = GetObjectRequest(bucket, key) - if (!withContent) { - request.setRange(0, 0) - } - - return try { - repository.withClient(this) { getObject(request) } - } catch (e: AmazonServiceException) { - val errorCode = e.errorCode - if (errorCode != null && "NoSuchKey".compareTo(errorCode, ignoreCase = true) == 0) { - null - } else { - e.printStackTrace() - throw e - } - } - } - - private data class Source( - val bucket: String, - val key: String, - val region: Region? - ) - - private data class Metadata( - val exists: Boolean, - val contentLength: Long, - val lastModified: Long - ) - - companion object { - private val REGIONAL_ENDPOINT_PATTERN = - Regex("""^s3://(.+)?\.s3[.-]([a-z0-9-]+)\.amazonaws\.com(\.[a-z]+)?/(.+)""") - } -} - -class S3RepositoryException : RuntimeException { - constructor() : super() - - constructor(throwable: Throwable) : super(throwable) -} - -private fun URI.bucket(): String = normalize().host - -private fun URI.key(): String = normalize().path.removePrefix("/") - -// Used for testing. -private fun createBlindlyTrustingSslContext(): SSLContext? { - return try { - SSLContext.getInstance("TLS").apply { - init(null, arrayOf(object : X509ExtendedTrustManager() { - override fun getAcceptedIssuers(): Array? = null - override fun checkClientTrusted(arg0: Array?, arg1: String?, arg2: Socket?) {} - override fun checkClientTrusted(arg0: Array?, arg1: String?, arg2: SSLEngine?) {} - override fun checkClientTrusted(certs: Array?, authType: String?) {} - override fun checkServerTrusted(certs: Array?, authType: String?) {} - override fun checkServerTrusted(arg0: Array?, arg1: String?, arg2: Socket?) {} - override fun checkServerTrusted(arg0: Array?, arg1: String?, arg2: SSLEngine?) {} - }), SecureRandom()) - } - } catch (e: NoSuchAlgorithmException) { - throw RuntimeException("Unexpected exception", e) - } catch (e: KeyManagementException) { - throw RuntimeException("Unexpected exception", e) - } -} diff --git a/ivy/src/test/kotlin/org/nixos/gradle2nix/S3Test.kt b/ivy/src/test/kotlin/org/nixos/gradle2nix/S3Test.kt deleted file mode 100644 index df4a895..0000000 --- a/ivy/src/test/kotlin/org/nixos/gradle2nix/S3Test.kt +++ /dev/null @@ -1,113 +0,0 @@ -package org.nixos.gradle2nix - -import com.adobe.testing.s3mock.junit5.S3MockExtension -import com.amazonaws.services.s3.AmazonS3 -import org.apache.ivy.ant.IvyDependencyArtifact -import org.apache.ivy.core.module.descriptor.Artifact -import org.apache.ivy.core.module.descriptor.DefaultArtifact -import org.apache.ivy.core.module.id.ArtifactRevisionId -import org.apache.ivy.core.module.id.ModuleRevisionId -import org.apache.ivy.core.settings.IvySettings -import org.apache.ivy.plugins.repository.Resource -import org.apache.ivy.plugins.resolver.IBiblioResolver -import org.apache.ivy.plugins.resolver.URLResolver -import org.junit.jupiter.api.BeforeAll -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.TestInstance -import org.junit.jupiter.api.extension.RegisterExtension -import strikt.api.expectThat -import strikt.assertions.containsExactly -import strikt.assertions.isEmpty -import strikt.assertions.isEqualTo -import strikt.assertions.isFalse -import strikt.assertions.isNotEqualTo -import strikt.assertions.isNotNull -import strikt.assertions.isTrue -import java.io.File -import java.net.URI -import kotlin.io.path.ExperimentalPathApi -import kotlin.io.path.createTempDirectory - -@TestInstance(TestInstance.Lifecycle.PER_CLASS) -class S3Test { - companion object { - const val bucket = "repositories" - - val fixtureRoot = File(System.getProperty("fixtures")).resolve(bucket) - - @JvmField - @RegisterExtension - val s3mock: S3MockExtension = S3MockExtension.builder().withInitialBuckets(bucket).build() - } - - @BeforeAll - fun populateBucket(client: AmazonS3) { - fixtureRoot.walkTopDown() - .filter { it.isFile } - .forEach { file -> - val key = file.toRelativeString(fixtureRoot) - client.putObject(bucket, key, file) - } - } - - @Test - fun listsContents(client: AmazonS3) { - val repository = S3Repository(client) - expectThat(repository.list("s3://repositories/m2/org/apache/test/1.0.0/")).containsExactly( - "m2/org/apache/test/1.0.0/test-1.0.0.jar", - "m2/org/apache/test/1.0.0/test-1.0.0.pom", - ) - } - - @Test - fun findsResourceMetadata(client: AmazonS3) { - val repository = S3Repository(client) - val resource: Resource = S3Resource(repository, URI("s3://repositories/m2/org/apache/test/1.0.0/test-1.0.0.pom")) - expectThat(resource).and { - get { exists() }.isTrue() - get { contentLength }.isNotEqualTo(-1L) - get { lastModified }.isNotEqualTo(-1L) - } - } - - @Test - fun downloadsResource(client: AmazonS3) { - val repository = S3Repository(client) - val resource: Resource = S3Resource(repository, URI("s3://repositories/m2/org/apache/test/1.0.0/test-1.0.0.pom")) - val source = fixtureRoot.resolve("m2/org/apache/test/1.0.0/test-1.0.0.pom").readText() - - expectThat(resource.openStream().bufferedReader().readText()).isEqualTo(source) - } - - @ExperimentalPathApi - @Test - fun locatesArtifact(client: AmazonS3) { - val resolver = IBiblioResolver().apply { - name = "s3" - root = "s3://repositories/m2/" - isM2compatible = true - settings = IvySettings().apply { - defaultInit() - setDefaultRepositoryCacheBasedir(createTempDirectory().toString()) - } - repository = S3Repository(client) - } - val origin = resolver.locate(DefaultArtifact( - ArtifactRevisionId.newInstance( - ModuleRevisionId.newInstance("org.apache", "test", "1.0.0"), - "test", - "jar", - "jar" - ), - null, - null, - false - )) - - expectThat(origin).isNotNull().and { - get { isExists }.isTrue() - get { isLocal }.isFalse() - get { location }.isEqualTo("s3://repositories/m2/org/apache/test/1.0.0/test-1.0.0.jar") - } - } -} \ No newline at end of file diff --git a/model/build.gradle.kts b/model/build.gradle.kts index f8a0080..b12db2a 100644 --- a/model/build.gradle.kts +++ b/model/build.gradle.kts @@ -1,10 +1,22 @@ +import org.jetbrains.kotlin.gradle.dsl.JvmTarget + plugins { - `embedded-kotlin` - kotlin("kapt") + id("org.jetbrains.kotlin.jvm") + id("org.jetbrains.kotlin.plugin.serialization") } dependencies { - api("com.squareup.moshi:moshi:latest.release") - kapt("com.squareup.moshi:moshi-kotlin-codegen:latest.release") - implementation("net.swiftzer.semver:semver:latest.release") + implementation(libs.serialization.json) + implementation(libs.semver) +} + +java { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 +} + +kotlin { + compilerOptions { + jvmTarget.set(JvmTarget.JVM_1_8) + } } diff --git a/model/gradle.lockfile b/model/gradle.lockfile deleted file mode 100644 index 5d5d7bb..0000000 --- a/model/gradle.lockfile +++ /dev/null @@ -1,13 +0,0 @@ -# This is a Gradle generated file for dependency locking. -# Manual edits can break the build and are not advised. -# This file is expected to be part of source control. -com.squareup.moshi:moshi:1.11.0=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.squareup.okio:okio:1.17.5=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -net.swiftzer.semver:semver:1.1.1=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.jetbrains.kotlin:kotlin-reflect:1.4.20=compileClasspath,testCompileClasspath,testRuntimeClasspath -org.jetbrains.kotlin:kotlin-stdlib-common:1.4.20=compileClasspath,testCompileClasspath,testRuntimeClasspath -org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.4.20=compileClasspath,testCompileClasspath,testRuntimeClasspath -org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.20=compileClasspath,testCompileClasspath,testRuntimeClasspath -org.jetbrains.kotlin:kotlin-stdlib:1.4.20=compileClasspath,testCompileClasspath,testRuntimeClasspath -org.jetbrains:annotations:13.0=compileClasspath,testCompileClasspath,testRuntimeClasspath -empty= diff --git a/model/src/main/kotlin/org/nixos/gradle2nix/DependencyCoordinates.kt b/model/src/main/kotlin/org/nixos/gradle2nix/DependencyCoordinates.kt new file mode 100644 index 0000000..2f32f94 --- /dev/null +++ b/model/src/main/kotlin/org/nixos/gradle2nix/DependencyCoordinates.kt @@ -0,0 +1,6 @@ +package org.nixos.gradle2nix.dependencygraph.model + +import kotlinx.serialization.Serializable + +@Serializable +data class DependencyCoordinates(val group: String, val module: String, val version: String) diff --git a/model/src/main/kotlin/org/nixos/gradle2nix/DependencySource.kt b/model/src/main/kotlin/org/nixos/gradle2nix/DependencySource.kt new file mode 100644 index 0000000..18b2767 --- /dev/null +++ b/model/src/main/kotlin/org/nixos/gradle2nix/DependencySource.kt @@ -0,0 +1,13 @@ +package org.nixos.gradle2nix.dependencygraph.model + +import kotlinx.serialization.Serializable + +/** + * The source of a dependency declaration, representing where the direct dependency is declared, + * or where the parent dependency is declared for transitive dependencies. + * In most cases, this will be the project component that declares the dependency, + * but may also be a Version Catalog or the build as a whole. + * We attempt to map this to an actual source file location when building a dependency report. + */ +@Serializable +data class DependencySource(val id: String, val path: String) diff --git a/model/src/main/kotlin/org/nixos/gradle2nix/Impl.kt b/model/src/main/kotlin/org/nixos/gradle2nix/Impl.kt deleted file mode 100644 index c4e4d17..0000000 --- a/model/src/main/kotlin/org/nixos/gradle2nix/Impl.kt +++ /dev/null @@ -1,139 +0,0 @@ -package org.nixos.gradle2nix - -import com.squareup.moshi.JsonClass -import net.swiftzer.semver.SemVer -import java.io.Serializable -import java.lang.IllegalArgumentException - -@JsonClass(generateAdapter = true) -data class DefaultBuild( - override val gradle: DefaultGradle, - override val settingsDependencies: List, - override val pluginDependencies: List, - override val rootProject: DefaultProject, - override val includedBuilds: List -) : Build, Serializable { - constructor(model: Build) : this( - DefaultGradle(model.gradle), - model.settingsDependencies.map(::DefaultArtifact), - model.pluginDependencies.map(::DefaultArtifact), - DefaultProject(model.rootProject), - model.includedBuilds.map(::DefaultIncludedBuild) - ) -} - -@JsonClass(generateAdapter = true) -data class DefaultIncludedBuild( - override val name: String, - override val projectDir: String -) : IncludedBuild, Serializable { - constructor(model: IncludedBuild) : this( - model.name, - model.projectDir - ) -} - -@JsonClass(generateAdapter = true) -data class DefaultGradle( - override val version: String, - override val type: String, - override val url: String, - override val sha256: String, - override val nativeVersion: String -) : Gradle, Serializable { - constructor(model: Gradle) : this( - model.version, - model.type, - model.url, - model.sha256, - model.nativeVersion - ) -} - -@JsonClass(generateAdapter = true) -data class DefaultProject( - override val name: String, - override val version: String, - override val path: String, - override val projectDir: String, - override val buildscriptDependencies: List, - override val projectDependencies: List, - override val children: List -) : Project, Serializable { - constructor(model: Project) : this( - model.name, - model.version, - model.path, - model.projectDir, - model.buildscriptDependencies.map(::DefaultArtifact), - model.projectDependencies.map(::DefaultArtifact), - model.children.map { DefaultProject(it) } - ) -} - -@JsonClass(generateAdapter = true) -data class DefaultArtifact( - override val id: DefaultArtifactIdentifier, - override val name: String, - override val path: String, - override val timestamp: String? = null, - override val build: Int? = null, - override val urls: List, - override val sha256: String -) : Artifact, Comparable, Serializable { - constructor(model: Artifact) : this( - DefaultArtifactIdentifier(model.id), - model.name, - model.path, - model.timestamp, - model.build, - model.urls, - model.sha256 - ) - - override fun toString() = id.toString() - override fun compareTo(other: DefaultArtifact): Int = id.compareTo(other.id) -} - -@JsonClass(generateAdapter = true) -data class DefaultArtifactIdentifier( - override val group: String, - override val name: String, - override val version: String, - override val type: String, - override val extension: String = type, - override val classifier: String? = null -) : ArtifactIdentifier, Comparable, Serializable { - constructor(model: ArtifactIdentifier) : this( - model.group, - model.name, - model.version, - model.type, - model.extension, - model.classifier - ) - - @delegate:Transient - private val semver: SemVer? by lazy { - try { - SemVer.parse(version) - } catch (_: IllegalArgumentException) { - null - } - } - - override fun compareTo(other: DefaultArtifactIdentifier): Int { - return group.compareTo(other.group).takeUnless { it == 0 } - ?: name.compareTo(other.name).takeUnless { it == 0 } - ?: other.semver?.let { semver?.compareTo(it) }?.takeUnless { it == 0 } - ?: type.compareTo(other.type).takeUnless { it == 0 } - ?: other.classifier?.let { classifier?.compareTo(it) }?.takeUnless { it == 0 } - ?: 0 - } - - override fun toString(): String = buildString { - append("$group:$name:$version") - if (classifier != null) append(":$classifier") - append("@$type") - } -} \ No newline at end of file diff --git a/model/src/main/kotlin/org/nixos/gradle2nix/Model.kt b/model/src/main/kotlin/org/nixos/gradle2nix/Model.kt deleted file mode 100644 index 153c051..0000000 --- a/model/src/main/kotlin/org/nixos/gradle2nix/Model.kt +++ /dev/null @@ -1,51 +0,0 @@ -package org.nixos.gradle2nix - -interface Build { - val gradle: Gradle - val settingsDependencies: List - val pluginDependencies: List - val rootProject: Project - val includedBuilds: List -} - -interface IncludedBuild { - val name: String - val projectDir: String -} - -interface Gradle { - val version: String - val type: String - val url: String - val sha256: String - val nativeVersion: String -} - -interface Project { - val name: String - val version: String - val path: String - val projectDir: String - val buildscriptDependencies: List - val projectDependencies: List - val children: List -} - -interface Artifact { - val id: ArtifactIdentifier - val name: String - val path: String - val timestamp: String? - val build: Int? - val urls: List - val sha256: String -} - -interface ArtifactIdentifier { - val group: String - val name: String - val version: String - val type: String - val extension: String - val classifier: String? -} \ No newline at end of file diff --git a/model/src/main/kotlin/org/nixos/gradle2nix/PluginParameters.kt b/model/src/main/kotlin/org/nixos/gradle2nix/PluginParameters.kt new file mode 100644 index 0000000..f98f083 --- /dev/null +++ b/model/src/main/kotlin/org/nixos/gradle2nix/PluginParameters.kt @@ -0,0 +1,7 @@ +package org.nixos.gradle2nix + +const val PARAM_INCLUDE_PROJECTS = "NIX_INCLUDE_PROJECTS" +const val PARAM_INCLUDE_CONFIGURATIONS = "NIX_INCLUDE_CONFIGURATIONS" +const val PARAM_REPORT_DIR = "NIX_REPORT_DIR" +const val RESOLVE_PROJECT_TASK = "resolveProjectDependencies" +const val RESOLVE_ALL_TASK = "resolveAllDependencies" diff --git a/model/src/main/kotlin/org/nixos/gradle2nix/Repository.kt b/model/src/main/kotlin/org/nixos/gradle2nix/Repository.kt new file mode 100644 index 0000000..69659d3 --- /dev/null +++ b/model/src/main/kotlin/org/nixos/gradle2nix/Repository.kt @@ -0,0 +1,20 @@ +package org.nixos.gradle2nix.dependencygraph.model + +import kotlinx.serialization.Serializable + +@Serializable +data class Repository( + val id: String, + val type: Type, + val name: String, + val m2Compatible: Boolean, + val metadataSources: List, + val metadataResources: List, + val artifactResources: List, +) { + enum class Type { + MAVEN, + IVY, + FLAT_DIR + } +} diff --git a/model/src/main/kotlin/org/nixos/gradle2nix/ResolvedConfiguration.kt b/model/src/main/kotlin/org/nixos/gradle2nix/ResolvedConfiguration.kt new file mode 100644 index 0000000..3ff51b5 --- /dev/null +++ b/model/src/main/kotlin/org/nixos/gradle2nix/ResolvedConfiguration.kt @@ -0,0 +1,19 @@ +package org.nixos.gradle2nix.dependencygraph.model + +import kotlinx.serialization.Serializable + +@Serializable +data class ResolvedConfiguration( + val rootSource: DependencySource, + val configurationName: String, + val repositories: List = emptyList(), + val allDependencies: MutableList = mutableListOf() +) { + fun addDependency(component: ResolvedDependency) { + allDependencies.add(component) + } + + fun hasDependency(componentId: String): Boolean { + return allDependencies.any { it.id == componentId } + } +} diff --git a/model/src/main/kotlin/org/nixos/gradle2nix/ResolvedDependency.kt b/model/src/main/kotlin/org/nixos/gradle2nix/ResolvedDependency.kt new file mode 100644 index 0000000..b450e25 --- /dev/null +++ b/model/src/main/kotlin/org/nixos/gradle2nix/ResolvedDependency.kt @@ -0,0 +1,32 @@ +package org.nixos.gradle2nix.dependencygraph.model + +import kotlinx.serialization.Serializable + +// private const val DEFAULT_MAVEN_REPOSITORY_URL = "https://repo.maven.apache.org/maven2" + +@Serializable +data class ResolvedDependency( + val id: String, + val source: DependencySource, + val direct: Boolean, + val coordinates: DependencyCoordinates, + val repository: String?, + val dependencies: List +) +//{ +// fun packageUrl() = +// PackageURLBuilder +// .aPackageURL() +// .withType("maven") +// .withNamespace(coordinates.group.ifEmpty { coordinates.module }) // TODO: This is a sign of broken mapping from component -> PURL +// .withName(coordinates.module) +// .withVersion(coordinates.version) +// .also { +// if (repositoryUrl != null && repositoryUrl != DEFAULT_MAVEN_REPOSITORY_URL) { +// it.withQualifier("repository_url", repositoryUrl) +// } +// } +// .build() +// .toString() +// +//} diff --git a/plugin/.stutter/java11.lock b/plugin/.stutter/java11.lock deleted file mode 100644 index 9717484..0000000 --- a/plugin/.stutter/java11.lock +++ /dev/null @@ -1,5 +0,0 @@ -# DO NOT MODIFY: Generated by Stutter plugin. -5.0 -5.6.4 -6.0.1 -6.8.1 diff --git a/plugin/.stutter/java8.lock b/plugin/.stutter/java8.lock deleted file mode 100644 index db50dba..0000000 --- a/plugin/.stutter/java8.lock +++ /dev/null @@ -1,7 +0,0 @@ -# DO NOT MODIFY: Generated by Stutter plugin. -4.4.1 -4.10.3 -5.0 -5.6.4 -6.0.1 -6.8.1 diff --git a/plugin/build.gradle.kts b/plugin/build.gradle.kts index c59e544..915e72c 100644 --- a/plugin/build.gradle.kts +++ b/plugin/build.gradle.kts @@ -1,52 +1,29 @@ -buildscript { - configurations.classpath { - resolutionStrategy.activateDependencyLocking() - } -} +import com.github.jengelman.gradle.plugins.shadow.relocation.SimpleRelocator +import org.jetbrains.kotlin.gradle.dsl.JvmTarget +import org.jetbrains.kotlin.gradle.utils.extendsFrom plugins { - `kotlin-dsl` + id("org.jetbrains.kotlin.jvm") + id("com.gradle.plugin-publish") id("com.github.johnrengelman.shadow") - id("org.ajoberstar.stutter") -} - -sourceSets { - test { - java.srcDir("src/test/kotlin") - } -} - -dependencyLocking { - lockAllConfigurations() -} - -configurations { - compile { - dependencies.remove(project.dependencies.gradleApi()) - } } dependencies { - compileOnly("org.gradle:gradle-tooling-api:${gradle.gradleVersion}") - implementation("org.apache.maven:maven-repository-metadata:latest.release") - implementation(project(":ivy")) + compileOnly(kotlin("stdlib-jdk8")) + compileOnly(kotlin("reflect")) implementation(project(":model")) - shadow(gradleApi()) + implementation(libs.serialization.json) +} - compatTestImplementation("com.adobe.testing:s3mock-junit5:latest.release") - compatTestImplementation("com.squareup.okio:okio:latest.release") - compatTestImplementation("dev.minutest:minutest:latest.release") - compatTestImplementation("io.javalin:javalin:latest.release") - compatTestImplementation("io.strikt:strikt-core:latest.release") - compatTestImplementation("org.junit.jupiter:junit-jupiter-api:latest.release") - compatTestImplementation("org.junit.jupiter:junit-jupiter-params:latest.release") - compatTestImplementation(embeddedKotlin("reflect")) - compatTestImplementation(embeddedKotlin("stdlib-jdk8")) - compatTestImplementation(embeddedKotlin("test-junit5")) - compatTestImplementation(gradleTestKit()) - compatTestImplementation(project(":model")) - compatTestRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:latest.release") - compatTestRuntimeOnly("org.junit.platform:junit-platform-launcher:latest.release") +java { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 +} + +kotlin { + compilerOptions { + jvmTarget.set(JvmTarget.JVM_1_8) + } } gradlePlugin { @@ -60,35 +37,25 @@ gradlePlugin { } } -kotlinDslPluginOptions { - experimentalWarning.set(false) -} - -stutter { - isSparse = true - java(8) { - compatibleRange("4.4") - } - java(11) { - compatibleRange("5.0") - } -} - tasks { - pluginUnderTestMetadata { - pluginClasspath.setFrom(files(shadowJar)) + jar { + manifest { + attributes["Implementation-Version"] = archiveVersion.get() + attributes["Implementation-Title"] = "Gradle2Nix Plugin" + attributes["Implementation-Vendor"] = "Tad Fisher" + } } - withType { - useJUnitPlatform { - includeEngines("junit-jupiter") - } + shadowJar { + archiveClassifier.set("") + relocate("kotlin", "${project.group}.shadow.kotlin") + relocate("kotlinx.serialization", "${project.group}.shadow.serialization") + relocate("net.swiftzer.semver", "${project.group}.shadow.semver") + relocate("org.intellij", "${project.group}.shadow.intellij") + relocate("org.jetbrains", "${project.group}.shadow.jetbrains") + } - // Default logging config exposes a classpath conflict between - // the Gradle API and SFL4J. - // (Sprint Boot is used in S3Mock) - systemProperty("org.springframework.boot.logging.LoggingSystem", "org.springframework.boot.logging.java.JavaLoggingSystem") - - systemProperty("fixtures", "$rootDir/fixtures") + validatePlugins { + enableStricterValidation.set(true) } } diff --git a/plugin/buildscript-gradle.lockfile b/plugin/buildscript-gradle.lockfile deleted file mode 100644 index 99b963e..0000000 --- a/plugin/buildscript-gradle.lockfile +++ /dev/null @@ -1,36 +0,0 @@ -# This is a Gradle generated file for dependency locking. -# Manual edits can break the build and are not advised. -# This file is expected to be part of source control. -com.github.gundy:semver4j:0.16.4=classpath -com.google.code.gson:gson:2.8.6=classpath -de.undercouch:gradle-download-task:4.0.2=classpath -org.antlr:antlr4-runtime:4.5.2-1=classpath -org.gradle.kotlin.kotlin-dsl:org.gradle.kotlin.kotlin-dsl.gradle.plugin:1.4.9=classpath -org.gradle.kotlin:gradle-kotlin-dsl-plugins:1.4.9=classpath -org.jetbrains.intellij.deps:trove4j:1.0.20181211=classpath -org.jetbrains.kotlin:kotlin-android-extensions:1.4.20=classpath -org.jetbrains.kotlin:kotlin-annotation-processing-gradle:1.4.20=classpath -org.jetbrains.kotlin:kotlin-build-common:1.4.20=classpath -org.jetbrains.kotlin:kotlin-compiler-embeddable:1.4.20=classpath -org.jetbrains.kotlin:kotlin-compiler-runner:1.4.20=classpath -org.jetbrains.kotlin:kotlin-daemon-client:1.4.20=classpath -org.jetbrains.kotlin:kotlin-daemon-embeddable:1.4.20=classpath -org.jetbrains.kotlin:kotlin-gradle-plugin-api:1.4.20=classpath -org.jetbrains.kotlin:kotlin-gradle-plugin-model:1.4.20=classpath -org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.20=classpath -org.jetbrains.kotlin:kotlin-reflect:1.4.20=classpath -org.jetbrains.kotlin:kotlin-sam-with-receiver:1.4.20=classpath -org.jetbrains.kotlin:kotlin-script-runtime:1.4.20=classpath -org.jetbrains.kotlin:kotlin-scripting-common:1.4.20=classpath -org.jetbrains.kotlin:kotlin-scripting-compiler-embeddable:1.4.20=classpath -org.jetbrains.kotlin:kotlin-scripting-compiler-impl-embeddable:1.4.20=classpath -org.jetbrains.kotlin:kotlin-scripting-jvm:1.4.20=classpath -org.jetbrains.kotlin:kotlin-stdlib-common:1.4.20=classpath -org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.4.20=classpath -org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.20=classpath -org.jetbrains.kotlin:kotlin-stdlib:1.4.20=classpath -org.jetbrains.kotlin:kotlin-util-io:1.4.20=classpath -org.jetbrains.kotlin:kotlin-util-klib:1.4.20=classpath -org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.7=classpath -org.jetbrains:annotations:13.0=classpath -empty= diff --git a/plugin/gradle.lockfile b/plugin/gradle.lockfile deleted file mode 100644 index d1cb2b7..0000000 --- a/plugin/gradle.lockfile +++ /dev/null @@ -1,170 +0,0 @@ -# This is a Gradle generated file for dependency locking. -# Manual edits can break the build and are not advised. -# This file is expected to be part of source control. -ch.qos.logback:logback-classic:1.2.3=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -ch.qos.logback:logback-core:1.2.3=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -com.adobe.testing:s3mock-junit5:2.1.28=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -com.adobe.testing:s3mock-testsupport-common:2.1.28=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -com.adobe.testing:s3mock:2.1.28=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -com.amazonaws:aws-java-sdk-core:1.11.488=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -com.amazonaws:aws-java-sdk-core:1.11.946=compileClasspath,default,implementationDependenciesMetadata,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath -com.amazonaws:aws-java-sdk-kms:1.11.488=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -com.amazonaws:aws-java-sdk-kms:1.11.946=compileClasspath,default,implementationDependenciesMetadata,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath -com.amazonaws:aws-java-sdk-s3:1.11.488=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -com.amazonaws:aws-java-sdk-s3:1.11.946=compileClasspath,default,implementationDependenciesMetadata,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath -com.amazonaws:jmespath-java:1.11.488=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -com.amazonaws:jmespath-java:1.11.946=compileClasspath,default,implementationDependenciesMetadata,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath -com.christophsturm:filepeek:0.1.2=compatTestRuntimeClasspath -com.fasterxml.jackson.core:jackson-annotations:2.6.0=compileClasspath,default,implementationDependenciesMetadata,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath -com.fasterxml.jackson.core:jackson-annotations:2.9.0=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -com.fasterxml.jackson.core:jackson-core:2.6.7=compileClasspath,default,implementationDependenciesMetadata,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath -com.fasterxml.jackson.core:jackson-core:2.9.9=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -com.fasterxml.jackson.core:jackson-databind:2.6.7.4=compileClasspath,default,implementationDependenciesMetadata,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath -com.fasterxml.jackson.core:jackson-databind:2.9.9.3=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -com.fasterxml.jackson.dataformat:jackson-dataformat-cbor:2.6.7=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath,compileClasspath,default,implementationDependenciesMetadata,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath -com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.9.9=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.9.9=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.9.9=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -com.fasterxml.jackson.module:jackson-module-jaxb-annotations:2.9.9=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -com.fasterxml.jackson.module:jackson-module-parameter-names:2.9.9=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -com.fasterxml.woodstox:woodstox-core:5.1.0=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -com.fasterxml:classmate:1.3.4=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -com.squareup.moshi:moshi:1.11.0=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath,compileClasspath,default,implementationDependenciesMetadata,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath -com.squareup.okio:okio-metadata:3.0.0-alpha.1=compatTestImplementationDependenciesMetadata -com.squareup.okio:okio:1.17.5=compileClasspath,default,implementationDependenciesMetadata,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath -com.squareup.okio:okio:3.0.0-alpha.1=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -com.typesafe.netty:netty-reactive-streams-http:2.0.0=compatTestRuntimeClasspath -com.typesafe.netty:netty-reactive-streams:2.0.0=compatTestRuntimeClasspath -commons-codec:commons-codec:1.11=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath,compileClasspath,default,implementationDependenciesMetadata,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath -commons-io:commons-io:2.6=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -commons-logging:commons-logging:1.2=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath,compileClasspath,default,implementationDependenciesMetadata,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath -dev.minutest:minutest:2.0.0-alpha=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -io.github.classgraph:classgraph:4.8.28=compatTestRuntimeClasspath -io.javalin:javalin:3.13.3=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -io.netty:netty-buffer:4.1.33.Final=compatTestRuntimeClasspath -io.netty:netty-codec-http2:4.1.33.Final=compatTestRuntimeClasspath -io.netty:netty-codec-http:4.1.33.Final=compatTestRuntimeClasspath -io.netty:netty-codec:4.1.33.Final=compatTestRuntimeClasspath -io.netty:netty-common:4.1.33.Final=compatTestRuntimeClasspath -io.netty:netty-handler:4.1.33.Final=compatTestRuntimeClasspath -io.netty:netty-resolver:4.1.33.Final=compatTestRuntimeClasspath -io.netty:netty-transport-native-epoll:4.1.33.Final=compatTestRuntimeClasspath -io.netty:netty-transport-native-unix-common:4.1.33.Final=compatTestRuntimeClasspath -io.netty:netty-transport:4.1.33.Final=compatTestRuntimeClasspath -io.strikt:strikt-core:0.28.2=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -javax.annotation:javax.annotation-api:1.3.2=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -javax.servlet:javax.servlet-api:3.1.0=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -javax.validation:validation-api:2.0.1.Final=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -joda-time:joda-time:2.8.1=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath,compileClasspath,default,implementationDependenciesMetadata,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath -net.swiftzer.semver:semver:1.1.1=compatTestRuntimeClasspath,default,runtimeClasspath,testRuntimeClasspath -org.apache.commons:commons-lang3:3.8.1=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -org.apache.httpcomponents:httpclient:4.5.13=compileClasspath,default,implementationDependenciesMetadata,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath -org.apache.httpcomponents:httpclient:4.5.5=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata -org.apache.httpcomponents:httpclient:4.5.9=compatTestRuntimeClasspath -org.apache.httpcomponents:httpcore:4.4.11=compatTestRuntimeClasspath -org.apache.httpcomponents:httpcore:4.4.13=compileClasspath,default,implementationDependenciesMetadata,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath -org.apache.httpcomponents:httpcore:4.4.9=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata -org.apache.ivy:ivy:2.5.0=compileClasspath,default,implementationDependenciesMetadata,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath -org.apache.logging.log4j:log4j-api:2.11.2=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -org.apache.logging.log4j:log4j-to-slf4j:2.11.2=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -org.apache.maven:maven-repository-metadata:3.6.3=compileClasspath,default,implementationDependenciesMetadata,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath -org.apiguardian:apiguardian-api:1.1.0=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath,compatTestRuntimeOnlyDependenciesMetadata -org.codehaus.plexus:plexus-utils:3.2.1=compileClasspath,default,implementationDependenciesMetadata,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath -org.codehaus.woodstox:stax2-api:4.1=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -org.eclipse.jetty.websocket:websocket-api:9.4.35.v20201120=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -org.eclipse.jetty.websocket:websocket-client:9.4.35.v20201120=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -org.eclipse.jetty.websocket:websocket-common:9.4.35.v20201120=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -org.eclipse.jetty.websocket:websocket-server:9.4.35.v20201120=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -org.eclipse.jetty.websocket:websocket-servlet:9.4.35.v20201120=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -org.eclipse.jetty:jetty-client:9.4.35.v20201120=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -org.eclipse.jetty:jetty-continuation:9.4.19.v20190610=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -org.eclipse.jetty:jetty-http:9.4.35.v20201120=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -org.eclipse.jetty:jetty-io:9.4.35.v20201120=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -org.eclipse.jetty:jetty-security:9.4.35.v20201120=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -org.eclipse.jetty:jetty-server:9.4.35.v20201120=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -org.eclipse.jetty:jetty-servlet:9.4.35.v20201120=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -org.eclipse.jetty:jetty-servlets:9.4.19.v20190610=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -org.eclipse.jetty:jetty-util-ajax:9.4.35.v20201120=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -org.eclipse.jetty:jetty-util:9.4.35.v20201120=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -org.eclipse.jetty:jetty-webapp:9.4.35.v20201120=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -org.eclipse.jetty:jetty-xml:9.4.35.v20201120=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -org.gradle:gradle-tooling-api:6.8.1=compileClasspath,compileOnly,compileOnlyDependenciesMetadata -org.hibernate.validator:hibernate-validator:6.0.17.Final=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -org.jboss.logging:jboss-logging:3.3.2.Final=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -org.jetbrains.intellij.deps:trove4j:1.0.20181211=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath -org.jetbrains.kotlin:kotlin-compiler-embeddable:1.4.20=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath -org.jetbrains.kotlin:kotlin-daemon-embeddable:1.4.20=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath -org.jetbrains.kotlin:kotlin-gradle-plugin-api:1.4.20=kotlinCompilerPluginClasspath -org.jetbrains.kotlin:kotlin-gradle-plugin-model:1.4.20=kotlinCompilerPluginClasspath -org.jetbrains.kotlin:kotlin-klib-commonizer-embeddable:1.4.20=kotlinKlibCommonizerClasspath -org.jetbrains.kotlin:kotlin-reflect:1.4.20=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compileClasspath,compileOnly,compileOnlyDependenciesMetadata,embeddedKotlin,kotlinCompilerClasspath,kotlinKlibCommonizerClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath -org.jetbrains.kotlin:kotlin-reflect:1.4.21-2=compatTestRuntimeClasspath -org.jetbrains.kotlin:kotlin-sam-with-receiver:1.4.20=kotlinCompilerPluginClasspath -org.jetbrains.kotlin:kotlin-script-runtime:1.4.20=kotlinCompilerClasspath,kotlinCompilerPluginClasspath,kotlinKlibCommonizerClasspath -org.jetbrains.kotlin:kotlin-scripting-common:1.4.20=kotlinCompilerPluginClasspath -org.jetbrains.kotlin:kotlin-scripting-compiler-embeddable:1.4.20=kotlinCompilerPluginClasspath -org.jetbrains.kotlin:kotlin-scripting-compiler-impl-embeddable:1.4.20=kotlinCompilerPluginClasspath -org.jetbrains.kotlin:kotlin-scripting-jvm:1.4.20=kotlinCompilerPluginClasspath -org.jetbrains.kotlin:kotlin-stdlib-common:1.4.20=compileClasspath,compileOnly,compileOnlyDependenciesMetadata,default,embeddedKotlin,implementationDependenciesMetadata,kotlinCompilerClasspath,kotlinCompilerPluginClasspath,kotlinKlibCommonizerClasspath,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath -org.jetbrains.kotlin:kotlin-stdlib-common:1.4.21-2=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.4.20=compileClasspath,compileOnly,compileOnlyDependenciesMetadata,embeddedKotlin,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath -org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.4.21-2=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.20=compileClasspath,compileOnly,compileOnlyDependenciesMetadata,embeddedKotlin,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath -org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.21-2=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -org.jetbrains.kotlin:kotlin-stdlib:1.4.20=compileClasspath,compileOnly,compileOnlyDependenciesMetadata,default,embeddedKotlin,implementationDependenciesMetadata,kotlinCompilerClasspath,kotlinCompilerPluginClasspath,kotlinKlibCommonizerClasspath,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath -org.jetbrains.kotlin:kotlin-stdlib:1.4.21-2=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -org.jetbrains.kotlin:kotlin-test-annotations-common:1.4.20=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -org.jetbrains.kotlin:kotlin-test-common:1.4.20=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -org.jetbrains.kotlin:kotlin-test-junit5:1.4.20=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -org.jetbrains.kotlin:kotlin-test:1.4.20=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.4.2=compatTestRuntimeClasspath -org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.7=kotlinCompilerPluginClasspath -org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.2=compatTestRuntimeClasspath -org.jetbrains:annotations:13.0=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath,compileClasspath,compileOnly,compileOnlyDependenciesMetadata,default,embeddedKotlin,implementationDependenciesMetadata,kotlinCompilerClasspath,kotlinCompilerPluginClasspath,kotlinKlibCommonizerClasspath,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath -org.junit.jupiter:junit-jupiter-api:5.7.0=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath,compatTestRuntimeOnlyDependenciesMetadata -org.junit.jupiter:junit-jupiter-engine:5.7.0=compatTestRuntimeClasspath,compatTestRuntimeOnlyDependenciesMetadata -org.junit.jupiter:junit-jupiter-params:5.7.0=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -org.junit.platform:junit-platform-commons:1.7.0=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath,compatTestRuntimeOnlyDependenciesMetadata -org.junit.platform:junit-platform-engine:1.7.0=compatTestRuntimeClasspath,compatTestRuntimeOnlyDependenciesMetadata -org.junit.platform:junit-platform-launcher:1.7.0=compatTestRuntimeClasspath,compatTestRuntimeOnlyDependenciesMetadata -org.junit:junit-bom:5.7.0=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath,compatTestRuntimeOnlyDependenciesMetadata -org.mortbay.jasper:apache-el:8.5.40=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -org.opentest4j:opentest4j:1.2.0=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath,compatTestRuntimeOnlyDependenciesMetadata -org.reactivestreams:reactive-streams:1.0.2=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -org.slf4j:jul-to-slf4j:1.7.28=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -org.slf4j:slf4j-api:1.7.28=compileClasspath,compileOnly,compileOnlyDependenciesMetadata -org.slf4j:slf4j-api:1.7.30=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -org.springframework.boot:spring-boot-autoconfigure:2.1.9.RELEASE=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -org.springframework.boot:spring-boot-starter-jetty:2.1.9.RELEASE=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -org.springframework.boot:spring-boot-starter-json:2.1.9.RELEASE=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -org.springframework.boot:spring-boot-starter-logging:2.1.9.RELEASE=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -org.springframework.boot:spring-boot-starter-web:2.1.9.RELEASE=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -org.springframework.boot:spring-boot-starter:2.1.9.RELEASE=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -org.springframework.boot:spring-boot:2.1.9.RELEASE=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -org.springframework:spring-aop:5.1.10.RELEASE=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -org.springframework:spring-beans:5.1.10.RELEASE=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -org.springframework:spring-context:5.1.10.RELEASE=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -org.springframework:spring-core:5.1.10.RELEASE=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -org.springframework:spring-expression:5.1.10.RELEASE=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -org.springframework:spring-jcl:5.1.10.RELEASE=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -org.springframework:spring-web:5.1.10.RELEASE=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -org.springframework:spring-webmvc:5.1.10.RELEASE=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -org.yaml:snakeyaml:1.23=compatTestRuntimeClasspath -software.amazon.awssdk:annotations:2.7.19=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -software.amazon.awssdk:apache-client:2.7.19=compatTestRuntimeClasspath -software.amazon.awssdk:auth:2.7.19=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -software.amazon.awssdk:aws-core:2.7.19=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -software.amazon.awssdk:aws-query-protocol:2.7.19=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -software.amazon.awssdk:aws-xml-protocol:2.7.19=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -software.amazon.awssdk:http-client-spi:2.7.19=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -software.amazon.awssdk:netty-nio-client:2.7.19=compatTestRuntimeClasspath -software.amazon.awssdk:profiles:2.7.19=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -software.amazon.awssdk:protocol-core:2.7.19=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -software.amazon.awssdk:regions:2.7.19=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -software.amazon.awssdk:s3:2.7.19=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -software.amazon.awssdk:sdk-core:2.7.19=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -software.amazon.awssdk:url-connection-client:2.7.19=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -software.amazon.awssdk:utils:2.7.19=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -software.amazon.eventstream:eventstream:1.0.1=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath -software.amazon.ion:ion-java:1.0.2=compatTestCompileClasspath,compatTestImplementationDependenciesMetadata,compatTestRuntimeClasspath,compileClasspath,default,implementationDependenciesMetadata,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath -empty=annotationProcessor,apiDependenciesMetadata,archives,compatTestAnnotationProcessor,compatTestApiDependenciesMetadata,compatTestCompile,compatTestCompileOnly,compatTestCompileOnlyDependenciesMetadata,compatTestKotlinScriptDef,compatTestKotlinScriptDefExtensions,compatTestRuntime,compile,kotlinNativeCompilerPluginClasspath,kotlinScriptDef,kotlinScriptDefExtensions,runtime,runtimeOnlyDependenciesMetadata,shadow,testAnnotationProcessor,testApiDependenciesMetadata,testCompile,testCompileOnly,testCompileOnlyDependenciesMetadata,testKotlinScriptDef,testKotlinScriptDefExtensions,testRuntime,testRuntimeOnlyDependenciesMetadata diff --git a/plugin/src/compatTest/kotlin/org/nixos/gradle2nix/BasicTest.kt b/plugin/src/compatTest/kotlin/org/nixos/gradle2nix/BasicTest.kt deleted file mode 100644 index 3d3e833..0000000 --- a/plugin/src/compatTest/kotlin/org/nixos/gradle2nix/BasicTest.kt +++ /dev/null @@ -1,74 +0,0 @@ -package org.nixos.gradle2nix - -import dev.minutest.Tests -import dev.minutest.experimental.minus -import dev.minutest.junit.JUnit5Minutests -import dev.minutest.rootContext -import dev.minutest.test -import org.gradle.api.internal.artifacts.dsl.DefaultRepositoryHandler.BINTRAY_JCENTER_URL -import org.gradle.api.internal.artifacts.dsl.DefaultRepositoryHandler.MAVEN_CENTRAL_URL -import strikt.api.expectThat -import strikt.assertions.all -import strikt.assertions.containsExactly -import strikt.assertions.flatMap -import strikt.assertions.get -import strikt.assertions.hasSize -import strikt.assertions.isEqualTo -import strikt.assertions.map -import strikt.assertions.none -import strikt.assertions.startsWith - -class BasicTest : JUnit5Minutests { - @Tests - fun tests() = rootContext("basic tests") { - withFixture("basic/basic-java-project") { - test("builds basic java project") { - expectThat(build()) { - get("gradle version") { gradle.version }.isEqualTo(System.getProperty("compat.gradle.version")) - - get("all dependencies") { - pluginDependencies + - rootProject.buildscriptDependencies + - rootProject.projectDependencies - }.flatMap { it.urls }.none { startsWith("file:") } - - get("root project dependencies") { rootProject.projectDependencies }.and { - ids.containsExactly( - "com.squareup.moshi:moshi:1.8.0@jar", - "com.squareup.moshi:moshi:1.8.0@pom", - "com.squareup.moshi:moshi-parent:1.8.0@pom", - "com.squareup.okio:okio:2.2.2@jar", - "com.squareup.okio:okio:2.2.2@pom", - "org.jetbrains:annotations:13.0@jar", - "org.jetbrains:annotations:13.0@pom", - "org.jetbrains.kotlin:kotlin-stdlib:1.2.60@jar", - "org.jetbrains.kotlin:kotlin-stdlib:1.2.60@pom", - "org.jetbrains.kotlin:kotlin-stdlib-common:1.2.60@jar", - "org.jetbrains.kotlin:kotlin-stdlib-common:1.2.60@pom", - "org.sonatype.oss:oss-parent:7@pom" - ) - - map { it.urls }.all { - hasSize(2) - get(0).startsWith(BINTRAY_JCENTER_URL) - get(1).startsWith(MAVEN_CENTRAL_URL) - } - } - } - } - } - - withFixture("basic/basic-kotlin-project") { - GRADLE_MIN("4.9") - test("excludes embedded kotlin repo") { - - expectThat(build()) { - get("all dependencies") { - pluginDependencies + - rootProject.buildscriptDependencies + - rootProject.projectDependencies - }.flatMap { it.urls }.all { not { startsWith("file:") } } - } - } - } - } -} diff --git a/plugin/src/compatTest/kotlin/org/nixos/gradle2nix/DependencyTest.kt b/plugin/src/compatTest/kotlin/org/nixos/gradle2nix/DependencyTest.kt deleted file mode 100644 index cd60aa7..0000000 --- a/plugin/src/compatTest/kotlin/org/nixos/gradle2nix/DependencyTest.kt +++ /dev/null @@ -1,102 +0,0 @@ -package org.nixos.gradle2nix - -import dev.minutest.Tests -import dev.minutest.experimental.minus -import dev.minutest.junit.JUnit5Minutests -import dev.minutest.rootContext -import dev.minutest.test -import strikt.api.expectThat -import strikt.assertions.all -import strikt.assertions.containsExactly -import strikt.assertions.filter -import strikt.assertions.isEqualTo -import strikt.assertions.isNotNull -import strikt.assertions.isNull - -class DependencyTest : JUnit5Minutests { - @Tests - fun tests() = rootContext("dependency tests") { - - withRepository("m2") { - - withFixture("dependency/classifier") { - test("resolves dependency with classifier") { - expectThat(build()) { - get("root project dependencies") { rootProject.projectDependencies }.ids.containsExactly( - "com.badlogicgames.gdx:gdx-parent:1.9.9@pom", - "com.badlogicgames.gdx:gdx-platform:1.9.9:natives-desktop@jar", - "com.badlogicgames.gdx:gdx-platform:1.9.9@pom", - "org.sonatype.oss:oss-parent:5@pom" - ) - } - } - } - - withFixture("dependency/maven-bom") { - GRADLE_MIN("5.0") - test("resolves dependencies from maven bom platform") { - expectThat(build()) { - get("root project dependencies") { rootProject.projectDependencies } - .ids - .containsExactly( - "io.micrometer:micrometer-bom:1.5.1@pom", - "io.micrometer:micrometer-core:1.5.1@jar", - "io.micrometer:micrometer-core:1.5.1@pom", - "org.hdrhistogram:HdrHistogram:2.1.12@jar", - "org.hdrhistogram:HdrHistogram:2.1.12@pom" - ) - } - } - } - - withFixture("dependency/snapshot") { - test("resolves snapshot dependency") { - expectThat(build()) { - get("root project dependencies") { rootProject.projectDependencies } - .and { - ids.containsExactly( - "org.apache:test-SNAPSHOT2:2.0.2-SNAPSHOT@jar", - "org.apache:test-SNAPSHOT2:2.0.2-SNAPSHOT@pom" - ) - all { - get("timestamp") { timestamp }.isNull() - get("build") { build }.isNotNull() - } - } - } - } - } - - withFixture("dependency/snapshot-dynamic") { - test("resolves snapshot dependency with dynamic version") { - expectThat(build()) { - get("root project dependencies") { rootProject.projectDependencies } - .and { - ids.containsExactly( - "org.apache:test-SNAPSHOT1:2.0.2-SNAPSHOT@jar", - "org.apache:test-SNAPSHOT1:2.0.2-SNAPSHOT@pom" - ) - all { - get("timestamp") { timestamp }.isEqualTo("20070310.181613") - get("build") { build }.isEqualTo(3) - } - } - } - } - } - - withFixture("dependency/snapshot-redirect") { - test("resolves snapshot dependency with redirect") { - expectThat(build()) { - get("root project dependencies") { rootProject.projectDependencies } - .filter { it.id.name == "packr" } - .all { - get("id.version") { id.version }.isEqualTo("-SNAPSHOT") - get("timestamp") { timestamp }.isNotNull() - get("build") { build }.isNotNull() - } - } - } - } - } - } -} \ No newline at end of file diff --git a/plugin/src/compatTest/kotlin/org/nixos/gradle2nix/IvyTest.kt b/plugin/src/compatTest/kotlin/org/nixos/gradle2nix/IvyTest.kt deleted file mode 100644 index 3269ce3..0000000 --- a/plugin/src/compatTest/kotlin/org/nixos/gradle2nix/IvyTest.kt +++ /dev/null @@ -1,34 +0,0 @@ -package org.nixos.gradle2nix - -import dev.minutest.Tests -import dev.minutest.junit.JUnit5Minutests -import dev.minutest.rootContext -import dev.minutest.test -import strikt.api.expectThat -import strikt.assertions.all -import strikt.assertions.containsExactly -import strikt.assertions.map -import strikt.assertions.single -import strikt.assertions.startsWith - -class IvyTest : JUnit5Minutests { - @Tests - fun tests() = rootContext("ivy tests") { - withFixture("ivy/basic") { - test("resolves ivy dependencies") { - expectThat(build()) { - get("root project dependencies") { rootProject.projectDependencies }.and { - ids.containsExactly( - "org.opendof.core-java:dof-cipher-sms4:1.0@jar", - "org.opendof.core-java:dof-oal:7.0.2@jar" - ) - - map { it.urls }.all { - single().startsWith("https://asset.opendof.org/artifact") - } - } - } - } - } - } -} \ No newline at end of file diff --git a/plugin/src/compatTest/kotlin/org/nixos/gradle2nix/PluginTest.kt b/plugin/src/compatTest/kotlin/org/nixos/gradle2nix/PluginTest.kt deleted file mode 100644 index 961f505..0000000 --- a/plugin/src/compatTest/kotlin/org/nixos/gradle2nix/PluginTest.kt +++ /dev/null @@ -1,25 +0,0 @@ -package org.nixos.gradle2nix - -import dev.minutest.Tests -import dev.minutest.experimental.FOCUS -import dev.minutest.experimental.minus -import dev.minutest.junit.JUnit5Minutests -import dev.minutest.rootContext -import dev.minutest.test -import strikt.api.expectThat -import strikt.assertions.contains -import strikt.assertions.containsExactly - -class PluginTest : JUnit5Minutests { - @Tests - fun tests() = rootContext("plugin tests") { - withFixture("plugin/resolves-from-default-repo") { - test("resolves plugin from default repo") { - expectThat(build()) { - get("plugin dependencies") { pluginDependencies }.ids - .contains("org.jetbrains.kotlin.jvm:org.jetbrains.kotlin.jvm.gradle.plugin:1.3.50@pom") - } - } - } - } -} \ No newline at end of file diff --git a/plugin/src/compatTest/kotlin/org/nixos/gradle2nix/S3Test.kt b/plugin/src/compatTest/kotlin/org/nixos/gradle2nix/S3Test.kt deleted file mode 100644 index f72c79c..0000000 --- a/plugin/src/compatTest/kotlin/org/nixos/gradle2nix/S3Test.kt +++ /dev/null @@ -1,52 +0,0 @@ -package org.nixos.gradle2nix - -import dev.minutest.Tests -import dev.minutest.junit.JUnit5Minutests -import dev.minutest.rootContext -import dev.minutest.test -import strikt.api.expectThat -import strikt.assertions.containsExactly -import strikt.assertions.flatMap -import strikt.assertions.map - -class S3Test : JUnit5Minutests { - @Tests - fun tests() = rootContext("s3 tests") { - withBucket("repositories") { - withFixture("s3/maven") { - - test("dependency from s3 maven repo") { - expectThat(build()) { - get("root project dependencies") { rootProject.projectDependencies }.and { - ids.containsExactly( - "org.apache:test:1.0.0@jar", - "org.apache:test:1.0.0@pom" - ) - flatMap { it.urls }.containsExactly( - "s3://repositories/m2/org/apache/test/1.0.0/test-1.0.0.jar", - "s3://repositories/m2/org/apache/test/1.0.0/test-1.0.0.pom" - ) - } - } - } - } - - withFixture("s3/maven-snapshot") { - test("snapshot dependency from s3 maven repo") { - expectThat(build()) { - get("root project dependencies") { rootProject.projectDependencies }.and { - ids.containsExactly( - "org.apache:test-SNAPSHOT1:2.0.0-SNAPSHOT@jar", - "org.apache:test-SNAPSHOT1:2.0.0-SNAPSHOT@pom" - ) - flatMap { it.urls }.containsExactly( - "s3://repositories/m2/org/apache/test-SNAPSHOT1/2.0.0-SNAPSHOT/test-SNAPSHOT1-2.0.0-20070310.181613-3.jar", - "s3://repositories/m2/org/apache/test-SNAPSHOT1/2.0.0-SNAPSHOT/test-SNAPSHOT1-2.0.0-20070310.181613-3.pom" - ) - } - } - } - } - } - } -} \ No newline at end of file diff --git a/plugin/src/compatTest/kotlin/org/nixos/gradle2nix/SettingsTest.kt b/plugin/src/compatTest/kotlin/org/nixos/gradle2nix/SettingsTest.kt deleted file mode 100644 index aaf089f..0000000 --- a/plugin/src/compatTest/kotlin/org/nixos/gradle2nix/SettingsTest.kt +++ /dev/null @@ -1,39 +0,0 @@ -package org.nixos.gradle2nix - -import dev.minutest.Tests -import dev.minutest.experimental.minus -import dev.minutest.junit.JUnit5Minutests -import dev.minutest.rootContext -import dev.minutest.test -import strikt.api.expectThat -import strikt.assertions.containsExactly - -class SettingsTest : JUnit5Minutests { - @Tests - fun tests() = rootContext("settings tests") { - withRepository("m2") { - - withFixture("settings/buildscript") { - test("resolves settings plugin in buildscript classpath") { - expectThat(build()) { - get("settings dependencies") { settingsDependencies }.ids.containsExactly( - "org.apache:test:1.0.0@jar", - "org.apache:test:1.0.0@pom" - ) - } - } - } - - withFixture("settings/dependency-resolution-management") { - GRADLE_MIN("6.8") - test("uses repositories from settings script") { - expectThat(build()) { - get("root project dependencies") { rootProject.projectDependencies }.ids.containsExactly( - "org.apache:test:1.0.0@jar", - "org.apache:test:1.0.0@pom" - ) - } - } - } - } - } -} \ No newline at end of file diff --git a/plugin/src/compatTest/kotlin/org/nixos/gradle2nix/SubprojectsTest.kt b/plugin/src/compatTest/kotlin/org/nixos/gradle2nix/SubprojectsTest.kt deleted file mode 100644 index f0bed36..0000000 --- a/plugin/src/compatTest/kotlin/org/nixos/gradle2nix/SubprojectsTest.kt +++ /dev/null @@ -1,152 +0,0 @@ -package org.nixos.gradle2nix - -import dev.minutest.Tests -import dev.minutest.junit.JUnit5Minutests -import dev.minutest.rootContext -import dev.minutest.test -import org.gradle.api.internal.artifacts.dsl.DefaultRepositoryHandler.BINTRAY_JCENTER_URL -import strikt.api.expectThat -import strikt.assertions.all -import strikt.assertions.containsExactly -import strikt.assertions.containsExactlyInAnyOrder -import strikt.assertions.get -import strikt.assertions.hasSize -import strikt.assertions.isEqualTo -import strikt.assertions.map -import strikt.assertions.single -import strikt.assertions.startsWith - -class SubprojectsTest : JUnit5Minutests { - @Tests - fun tests() = rootContext("subproject tests") { - withFixture("subprojects/multi-module") { - test("builds multi-module project") { - expectThat(build().rootProject) { - get("root project dependencies") { projectDependencies }.and { - ids.containsExactly( - "junit:junit:4.12@jar", - "junit:junit:4.12@pom", - "org.hamcrest:hamcrest-core:1.3@jar", - "org.hamcrest:hamcrest-core:1.3@pom", - "org.hamcrest:hamcrest-parent:1.3@pom" - ) - all { - get("urls") { urls }.single().startsWith(BINTRAY_JCENTER_URL) - } - } - - get("children") { children }.and { - hasSize(2) - - get(0).and { - get("name") { name }.isEqualTo("child-a") - get("projectDir") { projectDir }.isEqualTo("child-a") - - get("child-a project dependencies") { projectDependencies }.and { - ids.containsExactly( - "com.squareup.okio:okio:2.2.2@jar", - "com.squareup.okio:okio:2.2.2@pom", - "org.jetbrains:annotations:13.0@jar", - "org.jetbrains:annotations:13.0@pom", - "org.jetbrains.kotlin:kotlin-stdlib:1.2.60@jar", - "org.jetbrains.kotlin:kotlin-stdlib:1.2.60@pom", - "org.jetbrains.kotlin:kotlin-stdlib-common:1.2.60@jar", - "org.jetbrains.kotlin:kotlin-stdlib-common:1.2.60@pom" - ) - - all { - get("urls") { urls }.single().startsWith(BINTRAY_JCENTER_URL) - } - } - } - - get(1).and { - get("name") { name }.isEqualTo("child-b") - get("projectDir") { projectDir }.isEqualTo("child-b") - - get("child-b project dependencies") { projectDependencies }.and { - ids.containsExactly( - "com.squareup.moshi:moshi:1.8.0@jar", - "com.squareup.moshi:moshi:1.8.0@pom", - "com.squareup.moshi:moshi-parent:1.8.0@pom", - "com.squareup.okio:okio:1.16.0@jar", // compileClasspath - "com.squareup.okio:okio:1.16.0@pom", // compileClasspath - "com.squareup.okio:okio:2.2.2@jar", // runtimeClasspath - "com.squareup.okio:okio:2.2.2@pom", // runtimeClasspath - "com.squareup.okio:okio-parent:1.16.0@pom", // compileClasspath - "org.jetbrains:annotations:13.0@jar", - "org.jetbrains:annotations:13.0@pom", - "org.jetbrains.kotlin:kotlin-stdlib:1.2.60@jar", - "org.jetbrains.kotlin:kotlin-stdlib:1.2.60@pom", - "org.jetbrains.kotlin:kotlin-stdlib-common:1.2.60@jar", - "org.jetbrains.kotlin:kotlin-stdlib-common:1.2.60@pom", - "org.sonatype.oss:oss-parent:7@pom" - ) - - all { - get("urls") { urls }.single().startsWith(BINTRAY_JCENTER_URL) - } - } - } - } - } - } - - test("builds single subproject") { - expectThat(build(subprojects = listOf(":child-a")).rootProject) { - get("root project dependencies") { projectDependencies }.and { - ids.containsExactly( - "junit:junit:4.12@jar", - "junit:junit:4.12@pom", - "org.hamcrest:hamcrest-core:1.3@jar", - "org.hamcrest:hamcrest-core:1.3@pom", - "org.hamcrest:hamcrest-parent:1.3@pom" - ) - - all { - get("urls") { urls }.single().startsWith(BINTRAY_JCENTER_URL) - } - } - - get("children") { children }.single().and { - get("name") { name }.isEqualTo("child-a") - get("projectDir") { projectDir }.isEqualTo("child-a") - - get("child-a project dependencies") { projectDependencies }.and { - ids.containsExactly( - "com.squareup.okio:okio:2.2.2@jar", - "com.squareup.okio:okio:2.2.2@pom", - "org.jetbrains:annotations:13.0@jar", - "org.jetbrains:annotations:13.0@pom", - "org.jetbrains.kotlin:kotlin-stdlib:1.2.60@jar", - "org.jetbrains.kotlin:kotlin-stdlib:1.2.60@pom", - "org.jetbrains.kotlin:kotlin-stdlib-common:1.2.60@jar", - "org.jetbrains.kotlin:kotlin-stdlib-common:1.2.60@pom" - ) - - all { - get("urls") { urls }.single().startsWith(BINTRAY_JCENTER_URL) - } - } - } - } - } - } - - withFixture("subprojects/dependent-subprojects") { - test("includes dependent subprojects") { - expectThat(build(subprojects = listOf(":child-a"))) { - get("children") { rootProject.children } - .map { it.path } - .containsExactlyInAnyOrder(":child-a", ":child-b", ":child-c") - } - - expectThat(build(subprojects = listOf(":child-b"))) { - get("children") { rootProject.children } - .map { it.path } - .containsExactlyInAnyOrder(":child-b", ":child-c") - } - } - } - } -} diff --git a/plugin/src/compatTest/kotlin/org/nixos/gradle2nix/TestUtil.kt b/plugin/src/compatTest/kotlin/org/nixos/gradle2nix/TestUtil.kt deleted file mode 100644 index 114658d..0000000 --- a/plugin/src/compatTest/kotlin/org/nixos/gradle2nix/TestUtil.kt +++ /dev/null @@ -1,300 +0,0 @@ -package org.nixos.gradle2nix - -import com.adobe.testing.s3mock.S3MockApplication -import com.adobe.testing.s3mock.junit5.S3MockExtension -import com.adobe.testing.s3mock.testsupport.common.S3MockStarter -import com.squareup.moshi.Moshi -import dev.minutest.ContextBuilder -import dev.minutest.MinutestFixture -import dev.minutest.Node -import dev.minutest.TestContextBuilder -import dev.minutest.afterEach -import dev.minutest.beforeEach -import dev.minutest.experimental.SKIP -import dev.minutest.experimental.TransformingAnnotation -import dev.minutest.given -import dev.minutest.givenClosable -import dev.minutest.given_ -import io.javalin.Javalin -import io.javalin.http.staticfiles.Location -import okio.buffer -import okio.source -import org.gradle.internal.classpath.DefaultClassPath -import org.gradle.testkit.runner.GradleRunner -import org.gradle.testkit.runner.internal.PluginUnderTestMetadataReading -import org.gradle.util.GradleVersion -import org.junit.jupiter.api.Assumptions.assumeTrue -import strikt.api.Assertion -import strikt.assertions.map -import java.io.Closeable -import java.io.File -import java.io.StringWriter -import java.util.concurrent.atomic.AtomicBoolean - -private val moshi = Moshi.Builder().build() - -val fixtureRoot = File(System.getProperty("fixtures")) - -val gradleVersion = System.getProperty("compat.gradle.version") - ?.let(GradleVersion::version) - ?: GradleVersion.current() - -val GRADLE_4_5 = GradleVersion.version("4.5") - -fun GRADLE_MIN(version: String) = object : TransformingAnnotation() { - override fun transform(node: Node): Node = - if (gradleVersion < GradleVersion.version(version)) SKIP.transform(node) else node -} - -private fun File.initscript() = resolve("init.gradle").also { - it.writer().use { out -> - val classpath = DefaultClassPath.of(PluginUnderTestMetadataReading.readImplementationClasspath()) - .asFiles.joinToString { n -> "'$n'" } - out.append(""" - initscript { - dependencies { - classpath files($classpath) - } - } - - apply plugin: org.nixos.gradle2nix.Gradle2NixPlugin - - """.trimIndent()) - } -} - -fun File.buildKotlin( - script: String, - configurations: List = emptyList(), - subprojects: List = emptyList() -): DefaultBuild { - assumeTrue(gradleVersion >= GRADLE_4_5) - resolve("build.gradle.kts").writeText(script) - return build(configurations, subprojects) -} - -private fun File.build( - configurations: List, - subprojects: List, - extraArguments: List = emptyList() -): DefaultBuild { - val log = StringWriter() - - val result = GradleRunner.create() - .withGradleVersion(gradleVersion.version) - .withProjectDir(this) - .forwardStdOutput(log) - .forwardStdError(log) - .withArguments( - "nixModel", - "--init-script=${initscript()}", - "--stacktrace", - "-Porg.nixos.gradle2nix.configurations=${configurations.joinToString(",")}", - "-Porg.nixos.gradle2nix.subprojects=${subprojects.joinToString(",")}", - *(extraArguments.toTypedArray()) - ) - .runCatching { build() } - - result.onFailure { error -> - System.err.print(log) - throw error - } - - print(log) - - return resolve("build/nix/model.json").run { - println(readText()) - source().buffer().use { src -> - checkNotNull(moshi.adapter(DefaultBuild::class.java).fromJson(src)) - } - } -} - -val > Assertion.Builder.ids: Assertion.Builder> - get() = map { it.id.toString() } - -private fun File.parents() = generateSequence(parentFile) { it.parentFile } - -abstract class ArgumentsSupplier(private val parent: ArgumentsSupplier? = null) { - open val arguments: List = emptyList() - - val extraArguments: List get() = (parent?.extraArguments ?: emptyList()) + arguments -} - -@MinutestFixture -class RepositoryFixture( - private val server: Javalin, - parent: ArgumentsSupplier? = null -) : ArgumentsSupplier(parent), Closeable { - override fun close() { - server.stop() - } -} - -@MinutestFixture -class S3Fixture( - private val name: String, - parent: ArgumentsSupplier? = null -) : ArgumentsSupplier(parent), Closeable { - private val s3mock = S3Mock( - initialBuckets = listOf(name), - secureConnection = false - ) - - override val arguments: List get() = listOf( - "-Dorg.gradle.s3.endpoint=${s3mock.serviceEndpoint}", - "-Dorg.nixos.gradle2nix.s3test=true" - ) - - init { - s3mock.startServer() - - val s3root = fixtureRoot.resolve(name) - val s3client = s3mock.createS3Client() - require(s3root.exists() && s3root.isDirectory) { - "$name: S3 fixture not found: $s3root" - } - s3root.walkTopDown() - .filter { it.isFile } - .forEach { file -> - val key = file.toRelativeString(s3root) - s3client.putObject(name, key, file) - } - } - - override fun close() { - s3mock.stopServer() - } -} - -@MinutestFixture -class TestFixture( - val name: String, - val source: File, - parent: ArgumentsSupplier? = null -) : ArgumentsSupplier(parent), Closeable { - val dest: File - - init { - require(source.exists() && source.isDirectory) { - "$name: Test fixture not found: $source}" - } - dest = createTempDir(prefix = name, suffix = "") - } - - override fun close() { - dest.deleteRecursively() - } -} - -@MinutestFixture -data class ProjectFixture( - private val parent: TestFixture, - private val source: File -) : Closeable { - private val dest: File - - init { - require(source.exists() && source.isDirectory && parent.source in source.parents()) { - "${parent.name}: Test project not found: $source" - } - val rel = source.toRelativeString(parent.source) - dest = parent.dest.resolve(rel) - } - - fun copySource() { - source.copyRecursively(dest, true) - } - - fun build( - configurations: List = emptyList(), - subprojects: List = emptyList() - ) = dest.build(configurations, subprojects, parent.extraArguments) - - override fun close() { - dest.deleteRecursively() - } -} - -fun ContextBuilder<*>.withBucket( - name: String, - block: TestContextBuilder<*, S3Fixture>.() -> Unit -) = derivedContext("with s3 bucket: $name") { - given_ { parent -> - S3Fixture(name, parent as? ArgumentsSupplier) - } - - afterEach { it.close() } - - block() -} - -fun ContextBuilder<*>.withRepository( - name: String, - block: TestContextBuilder<*, RepositoryFixture>.() -> Unit -) = derivedContext("with repository: $name") { - given_ { parent -> - RepositoryFixture( - server = Javalin.create { config -> - config.addStaticFiles("${fixtureRoot}/repositories/$name", Location.EXTERNAL) - }.start(9999), - parent = parent as? ArgumentsSupplier - ) - } - - afterEach { it.close() } - - block() -} - -fun ContextBuilder<*>.withFixture( - name: String, - block: TestContextBuilder<*, ProjectFixture>.() -> Unit -) = derivedContext(name) { - - val projectRoot = fixtureRoot.resolve(name).also { - check(it.exists()) { "$name: project fixture not found: $it" } - } - - given_ { parent -> - TestFixture(name, projectRoot, parent as? ArgumentsSupplier) - } - - val testRoots = projectRoot.listFiles()!! - .filter { it.isDirectory } - .map { it.absoluteFile } - .toList() - - testRoots.forEach { testRoot -> - derivedContext(testRoot.name) { - given_ { parent -> ProjectFixture(parent, testRoot) } - beforeEach { copySource() } - afterEach { close() } - block() - } - } -} - -class S3Mock( - initialBuckets: List = emptyList(), - secureConnection: Boolean = true -) : S3MockStarter( - mapOf( - S3MockApplication.PROP_INITIAL_BUCKETS to initialBuckets.joinToString(","), - S3MockApplication.PROP_SECURE_CONNECTION to secureConnection - ) -) { - private val running = AtomicBoolean() - - fun startServer() { - if (running.compareAndSet(false, true)) { - start() - } - } - - fun stopServer() { - if (running.compareAndSet(true, false)) { - stop() - } - } -} \ No newline at end of file diff --git a/plugin/src/compatTest/kotlin/org/nixos/gradle2nix/WrapperTest.kt b/plugin/src/compatTest/kotlin/org/nixos/gradle2nix/WrapperTest.kt deleted file mode 100644 index ad4a552..0000000 --- a/plugin/src/compatTest/kotlin/org/nixos/gradle2nix/WrapperTest.kt +++ /dev/null @@ -1,27 +0,0 @@ -package org.nixos.gradle2nix - -import dev.minutest.Tests -import dev.minutest.given -import dev.minutest.junit.JUnit5Minutests -import dev.minutest.rootContext -import dev.minutest.test -import strikt.api.expectThat -import strikt.assertions.isEqualTo -import java.io.File - -class WrapperTest : JUnit5Minutests { - @Tests - fun tests() = rootContext("wrapper tests") { - given { createTempDir("gradle2nix") } - - test("resolves gradle wrapper version") { - expectThat(buildKotlin(""" - tasks.withType { - gradleVersion = "5.5.1" - } - """.trimIndent())) { - get("gradle version") { gradle.version }.isEqualTo("5.5.1") - } - } - } -} \ No newline at end of file diff --git a/plugin/src/main/java/org/nixos/gradle2nix/ApiHack.java b/plugin/src/main/java/org/nixos/gradle2nix/ApiHack.java deleted file mode 100644 index eb721d3..0000000 --- a/plugin/src/main/java/org/nixos/gradle2nix/ApiHack.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.nixos.gradle2nix; - -import org.gradle.api.artifacts.Dependency; -import org.gradle.api.internal.artifacts.dependencies.DefaultExternalModuleDependency; - -import javax.annotation.Nullable; - -/** - * Workarounds for APIs improperly marked with @NonNullApi. - */ -interface ApiHack { - static Dependency defaultExternalModuleDependency(String group, String name, @Nullable String version) { - return new DefaultExternalModuleDependency(group, name, version); - } -} diff --git a/plugin/src/main/kotlin/org/nixos/gradle2nix/ConfigurationResolver.kt b/plugin/src/main/kotlin/org/nixos/gradle2nix/ConfigurationResolver.kt deleted file mode 100644 index 039f894..0000000 --- a/plugin/src/main/kotlin/org/nixos/gradle2nix/ConfigurationResolver.kt +++ /dev/null @@ -1,303 +0,0 @@ -package org.nixos.gradle2nix - -import org.apache.ivy.Ivy -import org.apache.ivy.core.settings.IvySettings -import org.apache.ivy.plugins.parser.m2.PomReader -import org.apache.ivy.plugins.parser.xml.XmlModuleDescriptorParser -import org.apache.ivy.plugins.repository.url.URLResource -import org.apache.ivy.plugins.resolver.ChainResolver -import org.gradle.api.Project -import org.gradle.api.artifacts.Configuration -import org.gradle.api.artifacts.ModuleIdentifier -import org.gradle.api.artifacts.ResolvedArtifact -import org.gradle.api.artifacts.component.ComponentArtifactIdentifier -import org.gradle.api.artifacts.component.ModuleComponentIdentifier -import org.gradle.api.artifacts.dsl.DependencyHandler -import org.gradle.api.artifacts.dsl.RepositoryHandler -import org.gradle.api.artifacts.query.ArtifactResolutionQuery -import org.gradle.api.artifacts.result.ResolvedArtifactResult -import org.gradle.api.internal.artifacts.repositories.ResolutionAwareRepository -import org.gradle.ivy.IvyDescriptorArtifact -import org.gradle.ivy.IvyModule -import org.gradle.kotlin.dsl.getArtifacts -import org.gradle.kotlin.dsl.withArtifacts -import org.gradle.maven.MavenModule -import org.gradle.maven.MavenPomArtifact -import org.gradle.util.GradleVersion - -enum class ConfigurationScope { - BUILDSCRIPT, - PLUGIN, - PROJECT, - SETTINGS -} - -internal class ConfigurationResolverFactory( - project: Project, - scope: ConfigurationScope, - repositories: List -) { - private val ivySettings = IvySettings().apply { - defaultInit() - // This doesn't appear to be used, but it's better to define it explicitly than to introduce - // impurities into artifact resolution. - setDefaultRepositoryCacheBasedir(project.buildDir.resolve("tmp/gradle2nix/_cache").absolutePath) - setDictatorResolver(ChainResolver().also { chain -> - chain.settings = this@apply - for (resolver in resolvers) chain.add(resolver) - }) - } - - private val resolvers = repositories - .filterNot { it.createResolver().isLocal } - .mapNotNull { it.repositoryResolver(project, scope, ivySettings) } - - fun create(dependencies: DependencyHandler): ConfigurationResolver = - ConfigurationResolver(ivySettings, resolvers, dependencies) -} - -internal class ConfigurationResolver( - ivySettings: IvySettings, - private val resolvers: List, - private val dependencies: DependencyHandler -) { - private val failed = mutableSetOf() - private val ivy = Ivy.newInstance(ivySettings) - - val unresolved: List = failed.toList() - - fun resolve(configuration: Configuration): List { - val resolved = configuration.resolvedConfiguration.lenientConfiguration - - failed.addAll(resolved.unresolvedModuleDependencies.map { - DefaultArtifactIdentifier( - group = it.selector.group, - name = it.selector.name, - version = it.selector.version ?: "", - type = "module" - ) - }) - - val topLevelMetadata = resolved.firstLevelModuleDependencies - .flatMap { resolveMetadata(it.moduleGroup, it.moduleName, it.moduleVersion) } - - val allArtifacts = resolved.artifacts - .filter { it.id.componentIdentifier is ModuleComponentIdentifier } - .flatMap(::resolve) - - return (topLevelMetadata + allArtifacts).filter { it.urls.isNotEmpty() } - } - - private fun resolve(resolvedArtifact: ResolvedArtifact): List { - val componentId = resolvedArtifact.id.componentIdentifier as ModuleComponentIdentifier - - val artifactId = DefaultArtifactIdentifier( - group = componentId.group, - name = componentId.module, - version = componentId.version, - type = resolvedArtifact.type, - extension = resolvedArtifact.extension, - classifier = resolvedArtifact.classifier - ) - - val sha256 = resolvedArtifact.computeHash() - val artifacts = resolvers.mapNotNull { it.resolve(artifactId, sha256) }.merge() - if (artifacts.isEmpty()) failed.add(artifactId) - return artifacts + componentId.run { resolveMetadata(group, module, version) } - } - - private fun resolveMetadata( - group: String, - name: String, - version: String - ): List { - return resolvePoms(group, name, version) + - resolveDescriptors(group, name, version) + - resolveGradleMetadata(group, name, version) - } - - private fun resolvePoms( - group: String, - name: String, - version: String - ): List { - return dependencies.createArtifactResolutionQuery() - .forModuleCompat(group, name, version) - .withArtifacts(MavenModule::class, MavenPomArtifact::class) - .execute() - .resolvedComponents - .flatMap { it.getArtifacts(MavenPomArtifact::class) } - .filterIsInstance() - .flatMap { it.withParentPoms() } - .flatMap { resolvedPom -> - val componentId = resolvedPom.id.componentIdentifier as ModuleComponentIdentifier - val artifactId = DefaultArtifactIdentifier( - group = componentId.group, - name = componentId.module, - version = componentId.version, - type = "pom" - ) - // Intentionally not computing hash from the cached result; see ResolvedArtifact.computeHash() below. - val artifacts = resolvers.mapNotNull { it.resolve(artifactId) }.merge() - if (artifacts.isEmpty()) failed.add(artifactId) - artifacts - } - } - - private fun resolveDescriptors( - group: String, - name: String, - version: String - ): List { - return dependencies.createArtifactResolutionQuery() - .forModuleCompat(group, name, version) - .withArtifacts(IvyModule::class, IvyDescriptorArtifact::class) - .execute() - .resolvedComponents - .flatMap { it.getArtifacts(IvyDescriptorArtifact::class) } - .filterIsInstance() - .flatMap { it.withParentDescriptors() } - .flatMap { resolvedDesc -> - val componentId = resolvedDesc.id.componentIdentifier as ModuleComponentIdentifier - val artifactId = DefaultArtifactIdentifier( - group = componentId.group, - name = componentId.module, - version = componentId.version, - type = "ivy", - extension = "xml" - ) - // Intentionally not computing hash from the cached result; see ResolvedArtifact.computeHash() below. - val artifacts = resolvers.mapNotNull { it.resolve(artifactId) }.merge() - if (artifacts.isEmpty()) failed.add(artifactId) - artifacts - } - } - - private fun resolveGradleMetadata( - group: String, - name: String, - version: String - ): List { - val artifactId = DefaultArtifactIdentifier( - group = group, - name = name, - version = version, - type = "module" - ) - val artifacts = resolvers.mapNotNull { it.resolve(artifactId) }.merge() - if (artifacts.isEmpty()) failed.add(artifactId) - return artifacts - } - - private fun ResolvedArtifactResult.parentPom(): ResolvedArtifactResult? { - val resource = URLResource(file.toURI().toURL()) - val reader = PomReader(resource.url, resource) - - return if (reader.hasParent()) { - dependencies.createArtifactResolutionQuery() - .forModuleCompat(reader.parentGroupId, reader.parentArtifactId, reader.parentVersion) - .withArtifacts(MavenModule::class, MavenPomArtifact::class) - .execute() - .resolvedComponents - .flatMap { it.getArtifacts(MavenPomArtifact::class) } - .filterIsInstance() - .firstOrNull() - } else { - null - } - } - - private fun ResolvedArtifactResult.withParentPoms(): List = - generateSequence(this) { it.parentPom() }.toList() - - private fun ResolvedArtifactResult.parentDescriptors(seen: Set): List { - val url = file.toURI().toURL() - val parser = XmlModuleDescriptorParser.getInstance() - - val descriptor = parser.parseDescriptor(ivy.settings, url, false) - - return descriptor.inheritedDescriptors.mapNotNull { desc -> - dependencies.createArtifactResolutionQuery() - .forModuleCompat( - desc.parentRevisionId.organisation, - desc.parentRevisionId.name, - desc.parentRevisionId.revision - ) - .withArtifacts(IvyModule::class, IvyDescriptorArtifact::class) - .execute() - .resolvedComponents - .flatMap { it.getArtifacts(IvyDescriptorArtifact::class) } - .filterIsInstance() - .firstOrNull() - }.filter { it.id !in seen } - } - - private fun ResolvedArtifactResult.withParentDescriptors(): List { - val seen = mutableSetOf() - return generateSequence(listOf(this)) { descs -> - val parents = descs.flatMap { it.parentDescriptors(seen) } - seen.addAll(parents.map(ResolvedArtifactResult::getId)) - parents.takeUnless { it.isEmpty() } - }.flatten().distinct().toList() - } -} - -private fun ArtifactResolutionQuery.forModuleCompat( - group: String, - name: String, - version: String -): ArtifactResolutionQuery { - return if (GradleVersion.current() >= GradleVersion.version("4.5")) { - forModule(group, name, version) - } else { - forComponents(ModuleComponentId(group, name, version)) - } -} - -private data class ModuleComponentId( - private val moduleId: ModuleId, - private val version: String -) : ModuleComponentIdentifier { - - constructor( - group: String, - name: String, - version: String - ) : this(ModuleId(group, name), version) - - override fun getGroup(): String = moduleId.group - override fun getModule(): String = moduleId.name - override fun getVersion(): String = version - override fun getModuleIdentifier(): ModuleIdentifier = moduleId - override fun getDisplayName(): String = - arrayOf(group, module, version).joinToString(":") -} - -private data class ModuleId( - private val group: String, - private val name: String -) : ModuleIdentifier { - override fun getGroup(): String = group - override fun getName(): String = name -} - -private fun List.merge(): List { - return groupingBy { it.id } - .reduce { _, dest, next -> dest.copy(urls = dest.urls + next.urls) } - .values.toList() -} - -private fun ResolvedArtifact.computeHash(): String? { - // Hack: Some POM files are served with CRLF line endings, e.g. javax.servlet:javax.servlet-api:3.1.0. - // Gradle stores these normalized with LF line endings, which will not match the eventual hash - // of the fixed-output derivation which produces the POM artifact. - // - // A similar issue can exist for Apache Ivy; see https://issues.apache.org/jira/browse/IVY-1156. - // - // Ignore these artifacts, and defer hash calculation to RepositoryResolver. - if (type == "pom" || type == "ivy") { - return null - } - - return file.sha256() -} \ No newline at end of file diff --git a/plugin/src/main/kotlin/org/nixos/gradle2nix/Gradle2NixPlugin.kt b/plugin/src/main/kotlin/org/nixos/gradle2nix/Gradle2NixPlugin.kt index d0a0b3e..b5a0eac 100644 --- a/plugin/src/main/kotlin/org/nixos/gradle2nix/Gradle2NixPlugin.kt +++ b/plugin/src/main/kotlin/org/nixos/gradle2nix/Gradle2NixPlugin.kt @@ -1,282 +1,23 @@ +@file:Suppress("UnstableApiUsage") + package org.nixos.gradle2nix -import com.squareup.moshi.Moshi import org.gradle.api.Plugin -import org.gradle.api.Project -import org.gradle.api.Task -import org.gradle.api.artifacts.Configuration -import org.gradle.api.artifacts.ProjectDependency -import org.gradle.api.internal.GradleInternal -import org.gradle.api.internal.artifacts.repositories.ResolutionAwareRepository import org.gradle.api.invocation.Gradle -import org.gradle.api.tasks.TaskContainer -import org.gradle.api.tasks.wrapper.Wrapper -import org.gradle.kotlin.dsl.getByName -import org.gradle.kotlin.dsl.newInstance -import org.gradle.kotlin.dsl.support.serviceOf -import org.gradle.kotlin.dsl.withType -import org.gradle.plugin.management.PluginRequest -import org.gradle.tooling.provider.model.ToolingModelBuilder -import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry -import org.gradle.util.GradleVersion -import java.net.URL -import java.util.* +import org.nixos.gradle2nix.dependencygraph.AbstractDependencyExtractorPlugin @Suppress("unused") -open class Gradle2NixPlugin : Plugin { - override fun apply(gradle: Gradle): Unit = gradle.run { - val pluginRequests = collectPlugins() - - projectsLoaded { - val modelProperties = rootProject.loadModelProperties() - rootProject.serviceOf() - .register(NixToolingModelBuilder(modelProperties, pluginRequests)) - - rootProject.tasks.registerCompat("nixModel") { - doLast { - val outFile = project.mkdir(project.buildDir.resolve("nix")).resolve("model.json") - val model = project.buildModel(modelProperties, pluginRequests) - outFile.bufferedWriter().use { out -> - out.write( - Moshi.Builder().build() - .adapter(DefaultBuild::class.java) - .indent(" ") - .toJson(model) - ) - out.flush() - } - } - } +class Gradle2NixPlugin : Plugin { + override fun apply(gradle: Gradle) { + // Only apply the dependency extractor to the root build + if (gradle.parent == null) { + gradle.pluginManager.apply(NixDependencyExtractorPlugin::class.java) } - } -} - -private fun TaskContainer.registerCompat(name: String, configureAction: Task.() -> Unit) { - if (GradleVersion.current() >= GradleVersion.version("4.9")) { - register(name, configureAction) - } else { - create(name, configureAction) - } -} - -private class NixToolingModelBuilder( - private val modelProperties: ModelProperties, - private val pluginRequests: List -) : ToolingModelBuilder { - override fun canBuild(modelName: String): Boolean { - return modelName == NIX_MODEL_NAME + gradle.pluginManager.apply(ForceDependencyResolutionPlugin::class.java) } - override fun buildAll(modelName: String, project: Project): Build = - project.buildModel(modelProperties, pluginRequests) -} - -private fun Gradle.collectPlugins(): List { - val pluginRequests = mutableListOf() - gradle.settingsEvaluated { - pluginManagement.resolutionStrategy.eachPlugin { - if (requested.id.namespace != null && requested.id.namespace != "org.gradle") { - pluginRequests.add(target) - } - } - } - return pluginRequests -} - -private fun Project.buildModel( - modelProperties: ModelProperties, - pluginRequests: List -): DefaultBuild { - val settingsDependencies = settingsDependencies() - val plugins = buildPlugins(pluginRequests) - - val subprojects = if (modelProperties.subprojects.isEmpty()) { - project.subprojects - } else { - project.subprojects - .filter { it.path in modelProperties.subprojects } - // Include dependent subprojects as well - .flatMap { setOf(it) + it.dependentSubprojects(modelProperties.configurations) } - .toSet() - } - - return DefaultBuild( - gradle = buildGradle(), - settingsDependencies = settingsDependencies, - pluginDependencies = plugins, - rootProject = buildProject(modelProperties.configurations, subprojects, plugins), - includedBuilds = includedBuilds() - ) -} - -@Suppress("UnstableApiUsage") -private fun Project.buildGradle(): DefaultGradle = - with(tasks.getByName("wrapper")) { - DefaultGradle( - version = gradleVersion, - type = distributionType.name.toLowerCase(Locale.US), - url = distributionUrl, - sha256 = sha256, - nativeVersion = gradle.gradleHomeDir?.resolve("lib")?.listFiles() - ?.firstOrNull { f -> nativePlatformJarRegex matches f.name }?.let { nf -> - nativePlatformJarRegex.find(nf.name)?.groupValues?.get(1) - } - ?: throw IllegalStateException( - """ - Failed to find native-platform jar in ${gradle.gradleHomeDir}. - Ask Tad to fix this. - """.trimIndent() - ) - ) - } - -private fun Project.settingsDependencies(): List { - val buildscript = (gradle as GradleInternal).settings.buildscript - - val resolverFactory = ConfigurationResolverFactory( - this, - ConfigurationScope.SETTINGS, - buildscript.repositories.filterIsInstance() - ) - val resolver = resolverFactory.create(buildscript.dependencies) - - logger.lifecycle(" Settings script") - - val dependencies = buildscript.configurations - .flatMap(resolver::resolve) - .distinct() - .sorted() - - if (resolver.unresolved.isNotEmpty()) { - logger.warn(buildString { - append(" Failed to resolve settings script dependencies:\n") - for (id in resolver.unresolved) { - append(" - $id\n") - } - }) - } - - return dependencies -} - -private fun Project.buildPlugins(pluginRequests: List): List { - return objects.newInstance(this).resolve(pluginRequests).distinct().sorted() -} - -private fun Project.includedBuilds(): List = - gradle.includedBuilds.map { - DefaultIncludedBuild(it.name, it.projectDir.toRelativeString(project.projectDir)) - } - -private fun Project.buildProject( - explicitConfigurations: List, - explicitSubprojects: Collection, - pluginArtifacts: List -): DefaultProject { - logger.lifecycle(" Subproject: $path") - - val (buildscriptDependencies, buildscriptUnresolved) = buildscriptDependencies(pluginArtifacts) - - if (buildscriptUnresolved.isNotEmpty()) { - logger.warn(buildString { - append(" Failed to resolve buildscript dependencies:\n") - for (id in buildscriptUnresolved) { - append(" - $id\n") - } - }) - } - - val (projectDependencies, projectUnresolved) = projectDependencies(explicitConfigurations) - - if (projectUnresolved.isNotEmpty()) { - logger.warn(buildString { - append(" Failed to resolve project dependencies:\n") - for (id in projectUnresolved) { - append(" - $id\n") - } - }) - } - - return DefaultProject( - name = name, - version = version.toString(), - path = path, - projectDir = projectDir.toRelativeString(rootProject.projectDir), - buildscriptDependencies = buildscriptDependencies, - projectDependencies = projectDependencies, - children = explicitSubprojects.map { - it.buildProject(explicitConfigurations, emptyList(), pluginArtifacts) - } - ) -} - -private fun Project.buildscriptDependencies( - pluginArtifacts: List -): Pair, List> { - val resolverFactory = ConfigurationResolverFactory( - this, - ConfigurationScope.BUILDSCRIPT, - buildscript.repositories.filterIsInstance() - ) - val resolver = resolverFactory.create(buildscript.dependencies) - val pluginIds = pluginArtifacts.map(DefaultArtifact::id) - return buildscript.configurations - .flatMap(resolver::resolve) - .distinct() - .filter { it.id !in pluginIds } - .sorted() to resolver.unresolved -} - -private fun Project.projectDependencies( - explicitConfigurations: List -): Pair, List> { - val resolver = ConfigurationResolverFactory( - this, - ConfigurationScope.PROJECT, - RepositoriesCollector.create(project).collectRepositories() - ).create(dependencies) - return collectConfigurations(explicitConfigurations) - .flatMap(resolver::resolve) - .distinct() - .sorted() to resolver.unresolved -} - -private fun Project.dependentSubprojects(explicitConfigurations: List): Set { - return collectConfigurations(explicitConfigurations) - .flatMap { it.allDependencies.withType() } - .map { it.dependencyProject } - .toSet() - .flatMap { setOf(it) + it.dependentSubprojects(explicitConfigurations) } - .toSet() -} - -private fun Project.collectConfigurations( - explicitConfigurations: List -): Set { - return if (explicitConfigurations.isEmpty()) { - configurations.filter { it.isCanBeResolved }.toSet() - } else { - configurations.filter { it.name in explicitConfigurations }.toSet() + class NixDependencyExtractorPlugin : AbstractDependencyExtractorPlugin() { + override fun getRendererClassName(): String = + NixDependencyGraphRenderer::class.java.name } } - -private fun fetchDistSha256(url: String): String { - return URL("$url.sha256").openConnection().run { - connect() - getInputStream().reader().use { it.readText() } - } -} - -private val nativePlatformJarRegex = Regex("""native-platform-([\d.]+(-(alpha|beta|milestone)-\d+)?)\.jar""") - -private val Wrapper.sha256: String - get() { - return if (GradleVersion.current() < GradleVersion.version("4.5")) { - fetchDistSha256(distributionUrl) - } else { - @Suppress("UnstableApiUsage") - distributionSha256Sum ?: fetchDistSha256(distributionUrl) - } - } - -private const val NIX_MODEL_NAME = "org.nixos.gradle2nix.Build" diff --git a/plugin/src/main/kotlin/org/nixos/gradle2nix/ModelProperties.kt b/plugin/src/main/kotlin/org/nixos/gradle2nix/ModelProperties.kt deleted file mode 100644 index 2d0fad2..0000000 --- a/plugin/src/main/kotlin/org/nixos/gradle2nix/ModelProperties.kt +++ /dev/null @@ -1,20 +0,0 @@ -package org.nixos.gradle2nix - -import org.gradle.api.Project - -data class ModelProperties( - val configurations: List, - val subprojects: List -) - -internal fun Project.loadModelProperties(): ModelProperties { - return ModelProperties( - configurations = this["org.nixos.gradle2nix.configurations"]?.split(",") ?: emptyList(), - subprojects = this["org.nixos.gradle2nix.subprojects"]?.split(",") ?: emptyList() - ) -} - -private operator fun Project.get(key: String): String? { - return System.getProperty(key)?.takeIf { it.isNotEmpty() } - ?: (properties[key] as? String)?.takeIf { it.isNotEmpty() } -} \ No newline at end of file diff --git a/plugin/src/main/kotlin/org/nixos/gradle2nix/NixDependencyGraphRenderer.kt b/plugin/src/main/kotlin/org/nixos/gradle2nix/NixDependencyGraphRenderer.kt new file mode 100644 index 0000000..2f23bbd --- /dev/null +++ b/plugin/src/main/kotlin/org/nixos/gradle2nix/NixDependencyGraphRenderer.kt @@ -0,0 +1,21 @@ +package org.nixos.gradle2nix + +import java.io.File +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.encodeToStream +import org.nixos.gradle2nix.dependencygraph.DependencyGraphRenderer +import org.nixos.gradle2nix.dependencygraph.model.ResolvedConfiguration + +class NixDependencyGraphRenderer : DependencyGraphRenderer { + @OptIn(ExperimentalSerializationApi::class) + override fun outputDependencyGraph( + resolvedConfigurations: List, + outputDirectory: File + ) { + val graphOutputFile = File(outputDirectory, "dependency-graph.json") + graphOutputFile.outputStream().buffered().use { output -> + Json.encodeToStream(resolvedConfigurations, output) + } + } +} diff --git a/plugin/src/main/kotlin/org/nixos/gradle2nix/PluginResolver.kt b/plugin/src/main/kotlin/org/nixos/gradle2nix/PluginResolver.kt deleted file mode 100644 index fc0d8e2..0000000 --- a/plugin/src/main/kotlin/org/nixos/gradle2nix/PluginResolver.kt +++ /dev/null @@ -1,31 +0,0 @@ -package org.nixos.gradle2nix - -import org.gradle.api.Project -import org.gradle.api.internal.artifacts.repositories.ResolutionAwareRepository -import org.gradle.plugin.management.PluginRequest -import org.gradle.plugin.use.internal.PluginDependencyResolutionServices -import javax.inject.Inject - -internal open class PluginResolver @Inject constructor( - project: Project, - pluginDependencyResolutionServices: PluginDependencyResolutionServices -) { - private val configurations = pluginDependencyResolutionServices.configurationContainer - - private val resolver = ConfigurationResolverFactory( - project, - ConfigurationScope.PLUGIN, - pluginDependencyResolutionServices.resolveRepositoryHandler.filterIsInstance() - ).create(pluginDependencyResolutionServices.dependencyHandler) - - fun resolve(pluginRequests: List): List { - val markerDependencies = pluginRequests.map { request -> - request.module?.let { module -> - ApiHack.defaultExternalModuleDependency(module.group, module.name, module.version) - } ?: request.id.id.let { id -> - ApiHack.defaultExternalModuleDependency(id, "$id.gradle.plugin", request.version) - } - } - return resolver.resolve(configurations.detachedConfiguration(*markerDependencies.toTypedArray())) - } -} diff --git a/plugin/src/main/kotlin/org/nixos/gradle2nix/RepositoriesCollector.kt b/plugin/src/main/kotlin/org/nixos/gradle2nix/RepositoriesCollector.kt deleted file mode 100644 index 3b5dc4a..0000000 --- a/plugin/src/main/kotlin/org/nixos/gradle2nix/RepositoriesCollector.kt +++ /dev/null @@ -1,37 +0,0 @@ -package org.nixos.gradle2nix - -import org.gradle.api.internal.artifacts.repositories.ResolutionAwareRepository -import org.gradle.api.Project -import org.gradle.api.artifacts.dsl.RepositoryHandler -import org.gradle.api.initialization.dsl.ScriptHandler -import org.gradle.api.internal.artifacts.RepositoriesSupplier -import org.gradle.kotlin.dsl.newInstance -import org.gradle.util.GradleVersion -import javax.inject.Inject - -interface RepositoriesCollector { - fun collectRepositories(): List - - companion object { - fun create(project: Project): RepositoriesCollector = - if (GradleVersion.current() >= GradleVersion.version("6.8")) { - project.objects.newInstance() - } else { - project.objects.newInstance() - } - } -} - -open class RepositoriesCollectorBase @Inject constructor( - private val repositories: RepositoryHandler -): RepositoriesCollector { - override fun collectRepositories(): List = - repositories.filterIsInstance() -} - -open class RepositoriesCollector68 @Inject constructor( - private val repositoriesSupplier: RepositoriesSupplier -): RepositoriesCollector { - override fun collectRepositories(): List = - repositoriesSupplier.get() -} diff --git a/plugin/src/main/kotlin/org/nixos/gradle2nix/RepositoryResolver.kt b/plugin/src/main/kotlin/org/nixos/gradle2nix/RepositoryResolver.kt deleted file mode 100644 index 33ce076..0000000 --- a/plugin/src/main/kotlin/org/nixos/gradle2nix/RepositoryResolver.kt +++ /dev/null @@ -1,317 +0,0 @@ -package org.nixos.gradle2nix - -import com.amazonaws.auth.BasicAWSCredentials -import com.amazonaws.auth.BasicSessionCredentials -import org.apache.ivy.core.LogOptions -import org.apache.ivy.core.cache.ArtifactOrigin -import org.apache.ivy.core.cache.CacheResourceOptions -import org.apache.ivy.core.cache.DefaultRepositoryCacheManager -import org.apache.ivy.core.cache.RepositoryCacheManager -import org.apache.ivy.core.module.id.ArtifactRevisionId -import org.apache.ivy.core.module.id.ModuleRevisionId -import org.apache.ivy.core.resolve.DownloadOptions -import org.apache.ivy.core.settings.IvySettings -import org.apache.ivy.core.settings.TimeoutConstraint -import org.apache.ivy.plugins.repository.Repository -import org.apache.ivy.plugins.repository.Resource -import org.apache.ivy.plugins.repository.url.URLRepository -import org.apache.ivy.plugins.repository.url.URLResource -import org.apache.ivy.plugins.resolver.AbstractResolver -import org.apache.ivy.plugins.resolver.IBiblioResolver -import org.apache.ivy.plugins.resolver.URLResolver -import org.apache.maven.artifact.repository.metadata.io.xpp3.MetadataXpp3Reader -import org.codehaus.plexus.util.ReaderFactory -import org.codehaus.plexus.util.xml.pull.XmlPullParserException -import org.gradle.api.Project -import org.gradle.api.artifacts.repositories.ArtifactRepository -import org.gradle.api.artifacts.repositories.AuthenticationContainer -import org.gradle.api.artifacts.repositories.AuthenticationSupported -import org.gradle.api.artifacts.repositories.IvyArtifactRepository -import org.gradle.api.artifacts.repositories.MavenArtifactRepository -import org.gradle.api.artifacts.repositories.UrlArtifactRepository -import org.gradle.api.credentials.AwsCredentials -import org.gradle.api.internal.artifacts.repositories.ResolutionAwareRepository -import org.gradle.api.internal.artifacts.repositories.resolver.IvyResolver -import org.gradle.api.logging.Logger -import org.gradle.api.logging.Logging -import org.gradle.authentication.aws.AwsImAuthentication -import org.gradle.internal.authentication.AllSchemesAuthentication -import org.gradle.kotlin.dsl.getCredentials -import java.io.IOException -import java.net.URI -import org.apache.ivy.core.module.descriptor.Artifact as IvyArtifact -import org.apache.ivy.core.module.descriptor.DefaultArtifact as IvyDefaultArtifact -import org.apache.ivy.plugins.resolver.RepositoryResolver as IvyRepositoryResolver - -internal fun ResolutionAwareRepository.repositoryResolver( - project: Project, - scope: ConfigurationScope, - ivySettings: IvySettings -): RepositoryResolver? = - when(this) { - is MavenArtifactRepository -> MavenResolver(project, scope, ivySettings, this) - is IvyArtifactRepository -> IvyResolver(project, scope, ivySettings, this) - else -> null - } - -internal sealed class RepositoryResolver { - companion object { - @JvmStatic - protected val log: Logger = Logging.getLogger("gradle2nix") - } - - abstract val ivyResolver: IvyRepositoryResolver - - abstract fun resolve( - artifactId: DefaultArtifactIdentifier, - sha256: String? = null - ): DefaultArtifact? -} - -internal class MavenResolver( - project: Project, - scope: ConfigurationScope, - ivySettings: IvySettings, - repository: MavenArtifactRepository -) : RepositoryResolver() { - - override val ivyResolver: IBiblioResolver = IBiblioResolver().apply { - name = repository.name - root = repository.url.toString() - isM2compatible = true - settings = ivySettings - setCache(cacheManager(project, scope, ivySettings, repository).name) - setRepository(resolverRepository(repository)) - } - - override fun resolve(artifactId: DefaultArtifactIdentifier, sha256: String?): DefaultArtifact? { - val ivyArtifact: IvyArtifact = artifactId.toArtifact() - val origin = ivyResolver.locate(ivyArtifact) - if (origin == null || !origin.isExists) return null - val hash = if (sha256 != null) sha256 else { - val report = ivyResolver.download(origin, downloadOptions) - report.localFile?.sha256().also { - if (it == null) log.error(report.toString()) - } - } - if (hash == null) { - log.error("Failed to download '$artifactId' from repository '${ivyResolver.repository.name}'") - return null - } - val snapshotVersion: SnapshotVersion? = artifactId.version.snapshotVersion()?.let { - findSnapshotVersion(artifactId, it) - } - return DefaultArtifact( - id = artifactId, - name = artifactId.filename(snapshotVersion), - path = artifactId.repoPath(), - timestamp = snapshotVersion?.timestamp, - build = snapshotVersion?.build, - urls = listOf(origin.location), - sha256 = hash - ) - } - - private fun findSnapshotVersion( - artifactId: ArtifactIdentifier, - snapshotVersion: SnapshotVersion - ): SnapshotVersion { - if (snapshotVersion.timestamp != null) return snapshotVersion - val metadataLocation = "${ivyResolver.root}${artifactId.repoPath()}/maven-metadata.xml" - val metadataFile = ivyResolver.repositoryCacheManager.downloadRepositoryResource( - ivyResolver.repository.getResource(metadataLocation), - "maven-metadata", - "maven-metadata", - "xml", - CacheResourceOptions(), - ivyResolver.repository - ).localFile - - if (metadataFile == null) { - log.warn("maven-metadata.xml not found for snapshot dependency: $artifactId") - return snapshotVersion - } - - fun parseError(e: Throwable): Pair { - log.error("Failed to parse maven-metadata.xml for artifact: $artifactId") - log.error("Error was: ${e.message}", e) - return null to null - } - - val (timestamp: String?, build: Int?) = try { - MetadataXpp3Reader() - .read(ReaderFactory.newXmlReader(metadataFile)) - .versioning?.snapshot?.run { timestamp to buildNumber } - ?: null to null - } catch (e: IOException) { - parseError(e) - } catch (e: XmlPullParserException) { - parseError(e) - } - - return snapshotVersion.copy(timestamp = timestamp, build = build) - } -} - -internal class IvyResolver( - project: Project, - scope: ConfigurationScope, - ivySettings: IvySettings, - repository: IvyArtifactRepository -) : RepositoryResolver() { - - override val ivyResolver: URLResolver = URLResolver().apply { - name = repository.name - val ivyResolver = (repository as ResolutionAwareRepository).createResolver() as IvyResolver - isM2compatible = ivyResolver.isM2compatible - for (p in ivyResolver.ivyPatterns) addIvyPattern(p) - for (p in ivyResolver.artifactPatterns) addArtifactPattern(p) - settings = ivySettings - setCache(cacheManager(project, scope, ivySettings, repository).name) - setRepository(resolverRepository(repository)) - } - - override fun resolve(artifactId: DefaultArtifactIdentifier, sha256: String?): DefaultArtifact? { - val ivyArtifact: IvyArtifact = artifactId.toArtifact() - val origin = ivyResolver.locate(ivyArtifact)?.takeIf(ArtifactOrigin::isExists) ?: return null - val hash = if (sha256 != null) sha256 else { - val report = ivyResolver.download(origin, downloadOptions) - report.localFile?.sha256().also { - if (it == null) log.error(report.toString()) - } - } - if (hash == null) { - log.error("Failed to download '$artifactId' from repository '${ivyResolver.repository.name}'") - return null - } - return DefaultArtifact( - id = DefaultArtifactIdentifier(artifactId), - name = artifactId.filename(null), - path = artifactId.repoPath(), - urls = listOf(origin.location), - sha256 = hash - ) - } -} - -private fun cacheManager( - project: Project, - scope: ConfigurationScope, - ivySettings: IvySettings, - repository: ArtifactRepository -): RepositoryCacheManager { - return DefaultRepositoryCacheManager( - "${scope.name.toLowerCase()}-${repository.name}-cache", - ivySettings, - project.buildDir.resolve("tmp/gradle2nix/${repository.name}").also { - it.mkdirs() - } - ).also { - ivySettings.addRepositoryCacheManager(it) - } -} - -private val metadataTypes = setOf("pom", "ivy") - -private fun ArtifactIdentifier.toArtifact(): IvyArtifact { - val moduleRevisionId = ModuleRevisionId.newInstance(group, name, version) - val artifactRevisionId = ArtifactRevisionId.newInstance( - moduleRevisionId, - name, - type, - extension, - classifier?.let { mapOf("classifier" to it) } - ) - return IvyDefaultArtifact(artifactRevisionId, null, null, type in metadataTypes) -} - -private data class SnapshotVersion( - val base: String, - val timestamp: String?, - val build: Int? -) { - override fun toString(): String { - return if (timestamp != null && build != null) { - "$base-$timestamp-$build" - } else { - "$base-SNAPSHOT" - } - } -} - -private val SNAPSHOT_REGEX = Regex("^(.*)-SNAPSHOT$") -private val SNAPSHOT_TIMESTAMPED_REGEX = Regex("^(.*)-([0-9]{8}.[0-9]{6})-([0-9]+)$") - -private fun String.snapshotVersion(): SnapshotVersion? { - return SNAPSHOT_REGEX.find(this)?.destructured?.let { (base) -> - SnapshotVersion(base, null, null) - } ?: SNAPSHOT_TIMESTAMPED_REGEX.find(this)?.destructured?.let { (base, timestamp, build) -> - SnapshotVersion(base, timestamp, build.toInt()) - } -} - -private fun ArtifactIdentifier.repoPath(): String = - "${group.replace('.', '/')}/$name/$version" - -private fun ArtifactIdentifier.filename( - snapshotVersion: SnapshotVersion? -): String = buildString { - append(name, "-", snapshotVersion ?: version) - if (classifier != null) append("-", classifier) - append(".", extension) -} - -private val downloadOptions = DownloadOptions().apply { - log = LogOptions.LOG_DEFAULT -} - -private fun AbstractResolver.resolverRepository( - repository: T -) : Repository -where T : ArtifactRepository, - T : AuthenticationSupported = - when (val scheme = repository.url.scheme) { - "s3" -> s3Repository( - repository.getCredentials(AwsCredentials::class), - LazyTimeoutConstraint(this) - ) - "http", "https" -> URLRepository(LazyTimeoutConstraint(this)) - else -> throw IllegalStateException("Unknown repository URL scheme: $scheme") - } - -private fun s3Repository( - credentials: AwsCredentials?, - timeoutConstraint: TimeoutConstraint -): Repository { - val awsCredentials = credentials?.let { - if (it.sessionToken == null) { - BasicAWSCredentials(it.accessKey, it.secretKey) - } else { - BasicSessionCredentials(it.accessKey, it.secretKey, it.sessionToken) - } - } - return S3Repository( - credentials = awsCredentials, - endpoint = System.getProperty("org.gradle.s3.endpoint")?.let { URI(it) }, - timeoutConstraint = timeoutConstraint - ).apply { - name = "AWS S3" - } -} - -private class LazyTimeoutConstraint( - private val resolver: AbstractResolver -) : TimeoutConstraint { - override fun getConnectionTimeout(): Int = - resolver.timeoutConstraint?.connectionTimeout ?: -1 - - override fun getReadTimeout(): Int = - resolver.timeoutConstraint?.readTimeout ?: -1 -} - -// Compatibility shim as UrlArtifactRepository was added in Gradle 6.0 -private val ArtifactRepository.url: URI get() = when (this) { - is MavenArtifactRepository -> url - is IvyArtifactRepository -> url - else -> throw IllegalStateException("Unhandled repository type: ${this::class.simpleName}") -} \ No newline at end of file diff --git a/plugin/src/main/kotlin/org/nixos/gradle2nix/Util.kt b/plugin/src/main/kotlin/org/nixos/gradle2nix/Util.kt deleted file mode 100644 index f7f8074..0000000 --- a/plugin/src/main/kotlin/org/nixos/gradle2nix/Util.kt +++ /dev/null @@ -1,24 +0,0 @@ -package org.nixos.gradle2nix - -import java.io.File -import java.net.URI -import java.net.URL -import java.security.MessageDigest - -private const val HEX = "0123456789abcdef" - -internal fun File.sha256(): String = readBytes().sha256() - -private fun ByteArray.sha256() = buildString { - MessageDigest.getInstance("SHA-256").digest(this@sha256) - .asSequence() - .map(Byte::toInt) - .forEach { - append(HEX[it shr 4 and 0x0f]) - append(HEX[it and 0x0f]) - } -} - -internal fun String.toUrl(): URL = URL(this) - -internal fun String.toUri(): URI = URI(this) diff --git a/plugin/src/main/kotlin/org/nixos/gradle2nix/dependencygraph/AbstractDependencyExtractorPlugin.kt b/plugin/src/main/kotlin/org/nixos/gradle2nix/dependencygraph/AbstractDependencyExtractorPlugin.kt new file mode 100644 index 0000000..427b1e7 --- /dev/null +++ b/plugin/src/main/kotlin/org/nixos/gradle2nix/dependencygraph/AbstractDependencyExtractorPlugin.kt @@ -0,0 +1,128 @@ +package org.nixos.gradle2nix.dependencygraph + +import org.gradle.api.Plugin +import org.gradle.api.invocation.Gradle +import org.gradle.api.provider.Provider +import org.gradle.internal.build.event.BuildEventListenerRegistryInternal +import org.gradle.util.GradleVersion +import org.nixos.gradle2nix.dependencygraph.extractor.DependencyExtractor +import org.nixos.gradle2nix.dependencygraph.extractor.DependencyExtractorBuildService +import org.nixos.gradle2nix.dependencygraph.extractor.LegacyDependencyExtractor +import org.nixos.gradle2nix.dependencygraph.util.service + +abstract class AbstractDependencyExtractorPlugin : Plugin { + // Register extension functions on `Gradle` type + private companion object : org.nixos.gradle2nix.dependencygraph.util.GradleExtensions() + + /** + * The name of an accessible class that implements `org.gradle.dependencygraph.DependencyGraphRenderer`. + */ + abstract fun getRendererClassName(): String + + internal lateinit var dependencyExtractorProvider: Provider + + override fun apply(gradle: Gradle) { + val gradleVersion = GradleVersion.current() + // Create the adapter based upon the version of Gradle + val applicatorStrategy = when { + gradleVersion < GradleVersion.version("8.0") -> PluginApplicatorStrategy.LegacyPluginApplicatorStrategy + else -> PluginApplicatorStrategy.DefaultPluginApplicatorStrategy + } + + // Create the service + dependencyExtractorProvider = applicatorStrategy.createExtractorService(gradle, getRendererClassName()) + + gradle.rootProject { project -> + dependencyExtractorProvider + .get() + .rootProjectBuildDirectory = project.buildDir + } + + // Register the service to listen for Build Events + applicatorStrategy.registerExtractorListener(gradle, dependencyExtractorProvider) + + // Register the shutdown hook that should execute at the completion of the Gradle build. + applicatorStrategy.registerExtractorServiceShutdown(gradle, dependencyExtractorProvider) + } + + /** + * Adapters for creating the [DependencyExtractor] and installing it into [Gradle] based upon the Gradle version. + */ + private interface PluginApplicatorStrategy { + + fun createExtractorService( + gradle: Gradle, + rendererClassName: String + ): Provider + + fun registerExtractorListener( + gradle: Gradle, + extractorServiceProvider: Provider + ) + + fun registerExtractorServiceShutdown( + gradle: Gradle, + extractorServiceProvider: Provider + ) + + @Suppress("DEPRECATION") + object LegacyPluginApplicatorStrategy : PluginApplicatorStrategy { + + override fun createExtractorService( + gradle: Gradle, + rendererClassName: String + ): Provider { + val dependencyExtractor = LegacyDependencyExtractor(rendererClassName) + return gradle.providerFactory.provider { dependencyExtractor } + } + + override fun registerExtractorListener( + gradle: Gradle, + extractorServiceProvider: Provider + ) { + gradle.buildOperationListenerManager + .addListener(extractorServiceProvider.get()) + } + + override fun registerExtractorServiceShutdown( + gradle: Gradle, + extractorServiceProvider: Provider + ) { + gradle.buildFinished { + extractorServiceProvider.get().close() + gradle.buildOperationListenerManager + .removeListener(extractorServiceProvider.get()) + } + } + } + + object DefaultPluginApplicatorStrategy : PluginApplicatorStrategy { + private const val SERVICE_NAME = "dependencyExtractorService" + + override fun createExtractorService( + gradle: Gradle, + rendererClassName: String + ): Provider { + return gradle.sharedServices.registerIfAbsent( + SERVICE_NAME, + DependencyExtractorBuildService::class.java + ) { it.parameters.rendererClassName.set(rendererClassName) } + } + + override fun registerExtractorListener( + gradle: Gradle, + extractorServiceProvider: Provider + ) { + gradle.service() + .onOperationCompletion(extractorServiceProvider) + } + + override fun registerExtractorServiceShutdown( + gradle: Gradle, + extractorServiceProvider: Provider + ) { + // No-op as DependencyExtractorService is Auto-Closable + } + } + } +} diff --git a/plugin/src/main/kotlin/org/nixos/gradle2nix/dependencygraph/DependencyGraphRenderer.kt b/plugin/src/main/kotlin/org/nixos/gradle2nix/dependencygraph/DependencyGraphRenderer.kt new file mode 100644 index 0000000..9b786af --- /dev/null +++ b/plugin/src/main/kotlin/org/nixos/gradle2nix/dependencygraph/DependencyGraphRenderer.kt @@ -0,0 +1,11 @@ +package org.nixos.gradle2nix.dependencygraph + +import java.io.File +import org.nixos.gradle2nix.dependencygraph.model.ResolvedConfiguration + +interface DependencyGraphRenderer { + fun outputDependencyGraph( + resolvedConfigurations: List, + outputDirectory: File + ) +} diff --git a/plugin/src/main/kotlin/org/nixos/gradle2nix/dependencygraph/extractor/DependencyExtractor.kt b/plugin/src/main/kotlin/org/nixos/gradle2nix/dependencygraph/extractor/DependencyExtractor.kt new file mode 100644 index 0000000..c9010c4 --- /dev/null +++ b/plugin/src/main/kotlin/org/nixos/gradle2nix/dependencygraph/extractor/DependencyExtractor.kt @@ -0,0 +1,342 @@ +package org.nixos.gradle2nix.dependencygraph.extractor + +import java.io.File +import java.net.URI +import java.util.Collections +import org.gradle.api.GradleException +import org.gradle.api.artifacts.result.ResolvedComponentResult +import org.gradle.api.artifacts.result.ResolvedDependencyResult +import org.gradle.api.internal.artifacts.DefaultProjectComponentIdentifier +import org.gradle.api.internal.artifacts.configurations.ResolveConfigurationDependenciesBuildOperationType +import org.gradle.api.logging.Logging +import org.gradle.internal.exceptions.DefaultMultiCauseException +import org.gradle.internal.operations.BuildOperationDescriptor +import org.gradle.internal.operations.BuildOperationListener +import org.gradle.internal.operations.OperationFinishEvent +import org.gradle.internal.operations.OperationIdentifier +import org.gradle.internal.operations.OperationProgressEvent +import org.gradle.internal.operations.OperationStartEvent +import org.nixos.gradle2nix.PARAM_INCLUDE_CONFIGURATIONS +import org.nixos.gradle2nix.PARAM_INCLUDE_PROJECTS +import org.nixos.gradle2nix.PARAM_REPORT_DIR +import org.nixos.gradle2nix.dependencygraph.DependencyGraphRenderer +import org.nixos.gradle2nix.dependencygraph.model.DependencyCoordinates +import org.nixos.gradle2nix.dependencygraph.model.DependencySource +import org.nixos.gradle2nix.dependencygraph.model.Repository +import org.nixos.gradle2nix.dependencygraph.model.ResolvedConfiguration +import org.nixos.gradle2nix.dependencygraph.model.ResolvedDependency +import org.nixos.gradle2nix.dependencygraph.util.loadOptionalParam + +abstract class DependencyExtractor : + BuildOperationListener, + AutoCloseable { + + private val resolvedConfigurations = Collections.synchronizedList(mutableListOf()) + + private val thrownExceptions = Collections.synchronizedList(mutableListOf()) + + var rootProjectBuildDirectory: File? = null + + // Properties are lazily initialized so that System Properties are initialized by the time + // the values are used. This is required due to a bug in older Gradle versions. (https://github.com/gradle/gradle/issues/6825) + private val configurationFilter by lazy { + ResolvedConfigurationFilter( + loadOptionalParam(PARAM_INCLUDE_PROJECTS), + loadOptionalParam(PARAM_INCLUDE_CONFIGURATIONS) + ) + } + + private val dependencyGraphReportDir by lazy { + loadOptionalParam(PARAM_REPORT_DIR) + } + + abstract fun getRendererClassName(): String + + override fun started(buildOperation: BuildOperationDescriptor, startEvent: OperationStartEvent) { + // This method will never be called when registered in a `BuildServiceRegistry` (ie. Gradle 6.1 & higher) + // No-op + } + + override fun progress(operationIdentifier: OperationIdentifier, progressEvent: OperationProgressEvent) { + // This method will never be called when registered in a `BuildServiceRegistry` (ie. Gradle 6.1 & higher) + // No-op + } + + override fun finished(buildOperation: BuildOperationDescriptor, finishEvent: OperationFinishEvent) { + handleBuildOperationType< + ResolveConfigurationDependenciesBuildOperationType.Details, + ResolveConfigurationDependenciesBuildOperationType.Result + >(buildOperation, finishEvent) { details, result -> extractConfigurationDependencies(details, result) } + } + + private inline fun handleBuildOperationType( + buildOperation: BuildOperationDescriptor, + finishEvent: OperationFinishEvent, + handler: (details: D, result: R) -> Unit + ) { + try { + handleBuildOperationTypeRaw(buildOperation, finishEvent, handler) + } catch (e: Throwable) { + thrownExceptions.add(e) + throw e + } + } + + private inline fun handleBuildOperationTypeRaw( + buildOperation: BuildOperationDescriptor, + finishEvent: OperationFinishEvent, + handler: (details: D, result: R) -> Unit + ) { + val details: D? = buildOperation.details.let { + if (it is D) it else null + } + val result: R? = finishEvent.result.let { + if (it is R) it else null + } + if (details == null && result == null) { + return + } else if (details == null || result == null) { + throw IllegalStateException("buildOperation.details & finishedEvent.result were unexpected types") + } + handler(details, result) + } + + private fun extractConfigurationDependencies( + details: ResolveConfigurationDependenciesBuildOperationType.Details, + result: ResolveConfigurationDependenciesBuildOperationType.Result + ) { + val repositories = details.repositories?.mapNotNull { + @Suppress("UNCHECKED_CAST") + Repository( + id = it.id, + type = enumValueOf(it.type), + name = it.name, + m2Compatible = it.type == "MAVEN" || (it.properties["M2_COMPATIBLE"] as? Boolean) ?: false, + metadataSources = (it.properties["METADATA_SOURCES"] as? List) ?: emptyList(), + metadataResources = metadataResources(it), + artifactResources = artifactResources(it), + ) + } ?: emptyList() + val rootComponent = result.rootComponent + + if (rootComponent.dependencies.isEmpty()) { + // No dependencies to extract: can safely ignore + return + } + val projectIdentityPath = (rootComponent.id as? DefaultProjectComponentIdentifier)?.identityPath?.path + + // TODO: At this point, any resolution not bound to a particular project will be assigned to the root "build :" + // This is because `details.buildPath` is always ':', which isn't correct in a composite build. + // It is possible to do better. By tracking the current build operation context, we can assign more precisely. + // See the Gradle Enterprise Build Scan Plugin: `ConfigurationResolutionCapturer_5_0` + val rootPath = projectIdentityPath ?: details.buildPath + + if (!configurationFilter.include(rootPath, details.configurationName)) { + LOGGER.debug("Ignoring resolved configuration: $rootPath - ${details.configurationName}") + return + } + + val rootId = if (projectIdentityPath == null) "build $rootPath" else componentId(rootComponent) + val rootSource = DependencySource(rootId, rootPath) + val resolvedConfiguration = ResolvedConfiguration(rootSource, details.configurationName, repositories) + + for (directDependency in getResolvedDependencies(rootComponent)) { + val directDep = createComponentNode( + componentId(directDependency), + rootSource, + true, + directDependency, + result.getRepositoryId(directDependency) + ) + resolvedConfiguration.addDependency(directDep) + + walkComponentDependencies(result, directDependency, directDep.source, resolvedConfiguration) + } + + resolvedConfigurations.add(resolvedConfiguration) + } + + private fun walkComponentDependencies( + result: ResolveConfigurationDependenciesBuildOperationType.Result, + component: ResolvedComponentResult, + parentSource: DependencySource, + resolvedConfiguration: ResolvedConfiguration + ) { + val componentSource = getSource(component, parentSource) + val direct = componentSource != parentSource + + val dependencyComponents = getResolvedDependencies(component) + for (dependencyComponent in dependencyComponents) { + val dependencyId = componentId(dependencyComponent) + if (!resolvedConfiguration.hasDependency(dependencyId)) { + val dependencyNode = createComponentNode( + dependencyId, + componentSource, + direct, + dependencyComponent, + result.getRepositoryId(component) + ) + resolvedConfiguration.addDependency(dependencyNode) + + walkComponentDependencies(result, dependencyComponent, componentSource, resolvedConfiguration) + } + } + } + + private fun getSource(component: ResolvedComponentResult, source: DependencySource): DependencySource { + val componentId = component.id + if (componentId is DefaultProjectComponentIdentifier) { + return DependencySource(componentId(component), componentId.identityPath.path) + } + return source + } + + private fun getResolvedDependencies(component: ResolvedComponentResult): List { + return component.dependencies.filterIsInstance().map { it.selected }.filter { it != component } + } + + private fun createComponentNode(componentId: String, source: DependencySource, direct: Boolean, component: ResolvedComponentResult, repositoryId: String?): ResolvedDependency { + val componentDependencies = component.dependencies.filterIsInstance().map { componentId(it.selected) } + return ResolvedDependency( + componentId, + source, + direct, + coordinates(component), + repositoryId, + componentDependencies + ) + } + + private fun componentId(component: ResolvedComponentResult): String { + return component.id.displayName + } + + private fun coordinates(component: ResolvedComponentResult): DependencyCoordinates { + // TODO: Consider and handle null moduleVersion + val moduleVersionIdentifier = component.moduleVersion!! + return DependencyCoordinates( + moduleVersionIdentifier.group, + moduleVersionIdentifier.name, + moduleVersionIdentifier.version + ) + } + + private fun writeDependencyGraph() { + val outputDirectory = getOutputDir() + outputDirectory.mkdirs() + createRenderer().outputDependencyGraph(resolvedConfigurations, outputDirectory) + LOGGER.info("Wrote dependency graph to ${getOutputDir()}") + } + + private fun createRenderer(): DependencyGraphRenderer { + LOGGER.info("Constructing renderer: ${getRendererClassName()}") + return Class.forName(getRendererClassName()).getDeclaredConstructor().newInstance() as DependencyGraphRenderer + } + + private fun getOutputDir(): File { + if (dependencyGraphReportDir != null) { + return File(dependencyGraphReportDir!!) + } + + if (rootProjectBuildDirectory == null) { + throw RuntimeException("Cannot determine report file location") + } + return File( + rootProjectBuildDirectory, + "reports/nix-dependency-graph" + ) + } + + override fun close() { + if (thrownExceptions.isNotEmpty()) { + throw DefaultMultiCauseException( + "The Gradle2Nix plugin encountered errors while extracting dependencies. " + + "Please report this issue at: https://github.com/tadfisher/gradle2nix/issues", + thrownExceptions + ) + } + try { + writeDependencyGraph() + } catch (e: RuntimeException) { + throw GradleException( + "The Gradle2Nix plugin encountered errors while writing the dependency snapshot json file. " + + "Please report this issue at: https://github.com/tadfisher/gradle2nix/issues", + e + ) + } + } + + companion object { + private val LOGGER = Logging.getLogger(DependencyExtractor::class.java) + + private const val M2_PATTERN = + "[organisation]/[module]/[revision]/[artifact]-[revision](-[classifier])(.[ext])" + + private const val IVY_ARTIFACT_PATTERN = "[organisation]/[module]/[revision]/[type]s/[artifact](.[ext])"; + + private fun resources(urls: List, patterns: List): List { + if (urls.isEmpty()) { + return patterns + } + if (patterns.isEmpty()) { + return urls.map { it.toString() } + } + return mutableListOf().apply { + for (pattern in patterns) { + for (url in urls) { + add( + url.toString() + .removeSuffix("/") + .plus("/") + .plus(pattern.removePrefix("/")) + ) + } + } + } + } + + private fun metadataResources( + repository: ResolveConfigurationDependenciesBuildOperationType.Repository + ): List { + return when (repository.type) { + Repository.Type.MAVEN.name -> { + resources( + listOfNotNull(repository.properties["URL"] as? URI), + listOf(M2_PATTERN) + ) + } + Repository.Type.IVY.name -> { + @Suppress("UNCHECKED_CAST") + resources( + listOfNotNull(repository.properties["URL"] as? URI), + repository.properties["IVY_PATTERNS"] as? List ?: listOf(IVY_ARTIFACT_PATTERN) + ) + } + else -> emptyList() + } + } + + private fun artifactResources( + repository: ResolveConfigurationDependenciesBuildOperationType.Repository + ): List { + return when (repository.type) { + Repository.Type.MAVEN.name -> { + @Suppress("UNCHECKED_CAST") + resources( + listOfNotNull(repository.properties["URL"] as? URI) + .plus(repository.properties["ARTIFACT_URLS"] as? List ?: emptyList()), + listOf(M2_PATTERN) + ) + } + Repository.Type.IVY.name -> { + @Suppress("UNCHECKED_CAST") + resources( + listOfNotNull(repository.properties["URL"] as? URI), + repository.properties["ARTIFACT_PATTERNS"] as? List ?: listOf(IVY_ARTIFACT_PATTERN) + ) + } + else -> emptyList() + } + } + } +} diff --git a/plugin/src/main/kotlin/org/nixos/gradle2nix/dependencygraph/extractor/DependencyExtractorBuildService.kt b/plugin/src/main/kotlin/org/nixos/gradle2nix/dependencygraph/extractor/DependencyExtractorBuildService.kt new file mode 100644 index 0000000..5f3c7bd --- /dev/null +++ b/plugin/src/main/kotlin/org/nixos/gradle2nix/dependencygraph/extractor/DependencyExtractorBuildService.kt @@ -0,0 +1,19 @@ +package org.nixos.gradle2nix.dependencygraph.extractor + +import org.gradle.api.provider.Property +import org.gradle.api.services.BuildService +import org.gradle.api.services.BuildServiceParameters + +abstract class DependencyExtractorBuildService : + DependencyExtractor(), + BuildService +{ + // Some parameters for the web server + internal interface Params : BuildServiceParameters { + val rendererClassName: Property + } + + override fun getRendererClassName(): String { + return parameters.rendererClassName.get() + } +} diff --git a/plugin/src/main/kotlin/org/nixos/gradle2nix/dependencygraph/extractor/LegacyDependencyExtractor.kt b/plugin/src/main/kotlin/org/nixos/gradle2nix/dependencygraph/extractor/LegacyDependencyExtractor.kt new file mode 100644 index 0000000..92dd5ea --- /dev/null +++ b/plugin/src/main/kotlin/org/nixos/gradle2nix/dependencygraph/extractor/LegacyDependencyExtractor.kt @@ -0,0 +1,10 @@ +package org.nixos.gradle2nix.dependencygraph.extractor + +open class LegacyDependencyExtractor( + private val rendererClassName: String +) : DependencyExtractor() { + + override fun getRendererClassName(): String { + return rendererClassName + } +} diff --git a/plugin/src/main/kotlin/org/nixos/gradle2nix/dependencygraph/extractor/ResolvedConfigurationFilter.kt b/plugin/src/main/kotlin/org/nixos/gradle2nix/dependencygraph/extractor/ResolvedConfigurationFilter.kt new file mode 100644 index 0000000..5cbd4af --- /dev/null +++ b/plugin/src/main/kotlin/org/nixos/gradle2nix/dependencygraph/extractor/ResolvedConfigurationFilter.kt @@ -0,0 +1,16 @@ +package org.nixos.gradle2nix.dependencygraph.extractor + +class ResolvedConfigurationFilter(projectFilter: String?, configurationFilter: String?) { + val projectRegex = projectFilter?.toRegex() + val configurationRegex = configurationFilter?.toRegex() + + fun include(projectPath: String, configurationName: String): Boolean { + if (projectRegex != null && !projectRegex.matches(projectPath)) { + return false + } + if (configurationRegex != null && !configurationRegex.matches(configurationName)) { + return false + } + return true + } +} diff --git a/plugin/src/main/kotlin/org/nixos/gradle2nix/dependencygraph/util/GradleExtensions.kt b/plugin/src/main/kotlin/org/nixos/gradle2nix/dependencygraph/util/GradleExtensions.kt new file mode 100644 index 0000000..b45d034 --- /dev/null +++ b/plugin/src/main/kotlin/org/nixos/gradle2nix/dependencygraph/util/GradleExtensions.kt @@ -0,0 +1,19 @@ +package org.nixos.gradle2nix.dependencygraph.util + +import org.gradle.api.internal.GradleInternal +import org.gradle.api.invocation.Gradle +import org.gradle.api.model.ObjectFactory +import org.gradle.api.provider.ProviderFactory +import org.gradle.internal.operations.BuildOperationListenerManager + +internal abstract class GradleExtensions { + + inline val Gradle.providerFactory: ProviderFactory + get() = service() + + inline val Gradle.buildOperationListenerManager: BuildOperationListenerManager + get() = service() +} + +internal inline fun Gradle.service(): T = + (this as GradleInternal).services.get(T::class.java) diff --git a/plugin/src/main/kotlin/org/nixos/gradle2nix/dependencygraph/util/PluginParameters.kt b/plugin/src/main/kotlin/org/nixos/gradle2nix/dependencygraph/util/PluginParameters.kt new file mode 100644 index 0000000..a889f56 --- /dev/null +++ b/plugin/src/main/kotlin/org/nixos/gradle2nix/dependencygraph/util/PluginParameters.kt @@ -0,0 +1,6 @@ +package org.nixos.gradle2nix.dependencygraph.util + +internal fun loadOptionalParam(envName: String): String? { + return System.getProperty(envName) + ?: System.getenv()[envName] +} diff --git a/plugin/src/main/kotlin/org/nixos/gradle2nix/forceresolve/ForceDependencyResolutionPlugin.kt b/plugin/src/main/kotlin/org/nixos/gradle2nix/forceresolve/ForceDependencyResolutionPlugin.kt new file mode 100644 index 0000000..e5ade2e --- /dev/null +++ b/plugin/src/main/kotlin/org/nixos/gradle2nix/forceresolve/ForceDependencyResolutionPlugin.kt @@ -0,0 +1,65 @@ +package org.nixos.gradle2nix + +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.Task +import org.gradle.api.invocation.Gradle +import org.gradle.api.tasks.TaskProvider +import org.gradle.util.GradleVersion +import org.nixos.gradle2nix.forceresolve.LegacyResolveProjectDependenciesTask +import org.nixos.gradle2nix.forceresolve.ResolveProjectDependenciesTask + +// TODO: Rename these + +/** + * Adds a task to resolve all dependencies in a Gradle build tree. + */ +class ForceDependencyResolutionPlugin : Plugin { + override fun apply(gradle: Gradle) { + gradle.projectsEvaluated { + val resolveAllDeps = gradle.rootProject.tasks.register(RESOLVE_ALL_TASK) + + // Depend on "dependencies" task in all projects + gradle.allprojects { project -> + val projectTaskFactory = getResolveProjectDependenciesTaskFactory() + val resolveProjectDeps = projectTaskFactory.create(project) + resolveAllDeps.configure { + it.dependsOn(resolveProjectDeps) + } + } + + // Depend on all 'resolveBuildDependencies' task in each included build + gradle.includedBuilds.forEach { includedBuild -> + resolveAllDeps.configure { + it.dependsOn(includedBuild.task(":$RESOLVE_ALL_TASK")) + } + } + } + } + + private fun getResolveProjectDependenciesTaskFactory(): ResolveProjectDependenciesTaskFactory { + val gradleVersion = GradleVersion.current() + val gradle8 = GradleVersion.version("8.0") + return if (gradleVersion >= gradle8) { + ResolveProjectDependenciesTaskFactory.Current + } else { + ResolveProjectDependenciesTaskFactory.Legacy + } + } + + private sealed interface ResolveProjectDependenciesTaskFactory { + fun create(project: Project): TaskProvider + + data object Current : ResolveProjectDependenciesTaskFactory { + override fun create(project: Project): TaskProvider { + return project.tasks.register(RESOLVE_PROJECT_TASK, ResolveProjectDependenciesTask::class.java) + } + } + + data object Legacy : ResolveProjectDependenciesTaskFactory { + override fun create(project: Project): TaskProvider { + return project.tasks.register(RESOLVE_PROJECT_TASK, LegacyResolveProjectDependenciesTask::class.java) + } + } + } +} diff --git a/plugin/src/main/kotlin/org/nixos/gradle2nix/forceresolve/LegacyResolveProjectDependenciesTask.kt b/plugin/src/main/kotlin/org/nixos/gradle2nix/forceresolve/LegacyResolveProjectDependenciesTask.kt new file mode 100644 index 0000000..cae57d0 --- /dev/null +++ b/plugin/src/main/kotlin/org/nixos/gradle2nix/forceresolve/LegacyResolveProjectDependenciesTask.kt @@ -0,0 +1,20 @@ +package org.nixos.gradle2nix.forceresolve + +import org.gradle.api.DefaultTask +import org.gradle.api.artifacts.Configuration +import org.gradle.api.tasks.TaskAction +import org.gradle.work.DisableCachingByDefault + +@DisableCachingByDefault(because = "Not worth caching") +abstract class LegacyResolveProjectDependenciesTask: DefaultTask() { + private fun getReportableConfigurations(): List { + return project.configurations.filter { it.isCanBeResolved } + } + + @TaskAction + fun action() { + for (configuration in getReportableConfigurations()) { + configuration.incoming.resolutionResult.root + } + } +} diff --git a/plugin/src/main/kotlin/org/nixos/gradle2nix/forceresolve/ResolveProjectDependenciesTask.kt b/plugin/src/main/kotlin/org/nixos/gradle2nix/forceresolve/ResolveProjectDependenciesTask.kt new file mode 100644 index 0000000..5747b21 --- /dev/null +++ b/plugin/src/main/kotlin/org/nixos/gradle2nix/forceresolve/ResolveProjectDependenciesTask.kt @@ -0,0 +1,31 @@ +package org.nixos.gradle2nix.forceresolve + +import org.gradle.api.DefaultTask +import org.gradle.api.artifacts.Configuration +import org.gradle.api.artifacts.result.ResolvedComponentResult +import org.gradle.api.provider.Provider +import org.gradle.api.tasks.TaskAction +import org.gradle.internal.serialization.Cached +import org.gradle.work.DisableCachingByDefault + +@DisableCachingByDefault(because = "Not worth caching") +abstract class ResolveProjectDependenciesTask: DefaultTask() { + private val configurationResolvers = Cached.of { createConfigurationResolvers() } + + private fun createConfigurationResolvers(): List> { + return getReportableConfigurations().map { + it.incoming.resolutionResult.rootComponent + } + } + + private fun getReportableConfigurations(): List { + return project.configurations.filter { it.isCanBeResolved } + } + + @TaskAction + fun action() { + for (configuration in configurationResolvers.get()) { + configuration.get() + } + } +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 4376732..dd7323f 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,44 +1,16 @@ @file:Suppress("UnstableApiUsage") -enableFeaturePreview("ONE_LOCKFILE_PER_PROJECT") - -pluginManagement { - val shadowVersion: String by settings - val stutterVersion: String by settings - - resolutionStrategy { - eachPlugin { - when (requested.id.id) { - "com.github.johnrengelman.shadow" -> useVersion(shadowVersion) - "org.ajoberstar.stutter" -> useVersion(stutterVersion) - } - when (requested.id.namespace) { - "org.jetbrains.kotlin", - "org.jetbrains.kotlin.plugin" -> useVersion(embeddedKotlinVersion) - } - } - } -} - -plugins { - kotlin("jvm") apply false - kotlin("kapt") apply false - id("com.github.johnrengelman.shadow") apply false - id("org.ajoberstar.stutter") apply false -} - dependencyResolutionManagement { repositories { - jcenter() + mavenCentral() + gradlePluginPortal() maven { url = uri("https://repo.gradle.org/gradle/libs-releases") } } - repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS) + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) } -buildscript { - configurations.classpath { - resolutionStrategy.activateDependencyLocking() - } -} - -include(":app", ":ivy", ":model", ":plugin") +include( + ":app", + ":model", + ":plugin", +)