Расскажу про автоматизацию рутинных задач, а точнее, про проект taskfile.dev.

Историческая справка

В 1976 году (почти 50 лет назад!) была создана утилита make, которая по сей день прекрасно выполняет свою задачу.

Разработчик может описать какие-то шаблонные действия в файле Makefile и получить набор именованных сценариев.

Набираем make build, и оно собирается.

Набираем make test, и оно тестируется.

Благолепие.

Makefile устарел

В современной разработке, с Makefile встречается ряд неудобств:

  • Адепты env-подхода из 12-factor app могут встретить сложности в сценариях, когда нужно использовать значение из env-переменной или, если его нет, то значение по умолчанию

  • Нет возможности группировать сценарии, вроде build:all, который включал бы в себя build:deb, и прочие build:docker надо писать отдельный сценарий build-all и вручную поддерживать актуальный список наследников

  • Нет механизмов дедупликации, чтобы не гонять дважды одну и ту же долгую сборку, когда поменялся только README

  • И, наконец, нет удобных свистоперделок!

В общем, ручная дрель делает свою работу, но каким-то условным перфоратором от Bosch (у меня такой) оно как-то сподручней.

И вот приходит Taskfile

На этот прекрасный проект я наткнулся в октябре 2022 года.

Вот тут его GitHub: go-task/task. Конечно же, там внутри Golang.

Суть проекта сводится к попытке пересмотреть Makefile на более современный лад.

Ставится в виде одного бинаря, обычно пакет называется task или go-task, из-за пересечения с проектом Taskwarrior.

Что Taskfile умеет?

Ну, во-первых, теперь надо писать не странный свой-собственный-синтаксис, а прекрасный/богомерзкий YAML.

Оно умеет в глобальные и per task env-переменные, а также в .env-файлы:

env:
  ENV: testing
  GREETING: Hey, there!

dotenv:
  - '.env'
  - '{{.ENV}}/.env.'
  - '{{.HOME}}/.env'

tasks:
  greet:
    cmds:
      - 'echo "$ENV: $GREETING"'

Оно умеет в platform-specific таски:

tasks:
  build-windows:
    platforms:
      - windows
    cmds:
      - echo 'Running command on Windows'

Оно умеет в зависимости, когда мы хотим выполнить таску test только после таски build:

tasks:
  build:
    deps: [assets]
    cmds:
      - go build -v -i main.go

  assets:
    cmds:
      - minify -o public/style.css src/css

Оно умеет в предусловия, когда мы хотим перед выполнением таски build проверить, что в системе есть всякие там gcc, make и прочие maven-ы:

tasks:
  up:
    dir: '{{.USER_WORKING_DIR}}'
    preconditions:
      - test -f docker-compose.yml
    cmds:
      - docker-compose up -d

Ну и множество всего другого прочего, дока с примерами доступна по ссылке.

Реальный пример

Вот живой кусок из одного pet-проекта:

---

version: '3'

vars:
  KUBE_CONTEXT: my-k8s
  KUBE_CONFIG: ~/.kube/my-k8s.yaml

tasks:
  secrets:seal:
    desc: Seal secrets
    deps: []
    preconditions:
      - kubeseal --version
      - test -f kubernetes/config.secret.yaml
    cmds:
      - >-
        kubeseal \
          --context {{.KUBE_CONTEXT}} \
          --controller-name sealed-secrets -o yaml \
          < kubernetes/config.secret.yaml \
          > kubernetes/config.sealedsecret.yaml        
    sources:
      - ./kubernetes/*.secret.yaml
    generates:
      - ./kubernetes/*.sealedsecret.yaml

  ...

Кажется, всё интуитивно понятно:

  • По task secrets:seal произойдет запечатывание секретов
  • Команда kubeseal будет вызвана с нужным kubernetes context
  • Команда не будет повторяться, если не поменялись source-файлы или если уже существуют актуальные generated-файлы

Разве что в последнем пункте надо знать, что актуальность будет проверена по timestamp generated vs source.

Примеры запуска

Список сценариев:

> task --list
task: Available tasks for this project:
* dev:          Run dev mode
* secrets:seal: Seal secrets

Обзор отдельной команды:

> task secrets:seal --summary
task: secrets:seal

Seal secrets

commands:
 - kubeseal \
  --context my-k8s \
  --controller-name sealed-secrets -o yaml \
  < kubernetes/config.secret.yaml \
  > kubernetes/config.sealedsecret.yaml

Выполнение таски:

> task secrets:seal
task: [secrets:seal] kubeseal \
  --context my-k8s \
  --controller-name sealed-secrets -o yaml \
  < kubernetes/config.secret.yaml \
  > kubernetes/config.sealedsecret.yaml

Или, если уже запечатано, то:

> task secrets:seal
task: Task "secrets:seal" is up to date

А если не выполняется какое-то условие, то ошибочка:

> task secrets:seal
task: `kubeseal --version` failed
task: precondition not met

Подытожим

  • Taskfile - классная штука.

    Пользуйтесь в проектах, рекомендуйте друзьям и коллегам.