diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 6ebd25ec..6567a38f 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -29,6 +29,7 @@ jobs: uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.sha }} + - name: Setup Go with cache uses: jfrog/.github/actions/install-go-with-cache@main @@ -43,5 +44,18 @@ jobs: java-version: "8" distribution: "zulu" + - name: Wait for Artifactory + run: | + for i in {1..30}; do + if curl -sf http://localhost:8081/artifactory/api/system/ping; then + echo "Artifactory is up!" + exit 0 + fi + echo "Waiting for Artifactory..." + sleep 10 + done + echo "Artifactory did not start in time" + exit 1 + - name: Run tests run: ./gradlew${{ matrix.gradlewSuffix }} clean test diff --git a/build.gradle b/build.gradle index 025705ce..f5860799 100644 --- a/build.gradle +++ b/build.gradle @@ -58,16 +58,48 @@ subprojects { sourceCompatibility = 1.8 targetCompatibility = 1.8 + // Force secure versions to fix vulnerabilities + configurations.all { + resolutionStrategy { + // Use latest confirmed available Jetty 9.4.x versions + force 'org.eclipse.jetty:jetty-server:9.4.56.v20240826' + force 'org.eclipse.jetty:jetty-servlets:9.4.56.v20240826' + force 'org.eclipse.jetty:jetty-http:9.4.56.v20240826' + force 'org.eclipse.jetty:jetty-util:9.4.56.v20240826' + force 'org.eclipse.jetty:jetty-io:9.4.56.v20240826' + force 'org.eclipse.jetty:jetty-client:9.4.56.v20240826' + force 'org.eclipse.jetty:jetty-security:9.4.56.v20240826' + force 'org.eclipse.jetty:jetty-servlet:9.4.56.v20240826' + force 'org.eclipse.jetty:jetty-webapp:9.4.56.v20240826' + force 'org.eclipse.jetty:jetty-proxy:9.4.56.v20240826' + force 'org.eclipse.jetty:jetty-continuation:9.4.56.v20240826' + force 'org.eclipse.jetty:jetty-util-ajax:9.4.56.v20240826' + force 'org.eclipse.jetty:jetty-xml:9.4.56.v20240826' + force 'org.eclipse.jetty.http2:http2-server:9.4.56.v20240826' + force 'org.eclipse.jetty.http2:http2-common:9.4.56.v20240826' + force 'org.eclipse.jetty.http2:http2-hpack:9.4.56.v20240826' + // Latest secure versions + force 'commons-io:commons-io:2.18.0' + force 'net.minidev:json-smart:2.5.2' + force 'com.jayway.jsonpath:json-path:2.9.0' + force 'com.google.guava:guava:33.4.0-jre' + force 'org.xmlunit:xmlunit-core:2.10.0' + } + + // Exclude problematic dependencies + exclude group: 'commons-fileupload', module: 'commons-fileupload' + } + dependencies { implementation('org.apache.httpcomponents:httpclient:4.5.13') { exclude group: 'commons-codec', module: 'commons-codec' } implementation 'commons-codec:commons-codec:1.13' - implementation 'org.apache.commons:commons-lang3:3.12.0' - implementation 'com.fasterxml.jackson.core:jackson-core:2.14.1' - implementation 'com.fasterxml.jackson.core:jackson-databind:2.14.1' - implementation 'com.fasterxml.jackson.core:jackson-annotations:2.14.1' - api 'org.jfrog.filespecs:file-specs-java:1.1.1' + implementation 'org.apache.commons:commons-lang3:3.18.0' + implementation 'com.fasterxml.jackson.core:jackson-core:2.19.1' + implementation 'com.fasterxml.jackson.core:jackson-databind:2.19.1' + implementation 'com.fasterxml.jackson.core:jackson-annotations:2.19.1' + api 'org.jfrog.filespecs:file-specs-java:1.1.2' } task sourcesJar(type: Jar, dependsOn: classes) { diff --git a/httpClient/build.gradle b/httpClient/build.gradle index 8c8dd627..de6d80c6 100644 --- a/httpClient/build.gradle +++ b/httpClient/build.gradle @@ -8,5 +8,5 @@ repositories { dependencies { testImplementation group: 'org.testng', name: 'testng', version: '7.5.1' - testImplementation group: 'com.github.tomakehurst', name: 'wiremock-jre8', version: '2.35.1' + testImplementation group: 'com.github.tomakehurst', name: 'wiremock-jre8', version: '2.35.0' } \ No newline at end of file diff --git a/httpClient/src/main/java/org/jfrog/artifactory/client/httpClient/http/HttpBuilderBase.java b/httpClient/src/main/java/org/jfrog/artifactory/client/httpClient/http/HttpBuilderBase.java index 349eb872..5be76fde 100644 --- a/httpClient/src/main/java/org/jfrog/artifactory/client/httpClient/http/HttpBuilderBase.java +++ b/httpClient/src/main/java/org/jfrog/artifactory/client/httpClient/http/HttpBuilderBase.java @@ -399,7 +399,7 @@ private SSLContext buildSslContext() { sslContext = sslBuilder.build(); } } catch (Exception e) { - e.printStackTrace(); + throw new RuntimeException("Error building SSLContext: " + e.getMessage(), e); } return sslContext != null ? sslContext : SSLContexts.createDefault(); } diff --git a/services/build.gradle b/services/build.gradle index 85d5b40f..86500c2f 100644 --- a/services/build.gradle +++ b/services/build.gradle @@ -7,7 +7,7 @@ dependencies { implementation addSlf4J('slf4j-api') implementation addSlf4J('log4j-over-slf4j') implementation addSlf4J('jcl-over-slf4j') - implementation 'commons-io:commons-io:2.17.0' + implementation 'commons-io:commons-io:2.18.0' testImplementation group: 'org.hamcrest', name: 'hamcrest-core', version: '2.2' testImplementation group: 'org.testng', name: 'testng', version: '7.5.1' /* @@ -15,7 +15,7 @@ dependencies { * https://github.com/jfrog/artifactory-client-java/issues/43 * https://github.com/jfrog/artifactory-client-java/issues/232 */ - testRuntimeOnly group: 'ch.qos.logback', name: 'logback-classic', version: '1.2.9' + testRuntimeOnly group: 'ch.qos.logback', name: 'logback-classic', version: '1.3.15' } task createReleasePropertiesFile(type: Exec) { diff --git a/services/src/test/groovy/org/jfrog/artifactory/client/BaseRepositoryTests.groovy b/services/src/test/groovy/org/jfrog/artifactory/client/BaseRepositoryTests.groovy index 911dd282..c495bd5e 100644 --- a/services/src/test/groovy/org/jfrog/artifactory/client/BaseRepositoryTests.groovy +++ b/services/src/test/groovy/org/jfrog/artifactory/client/BaseRepositoryTests.groovy @@ -57,6 +57,7 @@ abstract class BaseRepositoryTests extends ArtifactoryTestsBase { @BeforeMethod protected void setUp() { String id = Long.toString(repoUniqueId) + println "[SETUP] Starting test setup for repo id: $id at ${new Date()}" if (prepareGenericRepo) { RepositorySettings settings = getRepositorySettings(RepositoryTypeImpl.LOCAL) @@ -174,12 +175,12 @@ abstract class BaseRepositoryTests extends ArtifactoryTestsBase { @AfterMethod protected void tearDown() { - // Invoking sequence is important! - deleteRepoIfExists(genericRepo?.getKey()) - deleteRepoIfExists(localRepo?.getKey()) - deleteRepoIfExists(remoteRepo?.getKey()) - deleteRepoIfExists(federatedRepo?.getKey()) - deleteRepoIfExists(virtualRepo?.getKey()) + // Invoking sequence is important! Delete in reverse dependency order + deleteRepoWithRetry(virtualRepo?.getKey()) // Delete virtual repo first (depends on generic) + deleteRepoWithRetry(federatedRepo?.getKey()) + deleteRepoWithRetry(remoteRepo?.getKey()) + deleteRepoWithRetry(localRepo?.getKey()) + deleteRepoWithRetry(genericRepo?.getKey()) // Delete generic repo last (after dependents) repoUniqueId++ } diff --git a/services/src/test/groovy/org/jfrog/artifactory/client/TerraformPackageTypeRepositoryTests.groovy b/services/src/test/groovy/org/jfrog/artifactory/client/TerraformPackageTypeRepositoryTests.groovy index 1072b295..025919bf 100644 --- a/services/src/test/groovy/org/jfrog/artifactory/client/TerraformPackageTypeRepositoryTests.groovy +++ b/services/src/test/groovy/org/jfrog/artifactory/client/TerraformPackageTypeRepositoryTests.groovy @@ -15,6 +15,7 @@ class TerraformPackageTypeRepositoryTests extends BaseRepositoryTests { TerraformPackageTypeRepositoryTests() { remoteRepoUrl = "https://github.com" + storeArtifactsLocallyInRemoteRepo = true } @Override diff --git a/services/src/test/java/org/jfrog/artifactory/client/ArtifactoryTestsBase.java b/services/src/test/java/org/jfrog/artifactory/client/ArtifactoryTestsBase.java index 1401e0d7..804ecfda 100644 --- a/services/src/test/java/org/jfrog/artifactory/client/ArtifactoryTestsBase.java +++ b/services/src/test/java/org/jfrog/artifactory/client/ArtifactoryTestsBase.java @@ -26,6 +26,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.Properties; +import java.util.logging.Logger; import static org.apache.commons.codec.binary.Base64.encodeBase64; import static org.apache.commons.lang3.StringUtils.isEmpty; @@ -55,6 +56,7 @@ public abstract class ArtifactoryTestsBase { protected VirtualRepository virtualRepository; protected RemoteRepository remoteRepository; protected String federationUrl; + private static final Logger logger = Logger.getLogger(ArtifactoryTestsBase.class.getName()); @BeforeClass public void init() throws IOException { @@ -208,6 +210,40 @@ protected String textFrom(InputStream is) throws IOException { } } + protected void deleteRepoWithRetry(String repoKey) { + for (int attempt = 1; attempt <= 3; attempt++) { + try { + logger.info("Attempt " + attempt + " to delete repo: " + repoKey); + deleteRepoIfExists(repoKey); + logger.info("Successfully deleted repo: " + repoKey + " on attempt " + attempt); + return; + } catch (RuntimeException e) { + Throwable cause = e.getCause(); + logger.warning("Attempt " + attempt + " failed to delete repo: " + repoKey + ". Reason: " + e.getMessage()); + if (cause instanceof HttpResponseException && + ((HttpResponseException) cause).getStatusCode() == 500 && + cause.getMessage() != null && cause.getMessage().contains("Lock on LockEntryId")) { + + if (attempt < 3) { + logger.info("Lock detected. Retrying after 5 seconds..."); + try { + Thread.sleep(5000); + } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + logger.warning("Retry interrupted while waiting to retry repo deletion: " + repoKey); + return; + } + } else { + logger.severe("Failed to delete repo after 3 attempts due to lock: " + repoKey); + } + } else { + logger.severe("Non-lock error occurred. Not retrying. Repo: " + repoKey); + return; // Non-lock error, don't retry + } + } + } + } + protected String deleteRepoIfExists(String repoName) { if (isEmpty(repoName)) { return null; @@ -220,7 +256,8 @@ protected String deleteRepoIfExists(String repoName) { //if repo wasn't found - that's ok. return e.getMessage(); } else { - throw e; + // Wrap checked exception in a RuntimeException to avoid signature changes + throw new RuntimeException(e); } } }