barbitoff programmer`s blog

Здесь я публикую заметки из программерской жизни: грабли, на которые мне случилось наступить, проблемы, для которых было найдено элегантное (или не очень) решение, а также все, с чем мне пришлось столкнуться и чем хотелось бы поделиться =)
PS Если хотите меня поблагодарить - на странице есть 3 места, чтобы это сделать =)

понедельник, 13 апреля 2020 г.

Grep: поиск строки, начинающейся с дефиса

Задача

Выполнить поиск подстроки, начинающейся с "-", например, "->". При попытке сделать grep "->" получаем ошибку grep: invalid option -- '>', т.к. grep трактует искомую строку как опцию.

Решение

grep -- "->"

понедельник, 6 апреля 2020 г.

Gradle 6 Jacoco plugin: как исключить пакеты и классы из отчетов и верификации

В конфигурацию тасков jacocoTestReport и/или jacocoTestCoverageVerification добавляем:
    afterEvaluate {
        classDirectories.setFrom(files(classDirectories.files.collect {
            fileTree(dir: it, exclude: [
                    '**/poc/**',
                    '**/model/**',
                    '**/*Config.class',
                    '**/*Application.class'

            ])
        })
        )
    }
Где, собственно, и указываем маски на папки и файлы для исключения. 

среда, 18 марта 2020 г.

Spring boot 2.2.5 и Tinkoff openapi-java-sdk-java8 0.4.1: конфликт версий okhttp

Есть проект, собираемый Gradle. При попытке использовать Tinkoff openapi-java-sdk-java8 версии 0.4.1 вместе со Spring boot 2.2.5 возникает ошибка вида:
An attempt was made to call a method that does not exist. The attempt was made from the following location:

     ru.tinkoff.invest.openapi.okhttp.SandboxContextImpl.performRegistration(SandboxContextImpl.java:45)

The following method did not exist:

     okhttp3.RequestBody.create([B)Lokhttp3/RequestBody;
Если сделать gradle dependencies, то видна и причина данной ошибки:
com.squareup.okhttp3:okhttp:4.3.1 -> 3.14.7
Видно, что версия okhttp, требуемая для openapi-java-sdk, даунгэйдится до 3.14.7. Причина - в https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-dependencies/2.2.5.RELEASE, где определяется версия 3.14.7. 
Выход из ситуации - добавление в build.gradle:
ext['okhttp3.version'] = '4.3.1'
В целом про переопределение версий зависимостей, диктуемых spring boot'ом, написано тут: https://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#howto-customize-dependency-versions.

четверг, 5 марта 2020 г.

minikube для Windows: как дать больше памяти?

При старте minikube есть возможность установить опцию --memory с большим/меньшим размером памяти, чем дефолтные 3Гб. Однако, чтобы эта опция действительно применялась, придется пересоздать кластер, т.е. сделать:
minikube stop
minikube delete
minikube start --memory=4096m
Тогда получим в логе заветные:
Creating hyperv VM (CPUs=2, Memory=4096MB, Disk=20000MB)
, которых нет при обычном stop/start.

Необходимость удаления вызвана тем, что объем памяти - это параметр HyperV VM, которая создается единожды и при последующих запусках лишь переиспользуется. Возможно, есть возможность поменять объем памяти HyperV VM без ее удаления, но по крайней мере средствами minikube cli этого не сделать.

пятница, 21 февраля 2020 г.

echo и многострочные переменные окружения

Задача

Есть переменная окружения, содержащая текст с несколькими строками. Нужно сделать echo этой переменной так, чтобы не потерялись переводы строк.

Решение

Если делать просто echo $var, переносы строки теряются. А вот если заключить переменную в кавычки:
echo "$var"
,то строки выводятся корректно.

среда, 19 февраля 2020 г.

GitLab CI/CD и Gradle: корректное кэширование и передача результатов запуска между джобами

Кейс

Есть проект, собираемый при помощи Gradle, и CI/CD-пайплайн в GitLab, выполняющий некоторые Gradle-таски на разных stage. Необходимо:

  1. Реализовать кэширование зависимостей, чтобы не выкачивать их заново на каждом stage, а также при каждом новом запуске pipeline
  2. Реализовать кэширование результатов сборки, чтобы последующий stage выполнял не всю сборку "с нуля", а использовал результаты предыдущих stage'й
Решение

Во-первых, настраиваем GRADLE_USER_HOME, чтобы он размещался внутри сборочной директории, и настраиваем кэширование для папки с зависимостями внутри GRADLE_USER_HOME в соответствии с https://docs.gradle.org/6.1.1/userguide/dependency_resolution.html#sec:dependency_cache:
before_script:
  - export GRADLE_USER_HOME=`pwd`/.gradle
myjob:
  ...
  cache:
    key: gradle_modules_cache
    paths:
      - .gradle/caches/modules-2
    policy: pull-push
Т.к. это кэш, он будет разделяться не только джобами внутри пайплайна, но и между разными пайплайнами.

Во-вторых, в settings.gradle настраиваем кастомную директорию для билд-кэша (см. https://docs.gradle.org/current/userguide/build_cache.html):
buildCache {
    local {
        directory = new File(rootDir, 'build-cache')
    }
}
и для промежуточных джобов настраиваем публикацию этой директории в качестве артефакта:
  artifacts:
    paths:
      - build-cache/
    expire_in: 1 hour
В последующих джобах прописываем dependencies на предыдущие джобы, чтобы они подхватывали build-cache/ оттуда. Таким образом, build cache будет передаваться между джобами, но не будет передаваться между пайплайнами, что как раз корректно.

Ну и последнее - при вызове gradle используем --build-cache, чтобы собственно включить использования билд-кэша.

Вуаля, в логах сборки видим, что результаты предыдущих джобов подтягиваются из кэша: