[GRADLE-1485] Snapshot dependency fails with "always" update strategy Created: 18/Apr/11  Updated: 04/Jan/13  Resolved: 02/Feb/12

Status: Resolved
Project: Gradle
Affects Version/s: 1.0-milestone-2
Fix Version/s: 1.0-milestone-8

Type: Bug
Reporter: Alexander Kitaev Assignee: Daz DeBoer
Resolution: Duplicate Votes: 2


 Description   

Project A is built by CI server contiuously and publishes its SNAPSHOT artifacts in the Maven repository.
Project B depends on project A SNAPSHOT version and I would like it to always check whether there is a new snapshot available. To achieve this I wrote the following build.gradle for project B:

version = '1.0.0-SNAPSHOT'
group = 'org.project.test'

apply plugin: 'java'

repositories

{ // there is no documented way to set "always" strategy for maven repository. mavenRepo(urls : [mavenPublicRepositoryURL]). setSnapshotTimeout(org.gradle.api.internal.artifacts.ivyservice.GradleIBiblioResolver.ALWAYS) }

dependencies

{ compile group : 'org.project.core', name: 'core', version: '1.1.0-SNAPSHOT' }

assemble << {
copy

{ from configurations.compile.files into 'build/dependencies' }

}

What happens on 'build' is:

compileJava:
ivy resolves and downloads new artifact of A.

compileTests:
test:
ivy resolves and downloads new artifact of A again (artifact actually didn't changed during build).
ivy fails trying to remove existing A's artifact:

19:06:06.728 [ERROR] [org.gradle.api.internal.artifacts.ivyservice.IvyLoggingAdaper] :: problems summary ::
19:06:06.731 [ERROR] [org.gradle.api.internal.artifacts.ivyservice.IvyLoggingAdaper] :::: ERRORS
19:06:06.731 [ERROR] [org.gradle.api.internal.artifacts.ivyservice.IvyLoggingAdaper] Couldn't delete outdated artifact from cache: C:\Users\alex\.gradle\cache\project\core\jars\core-1.1.0-SNAPSHOT.jar

I'd like to have an ability to specify ALWAYS update strategy for Maven repository so, that this strategy only triggers once per artifact during the whole build.

I probably could workaround this by setting custom resolver which return 'null' from 'findModuleInCache' only once for module (assuming module was found in cache), but so far didn't manage to write a custom resolver to solve this problem.

This issue is related to Gradle-1049, but I'd like to stress that in this case build always fails when new dependency snapshot becomes available.



 Comments   
Comment by Alexander Kitaev [ 18/Apr/11 ]

I've managed to write custom resolver that overrides "findModuleInCache" method.

When called first time for "SNAPSHOT" dependency cache expiration strategy is forced to ALWAYS. On subsequent calls for the same module cache strategy is set to NEVER.

 
import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
import org.apache.ivy.core.resolve.ResolveData;
import org.apache.ivy.core.resolve.ResolvedModuleRevision;
import org.gradle.api.artifacts.ResolverContainer;

class SnapshotAlwaysResolver extends org.gradle.api.internal.artifacts.ivyservice.GradleIBiblioResolver {

    def static Set cachedModules = new HashSet()

    def ResolvedModuleRevision findModuleInCache(DependencyDescriptor dd, ResolveData data) {
        synchronized(cachedModules) {
          def isSnapshot = dd != null && dd.dependencyRevisionId.getRevision().endsWith('-SNAPSHOT')

          def savedTimeout = getSnapshotTimeout()
          try {
            if (isSnapshot) {
              setSnapshotTimeout(cachedModules.add(dd.dependencyRevisionId) ? ALWAYS : NEVER)
            }
            return super.findModuleInCache(dd, data)
          } finally {
            setSnapshotTimeout(savedTimeout)
          }
        }
    }
}

allprojects {

apply plugin: 'java'

repositories {
   add(new SnapshotAlwaysResolver()) { 
       name ='tmatesoft' 
       root = mavenPublicRepositoryURL
       usepoms = true
       pattern = ResolverContainer.MAVEN_REPO_PATTERN
       m2compatible = true
       useMavenMetadata = true
   }
}

Note, that cachedModules set is static, so it is reused in all projects in the build.

With that resolver one should not set dependency 'changing' property to 'true' - in this case cache will never be used and snapshot will be downloaded multiple times (which causes crash). Also, I did not test it with dependencies on a non-default configuration of an artifact.

Frankly, after having to resolve issues like this (basically, inability to work with snapshot dependencies - new snapshot crashes the most simple build independently on the cache timeout settings) my initial excitement with Gradle somehow faded out However, I still able to fix it for my build, without patching Gradle itself - this is good thing

Comment by Shawn Ma [ 04/Nov/11 ]

Another strategy:

class MyTimeoutStrategy implements GradleIBiblioResolver.CacheTimeoutStrategy {
    int snapshots = 0;
    int resolved = 0;
    public MyTimeoutStrategy(int snapshots) { this.snapshots = snapshots; }
    public boolean isCacheTimedOut(long lastResolvedTime) {
        resolved ++
        return resolved <= snapshots
    }

    public String toString() { return "ALWAYS Timeout Strategy-revised"; }
}
repositories {
    mavenRepo (urls: 'http://xyz.com/repo') {
        snapshotTimeout = new MyTimeoutStrategy(1) // howmany SNAPSHOTs
    }
}
Comment by Daz DeBoer [ 02/Feb/12 ]

Duplicate of GRADLE-1049

Generated at Wed Jun 30 11:56:43 CDT 2021 using Jira 8.4.2#804003-sha1:d21414fc212e3af190e92c2d2ac41299b89402cf.