Skip to content

forceUpdatePeriod doesn't work with non-fresh sbt run #6512

@unkarjedy

Description

@unkarjedy

relates to Add forceUpdatePeriod
@ajsquared

Steps

sbt.version = 1.5.2

Part 1 (publish some SNAPSHOT library)

  1. Create some dummy sbt project as a library.
    Note, that it should be published to some artifactory (thus no via publishLocal)
name := "my-library-3"

ThisBuild / scalaVersion := "2.13.5"
ThisBuild / organization := "org.example"
ThisBuild / version      := "0.1-SNAPSHOT"

// Configure your own repository here
credentials += Credentials(Path.userHome / ".sbt" / "my.credentials")
resolvers += MyResolvers.myRepository
publishTo := Some(MyResolvers.myRepository)
  1. In the library sources create some test object with method foo1
object MyObject3 {
  def foo1(): Unit = {
    println(s"hello from: ${this.getClass}")
  }
}
  1. Publish the library with publish command

Part 2 (use the library in another project)

  1. Create another sbt project which will use the library:
import scala.concurrent.duration.DurationInt

name := "library-reindex"
version := "0.1"
scalaVersion := "2.13.5"

ThisBuild / forceUpdatePeriod := Some(0.seconds) // doesn't help :(

libraryDependencies ++= Seq(
  "org.example" %% "my-library-3" % "0.1-SNAPSHOT" /*withSources()*/,
)

myTask2 := myTaskDef2.value

// TODO: provide same resolvers as in library project
credentials += ???
resolvers += ???
  1. In this project create some task which depends on update task:
    in build.sbt add:
myTask2 := myTaskDef2.value

in project/commons.scala:

import org.jetbrains.sbt.CreateTasks.`enrich TaskKey`
import org.jetbrains.sbt.{Options, StructureKeys, UpdateReportAdapter}
import sbt.Def.Initialize
import sbt._

object commons {
  def myTaskDef2: Initialize[Task[String]] = Def.task {
    val result = Keys.update.value
    println(s"! $result")
    "done task 2"
  }
}
  1. Run sbt for the second project

  2. Run update just in case

  3. Ensure that the library was resolved by courier.
    Go to something like:
    .../Coursier/cache/v1/https/<repository_path>/org/example//my-library-3_2.13/0.1-SNAPSHOT
    and ensure that my-library-3_2.13-0.1-SNAPSHOT.jar exits

Part 3 (modify the snapshot library, republish, and see that it's not updated when update is called indirectly)

  1. First, ensure that you disable Coursier TTL.
    Without this, whatever you do in SBT, Coursier will not update the library from the server.
    According to https://get-coursier.io/docs/ttl you can disable TTL using COURSIER_TTL=0 environment variable.
    Also you can use JVM property -Dlmcoursier.internal.shaded.coursier.ttl=0.
    (BTW, it should be -Dcoursier.ttl=0 according to the coursier sources, but for some reason, in SBT it's different (I inferred it from the bytecode). Probably this is due to the shading rules: https://github.com/coursier/sbt-coursier/blob/master/build.sbt#L57)

  2. Change method from foo1 to foo2 in the library project, and republish it using publish

  3. Go to the second project, open sbt and do update

  4. See that library jar was updated.
    Class contains method foo2 now.
    This is the expected behaviour according to the docs:
    Directly running the update task (as opposed to a task that depends on it) will force resolution to run, whether or not configuration changed.

You can use this bash script to quickly check the .jar in the coursier cache folder:

stat my-library-3_2.13-0.1-SNAPSHOT.jar ; \echo "" ; \
unzip -p my-library-3_2.13-0.3-SNAPSHOT.jar MyObject3$.class > temp.class ; javap temp.class | grep foo
  1. Change method from foo2 to foo3 in the library project, and republish it using publish
  2. Go to the second project, open sbt and run myTask2.
    Note 1: myTask2 depends on update
    Note 2: the project has ThisBuild / forceUpdatePeriod := Some(0.seconds) setting
  3. See that the library jar WAS NOT updated. The class still contains foo2
  4. Close sbt in the second project , remove all target folders, restart sbt
  5. Again, run myTask2
  6. See that the library jar WAS updated. The class now contains foo3

problem

When you have a non-fresh SBT context
(you already run some commands and caches in target folder are poluted)
forceUpdatePeriod setting doesn't work as expected.

expectation

forceUpdatePeriod should force update in any circumstances
After step 13 and 14 the class should contain method foo3

notes

The code responsible for that is located in
sbt.Classpaths.updateTask0
Looks like some wrong folder is used to detect the last update timestamp:

val fullUpdateOutput = cacheDirectory / "out"

in the debugger fullUpdateOutput.exists() returned false when I was doing myTask2

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions