sbt всегда перекомпилирует полный проект в CI, даже с кэшированием? - PullRequest
7 голосов
/ 21 марта 2019

Я изо всех сил пытаюсь использовать SBT для процесса CI с этим основным рабочим процессом:

  1. тесты компиляции
  2. кеш ~/.sbt и ~/.ivy2/cache
  3. кэшировать все target каталогов в моем проекте

На следующем шаге:

  1. восстановление ~/.sbt и ~/.ivy2/cache
  2. восстановить полный проект, включая ранее сгенерированные каталоги target с содержащимися .class файлами и идентичным исходным кодом (это должна быть такая же проверка)
  3. запускать тесты через 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 файлов.

Сводка изменений:

  1. Обновите док-контейнер, включив в него установку scala cli. (К сожалению, сейчас мне нужно синхронизировать эти версии)
  2. Фаза сборки: sbt test:compile 'inspect run' 'export test:fullClasspath' | tee >(grep -F '.jar' > ~test-classpath.txt)
    • компилирует, но также записывает копируемую строку пути к классу, пригодную для передачи в scala -cp VALUE_HERE для запуска тестов
  3. тестовый этап: 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 в стандартный вывод, а также каталог отчетов

Мне это не нравится, и у него есть некоторые проблемы, но я решил поделиться этим обходным путем.

Ответы [ 2 ]

0 голосов
/ 05 июля 2019

SBT очень требователен к перекомпиляции, и Docker доставляет ему особые проблемы.

Взгляните на:

0 голосов
/ 01 апреля 2019

Я также сталкиваюсь с этим с sbt 1.2.8 в работе gitlab.Ранее (в sbt 0.13) кэширование каталогов target работало нормально.

Сейчас я пытаюсь отладить вручную, установив:

logLevel := Level.Debug,
incOptions := incOptions.value.withApiDebug(true).withRelationsDebug(true),

в моих сборках.Это должно напечатать причины недействительности.Однако он выдает слишком много выходных данных, чтобы работать в CI, поэтому у меня возникают проблемы с воспроизведением точных условий, в которых я вижу проблему.

...