Я изо всех сил пытаюсь использовать SBT для процесса CI с этим основным рабочим процессом:
- тесты компиляции
- кеш
~/.sbt
и ~/.ivy2/cache
- кэшировать все
target
каталогов в моем проекте
На следующем шаге:
- восстановление
~/.sbt
и ~/.ivy2/cache
- восстановить полный проект, включая ранее сгенерированные каталоги
target
с содержащимися .class
файлами и идентичным исходным кодом (это должна быть такая же проверка)
- запускать тесты через
sbt test
100% времени, sbt test
перекомпилирует весь проект. Я хотел бы понять или отладить, почему это так, поскольку с момента последней компиляции ничего не изменилось (ну, ничего не должно измениться , так что заставляет его верить, что что-то изменилось?)
В настоящее время я использую окружность с исполнителем докера. Это означает, что для каждого шага выполняется новый экземпляр 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 ...
Что я могу сделать, чтобы определить, почему во второй раз происходит перекомпиляция всех источников, и облегчить его?
Я запускаю sbt 1.2.8 с scala 2.12.8 в контейнере linux.
Обновление
Я не решил проблему, но решил, что поделюсь, как решить худшую из моих проблем.
Основная проблема: отдельная «тестовая компиляция» с «тестовым прогоном»
Вторичная проблема: более быстрая сборка без перекомпиляции всего при каждом нажатии
У меня нет решения для вторичной. Для основного:
Я могу запустить самый большой бегун из CLI через scala -cp ... org.scalatest.tools.Runner
, а не через sbt test
, чтобы избежать любых попыток перекомпиляции. Бегун может работать с каталогом .class
файлов.
Сводка изменений:
- Обновите док-контейнер, включив в него установку scala cli. (К сожалению, сейчас мне нужно синхронизировать эти версии)
- Фаза сборки:
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
- запускает масштабируемо через runner, используя скомпилированные файлы
.class
в target/scala-2.12/test-classes
, используя путь к классам, о котором сообщалось на этапе компиляции, и printint в стандартный вывод, а также каталог отчетов
Мне это не нравится, и у него есть некоторые проблемы, но я решил поделиться этим обходным путем.