在此基本工作流程中,我正努力将SBT用于CI流程:
~/.sbt
和~/.ivy2/cache
target
目录在后续步骤中:
~/.sbt
和~/.ivy2/cache
target
目录以及包含的.class
文件和相同的源代码(应该为 same 结帐)sbt test
100%的时间,sbt test
重新编译整个项目。我想了解或调试为什么会这样,因为自上次编译以来没有任何变化(嗯,应该没有变化,所以是什么导致它相信某些东西呢?)
我目前正在将circleci与docker executor一起使用。这意味着从同一张图片开始,有一个新的docker实例运行每个步骤,尽管我希望缓存可以解决这个问题。
.circleci/config.yml
的相关部分(如果您不使用圆形,则该部分应该还是可以使用的;我已经注释了我可以做的事):
---
version: 2
jobs:
# compile and cache compilation
test-compile:
working_directory: /home/circleci/myteam/myproj
docker:
- image: myorg/myimage:sbt-1.2.8
steps:
# the directory to be persisted (cached/restored) to the next step
- attach_workspace:
at: /home/circleci/myteam
# git pull to /home/circleci/myteam/myproj
- checkout
- restore_cache:
# look for a pre-existing set of ~/.ivy2/cache, ~/.sbt dirs
# from a prior build
keys:
- sbt-artifacts-{{ checksum "project/build.properties"}}-{{ checksum "build.sbt" }}-{{ checksum "project/Dependencies.scala" }}-{{ checksum "project/plugins.sbt" }}-{{ .Branch }}
- restore_cache:
# look for pre-existing set of 'target' dirs from a prior build
keys:
- build-{{ checksum "project/build.properties"}}-{{ checksum "build.sbt" }}-{{ checksum "project/Dependencies.scala" }}-{{ checksum "project/plugins.sbt" }}-{{ .Branch }}
- run:
# the compile step
working_directory: /home/circleci/myteam/myproj
command: sbt test:compile
# per: https://www.scala-sbt.org/1.0/docs/Travis-CI-with-sbt.html
# Cleanup the cached directories to avoid unnecessary cache updates
- run:
working_directory: /home/circleci
command: |
rm -rf /home/circleci/.ivy2/.sbt.ivy.lock
find /home/circleci/.ivy2/cache -name "ivydata-*.properties" -print -delete
find /home/circleci/.sbt -name "*.lock" -print -delete
- save_cache:
# cache ~/.ivy2/cache and ~/.sbt for subsequent builds
key: sbt-artifacts-{{ checksum "project/build.properties"}}-{{ checksum "build.sbt" }}-{{ checksum "project/Dependencies.scala" }}-{{ checksum "project/plugins.sbt" }}-{{ .Branch }}-{{ .Revision }}
paths:
- /home/circleci/.ivy2/cache
- /home/circleci/.sbt
- save_cache:
# cache the `target` dirs for subsequenet builds
key: build-{{ checksum "project/build.properties"}}-{{ checksum "build.sbt" }}-{{ checksum "project/Dependencies.scala" }}-{{ checksum "project/plugins.sbt" }}-{{ .Branch }}-{{ .Revision }}
paths:
- /home/circleci/myteam/myproj/target
- /home/circleci/myteam/myproj/project/target
- /home/circleci/myteam/myproj/project/project/target
# in circle, a 'workflow' undergoes several jobs, this first one
# is 'compile', the next will run the tests (see next 'job' section
# 'test-run' below).
# 'persist to workspace' takes any files from this job and ensures
# they 'come with' the workspace to the next job in the workflow
- persist_to_workspace:
root: /home/circleci/myteam
# bring the git checkout, including all target dirs
paths:
- myproj
- persist_to_workspace:
root: /home/circleci
# bring the big stuff
paths:
- .ivy2/cache
- .sbt
# actually runs the tests compiled in the previous job
test-run:
environment:
SBT_OPTS: -XX:+UseConcMarkSweepGC -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -Duser.timezone=Etc/UTC -Duser.language=en -Duser.country=US
docker:
# run tests in the same image as before, but technically
# a different instance
- image: myorg/myimage:sbt-1.2.8
steps:
# bring over all files 'persist_to_workspace' in the last job
- attach_workspace:
at: /home/circleci/myteam
# restore ~/.sbt and ~/.ivy2/cache via `mv` from the workspace
# back to the home dir
- run:
working_directory: /home/circleci/myteam
command: |
[[ ! -d /home/circleci/.ivy2 ]] && mkdir /home/circleci/.ivy2
for d in .ivy2/cache .sbt; do
[[ -d "/home/circleci/$d" ]] && rm -rf "/home/circleci/$d"
if [ -d "$d" ]; then
mv -v "$d" "/home/circleci/$d"
else
echo "$d does not exist" >&2
ls -la . >&2
exit 1
fi
done
- run:
# run the tests, already compiled
# note: recompiles everything every time!
working_directory: /home/circleci/myteam/myproj
command: sbt test
no_output_timeout: 3900s
workflows:
version: 2
build-and-test:
jobs:
- test-compile
- test-run:
requires:
- test-compile
第二阶段的输出通常如下:
#!/bin/bash -eo pipefail
sbt test
[info] Loading settings for project myproj-build from native-packager.sbt,plugins.sbt ...
[info] Loading project definition from /home/circleci/myorg/myproj/project
[info] Updating ProjectRef(uri("file:/home/circleci/myorg/myproj/project/"), "myproj-build")...
[info] Done updating.
[warn] There may be incompatibilities among your library dependencies; run 'evicted' to see detailed eviction warnings.
[info] Compiling 1 Scala source to /home/circleci/myorg/myproj/project/target/scala-2.12/sbt-1.0/classes ...
[info] Done compiling.
[info] Loading settings for project root from build.sbt ...
[info] Set current project to Piranha (in build file:/home/circleci/myorg/myproj/)
[info] Compiling 1026 Scala sources to /home/circleci/myorg/myproj/target/scala-2.12/classes ...
我该怎么确定为什么这是第二次重新编译所有源代码并缓解它?
我正在linux容器中运行带有scala 2.12.8的sbt 1.2.8。
更新
我还没有解决问题,但我想针对最严重的问题我可以共享一种解决方法。
主要问题:将“测试编译”与“测试运行”分开 第二个问题:构建速度更快,而不必在每次推送时都重新编译所有内容
我对中学没有解决方案。对于主要对象:
我可以通过scala -cp ... org.scalatest.tools.Runner
而不是通过sbt test
从CLI运行scalatest runner,以避免尝试重新编译。运行程序可以对.class
个文件的目录进行操作。
更改摘要:
sbt test:compile 'inspect run' 'export test:fullClasspath' | tee >(grep -F '.jar' > ~test-classpath.txt)
scala -cp VALUE_HERE
中以运行测试scala -cp "$(cat test-classpath.txt)" org.scalatest.tools.Runner -R target/scala-2.12/test-classes/ -u target/test-reports -oD
.class
中已编译的target/scala-2.12/test-classes
文件,使用在编译阶段报告的类路径,printint到stdout以及报告目录,通过运行程序运行scalatest。我不喜欢这个并且它有一些问题,但是我想我会分享这个解决方法。
答案 0 :(得分:0)
我也在gitlab作业中使用sbt 1.2.8遇到了这个问题。以前(在sbt 0.13中)缓存target
目录工作正常。
现在,我正尝试通过设置
进行手动调试logLevel := Level.Debug,
incOptions := incOptions.value.withApiDebug(true).withRelationsDebug(true),
在我的作品中。这应该打印出无效的原因。但是,它产生了太多的输出,无法在CI中运行,所以我在重现遇到问题的确切条件时遇到了麻烦。
答案 1 :(得分:0)
SBT对于重新编译非常挑剔,Docker给它带来了特别的麻烦。
看看:
答案 2 :(得分:0)
我在travis版本中遇到了类似的问题,我怀疑此解决方案也适用于circle-ci。根本原因是缓存存储为tar文件,因此文件的修改时间只有第二个分辨率。您可以指定具有足够分辨率的格式。对我来说,解决方案是创建一个小脚本travis_tar.sh
:
#!/bin/bash
/bin/tar-orig --format=posix $@
然后用以下脚本替换系统tar:
sudo mv /bin/tar /bin/tar-orig
sudo mv .travis/travis_tar.sh /bin/tar
sudo chmod +x /bin/tar
这种情况可能会在加载缓存后发生,香草系统tar会解压缩posix格式的tar文件。
答案 3 :(得分:0)
如果您使用的SBT版本高于1.0.4,则缓存将对您不起作用,因为编译器将始终使所有内容无效。 此锌编译器问题已在此处报告:https://github.com/sbt/sbt/issues/4168
我的建议是降低CI的sbt版本。还可以检查并验证CI是否正在更改.sbt或.ivy2文件时间戳。如果更改了它们,请通过压缩和解压缩来分别缓存它们。
我在Bitbucket Pipelines CI上遇到了同样的问题,并设法使其here成功地工作了
答案 4 :(得分:0)
我有同样的问题。我放弃了试图使所有时间戳匹配的尝试,最终发现我可以使用:
@MockBean
private LoggingService loggingService;
@Before
public void setUp() {
myModelClass = new MyModelclass();
myModelClass.setStatus("U");
myModelClass.setTermCode("001");
myModelClass.setLocation("BGC");
}
@Test
public void testObjectToJson() throws JsonProcessingException {
Mockito.when(utils.mapToJsonString(myModelClass)).thenReturn("");
String output = utils.mapToJsonString(myModelClass);
assertThat(output).isEqualTo(utils.mapToJsonString(""));
}
@Test(expected=IOException.class)
public void testJsonParsingException() {
utils.mapToJsonString(null);
String output = utils.mapToJsonString(myModelClass);
Mockito.when(utils.mapToJsonString(null)).thenThrow(new IOException());
assertThat(output).isNull();
}
它仍然不是完美的,sbt 'set Compile / compile / skip := true' 'test'
也许还有其他东西可以运行,但是肯定比没有它要好得多。