Jenkins 2.0:在Docker容器中运行SBT

14

我有以下的Jenkinsfile

def notifySlack = { String color, String message ->
    slackSend(color: color, message: "${message}: Job ${env.JOB_NAME} [${env.BUILD_NUMBER}] (${env.BUILD_URL})")
}

node {
    try {
        notifySlack('#FFFF00', 'STARTED')
        stage('Checkout project') {
            checkout scm
        }
        scalaImage = docker.image('<myNexus>/centos-sbt:2.11.8')
        stage('Test project') {
            docker.withRegistry('<myNexus>', 'jenkins-nexus') {
                scalaImage.inside('-v /var/lib/jenkins/.ivy2:/root/.ivy2') { c ->
                    sh 'sbt clean test'
                }
            }
        }
        if (env.BRANCH_NAME == 'master') {
            stage('Release new version') {
                docker.withRegistry('<myNexus>', 'jenkins-nexus') {
                    scalaImage.inside('-v /var/lib/jenkins/.ivy2:/root/.ivy2') { c ->
                        sh 'sbt release'
                    }
                }
            }
        }
        notifySlack('#00FF00', 'SUCCESSFUL')
    } catch (e) {
        currentBuild.result = "FAILED"
        notifySlack('#FF0000', 'FAILED')
        throw e
    }

}

很不幸,当我执行sbt clean test命令时,会遇到以下错误:

java.lang.IllegalArgumentException: URI has a query component
    at java.io.File.<init>(File.java:427)
    at sbt.IO$.uriToFile(IO.scala:160)
    at sbt.IO$.toFile(IO.scala:135)
    at sbt.Classpaths$.sbt$Classpaths$$bootRepository(Defaults.scala:1942)
    at sbt.Classpaths$$anonfun$appRepositories$1.apply(Defaults.scala:1912)
    at sbt.Classpaths$$anonfun$appRepositories$1.apply(Defaults.scala:1912)
    at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:244)
    at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:244)
    at scala.collection.IndexedSeqOptimized$class.foreach(IndexedSeqOptimized.scala:33)
    at scala.collection.mutable.WrappedArray.foreach(WrappedArray.scala:34)
    at scala.collection.TraversableLike$class.map(TraversableLike.scala:244)
    at scala.collection.AbstractTraversable.map(Traversable.scala:105)
    at sbt.Classpaths$.appRepositories(Defaults.scala:1912)
    at sbt.Classpaths$$anonfun$58.apply(Defaults.scala:1193)
    at sbt.Classpaths$$anonfun$58.apply(Defaults.scala:1190)
    at scala.Function1$$anonfun$compose$1.apply(Function1.scala:47)
    at sbt.EvaluateSettings$MixedNode.evaluate0(INode.scala:175)
    at sbt.EvaluateSettings$INode.evaluate(INode.scala:135)
    at sbt.EvaluateSettings$$anonfun$sbt$EvaluateSettings$$submitEvaluate$1.apply$mcV$sp(INode.scala:69)
    at sbt.EvaluateSettings.sbt$EvaluateSettings$$run0(INode.scala:78)
    at sbt.EvaluateSettings$$anon$3.run(INode.scala:74)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)
如果我运行简单的docker run ...,然后跟着使用docker exec,我可以得到我想要的结果,但我想使用定义好的Jenkins功能。所以这似乎是SBT的问题。我在docker映像中使用版本0.13.16。据我所知,classpath包含一个查询参数,SBT:
  • 不喜欢
  • 不知道如何处理
  • 是非法的
我自己没有设置任何查询参数,所以我认为是这个.inside方法在做这件事。我检查了容器中的env,发现只有一个条目:RUN_CHANGES_DISPLAY_URL=<my_ip>/job/scheduler/job/fix-jenkins-pipeline/23/display/redirect?page=changes。我试图unset它,但没有成功。我想不出更多的主意,也不确定我走的方向是否正确。感谢任何帮助。
4个回答

15

经过漫长而繁琐的搜索,最终对我有效的方法是在Docker容器内明确设置.sbt.ivy2文件夹,如下所示:

所以,经过长时间而繁琐的搜索后,我发现在Docker容器内明确设置.sbt.ivy2文件夹最终适用于我。

sbt -Dsbt.global.base=.sbt -Dsbt.boot.directory=.sbt -Dsbt.ivy.home=.ivy2 clean test

这样可以防止sbt生成?文件夹,直接将上述文件夹放在检出目录的根目录中。


看起来 ? 目录名称是由 https://bugs.openjdk.org/browse/JDK-8193433 引起的。 - sschuberth

1
我在代码中花费了很多时间追踪这个问题。
看起来最简单的解决方案就是将-Duser.home=<path>传递给sbt,或者在SBT_OPTS环境变量中设置它;然后所有其余的目录都将被构建,就好像<path>是用户的主目录一样。

1
问题在于 Jenkins 在容器内使用特定用户运行。但覆盖该用户即可解决问题。
withDockerContainer(args: "-u root -v ${HOME}/.sbt:/root/.sbt -v ${HOME}/.ivy2:/root/.ivy2 -e HOME=/root",
            image: 'xyz/sbt:v') {

0

我通过设置Ivy的缓存目录解决了这个问题 -> 如何覆盖Ivy的缓存位置?

问题在于它没有设置,默认情况下会创建一个 ? 文件夹,无法被sbt本身处理。

我创建了一个自定义的Dockerfile以更好地控制sbt。以下是我执行的步骤:

我创建了一个名为ivysettings.xml的文件,具有以下内容:

<ivysettings>
    <properties environment="env" />
    <caches defaultCacheDir="/home/jenkins/.ivy2/cache" />
</ivysettings>

还有一个 Dockerfile:

FROM openjdk:8

RUN wget -O- "http://downloads.lightbend.com/scala/2.11.11/scala-2.11.11.tgz" \
    | tar xzf - -C /usr/local --strip-components=1

RUN curl -Ls https://git.io/sbt > /usr/bin/sbt && chmod 0755 /usr/bin/sbt

RUN adduser -u 1000 --disabled-password --gecos "" jenkins

ADD ./files/ivysettings.xml /home/jenkins/.ivy2/ivysettings.xml
RUN chown -R jenkins:jenkins /home/jenkins

USER jenkins

CMD ["sbt"]

然后我将图像推送到我们的私有Docker仓库,我们的流水线终于可以工作了!


我在这方面没有成功。你能否与我分享你的 Jenkinsfilebuild.sbtivysettings.xml 的示例?我会非常感激。 - zaxme
我看到在执行checkout scm命令后,Jenkins中的git克隆会生成一个?文件夹。它包含.ivy2.sbt文件。这个文件夹的作用是什么? - zaxme
通常 ? 文件夹是 SBT 用于存储 ivy2 和 SBT 自身缓存的文件夹。使用默认的 SBT 安装,该文件夹将是主目录(~$HOME)。由于大多数 Docker 映像都使用自定义安装,因此缓存路径未设置,需要在之前进行配置。 我会在下班后立即更新问题,提供更多信息。 - Julian Pieles
@zaxme 我更新了我的答案。希望它有所帮助。我相信一定有更好/更方便的解决方案,但现在这个可以工作。 - Julian Pieles

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接