From d7d96827632582278370324b262f9113ea5855f9 Mon Sep 17 00:00:00 2001 From: Klimenty Titov Date: Tue, 17 Dec 2024 14:42:39 +0300 Subject: [PATCH 1/7] Add addition cancelling while edit --- src/actions.rs | 4 +++- src/pipelines.rs | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/actions.rs b/src/actions.rs index 2e36c81..943e362 100644 --- a/src/actions.rs +++ b/src/actions.rs @@ -561,7 +561,9 @@ impl EditExtended for Vec { let selected = Select::new("Choose an Action to add:", k).prompt()?; if selected.as_str() == USE_ANOTHER { - self.push(DescribedAction::new_from_prompt(opts)?); + if let Ok(action) = DescribedAction::new_from_prompt(opts) { + self.push(action); + } } else { self.push((**h.get(&selected).ok_or(anyhow::anyhow!("Can't get specified Action!"))?).clone()); } diff --git a/src/pipelines.rs b/src/pipelines.rs index 4ec2c57..0b868e0 100644 --- a/src/pipelines.rs +++ b/src/pipelines.rs @@ -496,7 +496,9 @@ impl EditExtended for Vec { let selected = Select::new("Choose a Pipeline to add:", k).prompt()?; if selected.as_str() == USE_ANOTHER { - self.push(DescribedPipeline::new_from_prompt(opts)?); + if let Ok(pipeline) = DescribedPipeline::new_from_prompt(opts) { + self.push(pipeline); + } } else { self.push((**h.get(&selected).ok_or(anyhow::anyhow!("Can't get specified Pipeline!"))?).clone()); } From 6842d503bccdd1f6ae1167b14c9cd1b987ff48df Mon Sep 17 00:00:00 2001 From: Klimenty Titov Date: Tue, 17 Dec 2024 14:46:33 +0300 Subject: [PATCH 2/7] Add addition cancelling while collecting several commands --- src/actions.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/actions.rs b/src/actions.rs index 943e362..243640a 100644 --- a/src/actions.rs +++ b/src/actions.rs @@ -679,8 +679,12 @@ fn collect_multiple_commands() -> anyhow::Result> { use inquire::Confirm; let mut commands = Vec::new(); - while Confirm::new("Add command? (y/n)").prompt()? { - commands.push(CustomCommand::new_from_prompt()?); + let mut first = true; + while Confirm::new("Add command?").with_default(first).prompt()? { + if let Ok(command) = CustomCommand::new_from_prompt() { + commands.push(command); + } + first = false; } Ok(commands) } From 3e2c24275edac71a2d877a52d4092127ceac2d05 Mon Sep 17 00:00:00 2001 From: Klimenty Titov Date: Tue, 17 Dec 2024 16:00:07 +0300 Subject: [PATCH 3/7] Add edit for `Observe`, `Check` and `Custom` Actions --- src/actions.rs | 5 ++++- src/actions/check.rs | 22 ++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/actions.rs b/src/actions.rs index 243640a..9042efc 100644 --- a/src/actions.rs +++ b/src/actions.rs @@ -448,7 +448,10 @@ impl DescribedAction { Action::ConfigureDeploy(a) => a.commands.edit_from_prompt()?, Action::Deploy(a) => a.commands.edit_from_prompt()?, Action::PostDeploy(a) => a.commands.edit_from_prompt()?, - Action::Interrupt | Action::Custom(_) | Action::Check(_) | Action::ForceArtifactsEnplace | Action::Observe(_) => {}, + Action::Check(a) => a.edit_check_from_prompt()?, + Action::Observe(a) => a.command.edit_command_from_prompt()?, + Action::Custom(a) => a.edit_command_from_prompt()?, + Action::Interrupt | Action::ForceArtifactsEnplace => {}, } }, "Edit regexes" if let Action::Check(c_action) = &mut self.action => c_action.change_regexes_from_prompt()?, diff --git a/src/actions/check.rs b/src/actions/check.rs index cf8a471..8a7dfb3 100644 --- a/src/actions/check.rs +++ b/src/actions/check.rs @@ -57,6 +57,28 @@ impl CheckAction { Ok(()) } + pub(crate) fn edit_check_from_prompt(&mut self) -> anyhow::Result<()> { + const EDIT_COMMAND: &str = "Edit check command"; + const EDIT_REGEXES: &str = "Edit regexes"; + + loop { + if let Some(selected) = inquire::Select::new( + "Specify an action for Check Action:", + vec![EDIT_COMMAND, EDIT_REGEXES], + ).prompt_skippable()? { + match selected { + EDIT_COMMAND => self.command.edit_command_from_prompt()?, + EDIT_REGEXES => self.change_regexes_from_prompt()?, + _ => {}, + } + } else { + break + } + } + + Ok(()) + } + pub(crate) fn prompt_setup_for_project( &self, info: &ActionInfo, From 238891dc8dc69e0c83d4b53cc006e841c3eff758 Mon Sep 17 00:00:00 2001 From: Klimenty Titov Date: Tue, 17 Dec 2024 17:12:42 +0300 Subject: [PATCH 4/7] Fix lint --- src/actions/check.rs | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/src/actions/check.rs b/src/actions/check.rs index 8a7dfb3..4f9e005 100644 --- a/src/actions/check.rs +++ b/src/actions/check.rs @@ -61,18 +61,14 @@ impl CheckAction { const EDIT_COMMAND: &str = "Edit check command"; const EDIT_REGEXES: &str = "Edit regexes"; - loop { - if let Some(selected) = inquire::Select::new( - "Specify an action for Check Action:", - vec![EDIT_COMMAND, EDIT_REGEXES], - ).prompt_skippable()? { - match selected { - EDIT_COMMAND => self.command.edit_command_from_prompt()?, - EDIT_REGEXES => self.change_regexes_from_prompt()?, - _ => {}, - } - } else { - break + while let Some(selected) = inquire::Select::new( + "Specify an action for Check Action:", + vec![EDIT_COMMAND, EDIT_REGEXES], + ).prompt_skippable()? { + match selected { + EDIT_COMMAND => self.command.edit_command_from_prompt()?, + EDIT_REGEXES => self.change_regexes_from_prompt()?, + _ => {}, } } From e688186ad69363c7b8e851655fe0b481963e740f Mon Sep 17 00:00:00 2001 From: Klimenty Titov Date: Tue, 17 Dec 2024 17:12:59 +0300 Subject: [PATCH 5/7] Fix `only_when_fresh` adding --- src/entities/custom_command.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/entities/custom_command.rs b/src/entities/custom_command.rs index 8cb797c..c9ff40a 100644 --- a/src/entities/custom_command.rs +++ b/src/entities/custom_command.rs @@ -45,11 +45,7 @@ impl CustomCommand { let ignore_fails = inquire::Confirm::new("Ignore command failures?").with_default(false).prompt()?; let show_bash_c = inquire::Confirm::new("Show an entire command at build stage?").with_default(true).prompt()?; let show_success_output = inquire::Confirm::new("Show an output of command if it executed successfully?").with_default(false).prompt()?; - let only_when_fresh = if inquire::Confirm::new("Start a command only in fresh builds?").with_default(false).prompt()? { - Some(true) - } else { - None - }; + let only_when_fresh = Some(inquire::Confirm::new("Start a command only in fresh builds?").with_default(false).prompt()?); Ok(CustomCommand { bash_c, From 6a89c0091d4917bc1f0253758e8a4cbd5ccf41a9 Mon Sep 17 00:00:00 2001 From: Klimenty Titov Date: Tue, 17 Dec 2024 17:13:09 +0300 Subject: [PATCH 6/7] Add `ru` docs --- DOCS.ru.md | 525 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 525 insertions(+) create mode 100644 DOCS.ru.md diff --git a/DOCS.ru.md b/DOCS.ru.md new file mode 100644 index 0000000..6ee92c3 --- /dev/null +++ b/DOCS.ru.md @@ -0,0 +1,525 @@ +# Деплойер: документация по версии `0.1.1-beta-X` + +## Описание основных сущностей + +### 1. Действие - `Action` + +Действие -- это основная сущность Деплойера. На Действиях в составе Пайплайнов строятся процессы сборки, установки и развёртывания. Однако само по себе Действие быть назначенным проекту не может, для этого и нужны Пайплайны (см. ниже). + +В составе Пайплайнов или в Реестре Действий Деплойера действие выглядит как конструкция: + +```json +{ + "title": "UPX Compress", + "desc": "Compress the binary file with UPX.", + "info": "upx-compress@0.1.0", + "tags": [ + "upx" + ], + "action": { + "PostBuild": { + "supported_langs": [ + "Rust", + "Go", + "C", + "Cpp", + "Python", + { + "Other": "any" + } + ], + "commands": [ + { + "bash_c": "upx ", + "placeholders": [ + "" + ], + "ignore_fails": false, + "show_success_output": false, + "show_bash_c": false, + "only_when_fresh": false + } + ] + } + } +} +``` + +В составе Реестров каждое Действие и каждый Пайплайн являются значениями в словаре с ключом `info` (например, `"upx-compress@0.1.0": { ... }`). Таким образом их можно быстро редактировать, выводить на экран содержимое, добавлять в Пайплайны и проекты. + +Существует 3 категории основных Действий и 5 дополнительных видов Действий: + +1. Действия сборки (`PreBuild`, `Build`, `PostBuild` и `Test`) +2. Действия установки (`Pack`, `Deliver`, `Install`) +3. Действия развёртывания (`ConfigureDeploy`, `Deploy`, `PostDeploy`) +4. Действие наблюдения `Observe` +5. Действие прерывания `Interrupt` +6. Действие принудительной синхронизации готовых артефактов `ForceArtifactsEnplace` +7. Действие с кастомной командой `Custom` +8. Действие проверки вывода кастомной команды `Check` + +Основополагающим является концепт кастомной команды -- команды для оболочки терминала. Действия `Custom`, `Observe` и три основные категории Действий содержат внутри одну или больше кастомных команд. + +#### 1.1. Кастомная команда + +Описание команды для Деплойера выглядит следующим образом: + +```json +{ + "bash_c": "upx ", + "placeholders": [ + "" + ], + "ignore_fails": false, + "show_success_output": false, + "show_bash_c": false, + "only_when_fresh": false +} +``` + +- `bash_c` содержит текст команды, которая будет выполняться в терминале +- `placeholders` содержит список плейсхолдеров, которые можно будет заменять на переменные и артефакты проекта, чтобы выполнять с ними необходимые действия +- `ignore_fails` говорит Деплойеру, нужно ли квалифицировать статус выхода процесса, не равный нулю, как нормальное поведение команды, или нет; если нет, то Деплойер прервёт выполнение Пайплайна и выйдет со статусом `1` +- `show_success_output` говорит Деплойеру, нужно ли печатать вывод команды всегда (в т.ч. когда статус выхода процесса - `0`), или же нужно печатать только при ошибке +- `show_bash_c` говорит Деплойеру, нужно ли печатать на экране полный текст команды; это может быть полезным, когда команда содержит уязвимые переменные +- `only_when_fresh` говорит Деплойеру, что это действие нужно выполнять только при свежей сборке (либо при первой сборке, либо при явном указании пересобрать с нуля при помощи опции `-f`) + +Когда команда специализируется для конкретного проекта, она обрастает дополнительным свойством - `replacements`: + +```json +{ + "bash_c": "upx ", + "placeholders": [ + "" + ], + "replacements": [ + [ + [ + "", + { + "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` содержит список замен плейсхолдеров в команде на указанные артефакты или переменные. Следует заметить, что одна и та же команда может выполняться несколько раз для разных наборов переменных, даже если указана в Действии один раз: + +```json +{ + "bash_c": "upx ", + "placeholders": [ + "" + ], + "replacements": [ + [ + [ + "", + { + "title": "target/release/deployer", + "is_secret": false, + "value": { + "Plain": "target/release/deployer" + } + } + ] + ], + [ + [ + "", + { + "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 +} +``` + +В указанном примере используется только один плейсхолдер ``, но их может быть несколько, в т.ч. - различные опции для выполнения команды. + +Соответственно, если вы хотите просто выполнять команды, которые нельзя отнести к одному из трёх основных видов Действий, следует использовать Действие типа `Custom`: + +```json +{ + "title": "List all files and folders", + "desc": "", + "info": "ls@0.1.0", + "tags": [], + "action": { + "Custom": { + "bash_c": "ls", + "ignore_fails": false, + "show_success_output": true, + "show_bash_c": true, + "only_when_fresh": false + } + } +} +``` + +#### 1.2. Действия сборки - `PreBuild`, `Build`, `PostBuild` и `Test` + +Для Действий сборки является специфичной специализация на языках программирования: в зависимости от того, соответствует ли набор языков, используемых в проекте, тому набору, который указан в действиях по сборке, Деплойер будет предупреждать вас об использовании несовместимых с проектом Действий. + +В вышеуказанном примере мы видим действие, которое должно выполняться после сборки: + +```json +{ + "PostBuild": { + "supported_langs": [ + "Rust", + "Go", + "C", + "Cpp", + "Python", + { + "Other": "any" + } + ], + "commands": [ + { + "bash_c": "upx ", + "placeholders": [ + "" + ], + "ignore_fails": false, + "show_success_output": false, + "show_bash_c": false, + "only_when_fresh": false + } + ] + } +} +``` + +#### 1.3. Действия установки - `Pack`, `Deliver` и `Install` + +Для этой группы Действий ключевым фактором специализации является целевой объект установки -- *таргет*. Если характеристики таргета проекта - аппаратная или программная платформа - не соответствуют характеристикам Действия установки, будет выдано предупреждение. + +С удовольствием заметим, что UPX скорее относится к Действию упаковки, нежели к Действию после сборки: + +```json +{ + "title": "UPX Pack", + "desc": "Pack the binary by UPX.", + "info": "upx-pack@0.1.0", + "tags": [ + "upx" + ], + "action": { + "Pack": { + "target": { + "arch": "x86_64", + "os": "Linux", + "derivative": "any", + "version": "No" + }, + "commands": [ + { + "bash_c": "upx ", + "placeholders": [ + "" + ], + "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`. + +#### 1.4. Действия развёртывания - `ConfigureDeploy`, `Deploy`, `PostDeploy` + +Для этой группы Действий ключевым фактором специализации является тулкит для развёртывания - Docker, Docker Compose, Podman, k8s или иной инструментарий контейнеризации или виртуализации. Если в проекте будет указан не тот тулкит, Деплойер выдаст предупреждение. + +Приведём пример с Docker Compose: + +```json +{ + "title": "Build Docker Compose Image", + "desc": "Build Docker image with Docker Compose", + "info": "docker-compose-build@0.1.0", + "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. Другие действия - `Interrupt`, `ForceArtifactsEnplace`, `Observe` и `Check` + +> NOTE: Нет нужного примера конфигурации? Создайте действие самостоятельно при помощи команды `deployer new action` и выведите его на экран при помощи `deployer cat action my-action@x.y.z`. + +`Interrupt` используется для ручного прерывания сборки/развёртывания проекта. Когда Деплойер доходит до этого действия, он ожидает пользовательского ввода, чтобы продолжить, когда вы выполните необходимые действия вручную. + +`ForceArtifactsEnplace` используется для того, чтобы принудительно синхронизировать артефакты, даже когда не все артефакты сгенерированы. По умолчанию указанные в конфигурации проекта артефакты переносятся в папку `artifacts`, но с помощью такого действия это можно выполнить чуть раньше, например, когда происходит рекурсивная сборка проекта с помощью Деплойера: + +```json +{ + "title": "Force enplace", + "desc": "", + "info": "force-enplace@0.1.0", + "tags": [], + "action": "ForceArtifactsEnplace" +} +``` + +`Observe` -- Действие, которое практически идентично `Custom`. Оно используется, например, чтобы запустить Prometheus, Jaeger или что угодно ещё. + +А вот `Check` -- особенное действие, позволяющее проверять, что вывела команда в `stdout`/`stderr`: + +```json +{ + "Check": { + "command": { + "bash_c": "", + "placeholders": [ + "" + ], + "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` сообщает Деплойеру, что если он не найдёт указанное регулярное выражение, то выполнение команды будет считаться успешным + +Причём, если оба поля указаны, то успешным запуск будет считаться в случае, если оба варианта были успешны (первое регулярное выражение должен найти, второе - должен не найти). + +На этом описание Действий заканчивается, и мы переходим к Пайплайнам. + +### 2. Пайплайн - `Pipeline` + +Пайплайн -- это упорядоченный набор Действий, который необходим для достижения определённой цели. Например, когда нужно проверить качество кода, проверить код с помощью статического анализатора, затем собрать, сжать, упаковать в пакет для определённого дистрибутива и загрузить на хостинг. Или когда нужно собрать Android-приложение, подписать и установить на устройство, подключённое по ADB. Композиция Пайплайна может быть любой, главный же пример приведён в файле `deploy-config.json` этого репозитория: + +```json +{ + "title": "Deployer Pipeline", + "desc": "Default Deployer Pipeline for itself.", + "info": "deployer-default@0.1.0", + "tags": [ + "cargo", + "clippy", + "build", + "upx" + ], + "actions": [ + { + "title": "Lint", + "desc": "Got from `Cargo Clippy`.", + "info": "cargo-clippy@0.1.0", + "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": "cargo-rel@0.1", + "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": "upx@0.1.0", + "tags": [ + "upx" + ], + "action": { + "PostBuild": { + "supported_langs": [ + "Rust", + "Go", + "C", + "Cpp", + "Python", + { + "Other": "any" + } + ], + "commands": [ + { + "bash_c": "upx ", + "placeholders": [ + "" + ], + "replacements": [ + [ + [ + "", + { + "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": "install-to-cargo-bin@0.1.1", + "tags": [ + "cargo" + ], + "action": { + "Install": { + "target": { + "arch": "x86_64", + "os": "Linux", + "derivative": "any", + "version": "No" + }, + "commands": [ + { + "bash_c": "cp -f ~/.cargo/bin", + "placeholders": [ + "" + ], + "replacements": [ + [ + [ + "", + { + "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`. Остальное - такое же, как и у Действия. + +## Описание утилиты CLI + +Деплойер, в первую очередь, -- CLI-утилита. По любой команде Деплойера можно посмотреть справку, указав опцию `-h`. Приведём примеры самых распространённых команд: + +```bash +deployer new action # создать Действие и поместить в Реестр +deployer new pipeline # создать Пайплайн и поместить в Реестр +deployer init # инициализировать проект, указать все свойства +deployer with # проверить совместимость и назначить Пайплайн для проекта, + # а также указать необходимые переменные и артефакты вместо плейсхолдеров +deployer build # запустить Пайплайн, назначенный по умолчанию +deployer build my-pipe # запустить Пайплайн по короткому имени +``` + +### Интерфейс консоли (TUI) + +Деплойер обладает поддержкой высококлассного настройщика через терминал, что позволяет вам вообще забыть про ручное написание Действий и Пайплайнов для ваших проектов. Просто попробуйте создать Действие или Пайплайн, и Деплойер сам вас обо всём спросит. From 131dd8bddd19c314b1cfffe591808fd38b7116b6 Mon Sep 17 00:00:00 2001 From: Klimenty Titov Date: Tue, 17 Dec 2024 17:13:27 +0300 Subject: [PATCH 7/7] Bump to `beta-9` --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 738aaba..aa2ff33 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "deployer" -version = "0.1.1-beta-8" +version = "0.1.1-beta-9" edition = "2021" [dependencies]