diff options
author | Bjørn Christian Seime <bjorncs@yahooinc.com> | 2023-09-27 16:45:35 +0200 |
---|---|---|
committer | Bjørn Christian Seime <bjorncs@yahooinc.com> | 2023-09-27 16:45:35 +0200 |
commit | cd633c8cadc784bc28b5c080c7bfd7756f8fa8ff (patch) | |
tree | 6a7e5e04285c69d67237657165a5a74136ef8124 | |
parent | 22c0ed22e2db624475a62f5c3bba6559f7a06e5d (diff) |
Allow properties in rule set
9 files changed, 541 insertions, 690 deletions
diff --git a/maven-plugins/allowed-maven-dependencies.txt b/maven-plugins/allowed-maven-dependencies.txt index 6853632ea40..06f2f34964b 100644 --- a/maven-plugins/allowed-maven-dependencies.txt +++ b/maven-plugins/allowed-maven-dependencies.txt @@ -1,41 +1,31 @@ # Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -#[non-test] -# Contains dependencies that are not used exclusively in 'test' scope -aopalliance:aopalliance:1.0 -com.fasterxml.jackson.core:jackson-annotations:2.15.2 -com.fasterxml.jackson.core:jackson-core:2.15.2 -com.fasterxml.jackson.core:jackson-databind:2.15.2 -com.github.luben:zstd-jni:1.5.5-5 -com.google.errorprone:error_prone_annotations:2.22.0 +aopalliance:aopalliance:${aopalliance.vespa.version} +com.fasterxml.jackson.core:jackson-annotations:${jackson2.vespa.version} +com.fasterxml.jackson.core:jackson-core:${jackson2.vespa.version} +com.fasterxml.jackson.core:jackson-databind:${jackson-databind.vespa.version} +com.github.luben:zstd-jni:${luben.zstd.vespa.version} +com.google.errorprone:error_prone_annotations:${error-prone-annotations.vespa.version} com.google.guava:failureaccess:1.0.1 -com.google.guava:guava:32.1.2-jre -com.google.inject:guice:6.0.0 +com.google.guava:guava:${guava.vespa.version} +com.google.inject:guice:${guice.vespa.version} com.google.j2objc:j2objc-annotations:2.8 -commons-codec:commons-codec:1.16.0 -commons-io:commons-io:2.13.0 -jakarta.inject:jakarta.inject-api:2.0.1 -javax.annotation:javax.annotation-api:1.2 -javax.inject:javax.inject:1 +commons-codec:commons-codec:${commons-codec.vespa.version} +commons-io:commons-io:${commons-io.vespa.version} +jakarta.inject:jakarta.inject-api:${jakarta.inject.vespa.version} +javax.annotation:javax.annotation-api:${commons-logging.vespa.version} +javax.inject:javax.inject:${javax.inject.vespa.version} +junit:junit:${junit4.vespa.version} +net.bytebuddy:byte-buddy-agent:${byte-buddy.vespa.version} +net.bytebuddy:byte-buddy:${byte-buddy.vespa.version} org.apache-extras.beanshell:bsh:2.0b6 org.apache.commons:commons-collections4:4.4 -org.apache.commons:commons-compress:1.24.0 -org.apache.commons:commons-lang3:3.13.0 -org.apache.maven:maven-archiver:3.6.1 -org.apache.maven:maven-artifact:3.9.4 -org.apache.maven:maven-builder-support:3.9.4 -org.apache.maven:maven-core:3.9.4 -org.apache.maven:maven-model:3.9.4 -org.apache.maven:maven-model-builder:3.9.4 -org.apache.maven:maven-plugin-api:3.9.4 -org.apache.maven:maven-repository-metadata:3.9.4 -org.apache.maven:maven-resolver-provider:3.9.4 -org.apache.maven:maven-settings:3.9.4 -org.apache.maven:maven-settings-builder:3.9.4 -org.apache.maven.enforcer:enforcer-api:3.4.1 -org.apache.maven.enforcer:enforcer-rules:3.4.1 -org.apache.maven.plugin-tools:maven-plugin-annotations:3.9.0 -org.apache.maven.plugins:maven-shade-plugin:3.5.1 +org.apache.commons:commons-compress:${commons-compress.vespa.version} +org.apache.commons:commons-lang3:${commons-lang3.vespa.version} +org.apache.maven.enforcer:enforcer-api:${maven-enforcer-plugin.vespa.version} +org.apache.maven.enforcer:enforcer-rules:${maven-enforcer-plugin.vespa.version} +org.apache.maven.plugin-tools:maven-plugin-annotations:${maven-plugin-tools.vespa.version} +org.apache.maven.plugins:maven-shade-plugin:${maven-shade-plugin.vespa.version} org.apache.maven.resolver:maven-resolver-api:1.9.14 org.apache.maven.resolver:maven-resolver-impl:1.9.14 org.apache.maven.resolver:maven-resolver-named-locks:1.9.14 @@ -43,42 +33,47 @@ org.apache.maven.resolver:maven-resolver-spi:1.9.14 org.apache.maven.resolver:maven-resolver-util:1.9.14 org.apache.maven.shared:maven-dependency-tree:3.2.1 org.apache.maven.shared:maven-shared-utils:3.3.4 +org.apache.maven:maven-archiver:${maven-archiver.vespa.version} +org.apache.maven:maven-artifact:${maven-core.vespa.version} +org.apache.maven:maven-builder-support:${maven-core.vespa.version} +org.apache.maven:maven-core:${maven-core.vespa.version} +org.apache.maven:maven-model-builder:${maven-core.vespa.version} +org.apache.maven:maven-model:${maven-core.vespa.version} +org.apache.maven:maven-plugin-api:${maven-plugin-api.vespa.version} +org.apache.maven:maven-repository-metadata:${maven-core.vespa.version} +org.apache.maven:maven-resolver-provider:${maven-core.vespa.version} +org.apache.maven:maven-settings-builder:${maven-core.vespa.version} +org.apache.maven:maven-settings:${maven-core.vespa.version} +org.apiguardian:apiguardian-api:${apiguardian.vespa.version} org.codehaus.plexus:plexus-archiver:4.8.0 org.codehaus.plexus:plexus-cipher:2.0 org.codehaus.plexus:plexus-classworlds:2.7.0 org.codehaus.plexus:plexus-component-annotations:2.1.0 org.codehaus.plexus:plexus-interpolation:1.26 -org.codehaus.plexus:plexus-io:3.4.1 +org.codehaus.plexus:plexus-io:${maven-enforcer-plugin.vespa.version} org.codehaus.plexus:plexus-sec-dispatcher:2.0 -org.codehaus.plexus:plexus-utils:3.5.1 +org.codehaus.plexus:plexus-utils:${maven-shade-plugin.vespa.version} org.eclipse.aether:aether-api:1.0.0.v20140518 org.eclipse.aether:aether-util:1.0.0.v20140518 org.eclipse.sisu:org.eclipse.sisu.inject:0.3.5 org.eclipse.sisu:org.eclipse.sisu.plexus:0.3.5 +org.hamcrest:hamcrest-core:${hamcrest.vespa.version} +org.hamcrest:hamcrest:${hamcrest.vespa.version} org.iq80.snappy:snappy:0.4 org.jdom:jdom2:2.0.6.1 -org.ow2.asm:asm:9.5 -org.ow2.asm:asm-commons:9.5 -org.ow2.asm:asm-tree:9.5 -org.slf4j:slf4j-api:1.7.36 +org.junit.jupiter:junit-jupiter-api:${junit.vespa.version} +org.junit.jupiter:junit-jupiter-engine:${junit.vespa.version} +org.junit.jupiter:junit-jupiter-params:${junit.vespa.version} +org.junit.jupiter:junit-jupiter:${junit.vespa.version} +org.junit.platform:junit-platform-commons:${junit.platform.vespa.version} +org.junit.platform:junit-platform-engine:${junit.platform.vespa.version} +org.mockito:mockito-core:${mockito.vespa.version} +org.objenesis:objenesis:3.3 +org.opentest4j:opentest4j:${opentest4j.vespa.version} +org.ow2.asm:asm-commons:${asm.vespa.version} +org.ow2.asm:asm-tree:${asm.vespa.version} +org.ow2.asm:asm:${asm.vespa.version} +org.slf4j:slf4j-api:${slf4j.vespa.version} org.tukaani:xz:1.9 -org.twdata.maven:mojo-executor:2.4.0 +org.twdata.maven:mojo-executor:${mojo-executor.vespa.version} org.vafer:jdependency:2.9.0 - -#[test-only] -# Contains dependencies that are used exclusively in 'test' scope -junit:junit:4.13.2 -net.bytebuddy:byte-buddy:1.14.8 -net.bytebuddy:byte-buddy-agent:1.14.8 -org.apiguardian:apiguardian-api:1.1.2 -org.hamcrest:hamcrest:2.2 -org.hamcrest:hamcrest-core:2.2 -org.junit.jupiter:junit-jupiter:5.10.0 -org.junit.jupiter:junit-jupiter-api:5.10.0 -org.junit.jupiter:junit-jupiter-engine:5.10.0 -org.junit.jupiter:junit-jupiter-params:5.10.0 -org.junit.platform:junit-platform-commons:1.10.0 -org.junit.platform:junit-platform-engine:1.10.0 -org.mockito:mockito-core:5.5.0 -org.objenesis:objenesis:3.3 -org.opentest4j:opentest4j:1.3.0 diff --git a/maven-plugins/pom.xml b/maven-plugins/pom.xml index f12731ba9da..bc8ed090c3f 100644 --- a/maven-plugins/pom.xml +++ b/maven-plugins/pom.xml @@ -47,7 +47,7 @@ </goals> <configuration> <rules> - <enforceDependencies implementation="com.yahoo.vespa.maven.plugin.enforcer.EnforceDependenciesAllProjects"> + <enforceDependencies implementation="com.yahoo.vespa.maven.plugin.enforcer.AllowedDependencies"> <rootProjectId>com.yahoo.vespa:maven-plugins</rootProjectId> <specFile>allowed-maven-dependencies.txt</specFile> <ignored> diff --git a/vespa-dependencies-enforcer/allowed-maven-dependencies.txt b/vespa-dependencies-enforcer/allowed-maven-dependencies.txt index b70b3ff2bb1..3ae0923aef0 100644 --- a/vespa-dependencies-enforcer/allowed-maven-dependencies.txt +++ b/vespa-dependencies-enforcer/allowed-maven-dependencies.txt @@ -1,211 +1,206 @@ # Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -#[non-test] -# Contains dependencies that are not used exclusively in 'test' scope -ai.djl:api:0.23.0 ai.djl.huggingface:tokenizers:0.23.0 -aopalliance:aopalliance:1.0 +ai.djl:api:0.23.0 +aopalliance:aopalliance:${aopalliance.vespa.version} backport-util-concurrent:backport-util-concurrent:3.1 ch.qos.logback:logback-classic:1.2.10 ch.qos.logback:logback-core:1.2.10 classworlds:classworlds:1.1-alpha-2 -com.amazonaws:aws-java-sdk-core:1.12.540 -com.amazonaws:aws-java-sdk-kms:1.12.540 -com.amazonaws:aws-java-sdk-s3:1.12.540 -com.amazonaws:aws-java-sdk-ssm:1.12.540 -com.amazonaws:aws-java-sdk-sts:1.12.540 -com.amazonaws:jmespath-java:1.12.540 -com.auth0:java-jwt:4.4.0 -com.fasterxml.jackson.core:jackson-annotations:2.15.2 -com.fasterxml.jackson.core:jackson-core:2.15.2 -com.fasterxml.jackson.core:jackson-databind:2.15.2 -com.fasterxml.jackson.dataformat:jackson-dataformat-cbor:2.15.2 -com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.15.2 -com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.15.2 -com.github.luben:zstd-jni:1.5.5-5 +com.amazonaws:aws-java-sdk-core:${aws-sdk.vespa.version} +com.amazonaws:aws-java-sdk-kms:${aws-sdk.vespa.version} +com.amazonaws:aws-java-sdk-s3:${aws-sdk.vespa.version} +com.amazonaws:aws-java-sdk-ssm:${aws-sdk.vespa.version} +com.amazonaws:aws-java-sdk-sts:${aws-sdk.vespa.version} +com.amazonaws:jmespath-java:${aws-sdk.vespa.version} +com.auth0:java-jwt:${java-jwt.vespa.version} +com.fasterxml.jackson.core:jackson-annotations:${jackson2.vespa.version} +com.fasterxml.jackson.core:jackson-core:${jackson2.vespa.version} +com.fasterxml.jackson.core:jackson-databind:${jackson-databind.vespa.version} +com.fasterxml.jackson.dataformat:jackson-dataformat-cbor:${jackson2.vespa.version} +com.fasterxml.jackson.datatype:jackson-datatype-jdk8:${jackson2.vespa.version} +com.fasterxml.jackson.datatype:jackson-datatype-jsr310:${jackson2.vespa.version} +com.github.luben:zstd-jni:${luben.zstd.vespa.version} com.github.spotbugs:spotbugs-annotations:3.1.9 -com.google.code.findbugs:jsr305:3.0.2 -com.google.errorprone:error_prone_annotations:2.22.0 +com.google.code.findbugs:jsr305:${findbugs.vespa.version} +com.google.errorprone:error_prone_annotations:${error-prone-annotations.vespa.version} com.google.guava:failureaccess:1.0.1 -com.google.guava:guava:32.1.2-jre -com.google.inject:guice:6.0.0 +com.google.guava:guava:${guava.vespa.version} +com.google.inject:guice:${guice.vespa.version} com.google.j2objc:j2objc-annotations:2.8 -com.google.protobuf:protobuf-java:3.24.3 -com.ibm.icu:icu4j:73.2 -com.microsoft.onnxruntime:onnxruntime:1.15.1 -com.sun.activation:javax.activation:1.2.0 +com.google.jimfs:jimfs:${jimfs.vespa.version} +com.google.protobuf:protobuf-java:${protobuf.vespa.version} +com.ibm.icu:icu4j:${icu4j.vespa.version} +com.microsoft.onnxruntime:onnxruntime:${onnxruntime.vespa.version} +com.sun.activation:javax.activation:${properties-maven-plugin.vespa.version} com.sun.istack:istack-commons-runtime:4.1.2 -com.sun.xml.bind:jaxb-core:2.3.0.1 -com.sun.xml.bind:jaxb-impl:2.3.0 +com.sun.xml.bind:jaxb-core:${jaxb-core.vespa.version} +com.sun.xml.bind:jaxb-impl:${jaxb-impl.vespa.version} com.thaiopensource:jing:20091111 -com.yahoo.athenz:athenz-auth-core:1.11.42 -com.yahoo.athenz:athenz-client-common:1.11.42 -com.yahoo.athenz:athenz-zms-core:1.11.42 -com.yahoo.athenz:athenz-zpe-java-client:1.11.42 -com.yahoo.athenz:athenz-zts-core:1.11.42 +com.yahoo.athenz:athenz-auth-core:${athenz.vespa.version} +com.yahoo.athenz:athenz-client-common:${athenz.vespa.version} +com.yahoo.athenz:athenz-zms-core:${athenz.vespa.version} +com.yahoo.athenz:athenz-zpe-java-client:${athenz.vespa.version} +com.yahoo.athenz:athenz-zts-core:${athenz.vespa.version} com.yahoo.rdl:rdl-java:1.5.4 commons-cli:commons-cli:1.5.0 -commons-codec:commons-codec:1.16.0 +commons-codec:commons-codec:${commons-codec.vespa.version} commons-fileupload:commons-fileupload:1.5 -commons-io:commons-io:2.13.0 -commons-logging:commons-logging:1.2 -io.airlift:airline:0.9 -io.dropwizard.metrics:metrics-core:4.2.20 -io.jsonwebtoken:jjwt-api:0.11.5 -io.jsonwebtoken:jjwt-impl:0.11.5 -io.jsonwebtoken:jjwt-jackson:0.11.5 -io.netty:netty-buffer:4.1.98.Final -io.netty:netty-codec:4.1.98.Final -io.netty:netty-common:4.1.98.Final -io.netty:netty-handler:4.1.98.Final -io.netty:netty-resolver:4.1.98.Final -io.netty:netty-tcnative:2.0.61.Final -io.netty:netty-tcnative-classes:2.0.61.Final -io.netty:netty-transport:4.1.98.Final -io.netty:netty-transport-classes-epoll:4.1.98.Final -io.netty:netty-transport-native-epoll:4.1.98.Final -io.netty:netty-transport-native-unix-common:4.1.98.Final -io.prometheus:simpleclient:0.16.0 -io.prometheus:simpleclient_common:0.16.0 -io.prometheus:simpleclient_tracer_common:0.16.0 -io.prometheus:simpleclient_tracer_otel:0.16.0 -io.prometheus:simpleclient_tracer_otel_agent:0.16.0 -jakarta.inject:jakarta.inject-api:2.0.1 -javax.activation:javax.activation-api:1.2.0 -javax.annotation:javax.annotation-api:1.2 -javax.inject:javax.inject:1 -javax.servlet:javax.servlet-api:3.1.0 -javax.ws.rs:javax.ws.rs-api:2.1.1 -javax.xml.bind:jaxb-api:2.3.1 -joda-time:joda-time:2.12.5 -junit:junit:4.13.2 -net.java.dev.jna:jna:5.13.0 -net.openhft:zero-allocation-hashing:0.16 -org.antlr:antlr-runtime:3.5.3 -org.antlr:antlr4-runtime:4.13.1 -org.apache.aries.spifly:org.apache.aries.spifly.dynamic.bundle:1.3.6 -org.apache.commons:commons-compress:1.24.0 -org.apache.commons:commons-csv:1.10.0 -org.apache.commons:commons-exec:1.3 -org.apache.commons:commons-lang3:3.13.0 -org.apache.commons:commons-math3:3.6.1 -org.apache.curator:curator-client:5.5.0 -org.apache.curator:curator-framework:5.5.0 -org.apache.curator:curator-recipes:5.5.0 -org.apache.felix:org.apache.felix.framework:7.0.5 -org.apache.felix:org.apache.felix.log:1.3.0 -org.apache.httpcomponents:httpclient:4.5.14 -org.apache.httpcomponents:httpcore:4.4.16 -org.apache.httpcomponents:httpmime:4.5.14 -org.apache.httpcomponents.client5:httpclient5:5.2.1 -org.apache.httpcomponents.core5:httpcore5:5.2.3 -org.apache.httpcomponents.core5:httpcore5-h2:5.2.3 -org.apache.lucene:lucene-analysis-common:9.7.0 -org.apache.lucene:lucene-core:9.7.0 -org.apache.maven:maven-archiver:3.6.1 -org.apache.maven:maven-artifact:3.9.4 +commons-io:commons-io:${commons-io.vespa.version} +commons-logging:commons-logging:${commons-logging.vespa.version} +io.airlift:airline:${airline.vespa.version} +io.dropwizard.metrics:metrics-core:${dropwizard.metrics.vespa.version} +io.jsonwebtoken:jjwt-api:${java-jjwt.vespa.version} +io.jsonwebtoken:jjwt-impl:${java-jjwt.vespa.version} +io.jsonwebtoken:jjwt-jackson:${java-jjwt.vespa.version} +io.netty:netty-buffer:${netty.vespa.version} +io.netty:netty-codec:${netty.vespa.version} +io.netty:netty-common:${netty.vespa.version} +io.netty:netty-handler:${netty.vespa.version} +io.netty:netty-resolver:${netty.vespa.version} +io.netty:netty-tcnative-classes:${netty-tcnative.vespa.version} +io.netty:netty-tcnative:${netty-tcnative.vespa.version} +io.netty:netty-transport-classes-epoll:${netty.vespa.version} +io.netty:netty-transport-native-epoll:${netty.vespa.version} +io.netty:netty-transport-native-unix-common:${netty.vespa.version} +io.netty:netty-transport:${netty.vespa.version} +io.prometheus:simpleclient:${prometheus.client.vespa.version} +io.prometheus:simpleclient_common:${prometheus.client.vespa.version} +io.prometheus:simpleclient_tracer_common:${prometheus.client.vespa.version} +io.prometheus:simpleclient_tracer_otel:${prometheus.client.vespa.version} +io.prometheus:simpleclient_tracer_otel_agent:${prometheus.client.vespa.version} +jakarta.inject:jakarta.inject-api:${jakarta.inject.vespa.version} +javax.activation:javax.activation-api:${properties-maven-plugin.vespa.version} +javax.annotation:javax.annotation-api:${commons-logging.vespa.version} +javax.inject:javax.inject:${javax.inject.vespa.version} +javax.servlet:javax.servlet-api:${javax.servlet-api.vespa.version} +javax.ws.rs:javax.ws.rs-api:${javax.ws.rs-api.vespa.version} +javax.xml.bind:jaxb-api:${jaxb-api.vespa.version} +joda-time:joda-time:${joda-time.vespa.version} +junit:junit:${junit4.vespa.version} +net.bytebuddy:byte-buddy-agent:${byte-buddy.vespa.version} +net.bytebuddy:byte-buddy:${byte-buddy.vespa.version} +net.java.dev.jna:jna:${jna.vespa.version} +net.openhft:zero-allocation-hashing:${zero-allocation-hashing.vespa.version} +org.antlr:antlr-runtime:${antlr.vespa.version} +org.antlr:antlr4-runtime:${antlr4.vespa.version} +org.apache.aries.spifly:org.apache.aries.spifly.dynamic.bundle:${spifly.vespa.version} +org.apache.commons:commons-compress:${commons-compress.vespa.version} +org.apache.commons:commons-csv:${commons-csv.vespa.version} +org.apache.commons:commons-exec:${commons-exec.vespa.version} +org.apache.commons:commons-lang3:${commons-lang3.vespa.version} +org.apache.commons:commons-math3:${commons.math3.vespa.version} +org.apache.curator:curator-client:${curator.vespa.version} +org.apache.curator:curator-framework:${curator.vespa.version} +org.apache.curator:curator-recipes:${curator.vespa.version} +org.apache.curator:curator-test:${curator.vespa.version} +org.apache.felix:org.apache.felix.framework:${felix.vespa.version} +org.apache.felix:org.apache.felix.log:${felix.log.vespa.version} +org.apache.httpcomponents.client5:httpclient5:${apache.httpclient5.vespa.version} +org.apache.httpcomponents.core5:httpcore5-h2:${apache.httpcore5.vespa.version} +org.apache.httpcomponents.core5:httpcore5:${apache.httpcore5.vespa.version} +org.apache.httpcomponents:httpclient:${apache.httpclient.vespa.version} +org.apache.httpcomponents:httpcore:${apache.httpcore.vespa.version} +org.apache.httpcomponents:httpmime:${apache.httpclient.vespa.version} +org.apache.lucene:lucene-analysis-common:${lucene.vespa.version} +org.apache.lucene:lucene-core:${lucene.vespa.version} +org.apache.maven.plugin-tools:maven-plugin-annotations:${maven-plugin-tools.vespa.version} +org.apache.maven.plugins:maven-jar-plugin:${maven-jar-plugin.vespa.version} +org.apache.maven.shared:file-management:3.1.0 +org.apache.maven.wagon:wagon-provider-api:${maven-wagon.vespa.version} +org.apache.maven:maven-archiver:${maven-archiver.vespa.version} org.apache.maven:maven-artifact-manager:2.2.1 -org.apache.maven:maven-model:3.9.4 -org.apache.maven:maven-plugin-api:3.9.4 +org.apache.maven:maven-artifact:${maven-core.vespa.version} +org.apache.maven:maven-model:${maven-core.vespa.version} +org.apache.maven:maven-plugin-api:${maven-core.vespa.version} org.apache.maven:maven-plugin-registry:2.2.1 org.apache.maven:maven-profile:2.2.1 org.apache.maven:maven-project:2.2.1 -org.apache.maven:maven-repository-metadata:3.9.4 -org.apache.maven:maven-settings:3.9.4 -org.apache.maven.plugin-tools:maven-plugin-annotations:3.9.0 -org.apache.maven.plugins:maven-jar-plugin:3.3.0 -org.apache.maven.shared:file-management:3.1.0 -org.apache.maven.wagon:wagon-provider-api:3.5.3 -org.apache.opennlp:opennlp-tools:2.3.0 +org.apache.maven:maven-repository-metadata:${maven-core.vespa.version} +org.apache.maven:maven-settings:${maven-core.vespa.version} +org.apache.opennlp:opennlp-tools:${opennlp.vespa.version} org.apache.velocity:velocity-engine-core:2.3 org.apache.yetus:audience-annotations:0.12.0 -org.apache.zookeeper:zookeeper:3.8.0 -org.apache.zookeeper:zookeeper:3.8.1 -org.apache.zookeeper:zookeeper-jute:3.8.0 +org.apache.zookeeper:zookeeper-jute:${zookeeper.client.vespa.version} org.apache.zookeeper:zookeeper-jute:3.8.1 -org.apiguardian:apiguardian-api:1.1.2 -org.bouncycastle:bcpkix-jdk18on:1.76 -org.bouncycastle:bcprov-jdk18on:1.76 -org.bouncycastle:bcutil-jdk18on:1.76 +org.apache.zookeeper:zookeeper:${zookeeper.client.vespa.version} +org.apache.zookeeper:zookeeper:3.8.1 +org.apiguardian:apiguardian-api:${apiguardian.vespa.version} +org.assertj:assertj-core:${assertj.vespa.version} +org.bouncycastle:bcpkix-jdk18on:${bouncycastle.vespa.version} +org.bouncycastle:bcprov-jdk18on:${bouncycastle.vespa.version} +org.bouncycastle:bcutil-jdk18on:${bouncycastle.vespa.version} org.codehaus.plexus:plexus-archiver:4.8.0 org.codehaus.plexus:plexus-classworlds:2.7.0 org.codehaus.plexus:plexus-component-annotations:1.5.5 org.codehaus.plexus:plexus-container-default:1.0-alpha-9-stable-1 org.codehaus.plexus:plexus-interpolation:1.26 -org.codehaus.plexus:plexus-io:3.4.1 -org.codehaus.plexus:plexus-utils:3.5.1 -org.eclipse.angus:angus-activation:2.0.1 -org.eclipse.collections:eclipse-collections:11.1.0 -org.eclipse.collections:eclipse-collections-api:11.1.0 -org.eclipse.jetty:jetty-alpn-client:11.0.16 -org.eclipse.jetty:jetty-alpn-java-client:11.0.16 -org.eclipse.jetty:jetty-alpn-java-server:11.0.16 -org.eclipse.jetty:jetty-alpn-server:11.0.16 -org.eclipse.jetty:jetty-client:11.0.16 -org.eclipse.jetty:jetty-http:11.0.16 -org.eclipse.jetty:jetty-io:11.0.16 -org.eclipse.jetty:jetty-jmx:11.0.16 -org.eclipse.jetty:jetty-security:11.0.16 -org.eclipse.jetty:jetty-server:11.0.16 -org.eclipse.jetty:jetty-servlet:11.0.16 -org.eclipse.jetty:jetty-util:11.0.16 -org.eclipse.jetty.http2:http2-client:11.0.16 -org.eclipse.jetty.http2:http2-common:11.0.16 -org.eclipse.jetty.http2:http2-hpack:11.0.16 -org.eclipse.jetty.http2:http2-http-client-transport:11.0.16 -org.eclipse.jetty.http2:http2-server:11.0.16 -org.eclipse.jetty.toolchain:jetty-jakarta-servlet-api:5.0.2 +org.codehaus.plexus:plexus-io:${maven-enforcer-plugin.vespa.version} +org.codehaus.plexus:plexus-utils:${maven-shade-plugin.vespa.version} +org.eclipse.angus:angus-activation:${jakarta.inject.vespa.version} +org.eclipse.collections:eclipse-collections-api:${eclipse-collections.vespa.version} +org.eclipse.collections:eclipse-collections:${eclipse-collections.vespa.version} +org.eclipse.jetty.http2:http2-client:${jetty.vespa.version} +org.eclipse.jetty.http2:http2-common:${jetty.vespa.version} +org.eclipse.jetty.http2:http2-hpack:${jetty.vespa.version} +org.eclipse.jetty.http2:http2-http-client-transport:${jetty.vespa.version} +org.eclipse.jetty.http2:http2-server:${jetty.vespa.version} +org.eclipse.jetty.toolchain:jetty-jakarta-servlet-api:${jetty-servlet-api.vespa.version} +org.eclipse.jetty:jetty-alpn-client:${jetty.vespa.version} +org.eclipse.jetty:jetty-alpn-java-client:${jetty.vespa.version} +org.eclipse.jetty:jetty-alpn-java-server:${jetty.vespa.version} +org.eclipse.jetty:jetty-alpn-server:${jetty.vespa.version} +org.eclipse.jetty:jetty-client:${jetty.vespa.version} +org.eclipse.jetty:jetty-http:${jetty.vespa.version} +org.eclipse.jetty:jetty-io:${jetty.vespa.version} +org.eclipse.jetty:jetty-jmx:${jetty.vespa.version} +org.eclipse.jetty:jetty-security:${jetty.vespa.version} +org.eclipse.jetty:jetty-server:${jetty.vespa.version} +org.eclipse.jetty:jetty-servlet:${jetty.vespa.version} +org.eclipse.jetty:jetty-util:${jetty.vespa.version} org.eclipse.sisu:org.eclipse.sisu.inject:0.3.5 org.eclipse.sisu:org.eclipse.sisu.plexus:0.3.5 org.fusesource.jansi:jansi:1.18 -org.glassfish.jaxb:jaxb-core:4.0.3 -org.glassfish.jaxb:jaxb-runtime:4.0.3 -org.glassfish.jaxb:txw2:4.0.3 -org.hamcrest:hamcrest:2.2 -org.hamcrest:hamcrest-core:2.2 -org.hdrhistogram:HdrHistogram:2.1.12 +org.glassfish.jaxb:jaxb-core:${jaxb.runtime.vespa.version} +org.glassfish.jaxb:jaxb-runtime:${jaxb.runtime.vespa.version} +org.glassfish.jaxb:txw2:${jaxb.runtime.vespa.version} +org.hamcrest:hamcrest-core:${hamcrest.vespa.version} +org.hamcrest:hamcrest:${hamcrest.vespa.version} +org.hdrhistogram:HdrHistogram:${hdrhistogram.vespa.version} org.iq80.snappy:snappy:0.4 -org.json:json:20230618 -org.junit.jupiter:junit-jupiter-api:5.10.0 -org.junit.jupiter:junit-jupiter-api:5.8.1 -org.junit.jupiter:junit-jupiter-engine:5.8.1 -org.junit.platform:junit-platform-commons:1.8.1 -org.junit.platform:junit-platform-engine:1.8.1 -org.junit.platform:junit-platform-launcher:1.8.1 +org.json:json:${org.json.vespa.version} +org.junit.jupiter:junit-jupiter-api:${junit.vespa.tenant.version} +org.junit.jupiter:junit-jupiter-api:${junit.vespa.version} +org.junit.jupiter:junit-jupiter-engine:${junit.vespa.tenant.version} +org.junit.jupiter:junit-jupiter-engine:${junit.vespa.version} +org.junit.jupiter:junit-jupiter-params:${junit.vespa.version} +org.junit.jupiter:junit-jupiter:${junit.vespa.version} +org.junit.platform:junit-platform-commons:${junit.platform.vespa.tenant.version} +org.junit.platform:junit-platform-commons:${junit.platform.vespa.version} +org.junit.platform:junit-platform-engine:${junit.platform.vespa.tenant.version} +org.junit.platform:junit-platform-engine:${junit.platform.vespa.version} +org.junit.platform:junit-platform-launcher:${junit.platform.vespa.tenant.version} +org.junit.vintage:junit-vintage-engine:${junit.vespa.tenant.version} +org.junit.vintage:junit-vintage-engine:${junit.vespa.version} org.kohsuke:libpam4j:1.11 -org.lz4:lz4-java:1.8.0 -org.opentest4j:opentest4j:1.3.0 -org.ow2.asm:asm:9.5 -org.ow2.asm:asm-analysis:9.5 -org.ow2.asm:asm-commons:9.5 -org.ow2.asm:asm-tree:9.5 -org.ow2.asm:asm-util:9.5 -org.questdb:questdb:7.3.2 -org.slf4j:jcl-over-slf4j:1.7.36 -org.slf4j:log4j-over-slf4j:1.7.36 -org.slf4j:slf4j-api:1.7.36 -org.slf4j:slf4j-jdk14:1.7.36 -org.slf4j:slf4j-simple:1.7.36 +org.lz4:lz4-java:${org.lz4.vespa.version} +org.mockito:mockito-core:${mockito.vespa.version} +org.mockito:mockito-junit-jupiter:${mockito.vespa.version} +org.objenesis:objenesis:3.3 +org.opentest4j:opentest4j:${opentest4j.vespa.version} +org.ow2.asm:asm-analysis:${asm.vespa.version} +org.ow2.asm:asm-commons:${asm.vespa.version} +org.ow2.asm:asm-tree:${asm.vespa.version} +org.ow2.asm:asm-util:${asm.vespa.version} +org.ow2.asm:asm:${asm.vespa.version} +org.questdb:questdb:${questdb.vespa.version} +org.slf4j:jcl-over-slf4j:${slf4j.vespa.version} +org.slf4j:log4j-over-slf4j:${slf4j.vespa.version} +org.slf4j:slf4j-api:${slf4j.vespa.version} +org.slf4j:slf4j-jdk14:${slf4j.vespa.version} +org.slf4j:slf4j-simple:${slf4j.vespa.version} org.tukaani:xz:1.9 -org.xerial.snappy:snappy-java:1.1.10.4 +org.wiremock:wiremock-standalone:${wiremock.vespa.version} +org.xerial.snappy:snappy-java:${snappy.vespa.version} software.amazon.ion:ion-java:1.0.2 -xerces:xercesImpl:2.12.2 - -#[test-only] -# Contains dependencies that are used exclusively in 'test' scope -com.google.jimfs:jimfs:1.3.0 -net.bytebuddy:byte-buddy:1.14.8 -net.bytebuddy:byte-buddy-agent:1.14.8 -org.apache.curator:curator-test:5.5.0 -org.assertj:assertj-core:3.24.2 -org.junit.jupiter:junit-jupiter:5.10.0 -org.junit.jupiter:junit-jupiter-engine:5.10.0 -org.junit.jupiter:junit-jupiter-params:5.10.0 -org.junit.platform:junit-platform-commons:1.10.0 -org.junit.platform:junit-platform-engine:1.10.0 -org.junit.vintage:junit-vintage-engine:5.10.0 -org.junit.vintage:junit-vintage-engine:5.8.1 -org.mockito:mockito-core:5.5.0 -org.mockito:mockito-junit-jupiter:5.5.0 -org.objenesis:objenesis:3.3 -org.wiremock:wiremock-standalone:3.1.0 +xerces:xercesImpl:${xerces.vespa.version} diff --git a/vespa-dependencies-enforcer/pom.xml b/vespa-dependencies-enforcer/pom.xml index 768e5708ee5..91820fd292b 100644 --- a/vespa-dependencies-enforcer/pom.xml +++ b/vespa-dependencies-enforcer/pom.xml @@ -38,7 +38,7 @@ </goals> <configuration> <rules> - <enforceDependencies implementation="com.yahoo.vespa.maven.plugin.enforcer.EnforceDependenciesAllProjects"> + <enforceDependencies implementation="com.yahoo.vespa.maven.plugin.enforcer.AllowedDependencies"> <rootProjectId>com.yahoo.vespa:vespa</rootProjectId> <specFile>allowed-maven-dependencies.txt</specFile> <ignored> @@ -47,14 +47,6 @@ <i>com.yahoo.vespa.bundle-plugin:*:*</i> <i>com.yahoo.vespa.jdisc_core:*:*</i> </ignored> - - <!-- Classifly all dependencies of below modules as 'test' --> - <testUtilProjects> - <!-- Misc --> - <i>com.yahoo.vespa:testutil</i> - <!-- Bundle plugin integration test --> - <i>com.yahoo.vespa.bundle-plugin:*</i> - </testUtilProjects> </enforceDependencies> </rules> <fail>true</fail> diff --git a/vespa-enforcer-extensions/pom.xml b/vespa-enforcer-extensions/pom.xml index 9d8e99156f3..04abb00739c 100644 --- a/vespa-enforcer-extensions/pom.xml +++ b/vespa-enforcer-extensions/pom.xml @@ -45,6 +45,11 @@ <artifactId>junit-jupiter</artifactId> <scope>test</scope> </dependency> + <dependency> + <groupId>javax.inject</groupId> + <artifactId>javax.inject</artifactId> + <scope>provided</scope> + </dependency> </dependencies> <build> @@ -54,21 +59,18 @@ <artifactId>maven-compiler-plugin</artifactId> </plugin> <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-shade-plugin</artifactId> + <!-- generate index of project components --> + <groupId>org.eclipse.sisu</groupId> + <artifactId>sisu-maven-plugin</artifactId> + <version>0.9.0.M1</version> <executions> <execution> - <phase>package</phase> <goals> - <goal>shade</goal> + <goal>main-index</goal> </goals> - <configuration> - <createDependencyReducedPom>false</createDependencyReducedPom> - </configuration> </execution> </executions> </plugin> - </plugins> </build> diff --git a/vespa-enforcer-extensions/src/main/java/com/yahoo/vespa/maven/plugin/enforcer/AllowedDependencies.java b/vespa-enforcer-extensions/src/main/java/com/yahoo/vespa/maven/plugin/enforcer/AllowedDependencies.java new file mode 100644 index 00000000000..656e2f52558 --- /dev/null +++ b/vespa-enforcer-extensions/src/main/java/com/yahoo/vespa/maven/plugin/enforcer/AllowedDependencies.java @@ -0,0 +1,305 @@ +package com.yahoo.vespa.maven.plugin.enforcer; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.enforcer.rule.api.AbstractEnforcerRule; +import org.apache.maven.enforcer.rule.api.EnforcerRule; +import org.apache.maven.enforcer.rule.api.EnforcerRuleException; +import org.apache.maven.enforcer.rule.api.EnforcerRuleHelper; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.project.DefaultProjectBuildingRequest; +import org.apache.maven.project.MavenProject; +import org.apache.maven.shared.dependency.graph.DependencyGraphBuilder; +import org.apache.maven.shared.dependency.graph.DependencyGraphBuilderException; +import org.apache.maven.shared.dependency.graph.DependencyNode; +import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException; +import org.codehaus.plexus.component.repository.exception.ComponentLookupException; + +import javax.inject.Inject; +import javax.inject.Named; +import java.io.File; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Properties; +import java.util.Set; +import java.util.TreeMap; +import java.util.TreeSet; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * @author bjorncs + */ +@Named("allowedDependencies") +@SuppressWarnings("deprecation") +public class AllowedDependencies extends AbstractEnforcerRule implements EnforcerRule { + + private static final String WRITE_SPEC_PROP = "dependencyEnforcer.writeSpec"; + private static final String GUESS_VERSION = "dependencyEnforcer.guessProperty"; + + @Inject private MavenProject project; + @Inject private MavenSession session; + @Inject private DependencyGraphBuilder graphBuilder; + + // Injected parameters + public List<String> ignored; + public String rootProjectId; + public String specFile; + + @Override + public void execute(EnforcerRuleHelper helper) throws EnforcerRuleException { + try { + project = (MavenProject) helper.evaluate("${project}"); + session = (MavenSession) helper.evaluate("${session}"); + graphBuilder = helper.getComponent(DependencyGraphBuilder.class); + } catch (ExpressionEvaluationException | ComponentLookupException e) { + throw new RuntimeException(e); + } + execute(); + } + + public void execute() throws EnforcerRuleException { + var dependencies = getDependenciesOfAllProjects(); + getLog().info("Found %d unique dependencies ".formatted(dependencies.size())); + var specFile = Paths.get(project.getBasedir() + File.separator + this.specFile).normalize(); + var spec = loadDependencySpec(specFile); + var resolved = resolve(spec, dependencies); + if (System.getProperties().containsKey(WRITE_SPEC_PROP)) { + writeDependencySpec(specFile, resolved, System.getProperties().containsKey(GUESS_VERSION)); + getLog().info("Updated spec file '%s'".formatted(specFile.toString())); + } else { + warnOnDuplicateVersions(resolved); + validateDependencies(resolved, session.getRequest().getPom().toPath(), project.getArtifactId()); + } + getLog().info("The dependency enforcer completed successfully"); + } + + private static void validateDependencies(Resolved resolved, Path aggregatorPomRoot, String moduleName) + throws EnforcerRuleException { + if (!resolved.unmatchedRules().isEmpty() || !resolved.unmatchedDeps().isEmpty()) { + var errorMsg = new StringBuilder("The dependency enforcer failed:\n"); + if (!resolved.unmatchedRules().isEmpty()) { + errorMsg.append("Rules not matching any dependency:\n"); + resolved.unmatchedRules().forEach(r -> errorMsg.append(" - ").append(r.asString()).append('\n')); + } + if (!resolved.unmatchedDeps().isEmpty()) { + errorMsg.append("Dependencies not matching any rule:\n"); + resolved.unmatchedDeps().forEach(d -> errorMsg.append(" - ").append(d.asString(null)).append('\n')); + } + throw new EnforcerRuleException( + errorMsg.append("Maven dependency validation failed. ") + .append("If this change was intentional, update the dependency spec by running:\n") + .append("$ mvn validate -D").append(WRITE_SPEC_PROP).append(" -pl :").append(moduleName) + .append(" -f ").append(aggregatorPomRoot).append("\n").toString()); + } + } + + private Set<Dependency> getDependenciesOfAllProjects() throws EnforcerRuleException { + try { + Pattern depIgnorePattern = Pattern.compile( + ignored.stream() + .map(s -> s.replace(".", "\\.").replace("*", ".*").replace(":", "\\:").replace('?', '.')) + .collect(Collectors.joining(")|(", "^(", ")$"))); + List<MavenProject> projects = getAllProjects(session, rootProjectId); + Set<Dependency> dependencies = new HashSet<>(); + for (MavenProject project : projects) { + var req = new DefaultProjectBuildingRequest(session.getProjectBuildingRequest()); + req.setProject(project); + var root = graphBuilder.buildDependencyGraph(req, null); + addDependenciesRecursive(root, dependencies, depIgnorePattern); + } + return Set.copyOf(dependencies); + } catch (DependencyGraphBuilderException e) { + throw new RuntimeException(e.getMessage(), e); + } + } + + private static void addDependenciesRecursive(DependencyNode node, Set<Dependency> dependencies, Pattern ignored) { + if (node.getChildren() != null) { + for (DependencyNode dep : node.getChildren()) { + Artifact a = dep.getArtifact(); + Dependency dependency = Dependency.fromArtifact(a); + if (!ignored.matcher(dependency.asString(null)).matches()) { + dependencies.add(dependency); + } + addDependenciesRecursive(dep, dependencies, ignored); + } + } + } + + /** Only return the projects we'd like to enforce dependencies for: the root project, its modules, their modules, etc. */ + private static List<MavenProject> getAllProjects(MavenSession session, String rootProjectId) throws EnforcerRuleException { + if (rootProjectId == null) throw new EnforcerRuleException("Missing required <rootProjectId> in <enforceDependencies> in pom.xml"); + + List<MavenProject> allProjects = session.getAllProjects(); + if (allProjects.size() == 1) { + throw new EnforcerRuleException( + "Only a single Maven module detected. Enforcer must be executed from root of aggregator pom."); + } + MavenProject rootProject = allProjects + .stream() + .filter(project -> rootProjectId.equals(projectIdOf(project))) + .findAny() + .orElseThrow(() -> new EnforcerRuleException("Root project not found: " + rootProjectId)); + + Map<Path, MavenProject> projectsByBaseDir = allProjects + .stream() + .collect(Collectors.toMap(project -> project.getBasedir().toPath().normalize(), project -> project)); + + var projects = new ArrayList<MavenProject>(); + + var pendingProjects = new ArrayDeque<MavenProject>(); + pendingProjects.add(rootProject); + + while (!pendingProjects.isEmpty()) { + MavenProject project = pendingProjects.pop(); + projects.add(project); + + for (var module : project.getModules()) { + // Assumption: The module is a relative path to a project base directory. + Path moduleBaseDir = project.getBasedir().toPath().resolve(module).normalize(); + MavenProject moduleProject = projectsByBaseDir.get(moduleBaseDir); + if (moduleProject == null) + throw new EnforcerRuleException("Failed to find module '" + module + "' in project " + project.getBasedir()); + pendingProjects.add(moduleProject); + } + } + + projects.sort(Comparator.comparing(AllowedDependencies::projectIdOf)); + return projects; + } + + private List<Rule> loadDependencySpec(Path specFile) { + try (Stream<String> s = Files.lines(specFile)) { + return s.map(String::trim) + .filter(l -> !l.isEmpty() && !l.startsWith("#")) + .map(Rule::fromString) + .toList(); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + private Resolved resolve(List<Rule> spec, Set<Dependency> dependencies) { + var resolvedDeps = new HashSet<Dependency>(); + var resolveRules = new HashSet<Rule>(); + var unmatchedDeps = new HashSet<Dependency>(); + var unmatchedRules = new HashSet<Rule>(); + for (var rule : spec) { + var requiredDependency = rule.resolveToDependency(project.getProperties()); + if (dependencies.contains(requiredDependency)) { + resolvedDeps.add(requiredDependency); + resolveRules.add(rule); + } else { + unmatchedRules.add(rule); + } + } + for (var dependency : dependencies) { + if (!resolvedDeps.contains(dependency)) { + unmatchedDeps.add(dependency); + } + } + return new Resolved(resolvedDeps, resolveRules, unmatchedDeps, unmatchedRules); + } + + void writeDependencySpec(Path specFile, Resolved resolved, boolean guessVersion) { + var content = new TreeSet<String>(); + resolved.matchedRules().forEach(r -> content.add(r.asString())); + resolved.unmatchedDeps().forEach(d -> content.add(d.asString(guessVersion ? project.getProperties() : null))); + try (var out = Files.newBufferedWriter(specFile)) { + out.write("# Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.\n\n"); + for (var line : content) { + out.write(line); out.write('\n'); + } + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + private void warnOnDuplicateVersions(Resolved resolved) { + Map<String, Set<String>> versionsForDependency = new TreeMap<>(); + Set<Dependency> allDeps = new HashSet<>(resolved.matchedDeps()); + allDeps.addAll(resolved.unmatchedDeps()); + for (Dependency d : allDeps) { + String id = "%s:%s".formatted(d.groupId(), d.artifactId()); + versionsForDependency.computeIfAbsent(id, __ -> new TreeSet<>()).add(d.version()); + } + versionsForDependency.forEach((dependency, versions) -> { + if (versions.size() > 1) { + getLog().warn("'%s' has multiple versions %s".formatted(dependency, versions)); + } + }); + } + + private static String projectIdOf(MavenProject project) { return "%s:%s".formatted(project.getGroupId(), project.getArtifactId()); } + + private record Rule(String groupId, String artifactId, String version, Optional<String> classifier){ + static final Pattern PROPERTY_PATTERN = Pattern.compile("\\$\\{(.+?)}"); + + static Rule fromString(String s) { + String[] splits = s.split(":"); + return splits.length == 3 + ? new Rule(splits[0], splits[1], splits[2], Optional.empty()) + : new Rule(splits[0], splits[1], splits[2], Optional.of(splits[3])); + } + + Dependency resolveToDependency(Properties props) { + // Replace expressions on form ${property} in 'version' field with value from properties + var matcher = PROPERTY_PATTERN.matcher(version); + var resolvedVersion = version; + while (matcher.find()) { + String property = matcher.group(1); + String value = props.getProperty(property); + if (value == null) throw new IllegalArgumentException("Missing property: " + property); + resolvedVersion = version.replace(matcher.group(), value); + } + return new Dependency(groupId, artifactId, resolvedVersion, classifier); + } + + String asString() { + var b = new StringBuilder(groupId).append(':').append(artifactId).append(':').append(version); + classifier.ifPresent(c -> b.append(':').append(c)); + return b.toString(); + } + } + + record Dependency(String groupId, String artifactId, String version, Optional<String> classifier) { + static Dependency fromArtifact(Artifact a) { + return new Dependency( + a.getGroupId(), a.getArtifactId(), a.getVersion(), Optional.ofNullable(a.getClassifier())); + } + + String asString(Properties props) { + String versionStr = version; + if (props != null) { + // Guess property name if properties are provided + var matchingProps = props.entrySet().stream() + .filter(e -> e.getValue().equals(version)) + .map(v -> "${%s}".formatted(v.getKey())) + .collect(Collectors.joining("|")); + if (!matchingProps.isEmpty()) versionStr = matchingProps; + } + var b = new StringBuilder(groupId).append(':').append(artifactId).append(':').append(versionStr); + classifier.ifPresent(c -> b.append(':').append(c)); + return b.toString(); + } + } + + record Resolved(Set<Dependency> matchedDeps, Set<Rule> matchedRules, + Set<Dependency> unmatchedDeps, Set<Rule> unmatchedRules) {} + + // Mark rule as not cachable + @Override public boolean isCacheable() { return false; } + @Override public boolean isResultValid(EnforcerRule r) { return false; } + @Override public String getCacheId() { return ""; } +} diff --git a/vespa-enforcer-extensions/src/main/java/com/yahoo/vespa/maven/plugin/enforcer/EnforceDependenciesAllProjects.java b/vespa-enforcer-extensions/src/main/java/com/yahoo/vespa/maven/plugin/enforcer/EnforceDependenciesAllProjects.java deleted file mode 100644 index 3db1019a2b1..00000000000 --- a/vespa-enforcer-extensions/src/main/java/com/yahoo/vespa/maven/plugin/enforcer/EnforceDependenciesAllProjects.java +++ /dev/null @@ -1,319 +0,0 @@ -// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.vespa.maven.plugin.enforcer; - -import org.apache.maven.artifact.Artifact; -import org.apache.maven.enforcer.rule.api.EnforcerRule; -import org.apache.maven.enforcer.rule.api.EnforcerRuleException; -import org.apache.maven.enforcer.rule.api.EnforcerRuleHelper; -import org.apache.maven.execution.MavenSession; -import org.apache.maven.plugin.logging.Log; -import org.apache.maven.project.DefaultProjectBuildingRequest; -import org.apache.maven.project.MavenProject; -import org.apache.maven.shared.dependency.graph.DependencyGraphBuilder; -import org.apache.maven.shared.dependency.graph.DependencyGraphBuilderException; -import org.apache.maven.shared.dependency.graph.DependencyNode; -import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException; -import org.codehaus.plexus.component.repository.exception.ComponentLookupException; - -import java.io.File; -import java.io.IOException; -import java.io.UncheckedIOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.SortedSet; -import java.util.TreeMap; -import java.util.TreeSet; -import java.util.regex.Pattern; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -/** - * @author bjorncs - */ -@SuppressWarnings("deprecation") -public class EnforceDependenciesAllProjects implements EnforcerRule { - - private static final String WRITE_SPEC_PROP = "dependencyEnforcer.writeSpec"; - private static final String NON_TEST_HEADER = "#[non-test]"; - private static final String TEST_ONLY_HEADER = "#[test-only]"; - - private String rootProjectId; - private String specFile; - private List<String> ignored = List.of(); - private List<String> testUtilProjects = List.of(); - - @Override - public void execute(EnforcerRuleHelper helper) throws EnforcerRuleException { - Log log = helper.getLog(); - Dependencies deps = getDependenciesOfAllProjects(helper, ignored, testUtilProjects, rootProjectId); - log.info("Found %d unique dependencies (%d non-test, %d test only)".formatted( - deps.nonTest().size() + deps.testOnly().size(), deps.nonTest().size(), deps.testOnly().size())); - Path specFile = resolveSpecFile(helper, this.specFile); - if (System.getProperties().containsKey(WRITE_SPEC_PROP)) { - writeDependencySpec(specFile, deps); - log.info("Updated spec file '%s'".formatted(specFile.toString())); - } else { - warnOnDuplicateVersions(log, deps); - validateDependencies(deps, specFile, aggregatorPomRoot(helper), projectName(helper)); - } - log.info("The dependency enforcer completed successfully"); - } - - // Config injection for rule configuration. Method names must match config XML elements. - @SuppressWarnings("unused") public void setRootProjectId(String l) { this.rootProjectId = l; } - @SuppressWarnings("unused") public String getRootProjectId() { return rootProjectId; } - @SuppressWarnings("unused") public void setSpecFile(String f) { this.specFile = f; } - @SuppressWarnings("unused") public String getSpecFile() { return specFile; } - @SuppressWarnings("unused") public void setIgnored(List<String> l) { this.ignored = l; } - @SuppressWarnings("unused") public List<String> getIgnored() { return ignored; } - @SuppressWarnings("unused") public void setTestUtilProjects(List<String> l) { this.testUtilProjects = l; } - @SuppressWarnings("unused") public List<String> getTestUtilProjects() { return testUtilProjects; } - - record Dependency(String groupId, String artifactId, String version, Optional<String> classifier) - implements Comparable<Dependency> { - static Dependency fromArtifact(Artifact a) { - return new Dependency( - a.getGroupId(), a.getArtifactId(), a.getVersion(), Optional.ofNullable(a.getClassifier())); - } - - static Dependency fromString(String s) { - String[] splits = s.split(":"); - return splits.length == 3 - ? new Dependency(splits[0], splits[1], splits[2], Optional.empty()) - : new Dependency(splits[0], splits[1], splits[2], Optional.of(splits[3])); - } - - String asString() { - var b = new StringBuilder(groupId).append(':').append(artifactId).append(':').append(version); - classifier.ifPresent(c -> b.append(':').append(c)); - return b.toString(); - } - - static final Comparator<Dependency> COMPARATOR = Comparator.comparing(Dependency::groupId) - .thenComparing(Dependency::artifactId).thenComparing(Dependency::version) - .thenComparing(d -> d.classifier().orElse("")); - @Override public int compareTo(Dependency o) { return COMPARATOR.compare(this, o); } - } - - record Dependencies(SortedSet<Dependency> nonTest, SortedSet<Dependency> testOnly) {} - - static void validateDependencies(Dependencies dependencies, Path specFile, Path aggregatorPomRoot, - String moduleName) - throws EnforcerRuleException { - Dependencies allowedDependencies = loadDependencySpec(specFile); - if (!allowedDependencies.equals(dependencies)) { - StringBuilder errorMsg = new StringBuilder("The dependency enforcer failed:\n"); - generateDiff(errorMsg, "non-test", dependencies.nonTest(), allowedDependencies.nonTest()); - generateDiff(errorMsg, "test-only", dependencies.testOnly(), allowedDependencies.testOnly()); - throw new EnforcerRuleException( - errorMsg.append("Maven dependency validation failed. ") - .append("If this change was intentional, update the dependency spec by running:\n") - .append("$ mvn validate -D").append(WRITE_SPEC_PROP).append(" -pl :").append(moduleName) - .append(" -f ").append(aggregatorPomRoot).append("\n").toString()); - } - } - - static void generateDiff( - StringBuilder errorMsg, String label, SortedSet<Dependency> actual, SortedSet<Dependency> expected) { - SortedSet<Dependency> forbidden = new TreeSet<>(actual); - forbidden.removeAll(expected); - SortedSet<Dependency> removed = new TreeSet<>(expected); - removed.removeAll(actual); - if (!forbidden.isEmpty()) { - errorMsg.append("Forbidden ").append(label).append(" dependencies:\n"); - forbidden.forEach(d -> errorMsg.append(" - ").append(d.asString()).append('\n')); - } - if (!removed.isEmpty()) { - errorMsg.append("Removed ").append(label).append(" dependencies:\n"); - removed.forEach(d -> errorMsg.append(" - ").append(d.asString()).append('\n')); - } - } - - private static Dependencies getDependenciesOfAllProjects(EnforcerRuleHelper helper, List<String> ignored, - List<String> testUtilProjects, String rootProjectId) - throws EnforcerRuleException { - try { - Pattern depIgnorePattern = Pattern.compile( - ignored.stream() - .map(s -> s.replace(".", "\\.").replace("*", ".*").replace(":", "\\:").replace('?', '.')) - .collect(Collectors.joining(")|(", "^(", ")$"))); - Pattern projectIgnorePattern = Pattern.compile( - testUtilProjects.stream() - .map(s -> s.replace(".", "\\.").replace("*", ".*").replace(":", "\\:").replace('?', '.')) - .collect(Collectors.joining(")|(", "^(", ")$"))); - SortedSet<Dependency> nonTestDeps = new TreeSet<>(); - SortedSet<Dependency> testDeps = new TreeSet<>(); - MavenSession session = mavenSession(helper); - var graphBuilder = helper.getComponent(DependencyGraphBuilder.class); - List<MavenProject> projects = getAllProjects(session, rootProjectId); - for (MavenProject project : projects) { - var req = new DefaultProjectBuildingRequest(session.getProjectBuildingRequest()); - req.setProject(project); - DependencyNode root = graphBuilder.buildDependencyGraph(req, null); - String projectId = projectIdOf(project); - boolean overrideToTest = projectIgnorePattern.matcher(projectId).matches(); - if (overrideToTest) helper.getLog().info("Treating dependencies of '%s' as 'test'".formatted(projectId)); - addDependenciesRecursive(root, nonTestDeps, testDeps, depIgnorePattern, overrideToTest); - } - testDeps.removeAll(nonTestDeps); - return new Dependencies(nonTestDeps, testDeps); - } catch (DependencyGraphBuilderException | ComponentLookupException e) { - throw new RuntimeException(e.getMessage(), e); - } - } - - private static String projectIdOf(MavenProject project) { return "%s:%s".formatted(project.getGroupId(), project.getArtifactId()); } - - /** Only return the projects we'd like to enforce dependencies for: the root project, its modules, their modules, etc. */ - private static List<MavenProject> getAllProjects(MavenSession session, String rootProjectId) throws EnforcerRuleException { - if (rootProjectId == null) throw new EnforcerRuleException("Missing required <rootProjectId> in <enforceDependencies> in pom.xml"); - - List<MavenProject> allProjects = session.getAllProjects(); - if (allProjects.size() == 1) { - throw new EnforcerRuleException( - "Only a single Maven module detected. Enforcer must be executed from root of aggregator pom."); - } - MavenProject rootProject = allProjects - .stream() - .filter(project -> rootProjectId.equals(projectIdOf(project))) - .findAny() - .orElseThrow(() -> new EnforcerRuleException("Root project not found: " + rootProjectId)); - - Map<Path, MavenProject> projectsByBaseDir = allProjects - .stream() - .collect(Collectors.toMap(project -> project.getBasedir().toPath().normalize(), project -> project)); - - var projects = new ArrayList<MavenProject>(); - - var pendingProjects = new ArrayDeque<MavenProject>(); - pendingProjects.add(rootProject); - - while (!pendingProjects.isEmpty()) { - MavenProject project = pendingProjects.pop(); - projects.add(project); - - for (var module : project.getModules()) { - // Assumption: The module is a relative path to a project base directory. - Path moduleBaseDir = project.getBasedir().toPath().resolve(module).normalize(); - MavenProject moduleProject = projectsByBaseDir.get(moduleBaseDir); - if (moduleProject == null) - throw new EnforcerRuleException("Failed to find module '" + module + "' in project " + project.getBasedir()); - pendingProjects.add(moduleProject); - } - } - - projects.sort(Comparator.comparing(EnforceDependenciesAllProjects::projectIdOf)); - return projects; - } - - private static void addDependenciesRecursive( - DependencyNode node, Set<Dependency> nonTestDeps, Set<Dependency> testDeps, Pattern ignored, - boolean overrideToTest) { - if (node.getChildren() != null) { - for (DependencyNode dep : node.getChildren()) { - Artifact a = dep.getArtifact(); - Dependency dependency = Dependency.fromArtifact(a); - if (!ignored.matcher(dependency.asString()).matches()) { - if (a.getScope().equals("test") || overrideToTest) { - testDeps.add(dependency); - } else { - nonTestDeps.add(dependency); - } - } - addDependenciesRecursive(dep, nonTestDeps, testDeps, ignored, overrideToTest); - } - } - } - - private static void warnOnDuplicateVersions(Log log, Dependencies deps) { - Map<String, Set<String>> versionsForDependency = new TreeMap<>(); - Set<Dependency> allDeps = new TreeSet<>(deps.nonTest()); - allDeps.addAll(deps.testOnly()); - for (Dependency d : allDeps) { - String id = "%s:%s".formatted(d.groupId(), d.artifactId()); - versionsForDependency.computeIfAbsent(id, __ -> new TreeSet<>()).add(d.version()); - } - versionsForDependency.forEach((dependency, versions) -> { - if (versions.size() > 1) { - log.warn("'%s' has multiple versions %s".formatted(dependency, versions)); - } - }); - } - - private static Path resolveSpecFile(EnforcerRuleHelper helper, String specFile) { - return Paths.get(mavenProject(helper).getBasedir() + File.separator + specFile).normalize(); - } - - private static String projectName(EnforcerRuleHelper helper) { return mavenProject(helper).getArtifactId(); } - - private static Path aggregatorPomRoot(EnforcerRuleHelper helper) { - return mavenSession(helper).getRequest().getPom().toPath(); - } - - private static MavenProject mavenProject(EnforcerRuleHelper helper) { - try { - return (MavenProject) helper.evaluate("${project}"); - } catch (ExpressionEvaluationException e) { - throw new RuntimeException(e.getMessage(), e); - } - } - - private static MavenSession mavenSession(EnforcerRuleHelper helper) { - try { - return (MavenSession) helper.evaluate("${session}"); - } catch (ExpressionEvaluationException e) { - throw new RuntimeException(e.getMessage(), e); - } - } - - static void writeDependencySpec(Path specFile, Dependencies dependencies) { - try (var out = Files.newBufferedWriter(specFile)) { - out.write("# Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.\n\n"); - out.write(NON_TEST_HEADER); out.write('\n'); - out.write("# Contains dependencies that are not used exclusively in 'test' scope\n"); - for (Dependency d : dependencies.nonTest()) { - out.write(d.asString()); out.write('\n'); - } - out.write("\n"); out.write(TEST_ONLY_HEADER); out.write('\n'); - out.write("# Contains dependencies that are used exclusively in 'test' scope\n"); - for (Dependency d : dependencies.testOnly()) { - out.write(d.asString()); out.write('\n'); - } - } catch (IOException e) { - throw new UncheckedIOException(e); - } - } - - private static Dependencies loadDependencySpec(Path specFile) { - try { - List<String> lines; - try (Stream<String> s = Files.lines(specFile)) { - lines = s.map(String::trim).filter(l -> !l.isEmpty()).toList(); - } - SortedSet<Dependency> nonTest = parseDependencies(lines.stream().takeWhile(l -> !l.equals(TEST_ONLY_HEADER))); - SortedSet<Dependency> testOnly = parseDependencies(lines.stream().dropWhile(l -> !l.equals(TEST_ONLY_HEADER))); - return new Dependencies(nonTest, testOnly); - } catch (IOException e) { - throw new UncheckedIOException(e); - } - } - - private static SortedSet<Dependency> parseDependencies(Stream<String> lines) { - return lines.filter(l -> !l.startsWith("#")).map(Dependency::fromString) - .collect(Collectors.toCollection(TreeSet::new)); - } - - // Mark rule as not cachable - @Override public boolean isCacheable() { return false; } - @Override public boolean isResultValid(EnforcerRule r) { return false; } - @Override public String getCacheId() { return ""; } - -} diff --git a/vespa-enforcer-extensions/src/test/java/com/yahoo/vespa/maven/plugin/enforcer/EnforceDependenciesAllProjectsTest.java b/vespa-enforcer-extensions/src/test/java/com/yahoo/vespa/maven/plugin/enforcer/EnforceDependenciesAllProjectsTest.java deleted file mode 100644 index 59062cbd61c..00000000000 --- a/vespa-enforcer-extensions/src/test/java/com/yahoo/vespa/maven/plugin/enforcer/EnforceDependenciesAllProjectsTest.java +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.vespa.maven.plugin.enforcer; - -import com.yahoo.vespa.maven.plugin.enforcer.EnforceDependenciesAllProjects.Dependencies; -import com.yahoo.vespa.maven.plugin.enforcer.EnforceDependenciesAllProjects.Dependency; -import org.apache.maven.enforcer.rule.api.EnforcerRuleException; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.io.TempDir; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Set; -import java.util.SortedSet; -import java.util.TreeSet; - -import static com.yahoo.vespa.maven.plugin.enforcer.EnforceDependenciesAllProjects.validateDependencies; -import static com.yahoo.vespa.maven.plugin.enforcer.EnforceDependenciesAllProjects.writeDependencySpec; -import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; - -/** - * @author bjorncs - */ -class EnforceDependenciesAllProjectsTest { - - private static final Path POM_FILE = Paths.get("/vespa-src/pom.xml"); - - @Test - void succeeds_dependencies_matches_spec() { - SortedSet<Dependency> nonTest = new TreeSet<>(Set.of( - Dependency.fromString("com.example:foo:1.2.3"), - Dependency.fromString("com.example:bar:2.3.4"))); - SortedSet<Dependency> testOnly = new TreeSet<>(Set.of( - Dependency.fromString("com.example:testfoo:1.2.3"), - Dependency.fromString("com.example:testbar:2.3.4"))); - Path specFile = Paths.get("src/test/resources/allowed-dependencies.txt"); - Dependencies deps = new Dependencies(nonTest, testOnly); - assertDoesNotThrow(() -> validateDependencies(deps, specFile, POM_FILE, "my-dep-enforcer")); - } - - @Test - void fails_on_forbidden_dependency() { - SortedSet<Dependency> nonTest = new TreeSet<>(Set.of( - Dependency.fromString("com.example:foo:1.2.3"), - Dependency.fromString("com.example:bar:2.3.4"), - Dependency.fromString("com.example:foobar:3.4.5"))); - SortedSet<Dependency> testOnly = new TreeSet<>(Set.of( - Dependency.fromString("com.example:testfoo:1.2.3"), - Dependency.fromString("com.example:testbar:2.3.4"))); - Path specFile = Paths.get("src/test/resources/allowed-dependencies.txt"); - Dependencies deps = new Dependencies(nonTest, testOnly); - var exception = assertThrows(EnforcerRuleException.class, - () -> validateDependencies(deps, specFile, POM_FILE, "my-dep-enforcer")); - String expectedErrorMessage = - """ - The dependency enforcer failed: - Forbidden non-test dependencies: - - com.example:foobar:3.4.5 - Maven dependency validation failed. If this change was intentional, update the dependency spec by running: - $ mvn validate -DdependencyEnforcer.writeSpec -pl :my-dep-enforcer -f /vespa-src/pom.xml - """; - assertEquals(expectedErrorMessage, exception.getMessage()); - } - - @Test - void fails_on_missing_dependency() { - SortedSet<Dependency> nonTest = new TreeSet<>(Set.of( - Dependency.fromString("com.example:bar:2.3.4"))); - SortedSet<Dependency> testOnly = new TreeSet<>(Set.of( - Dependency.fromString("com.example:testfoo:1.2.3"))); - Path specFile = Paths.get("src/test/resources/allowed-dependencies.txt"); - Dependencies deps = new Dependencies(nonTest, testOnly); - var exception = assertThrows(EnforcerRuleException.class, - () -> validateDependencies(deps, specFile, POM_FILE, "my-dep-enforcer")); - String expectedErrorMessage = - """ - The dependency enforcer failed: - Removed non-test dependencies: - - com.example:foo:1.2.3 - Removed test-only dependencies: - - com.example:testbar:2.3.4 - Maven dependency validation failed. If this change was intentional, update the dependency spec by running: - $ mvn validate -DdependencyEnforcer.writeSpec -pl :my-dep-enforcer -f /vespa-src/pom.xml - """; - assertEquals(expectedErrorMessage, exception.getMessage()); - } - - @Test - void writes_valid_spec_file(@TempDir Path tempDir) throws IOException { - SortedSet<Dependency> nonTest = new TreeSet<>(Set.of( - Dependency.fromString("com.example:foo:1.2.3"), - Dependency.fromString("com.example:bar:2.3.4"))); - SortedSet<Dependency> testOnly = new TreeSet<>(Set.of( - Dependency.fromString("com.example:testfoo:1.2.3"), - Dependency.fromString("com.example:testbar:2.3.4"))); - Dependencies deps = new Dependencies(nonTest, testOnly); - Path outputFile = tempDir.resolve("allowed-dependencies.txt"); - writeDependencySpec(outputFile, deps); - assertEquals( - Files.readString(Paths.get("src/test/resources/allowed-dependencies.txt")), - Files.readString(outputFile)); - - } - -}
\ No newline at end of file diff --git a/vespa-enforcer-extensions/src/test/resources/allowed-dependencies.txt b/vespa-enforcer-extensions/src/test/resources/allowed-dependencies.txt deleted file mode 100644 index 2ef0f9e0c0c..00000000000 --- a/vespa-enforcer-extensions/src/test/resources/allowed-dependencies.txt +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. - -#[non-test] -# Contains dependencies that are not used exclusively in 'test' scope -com.example:bar:2.3.4 -com.example:foo:1.2.3 - -#[test-only] -# Contains dependencies that are used exclusively in 'test' scope -com.example:testbar:2.3.4 -com.example:testfoo:1.2.3 |