Деплойер, по своей сути, - локальный CI/CD. Иными словами, менеджер bash
-команд.
Как правило, запускает сборку Деплойер в отдельной папке, чтобы сохранять кэш и при этом держать папку с кодом чистой. Однако вы можете указать как любую папку, так и папку с кодом; если у вас уже есть кэши, вы можете их копировать из исходной папки, делать на них симлинки или полностью их игнорировать и собирать с нуля.
Действие - это основная сущность Деплойера. На Действиях в составе Пайплайнов строятся процессы сборки, установки и развёртывания. Однако само по себе Действие быть назначенным проекту не может, для этого и нужны Пайплайны (см. ниже).
В составе Пайплайнов или в Реестре Действий Деплойера действие выглядит как конструкция:
{
"title": "UPX Compress",
"desc": "Compress the binary file with UPX.",
"info": "[email protected]",
"tags": [
"upx"
],
"action": {
"PostBuild": {
"supported_langs": [
"Rust",
"Go",
"C",
"Cpp",
"Python",
{
"Other": "any"
}
],
"commands": [
{
"bash_c": "upx <artifact>",
"placeholders": [
"<artifact>"
],
"ignore_fails": false,
"show_success_output": false,
"show_bash_c": false,
"only_when_fresh": false
}
]
}
},
"requirements": [
{
"ExistsAny": [
"/usr/bin/upx",
"~/.local/bin/upx"
]
}
]
}
В составе Реестров каждое Действие и каждый Пайплайн являются значениями в словаре с ключом info
(например, "[email protected]": { ... }
). Таким образом их можно быстро редактировать, выводить на экран содержимое, добавлять в Пайплайны и проекты.
Для каждого Действия в составе Пайплайна можно назначить список требований requirements
. Они будут проверяться перед каждым запуском Пайплайнов, и если хотя бы одно требование не будет удовлетворено, Пайплайн не будет выполнен. Требование можно задать тремя способами:
[
{
// если один из этих путей будет найден, требование будет считаться удовлетворённым
"ExistsAny": [
"path-1",
"path-2"
]
},
{
// если данный путь существует, требование считается удовлетворённым
"Exists": "path"
},
{
// если данная проверка будет пройдена, требование будет считаться удовлетворённым (подробности см. ниже - Действие `Check`)
"CheckSuccess": {
"command": {
"bash_c": "/usr/bin/python -V",
"ignore_fails": true,
"show_success_output": false,
"show_bash_c": false,
"only_when_fresh": false
},
"success_when_found": "Python 3.",
"success_when_not_found": null
}
},
{
// если данный удалённый хост существует в Реестре, доступен, и его версия Деплойера идентична версии запущенного Деплойера,
// требование будет считаться удовлетворённым
"RemoteAccessibleAndReady": "short-name"
}
]
Существует 3 категории основных Действий и 9 дополнительных видов Действий:
- Действия сборки (
PreBuild
,Build
,PostBuild
иTest
) - Действия установки (
Pack
,Deliver
,Install
) - Действия развёртывания (
ConfigureDeploy
,Deploy
,PostDeploy
) - Действие наблюдения
Observe
- Действие прерывания
Interrupt
- Действие с кастомной командой
Custom
- Действие проверки вывода кастомной команды
Check
- Действия добавления контента в хранилище Деплойера
AddToStorage
и использования этого контентаUseFromStorage
- Действие применения патча
Patch
- Действия синхронизации папок сборки - с текущего хоста на удалённый
SyncToRemote
и наоборотSyncFromRemote
Основополагающим является концепт кастомной команды - команды для оболочки терминала. Действия Custom
, Observe
и три основные категории Действий содержат внутри одну или больше кастомных команд.
Описание команды для Деплойера выглядит следующим образом:
{
"bash_c": "upx <artifact>",
"placeholders": [
"<artifact>"
],
"ignore_fails": false,
"show_success_output": false,
"show_bash_c": false,
"only_when_fresh": false,
"remote_exec": []
}
bash_c
содержит текст команды, которая будет выполняться в терминалеplaceholders
содержит список плейсхолдеров, которые можно будет заменять на переменные и артефакты проекта, чтобы выполнять с ними необходимые действияignore_fails
говорит Деплойеру, нужно ли квалифицировать статус выхода процесса, не равный нулю, как нормальное поведение команды, или нет; если нет, то Деплойер прервёт выполнение Пайплайна и выйдет со статусом1
show_success_output
говорит Деплойеру, нужно ли печатать вывод команды всегда (в т.ч. когда статус выхода процесса -0
), или же нужно печатать только при ошибкеshow_bash_c
говорит Деплойеру, нужно ли печатать на экране полный текст команды; это может быть полезным, когда команда содержит уязвимые переменныеonly_when_fresh
говорит Деплойеру, что это действие нужно выполнять только при свежей сборке (либо при первой сборке, либо при явном указании пересобрать с нуля при помощи опции-f
)remote_exec
содержит список коротких имён хостов, на которых необходимо будет выполнить эту команду
Когда команда специализируется для конкретного проекта, она обрастает дополнительным свойством - replacements
:
{
"bash_c": "upx <artifact>",
"placeholders": [
"<artifact>"
],
"replacements": [
[
[
"<artifact>",
{
"title": "target/release/deployer",
"is_secret": false,
"value": {
"Plain": "target/release/deployer"
}
}
]
]
],
"ignore_fails": false,
"show_success_output": false,
"show_bash_c": false,
"only_when_fresh": false
}
replacements
содержит список замен плейсхолдеров в команде на указанные артефакты или переменные (см. п.3). Следует заметить, что одна и та же команда может выполняться несколько раз для разных наборов переменных, даже если указана в Действии один раз:
{
"bash_c": "upx <artifact>",
"placeholders": [
"<artifact>"
],
"replacements": [
[
[
"<artifact>",
{
"title": "target/release/deployer",
"is_secret": false,
"value": {
"Plain": "target/release/deployer"
}
}
]
],
[
[
"<artifact>",
{
"title": "target/release/another",
"is_secret": false,
"value": {
"Plain": "target/release/another"
}
}
]
]
],
"ignore_fails": false,
"show_success_output": false,
"show_bash_c": false,
"only_when_fresh": false
}
В указанном примере используется только один плейсхолдер <artifact>
, но их может быть несколько, в т.ч. - различные опции для выполнения команды.
Соответственно, если вы хотите просто выполнять команды, которые нельзя отнести к одному из трёх основных видов Действий, следует использовать Действие типа Custom
:
{
"title": "List all files and folders",
"desc": "",
"info": "[email protected]",
"tags": [],
"action": {
"Custom": {
"bash_c": "ls",
"ignore_fails": false,
"show_success_output": true,
"show_bash_c": true,
"only_when_fresh": false
}
}
}
Для Действий сборки является специфичной специализация на языках программирования: в зависимости от того, соответствует ли набор языков, используемых в проекте, тому набору, который указан в действиях по сборке, Деплойер будет предупреждать вас об использовании несовместимых с проектом Действий.
Note
Специализации работают только при назначении Действий или Пайплайнов из TUI. Если вручную отредактировать конфигурацию, добавив несовместимый Пайплайн, никаких предупреждений Деплойер не выдаст. Это отражает нестрогий и рекомендательный характер таких предупреждений, в отличие от требований requirements
.
В вышеуказанном примере мы видим действие, которое должно выполняться после сборки:
{
"PostBuild": {
"supported_langs": [
"Rust",
"Go",
"C",
"Cpp",
"Python",
{
"Other": "any"
}
],
"commands": [
{
"bash_c": "upx <artifact>",
"placeholders": [
"<artifact>"
],
"ignore_fails": false,
"show_success_output": false,
"show_bash_c": false,
"only_when_fresh": false
}
]
}
}
Для этой группы Действий ключевым фактором специализации является целевой объект установки - таргет (цель). Если характеристики таргета проекта - аппаратная или программная платформа - не соответствуют характеристикам Действия установки, будет выдано предупреждение.
С удовольствием заметим, что UPX скорее относится к Действию упаковки, нежели к Действию после сборки:
{
"title": "UPX Pack",
"desc": "Pack the binary by UPX.",
"info": "[email protected]",
"tags": [
"upx"
],
"action": {
"Pack": {
"target": {
"arch": "x86_64",
"os": "Linux",
"derivative": "any",
"version": "No"
},
"commands": [
{
"bash_c": "upx <af>",
"placeholders": [
"<af>"
],
"ignore_fails": false,
"show_success_output": false,
"show_bash_c": false,
"only_when_fresh": false
}
]
}
}
}
arch
- это строковое обозначение архитектуры аппаратного обеспечения таргетаos
- это один из вариантов (android
|ios
|linux
|unix-{unix-name}
|windows
|macos
) или любое другое строковое обозначение операционнной системыderivative
- это дополнительное описание операционной системы или программной платформыversion
- это версия операционной системы или программной платформы
Если derivative
отсутствует, рекомендуется писать any
.
Для этой группы Действий ключевым фактором специализации является тулкит для развёртывания - Docker, Docker Compose, Podman, k8s или иной инструментарий контейнеризации или виртуализации. Если в проекте будет указан не тот тулкит, Деплойер выдаст предупреждение.
Приведём пример с Docker Compose:
{
"title": "Build Docker Compose Image",
"desc": "Build Docker image with Docker Compose",
"info": "[email protected]",
"tags": [
"docker",
"compose"
],
"action": {
"ConfigureDeploy": {
"deploy_toolkit": "docker-compose",
"tags": [
"docker",
"compose"
],
"commands": [
{
"bash_c": "docker compose build",
"ignore_fails": false,
"show_success_output": false,
"show_bash_c": true,
"only_when_fresh": false
}
]
}
}
}
1.5. Действия добавления контента AddToStorage
, использования контента UseFromStorage
и применения патча Patch
Часто проекты могут быть достаточно шаблонными, чтобы одни и те же файлы копировались между проектами, но не изменялись и требовались только при сборке или развёртывании. Такие файлы могут быть расположены в специальной папке с сохранением относительных путей и добавлены в хранилище Деплойера:
deployer new content
Тогда для проектов, которые должны использовать эти файлы, можно добавить в Пайплайн сборки новое Действие - UseFromStorage
:
{
"title": "Sync content",
"desc": "",
"info": "[email protected]",
"tags": [],
"action": {
"UseFromStorage": "[email protected]"
}
}
В итоге при выполнении Пайплайна в папку сборки будет добавляться нужный вам контент.
Раз за разом вы начнёте замечать, что некоторые проекты переиспользуются в других проектах как зависимости, и их необходимо где-то публиковать. Для этого как нельзя лучше подходят репозитории пакетов, но если вы не хотите публиковать свой проект, то можете добавлять его в хранилище Деплойера в качестве контента. Более того, добавлять его можно автоматически - при помощи Действия AddToStorage
:
{
"title": "Add content",
"desc": "",
"info": "[email protected]",
"tags": [],
"action": {
"AddToStorage": {
"short_name": "my-project",
"auto_version_rule": {
"plain_file": "file-with-current-version.txt"
}
}
}
}
short_name
- строковое обозначение контента, которое будет использовано для размещения в хранилище и каждый раз при использованииauto_version_rule
- способ автоматического определения версии контента (либоplain_file
- файл, в котором будет указана только версия и больше ничего, либоcmd_stdout
- команда, которая выведет на экран только версию и больше ничего)
Однако иногда файл нужно каким-то образом редактировать - и не столько даже добавляемый контент из хранилища Деплойера, сколько, например, различные файлы в зависимостях сборки, например, вручную делать форки библиотек Python для добавления нужной функциональности и т.д. и т.п. Причём, как правило, хочется делать это без создания форков и синхронизации изменений с main
-репозиторием! Одними патчами git
'а не обойтись.
Для этого Деплойер использует для патчей библиотеку smart-patcher
. Такие патчи позволяют изменять исходные тексты, сложные документы и даже бинарные файлы, позволяя искать необходимые включения в содержимом на базе отсеивающих правил и даже используя скрипты на таких языках, как Python, Lua и Rhai. Например, в репозитории smart-patcher
есть пример с патчем для документа Microsoft Word - и много других примеров.
Для использования умных патчей вам необходимо сперва написать файл патча. Пример:
{
"patches": [
{
"files": [
{
"just": "test_v5.docx"
}
],
"decoder": {
"python": "../tests/test_v5.py"
},
"encoder": {
"python": "../tests/test_v5.py"
},
"path_find_graph": [],
"replace": {
"from_to": [
"game",
"rock"
]
}
}
]
}
Действие патча же выглядит так:
{
"title": "Apply patch",
"desc": "",
"info": "[email protected]",
"tags": [],
"action": {
"Patch": {
"patch": "my_path.json"
}
}
}
Патч должен располагаться в папке сборки при выполнении Пайплайна. Очень хорошей практикой является написание патчей и размещение их в качестве контента в хранилище Деплойера. Тогда и файл патча, и скрипты будут расположены рядом и будут добавляться в процессе сборки.
При применении патча Деплойер выводит количество его применений в проекте. Если патч не был применён ни разу в процессе выполнения Пайплайна, Деплойер выдаст ошибку.
1.6. Действия синхронизации папок сборки - с текущего хоста на удалённый SyncToRemote
и наоборот SyncFromRemote
Иногда нужно синхронизировать файлы сборки между удалёнными хостами и текущим хостом. Например, когда часть действий нужно обязательно выполнить на одном хосте, а часть - на другом. Для этого можно использовать встроенные Действия SyncToRemote
и SyncFromRemote
:
{
"title": "Send build folder to remote",
"desc": "",
"info": "[email protected]",
"tags": [],
"action": {
"SyncToRemote": "remote-pc"
}
}
NOTE: Нет нужного примера конфигурации? Создайте действие самостоятельно при помощи команды
deployer new action
и выведите его на экран при помощиdeployer cat action [email protected]
.
Interrupt
используется для ручного прерывания сборки/развёртывания проекта. Когда Деплойер доходит до этого действия, он ожидает пользовательского ввода, чтобы продолжить, когда вы выполните необходимые действия вручную.
Observe
- Действие, которое практически идентично Custom
. Оно используется, например, чтобы запустить Prometheus, Jaeger или что угодно ещё. Отличительной особенностью является то, что оно запускается без перенаправления ввода-вывода, т.е. в нём можно взаимодействовать с программами.
А вот Check
- особенное действие, позволяющее проверять, что вывела команда в stdout
/stderr
:
{
"Check": {
"command": {
"bash_c": "<af>",
"placeholders": [
"<af>"
],
"ignore_fails": true,
"show_success_output": false,
"show_bash_c": false,
"only_when_fresh": false
},
"success_when_found": "some rust regex",
"success_when_not_found": null
}
}
success_when_found
сообщает Деплойеру, что если он найдёт указанное регулярное выражение, то выполнение команды будет считаться успешнымsuccess_when_not_found
сообщает Деплойеру, что если он не найдёт указанное регулярное выражение, то выполнение команды будет считаться успешным
Причём, если оба поля указаны, то успешным запуск будет считаться в случае, если оба варианта были успешны (первое регулярное выражение должен найти, второе - должен не найти).
На этом описание Действий заканчивается, и мы переходим к Пайплайнам.
Пайплайн - это упорядоченный набор Действий, который необходим для достижения определённой цели. Например, когда нужно проверить качество кода, проверить код с помощью статического анализатора, затем собрать, сжать, упаковать в пакет для определённого дистрибутива и загрузить на хостинг. Или когда нужно собрать Android-приложение, подписать и установить на устройство, подключённое по ADB. Композиция Пайплайна может быть любой, главный же пример приведён в файле deploy-config.json
этого репозитория:
{
"title": "Deployer Pipeline",
"desc": "Default Deployer Pipeline for itself.",
"info": "[email protected]",
"tags": [
"cargo",
"clippy",
"build",
"upx"
],
"actions": [
{
"title": "Lint",
"desc": "Got from `Cargo Clippy`.",
"info": "[email protected]",
"tags": [
"cargo",
"clippy"
],
"action": {
"PreBuild": {
"supported_langs": [
"Rust"
],
"commands": [
{
"bash_c": "cargo clippy",
"ignore_fails": false,
"show_success_output": true,
"show_bash_c": true,
"only_when_fresh": null
}
]
}
}
},
{
"title": "Build",
"desc": "Got from `Cargo Build (Release)`. Build the Rust project with Cargo default settings in release mode",
"info": "[email protected]",
"tags": [
"rust",
"cargo"
],
"action": {
"Build": {
"supported_langs": [
"Rust"
],
"commands": [
{
"bash_c": "cargo build --release",
"ignore_fails": false,
"show_success_output": false,
"show_bash_c": true,
"only_when_fresh": null
}
]
}
}
},
{
"title": "Compress",
"desc": "Got from `UPX Compress`.",
"info": "[email protected]",
"tags": [
"upx"
],
"action": {
"PostBuild": {
"supported_langs": [
"Rust",
"Go",
"C",
"Cpp",
"Python",
{
"Other": "any"
}
],
"commands": [
{
"bash_c": "upx <artifact>",
"placeholders": [
"<artifact>"
],
"replacements": [
[
[
"<artifact>",
{
"title": "target/release/deployer",
"is_secret": false,
"value": {
"Plain": "target/release/deployer"
}
}
]
]
],
"ignore_fails": false,
"show_success_output": false,
"show_bash_c": false,
"only_when_fresh": null
}
]
}
}
},
{
"title": "Install to ~/.cargo/bin",
"desc": "",
"info": "[email protected]",
"tags": [
"cargo"
],
"action": {
"Install": {
"target": {
"arch": "x86_64",
"os": "Linux",
"derivative": "any",
"version": "No"
},
"commands": [
{
"bash_c": "cp -f <artifact> ~/.cargo/bin",
"placeholders": [
"<artifact>"
],
"replacements": [
[
[
"<artifact>",
{
"title": "target/release/deployer",
"is_secret": false,
"value": {
"Plain": "target/release/deployer"
}
}
]
]
],
"ignore_fails": false,
"show_success_output": false,
"show_bash_c": false,
"only_when_fresh": null
}
]
}
}
}
],
"default": true
}
В общем, Пайплайн содержит список Действий в поле actions
.
Помимо этого, если ваши Пайплайны должны управлять конфликтующими версиями кэша (например, при сборке проекта под разные целевые архитектуры), то вы можете указать эксклюзивную метку сборки в поле exclusive_exec_tag
. Например, укажите x86_64
при добавлении Пайплайна сборки для одной архитектуры, а aarch64
- для другой. Тогда Пайплайны будут собираться в разных папках, и информация о кэше будет сохранена в обоих случаях.
Одной из самых важных сущностей являются переменные. Они одновременно являются и хранителями ваших секретов, и теми самыми динамическими сущностями, которые могут поменять исход выполнения Пайплайна. Пример простой переменной:
{
"title": "deployer artifact location",
"is_secret": false,
"value": {
"Plain": "target/release/deployer"
}
}
title
- название переменной (то, как она будет отображаться в TUI)is_secret
- является ли переменная секретом (если является, то команда, которая её содержит, не будет показана на экране)value
- само значение переменной или же информация о том, откуда и как это значение брать
Есть три поддерживаемых сейчас типа переменных:
Plain
- содержимое строки и есть переменнаяFromEnvVar
- переменная будет взята из окружения оболочки ДеплойераFromEnvFile
- переменная будет взята из указанногоenv
-файла с указанным ключомFromHCVaultKv2
- переменная будет взята из HashiCorp Vault KV2-хранилища с указаннымиmount_path
иsecret_path
Примеры:
{
"title": "Grafana token",
"is_secret": true,
"value": {
"FromEnvFile": {
"env_file_path": ".env",
"key": "GRAFANA_TOKEN"
}
}
}
{
"title": "Simple env var",
"is_secret": false,
"value": {
"FromEnvVar": "variable-key"
}
}
{
"title": "Secret!",
"is_secret": true,
"value": {
"FromHCVaultKv2": {
"mount_path": "The mount path where your KV2 secrets engine is mounted",
"secret_path": "Path to your secret"
}
}
}
Заметьте, что вы должны перед использованием FromHCVaultKv2
-переменных указать две переменные окружения: DEPLOYER_VAULT_ADDR
(URL-адрес Vault) и DEPLOYER_VAULT_TOKEN
(токен Vault).
Ещё одной важной сущностью является удалённый хост. Деплойер хранит все хосты в Реестре (глобальный файл конфигурации - список remote_hosts
). Структура хоста выглядит так:
{
"short_name": "localhost",
"ip": "127.0.0.1",
"port": 22,
"username": "username",
"ssh_private_key_file": "/path/to/id_rsa"
}
Чтобы иметь возможность использовать хост, перед его добавлением необходимо создать ключ и разрешить авторизацию на удалённом хосте по ключу.
Деплойер, в первую очередь, - CLI-утилита. По любой команде Деплойера можно посмотреть справку, указав опцию -h
. Приведём примеры самых распространённых команд:
deployer new action # создать Действие и поместить в Реестр
deployer new pipeline # создать Пайплайн и поместить в Реестр
deployer new remote # добавить в Реестр новый удалённый хост
deployer init # инициализировать проект, указать все свойства
deployer with # проверить совместимость и назначить Пайплайн для проекта,
# а также указать необходимые переменные и артефакты вместо плейсхолдеров
deployer build # запустить Пайплайн, назначенный по умолчанию
deployer build my-pipe # запустить Пайплайн по короткому имени
deployer build configure,build -o build-folder # запустить Пайплайны `configure` и `build` в папке `build-folder`
deployer build -R my-remote my-pipe # запустить Пайплайн `my-pipe` на удалённом хосте `my-remote`
Деплойер обладает поддержкой высококлассного настройщика через терминал, что позволяет вам вообще забыть про ручное написание Действий и Пайплайнов для ваших проектов. Просто попробуйте создать Действие или Пайплайн, и Деплойер сам вас обо всём спросит.
В папке с кэшами сборки Деплойера есть папка logs
, в которой расположены файлы логов проектов с указанием даты и времени сборки. Информация в них повторяет информацию с экрана терминала и на данный момент не хранит весь лог выполнения каждой команды в оболочке.