diff --git a/.gitignore b/.gitignore index d77009a..99a3cb5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ cmake-* build/ build-artifact/ +tmp diff --git a/CHANGELOG b/CHANGELOG index ebb20d9..ea9fa97 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,10 @@ +* Mon May 24 2021 Matiss Treinis - 1.1.4 +- Add "local" command for local execution context runner +- Introduce strict mode +- Switch EL 8 compatible build to use AlmaLinux 8 +- Add support for Fedora 35 +- Remove support for Ubuntu 19.10 (EOL) + * Mon Oct 5 2020 Matiss Treinis - 1.1.3 - Fix build compatibility with libssh - Fix OSX build diff --git a/CMakeLists.txt b/CMakeLists.txt index 98850a3..e3a342a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.11.4) # IMPORTANT: updating version might require update in package dependencies at the end of this file. -set(KAFE_VERSION "1.1.3") +set(KAFE_VERSION "1.1.4") set(KAFE_SOVERSION "1.1") set(KAFE_VERSION_INT 11) set(KAFE_VERSION_DEP_NEXT_MAJOR "2.0.0") @@ -73,7 +73,7 @@ set(CPACK_DEBIAN_CLI_PACKAGE_NAME "kafe-cli") set(CPACK_DEBIAN_LIBKAFE_PACKAGE_NAME "libkafe") set(CPACK_DEBIAN_LIBKAFE-DEV_PACKAGE_NAME "libkafe-dev") set(CPACK_DEBIAN_CLI_PACKAGE_DEPENDS "libkafe (>=${KAFE_VERSION}), libkafe (<<${KAFE_VERSION_DEP_NEXT_MAJOR}), libstdc++6, libc6, libgcc1, libc6") -set(CPACK_DEBIAN_LIBKAFE_PACKAGE_DEPENDS "libstdc++6, libc6, libgcc1, libc6, liblua5.3-0, libssh-4 (>=0.7.0), libarchive13, libcurl3 | libcurl4, libgit2-24 | libgit2-26 | libgit2-27 | libgit2-28") +set(CPACK_DEBIAN_LIBKAFE_PACKAGE_DEPENDS "libstdc++6, libc6, libgcc1, libc6, liblua5.3-0, libssh-4 (>=0.7.0), libarchive13, libcurl3 | libcurl4, libgit2-24 | libgit2-26 | libgit2-27 | libgit2-28 | libgit2-1.1") set(CPACK_DEBIAN_LIBKAFE-DEV_PACKAGE_DEPENDS "libkafe (=${KAFE_VERSION})") set(CPACK_ARCHIVE_COMPONENT_INSTALL ON) diff --git a/DOWNLOAD.md b/DOWNLOAD.md index 48974f9..3490b65 100644 --- a/DOWNLOAD.md +++ b/DOWNLOAD.md @@ -8,16 +8,16 @@ Binary package repositories for Kafe are generously hosted by [Cloudsmith](https - [Debian 10](#debian-10) - [Debian 11](#debian-11) - [Ubuntu 18.04](#ubuntu-1804) -- [Ubuntu 19.10](#ubuntu-1910) - [Ubuntu 20.04](#ubuntu-2004) - [Ubuntu 20.10](#ubuntu-2010) - [Ubuntu 21.04](#ubuntu-2104) -- [CentOS and RHEL 7](#centos-and-rhel-7) -- [CentOS and RHEL 8](#centos-and-rhel-8) +- [EL7](#el7) +- [EL8](#el8) - [Fedora 31](#fedora-31) - [Fedora 32](#fedora-32) - [Fedora 33](#fedora-33) - [Fedora 34](#fedora-34) +- [Fedora 35](#fedora-35) - [macOS](https://github.com/libkafe/homebrew-kafe) See [releases](https://github.com/libkafe/kafe/releases) for all available downloads and historic versions, @@ -92,20 +92,6 @@ apt update && apt -y install kafe-cli kafe about ``` -#### Ubuntu 19.10 - -```shell script -# Setup Kafe repository signing key -apt-key adv --keyserver keyserver.ubuntu.com --recv AEE35447B5D3E4A69860622702F762CA6730E6A7 -# Setup repository -echo 'deb https://dl.cloudsmith.io/public/kafe/libkafe/deb/ubuntu eoan main' | \ - tee /etc/apt/sources.list.d/kafe.list -# Update APT package lists and install kafe-cli -apt update && apt -y install kafe-cli -# Print about -kafe about -``` - #### Ubuntu 20.04 ```shell script @@ -148,7 +134,7 @@ apt update && apt -y install kafe-cli kafe about ``` -#### CentOS and RHEL 7 +#### EL7 **IMPORTANT:** Kafe requires Lua version 5.3, a dependency not available in EL7 by default. You can either build the package yourself or use [Cheese](http://www.nosuchhost.net/~cheese/fedora/packages/epel-7/x86_64/cheese-release.html) @@ -165,7 +151,7 @@ yum -y install kafe-cli kafe about ``` -#### CentOS and RHEL 8 +#### EL8 ```shell script # Setup repository @@ -176,6 +162,8 @@ dnf install -y kafe-cli kafe about ``` +**NOTE:** this build WILL work on all RHEL 8 forks and derivatives. It SHOULD work on CentOS Stream 8. + #### Fedora 31 ```shell script @@ -219,3 +207,15 @@ dnf install -y kafe-cli # Print about kafe about ``` + + +#### Fedora 35 + +```shell script +# Setup repository +curl -1sLf 'https://dl.cloudsmith.io/public/kafe/libkafe/cfg/setup/bash.rpm.sh' | distro=fedora codename=35 bash +# Install kafe-cli +dnf install -y kafe-cli +# Print about +kafe about +``` diff --git a/README.md b/README.md index dd1e7d7..6836d7b 100644 --- a/README.md +++ b/README.md @@ -8,11 +8,11 @@ See [available package downloads and repositories](DOWNLOAD.md). You can easily install and use libkafe on following operating systems: -- **CentOS** and **RHEL** versions [71](./DOWNLOAD.md#centos-and-rhel-7), [8](./DOWNLOAD.md#centos-and-rhel-8) -- **Fedora** versions [31](./DOWNLOAD.md#fedora-31), [32](./DOWNLOAD.md#fedora-32), [33](./DOWNLOAD.md#fedora-33), [34](./DOWNLOAD.md#fedora-34) -- **Ubuntu** versions [18.04](./DOWNLOAD.md#ubuntu-1804), [19.10](./DOWNLOAD.md#ubuntu-1910), [20.04](./DOWNLOAD.md#ubuntu-2004), [20.10](./DOWNLOAD.md#ubuntu-2010), [21.04](./DOWNLOAD.md#ubuntu-2104) +- **RHEL/EL** versions [71](./DOWNLOAD.md#el7), [8](./DOWNLOAD.md#el8) +- **Fedora** versions [31](./DOWNLOAD.md#fedora-31), [32](./DOWNLOAD.md#fedora-32), [33](./DOWNLOAD.md#fedora-33), [34](./DOWNLOAD.md#fedora-34), [35](./DOWNLOAD.md#fedora-35) +- **Ubuntu** versions [18.04](./DOWNLOAD.md#ubuntu-1804), [20.04](./DOWNLOAD.md#ubuntu-2004), [20.10](./DOWNLOAD.md#ubuntu-2010), [21.04](./DOWNLOAD.md#ubuntu-2104) - **Debian** versions [9](./DOWNLOAD.md#debian-9), [10](./DOWNLOAD.md#debian-10), [11](./DOWNLOAD.md#debian-11) -- **macOS** versions 10.5 (Catalina), 11 (Big Sur) using [Homebrew vendor tap](https://github.com/libkafe/homebrew-kafe). +- **macOS** versions 10.5 (Catalina), 11 (Big Sur) and newer, using [Homebrew vendor tap](https://github.com/libkafe/homebrew-kafe). 1 - EL7 does not ship with Lua 5.3. We currently use [Cheese](http://www.nosuchhost.net/~cheese/fedora/packages/epel-7/x86_64/cheese-release.html) repository @@ -174,6 +174,14 @@ execute following Kafe CLI command: When executed, Kafe CLI will look for a file named `kafe.lua` in the current working direcory. This file will be interpreted and requested tasks from it will be executed against all relevant remote servers. +### Local tasks and local execution environment + +From version 1.1.4, Kafe adds support for local scripting using `kafe local` command line interface. +This is simply a shortcut that allows for writing local automation tasks, local scripts and similar directly in +Kafe and allows Kafe to be used as build automation framework and local task runner. + +**NOTE:** in local, APIs like `exec`, `shell` are simply aliased to `local_shell` and `local_exec`. + #### SSH and SSH authentication Kafe is using SSH for remote command execution and file uploads. It will attempt to use SSH agent, any known local diff --git a/build-dist-docker-clean.sh b/build-dist-docker-clean.sh index 9b688b4..20c2bfb 100755 --- a/build-dist-docker-clean.sh +++ b/build-dist-docker-clean.sh @@ -1,14 +1,15 @@ #!/usr/bin/env bash docker rmi kafe/centos:7-build -docker rmi kafe/centos:8-build +docker rmi kafe/almalinux:8-build docker rmi kafe/debian:9-build docker rmi kafe/debian:10-build docker rmi kafe/debian:11-build docker rmi kafe/ubuntu:1804-build -docker rmi kafe/ubuntu:1910-build docker rmi kafe/ubuntu:2004-build docker rmi kafe/ubuntu:2010-build docker rmi kafe/fedora:31-build docker rmi kafe/fedora:32-build -docker rmi kafe/fedora:33-build \ No newline at end of file +docker rmi kafe/fedora:33-build +docker rmi kafe/fedora:34-build +docker rmi kafe/fedora:35-build diff --git a/build-dist.sh b/build-dist.sh index f8f7697..88f0f25 100755 --- a/build-dist.sh +++ b/build-dist.sh @@ -6,7 +6,7 @@ set -xe rm -rf build/ rm -rf build-artifact/ -# CentOS 7 +# EL7 if [[ "$(docker images -q "kafe/centos:7-build" 2> /dev/null)" == "" ]]; then docker build -t kafe/centos:7-build dist/centos/7 fi @@ -17,16 +17,16 @@ cp build/centos/7/kafe-cli-*.rpm build-artifact/centos-7/ cp build/centos/7/libkafe-*.rpm build-artifact/centos-7/ for f in build-artifact/centos-7/*; do mv -v "$f" $(echo "$f" | sed "s/\.rpm/\.el7\.rpm/"); done -# CentOS 8 -if [[ "$(docker images -q "kafe/centos:8-build" 2> /dev/null)" == "" ]]; then - docker build -t kafe/centos:8-build dist/centos/8 +# EL 8 +if [[ "$(docker images -q "kafe/almalinux:8-build" 2> /dev/null)" == "" ]]; then + docker build -t kafe/almalinux:8-build dist/almalinux/8 fi -docker run -it --rm -v `pwd`:/kafe kafe/centos:8-build -mkdir -p build-artifact/centos-8/ -cp build/centos/8/kafe-cli-*.rpm build-artifact/centos-8/ -cp build/centos/8/libkafe-*.rpm build-artifact/centos-8/ -for f in build-artifact/centos-8/*; do mv -v "$f" $(echo "$f" | sed "s/\.rpm/\.el8\.rpm/"); done +docker run -it --rm -v `pwd`:/kafe kafe/almalinux:8-build +mkdir -p build-artifact/almalinux-8/ +cp build/almalinux/8/kafe-cli-*.rpm build-artifact/almalinux-8/ +cp build/almalinux/8/libkafe-*.rpm build-artifact/almalinux-8/ +for f in build-artifact/almalinux-8/*; do mv -v "$f" $(echo "$f" | sed "s/\.rpm/\.el8\.rpm/"); done # Debian 9 if [[ "$(docker images -q "kafe/debian:9-build" 2> /dev/null)" == "" ]]; then @@ -72,17 +72,6 @@ cp build/ubuntu/1804/kafe-cli*.deb build-artifact/ubuntu-1804/ cp build/ubuntu/1804/libkafe*.deb build-artifact/ubuntu-1804/ for f in build-artifact/ubuntu-1804/*; do mv -v "$f" $(echo "$f" | sed "s/\.deb/\.ubu1804\.deb/"); done -# Ubuntu 19.10 -if [[ "$(docker images -q "kafe/ubuntu:1910-build" 2> /dev/null)" == "" ]]; then - docker build -t kafe/ubuntu:1910-build dist/ubuntu/1910 -fi - -docker run -it --rm -v `pwd`:/kafe kafe/ubuntu:1910-build -mkdir -p build-artifact/ubuntu-1910/ -cp build/ubuntu/1910/kafe-cli*.deb build-artifact/ubuntu-1910/ -cp build/ubuntu/1910/libkafe*.deb build-artifact/ubuntu-1910/ -for f in build-artifact/ubuntu-1910/*; do mv -v "$f" $(echo "$f" | sed "s/\.deb/\.ubu1910\.deb/"); done - # Ubuntu 20.04 if [[ "$(docker images -q "kafe/ubuntu:2004-build" 2> /dev/null)" == "" ]]; then docker build -t kafe/ubuntu:2004-build dist/ubuntu/2004 @@ -159,3 +148,14 @@ mkdir -p build-artifact/fedora-34/ cp build/fedora/34/kafe-cli-*.rpm build-artifact/fedora-34/ cp build/fedora/34/libkafe-*.rpm build-artifact/fedora-34/ for f in build-artifact/fedora-34/*; do mv -v "$f" $(echo "$f" | sed "s/\.rpm/\.f34\.rpm/"); done + +# Fedora 35 +if [[ "$(docker images -q "kafe/fedora:35-build" 2> /dev/null)" == "" ]]; then + docker build -t kafe/fedora:35-build dist/fedora/35 +fi + +docker run -it --rm -v `pwd`:/kafe kafe/fedora:35-build +mkdir -p build-artifact/fedora-35/ +cp build/fedora/35/kafe-cli-*.rpm build-artifact/fedora-35/ +cp build/fedora/35/libkafe-*.rpm build-artifact/fedora-35/ +for f in build-artifact/fedora-35/*; do mv -v "$f" $(echo "$f" | sed "s/\.rpm/\.f35\.rpm/"); done diff --git a/cli/logger.cpp b/cli/logger.cpp index d71581d..a52eda4 100644 --- a/cli/logger.cpp +++ b/cli/logger.cpp @@ -166,7 +166,7 @@ namespace kafe { } void Logger::on_stdout_line(string line) const { - on_stdout_line("out", line); + on_stdout_line("stdout", line); } void Logger::on_stdout_line(string prefix, string line) const { @@ -197,7 +197,7 @@ namespace kafe { } void Logger::on_stderr_line(string line) const { - on_stderr_line("err", line); + on_stderr_line("stderr", line); } void Logger::on_stderr_line(string prefix, string line) const { @@ -212,7 +212,7 @@ namespace kafe { fprintf( stderr, "%s%s%s %s%s", - IO_TTY_ANSI_COLOR_RED, + IO_TTY_ANSI_COLOR_YELLOW, expand_prefix(prefix).c_str(), IO_TTY_ANSI_COLOR_RESET, context_to_s(get_context()).c_str(), @@ -238,4 +238,4 @@ namespace kafe { LogLevel Logger::get_level() const { return get_env_log_level(); } -} \ No newline at end of file +} diff --git a/cli/main.cpp b/cli/main.cpp index df0d829..482633c 100644 --- a/cli/main.cpp +++ b/cli/main.cpp @@ -35,6 +35,18 @@ using namespace kafe; extern char** environ; #endif +void loadEnvMap(map &envVals) { + for (char **current = environ; *current; ++current) { + const string envVal = string(*current); + const ulong pos = envVal.find_first_of('='); + pair p = pair( + envVal.substr(0, pos), + envVal.substr(pos + 1) + ); + envVals.insert(p); + } +} + void print_usage() { fflush(stderr); fflush(stdout); @@ -44,6 +56,7 @@ void print_usage() { cout << "Usage: kafe [arguments, ...]" << endl; cout << " kafe do " << endl; + cout << " kafe local " << endl; cout << " kafe " << endl; cout << " kafe [--lib]" << endl; cout << " kafe " << endl; @@ -51,6 +64,7 @@ void print_usage() { cout << "\n"; cout << " kafe do: execute tasks from project file with given environment." << endl; + cout << " kafe local: execute tasks from project file on localhost." << endl; cout << " kafe help: display this help." << endl; cout << " kafe version: display KAFE program version and exit. Optionally," " show libkafe version used if argument --lib is set." << endl; @@ -118,24 +132,16 @@ int main(int argc, char *argv[]) { if (0 == strcmp("do", argv[1])) { if (4 > argc) { - cerr << "Command expects exactly at least two arguments - environment name and " - "a comma separated task list with any number of arbitrary arguments " + cerr << "Command expects at least two arguments - environment name and " + "a comma separated task list, and optionally - zero or more of arguments " "to forward to the tasks being invoked.\n" "Example: kafe do staging task1,task2,task3 "; print_usage(); return 1; } - map envvals; - for (char **current = environ; *current; ++current) { - const string envl = string(*current); - const int pos = envl.find_first_of('='); - pair p = pair( - envl.substr(0, pos), - envl.substr(pos + 1) - ); - envvals.insert(p); - } + map envVals; + loadEnvMap(envVals); string environment = argv[2]; string task_list_s = argv[3]; @@ -149,7 +155,41 @@ int main(int argc, char *argv[]) { try { auto project = Project("kafe.lua"); auto logger = Logger(); - auto context = Context(envvals, environment, task_list_v, &logger); + auto context = Context(envVals, environment, task_list_v, &logger); + auto inventory = Inventory(); + project.execute(context, inventory, extra_args); + } catch (RuntimeException &e) { + cerr << e.what() << endl; + return 1; + } + return 0; + } + + if (0 == strcmp("local", argv[1])) { + if (3 > argc) { + cerr << "Command expects at least one argument - " + "a comma separated task list, and optionally - zero or more arguments " + "to forward to the tasks being invoked.\n" + "Example: kafe run task1,task2,task3 "; + print_usage(); + return 1; + } + + map envVals; + loadEnvMap(envVals); + + string task_list_s = argv[2]; + vector task_list_v = split_csv_arguments(task_list_s, ','); + + vector extra_args; + for (int ii = 4; ii < argc; ++ii) { + extra_args.emplace_back(argv[ii]); + } + + try { + auto project = Project("kafe.lua"); + auto logger = Logger(); + auto context = Context(envVals, "", task_list_v, &logger); auto inventory = Inventory(); project.execute(context, inventory, extra_args); } catch (RuntimeException &e) { diff --git a/dist/centos/8/Dockerfile b/dist/almalinux/8/Dockerfile similarity index 63% rename from dist/centos/8/Dockerfile rename to dist/almalinux/8/Dockerfile index 87d0af1..4fb5fee 100644 --- a/dist/centos/8/Dockerfile +++ b/dist/almalinux/8/Dockerfile @@ -1,8 +1,8 @@ -FROM centos:8 +FROM almalinux:8 RUN yum -y update && \ yum -y install dnf-plugins-core && \ - yum config-manager --set-enabled PowerTools && \ + yum config-manager --set-enabled powertools && \ yum -y install \ make \ rpm-build \ @@ -15,7 +15,7 @@ RUN yum -y update && \ libssh-devel \ libgit2-devel -ENV CC /usr/bin/clang-9 -ENV CXX /usr/bin/clang++-9 +ENV CC /usr/bin/clang-10 +ENV CXX /usr/bin/clang++-10 -CMD /kafe/dist/centos/8/build-dist.sh \ No newline at end of file +CMD /kafe/dist/almalinux/8/build-dist.sh \ No newline at end of file diff --git a/dist/centos/8/build-dist.sh b/dist/almalinux/8/build-dist.sh similarity index 86% rename from dist/centos/8/build-dist.sh rename to dist/almalinux/8/build-dist.sh index 2b1c97a..d1f7b7c 100755 --- a/dist/centos/8/build-dist.sh +++ b/dist/almalinux/8/build-dist.sh @@ -2,13 +2,13 @@ set -e # Clean existing build workspace -rm -rf /kafe/build/centos/8 2> /dev/null +rm -rf /kafe/build/almalinux/8 2> /dev/null # Create build workspace -mkdir -p /kafe/build/centos/8/ +mkdir -p /kafe/build/almalinux/8/ # Warp to build workspace -cd /kafe/build/centos/8/ +cd /kafe/build/almalinux/8/ # Prepare build cmake -DCMAKE_BUILD_TYPE=Release -DCPACK_GENERATOR=RPM ../../../ diff --git a/dist/fedora/34/Dockerfile b/dist/fedora/34/Dockerfile index 1823d26..98061a8 100644 --- a/dist/fedora/34/Dockerfile +++ b/dist/fedora/34/Dockerfile @@ -13,7 +13,7 @@ RUN yum -y update && \ libssh-devel \ libgit2-devel -ENV CC /usr/bin/clang-11 -ENV CXX /usr/bin/clang++-11 +ENV CC /usr/bin/clang-12 +ENV CXX /usr/bin/clang++-12 CMD /kafe/dist/fedora/34/build-dist.sh diff --git a/dist/fedora/35/Dockerfile b/dist/fedora/35/Dockerfile new file mode 100644 index 0000000..f1e3c21 --- /dev/null +++ b/dist/fedora/35/Dockerfile @@ -0,0 +1,19 @@ +FROM fedora:35 + +RUN yum -y update && \ + yum -y install \ + make \ + rpm-build \ + wget \ + clang \ + cmake \ + lua-devel \ + libcurl-devel \ + libarchive-devel \ + libssh-devel \ + libgit2-devel + +ENV CC /usr/bin/clang-12 +ENV CXX /usr/bin/clang++-12 + +CMD /kafe/dist/fedora/35/build-dist.sh diff --git a/dist/ubuntu/1910/build-dist.sh b/dist/fedora/35/build-dist.sh similarity index 66% rename from dist/ubuntu/1910/build-dist.sh rename to dist/fedora/35/build-dist.sh index 620d534..2564aa6 100755 --- a/dist/ubuntu/1910/build-dist.sh +++ b/dist/fedora/35/build-dist.sh @@ -2,16 +2,16 @@ set -e # Clean existing build workspace -rm -rf /kafe/build/ubuntu/1910 2> /dev/null +rm -rf /kafe/build/fedora/35 2> /dev/null # Create build workspace -mkdir -p /kafe/build/ubuntu/1910/ +mkdir -p /kafe/build/fedora/35/ # Warp to build workspace -cd /kafe/build/ubuntu/1910/ +cd /kafe/build/fedora/35/ # Prepare build -cmake -DCMAKE_BUILD_TYPE=Release -DCPACK_GENERATOR=DEB ../../../ +cmake -DCMAKE_BUILD_TYPE=Release -DCPACK_GENERATOR=RPM ../../../ # Compile project make @@ -20,7 +20,7 @@ make cpack # Test install -apt-get install -y ./kafe-cli*.deb ./libkafe*.deb +rpm -i ./kafe-cli*.rpm ./libkafe*.rpm env kafe about # Fix permissions @@ -41,4 +41,4 @@ if [ "$GROUPID" -eq "0" ]; then GROUPID=1000 fi -chown -Rf $USERID:$GROUPID /kafe/build \ No newline at end of file +chown -Rf $USERID:$GROUPID /kafe/build diff --git a/dist/ubuntu/1910/Dockerfile b/dist/ubuntu/1910/Dockerfile deleted file mode 100644 index 64535a7..0000000 --- a/dist/ubuntu/1910/Dockerfile +++ /dev/null @@ -1,19 +0,0 @@ -FROM ubuntu:19.10 - -RUN DEBIAN_FRONTEND=noninteractive apt-get update && \ - DEBIAN_FRONTEND=noninteractive apt-get upgrade -y && \ - DEBIAN_FRONTEND=noninteractive apt-get install -y \ - wget \ - cmake \ - build-essential \ - clang-9 \ - liblua5.3-dev \ - libcurl4-gnutls-dev \ - libarchive-dev \ - libssh-dev \ - libgit2-dev - -ENV CC /usr/bin/clang-9 -ENV CXX /usr/bin/clang++-9 - -CMD /kafe/dist/ubuntu/1910/build-dist.sh \ No newline at end of file diff --git a/docs/SCRIPTING_API_L1.md b/docs/SCRIPTING_API_L1.md index 8a5e0ac..2c40823 100644 --- a/docs/SCRIPTING_API_L1.md +++ b/docs/SCRIPTING_API_L1.md @@ -209,6 +209,8 @@ Effectively prepends `cd &&` to all subsequent shell commands in the same subsequent remote shell commands in directory that does not exist will result in their hard failure. +**NOTE:** when running in local mode (`kafe local`) this command is an alias of `k.local_within`. + ##### An example of usage ```lua @@ -231,6 +233,8 @@ Execute a remote shell command and return it's outputs along with exit code. Second optional argument indicates if the remote output should also be logged in output of the tool. This option is enabled by default. +**NOTE:** when running in local mode (`kafe local`) this command is an alias of `k.local_exec`. + ##### An example of usage ```lua @@ -253,6 +257,8 @@ end) Execute a remote shell command, log output and return exit status as boolean. Will return true if exit status of the remote command is `0`, false otherwise. +**NOTE:** when running in local mode (`kafe local`) this command is an alias of `k.local_shell`. + ##### An example of usage ```lua @@ -555,6 +561,64 @@ k.task('example_task', function() end) ``` +## void k.strict([bool state = true]) +#### New in version 1.1.4 + +Set strict execution mode within current execution runtime context (function). + +##### What is strict mode? + +Strict mode enables early failure when executing commands. Whenever a function is run +with strict mode enable inside that function, any remote or local calls that does not +complete with successful exit code (0) will be considered failed and will immediately +interrupt execution of the current task or subtask. + +Strict mode is disabled by default to allow for graceful error handling, however, it might +be useful to enable strict mode in some cases, for example, when recovery is impossible, +not feasible or simply not required. + +Strict mode is automatically reset to disabled when: + +- a task is executed +- `invoke` is used +- `on` is used for every host in the matching inventory + +##### An example of usage + +```lua +local k = require('kafe') + +local subtask = function() + k.strict() -- Enabling strict mode in subtask context + k.shell('command_that_fails') -- Invoking a failing shell command + k.shell('will_not_run') -- This will NOT be executed if strict mode is enabled +end + +k.task('example_task', function() + k.invoke(subtask) +end) +``` + +## void k.invoke(function ref) +#### New in version 1.1.4 + +Invoke function within the same context as currently executing task or subtask. +Handles strict mode execution failures in a safe way. + +##### An example of usage + +```lua +local k = require('kafe') + +local subtask = function() + k.shell('echo "hello world"') +end + +k.task('example_task', function() + k.invoke(subtask) +end) +``` + ## `.kafeignore` support for file archiving #### New in version 1.1.2 diff --git a/libkafe/include/kafe/context.hpp b/libkafe/include/kafe/context.hpp index 1870d75..532879c 100644 --- a/libkafe/include/kafe/context.hpp +++ b/libkafe/include/kafe/context.hpp @@ -50,6 +50,8 @@ namespace kafe { [[nodiscard]] const vector &get_tasks() const; [[nodiscard]] const ILogEventListener *get_log_listener() const; + + [[nodiscard]] bool is_local_context() const; }; } diff --git a/libkafe/include/kafe/execution_scope.hpp b/libkafe/include/kafe/execution_scope.hpp index a84562d..865dc22 100644 --- a/libkafe/include/kafe/execution_scope.hpp +++ b/libkafe/include/kafe/execution_scope.hpp @@ -46,6 +46,7 @@ namespace kafe { LocalApi *local; const vector &extra_args; const string& project_file; + bool is_strict_exec_mode = false; public: explicit ExecutionScope( const Context &context, @@ -89,7 +90,11 @@ namespace kafe { const string &get_project_file() const; const string get_env(const string &key) const; + + void set_strict(bool b); + + bool is_strict() const; }; } -#endif \ No newline at end of file +#endif diff --git a/libkafe/include/kafe/scripting/script.hpp b/libkafe/include/kafe/scripting/script.hpp index b458f6b..c55b81b 100644 --- a/libkafe/include/kafe/scripting/script.hpp +++ b/libkafe/include/kafe/scripting/script.hpp @@ -42,6 +42,9 @@ namespace kafe::scripting { using ScriptEngineException::ScriptEngineException; }; + class ScriptStrictExecutionException : public exception { + }; + class Script { const ExecutionScope &scope; lua_State *lua_state; diff --git a/libkafe/src/context.cpp b/libkafe/src/context.cpp index 323ba4c..26d7166 100644 --- a/libkafe/src/context.cpp +++ b/libkafe/src/context.cpp @@ -46,4 +46,8 @@ namespace kafe { const map *Context::get_envvals() const { return &envvals; } -} \ No newline at end of file + + bool Context::is_local_context() const { + return this->environment.empty(); + } +} diff --git a/libkafe/src/execution_scope.cpp b/libkafe/src/execution_scope.cpp index b600bec..b888bcd 100644 --- a/libkafe/src/execution_scope.cpp +++ b/libkafe/src/execution_scope.cpp @@ -179,4 +179,12 @@ namespace kafe { const string &ExecutionScope::get_project_file() const { return project_file; } -} \ No newline at end of file + + void ExecutionScope::set_strict(bool state) { + this->is_strict_exec_mode = state; + } + + bool ExecutionScope::is_strict() const { + return is_strict_exec_mode; + } +} diff --git a/libkafe/src/project.cpp b/libkafe/src/project.cpp index 9783ac8..f6b409f 100644 --- a/libkafe/src/project.cpp +++ b/libkafe/src/project.cpp @@ -50,33 +50,50 @@ namespace kafe { logger->emit_debug("Resolved project file is <%s>", main_file.c_str()); - timer = logger->emit_info_wt("Loading project file <%s>", project_file.c_str()); + timer = logger->emit_debug_wt("Loading project file <%s>", project_file.c_str()); script.load_file(main_file); - logger->emit_success(&timer, "Loaded project file <%s>", project_file.c_str()); - timer = logger->emit_info_wt("Evaluating project file <%s>", project_file.c_str()); + if (logger->get_level() <= LogLevel::DEBUG) { + logger->emit_success(&timer, "Loaded project file <%s>", project_file.c_str()); + } + + timer = logger->emit_debug_wt("Evaluating project file <%s>", project_file.c_str()); script.evaluate(); - logger->emit_success(&timer, "Done evaluating project file <%s>", project_file.c_str()); - logger->emit_info("Verifying all tasks requested are defined"); + if (logger->get_level() <= LogLevel::DEBUG) { + logger->emit_success(&timer, "Done evaluating project file <%s>", project_file.c_str()); + } + + logger->emit_debug("Verifying all tasks requested are defined"); for (const auto &task_name : scope.get_context()->get_tasks()) { if (!scope.get_tasks()->task_exists(task_name)) { throw UnknownTaskException("Task <%s> is not defined in project", task_name.c_str()); } } - logger->emit_success("Tasks verified"); + + if (logger->get_level() <= LogLevel::DEBUG) { + logger->emit_success("Tasks verified"); + } for (const auto &task_name : scope.get_context()->get_tasks()) { + scope.set_strict(false); // Reset before every run of a task. + timer = logger->emit_info_wt("Executing task <%s>", task_name.c_str()); logger->context_push(task_name); const Task *task = scope.get_tasks()->get_task(task_name); int ref = task->get_function_reference(); - script.invoke_function_by_ref(ref, extra_args); + try { + script.invoke_function_by_ref(ref, extra_args); + logger->emit_success(&timer, "Task <%s> completed", task_name.c_str()); + } catch (ScriptStrictExecutionException &e) { + //pass + logger->emit_error(&timer, "Task <%s> failed in strict mode", task_name.c_str()); + break; + } logger->context_pop(); - logger->emit_success(&timer, "Task <%s> completed", task_name.c_str()); } } -} \ No newline at end of file +} diff --git a/libkafe/src/scripting/script.cpp b/libkafe/src/scripting/script.cpp index 7a50255..ea29c17 100644 --- a/libkafe/src/scripting/script.cpp +++ b/libkafe/src/scripting/script.cpp @@ -160,6 +160,126 @@ namespace kafe::scripting { return 0; } + int lua_api_strict_mode(lua_State *L) { + bool strict_mode_state; + + if (0 == lua_gettop(L)) { + strict_mode_state = true; + } else if (1 != lua_gettop(L) || !lua_isboolean(L, 1)) { + return luaL_error(L, "Expected zero or one argument - boolean to enable/disable strict mode"); + } else { + strict_mode_state = lua_toboolean(L, 1); + } + + get_scope(L)->set_strict(strict_mode_state); + + return 0; + } + + + int lua_api_local_within(lua_State *L) { + const auto *scope = get_scope(L); + + if (1 != lua_gettop(L) || !lua_isstring(L, 1)) { + luaL_error(L, "Expected one argument - string"); + } + + auto directory = scope->replace_vars(luaL_checkstring(L, 1)); + auto directory_norm = FileSystem::normalize(directory, scope->get_local_api()->get_chdir()); + + scope->get_context()->get_log_listener()->emit_info( + "Changing local working directory to <%s>", + directory_norm.c_str() + ); + + if (!FileSystem::is_directory(directory_norm)) { + return luaL_error(L, "Not a directory <%s>", directory_norm.c_str()); + } + + scope->get_local_api()->chdir(directory_norm); + + return 0; + } + + int lua_api_local_exec(lua_State *L) { + const auto *scope = get_scope(L); + + if (scope->has_current_api()) { + auto debug = get_lua_debug(L); + scope->get_context()->get_log_listener()->emit_warning( + "Executing local command with active remote scope in %s:%d. " + "Command will be executed for each server in context!", + debug.short_src, + debug.currentline + ); + } + + int n_args = lua_gettop(L); + + if (1 != n_args && 2 != n_args) { + return luaL_error(L, "Expected one or two arguments"); + } + + if (!lua_isstring(L, 1)) { + return luaL_error(L, "Argument one is expected to be string"); + } + + bool print_output = true; + if (n_args == 2) { + if (!lua_isboolean(L, 2)) { + return luaL_error(L, "Argument two is expected to be boolean"); + } + print_output = static_cast(lua_toboolean(L, 2)); + } + + auto command = scope->replace_vars(luaL_checkstring(L, 1)); + auto result = scope->get_local_api()->local_popen(command, print_output); + + if (scope->is_strict() && result.get_code() != 0) { + throw ScriptStrictExecutionException(); + } + + lua_pushstring(L, result.get_out().c_str()); + lua_pushinteger(L, result.get_code()); + + return 2; + } + + int lua_api_local_shell(lua_State *L) { + const auto *scope = get_scope(L); + + if (scope->has_current_api()) { + auto debug = get_lua_debug(L); + scope->get_context()->get_log_listener()->emit_warning( + "Executing local command with active remote scope in %s:%d. " + "Command will be executed for each server in context!", + debug.short_src, + debug.currentline + ); + } + + int n_args = lua_gettop(L); + + if (1 != n_args) { + return luaL_error(L, "Expected one argument"); + } + + if (!lua_isstring(L, 1)) { + return luaL_error(L, "Argument one is expected to be string"); + } + + auto command = scope->replace_vars(luaL_checkstring(L, 1)); + auto result = scope->get_local_api()->local_popen(command, true); + + if (scope->is_strict() && result.get_code() != 0) { + throw ScriptStrictExecutionException(); + } + + lua_pushboolean(L, 0 == result.get_code()); + + return 1; + } + // TODO: allow kDSN format - int lua_api_inventory_add(lua_State *L) { const auto *scope = get_scope(L); @@ -263,7 +383,7 @@ namespace kafe::scripting { tasks->add_task(*task); - scope->get_context()->get_log_listener()->emit_info( + scope->get_context()->get_log_listener()->emit_debug( "Defined task <%s>", task_name ); @@ -333,9 +453,18 @@ namespace kafe::scripting { auto ssh_api = SshApi(&ssh_manager, logger); scope->set_current_remote(&ssh_api); + scope->set_strict(false); - lua_rawgeti(L, LUA_REGISTRYINDEX, function_reference); - int status = lua_pcall(L, 0, 0, 0); + int status; + try { + lua_rawgeti(L, LUA_REGISTRYINDEX, function_reference); + status = lua_pcall(L, 0, 0, 0); + } catch (ScriptStrictExecutionException &e) { + failed = true; + scope->clear_current_api(); + logger->context_pop(); + break; + } scope->clear_current_api(); @@ -347,6 +476,7 @@ namespace kafe::scripting { logger->context_pop(); break; } + logger->context_pop(); } @@ -357,9 +487,35 @@ namespace kafe::scripting { return 1; } + int lua_api_invoke_func(lua_State *L) { + get_scope(L)->set_strict(false); + + if (1 != lua_gettop(L) || !lua_isfunction(L, 1)) { + return luaL_error(L,"Expected one argument - function reference"); + } + + auto function_reference = luaL_ref(L, LUA_REGISTRYINDEX); + + int status = -1; + try { + lua_rawgeti(L, LUA_REGISTRYINDEX, function_reference); + status = lua_pcall(L, 0, 0, 0); + } catch (ScriptStrictExecutionException &e) { + // pass + } + + lua_pushboolean(L, status == 0); + + return 1; + } + int lua_api_remote_within(lua_State *L) { const auto *scope = get_scope(L); + if (scope->get_context()->is_local_context()) { + return lua_api_local_shell(L); + } + if (!scope->has_current_api()) { return luaL_error(L, "Can not change remote directory when not in remote scope"); } @@ -384,6 +540,10 @@ namespace kafe::scripting { int lua_api_remote_exec(lua_State *L) { const auto *scope = get_scope(L); + if (scope->get_context()->is_local_context()) { + return lua_api_local_exec(L); + } + if (!scope->has_current_api()) { return luaL_error(L, "Can not execute remote command when not in remote scope"); } @@ -411,6 +571,10 @@ namespace kafe::scripting { auto result = api->execute(command, print_output); + if (scope->is_strict() && result.get_code() != 0) { + throw ScriptStrictExecutionException(); + } + lua_pushstring(L, result.get_stdout().c_str()); lua_pushstring(L, result.get_stderr().c_str()); lua_pushinteger(L, result.get_code()); @@ -421,6 +585,10 @@ namespace kafe::scripting { int lua_api_remote_shell(lua_State *L) { const auto *scope = get_scope(L); + if (scope->get_context()->is_local_context()) { + return lua_api_local_shell(L); + } + if (!scope->has_current_api()) { return luaL_error(L, "Can not execute remote command when not in remote scope"); } @@ -440,6 +608,10 @@ namespace kafe::scripting { auto result = api->execute(command, true); + if (scope->is_strict() && result.get_code() != 0) { + throw ScriptStrictExecutionException(); + } + lua_pushboolean(L, 0 == result.get_code()); return 1; @@ -569,6 +741,11 @@ namespace kafe::scripting { "Upload failed - %s", e.what() ); + + if (scope->is_strict()) { + throw ScriptStrictExecutionException(); + } + lua_pushboolean(L, false); } @@ -624,6 +801,9 @@ namespace kafe::scripting { "Download failed - %s", e.what() ); + if (scope->is_strict()) { + throw ScriptStrictExecutionException(); + } lua_pushboolean(L, false); } @@ -676,6 +856,9 @@ namespace kafe::scripting { "Upload failed - %s", e.what() ); + if (scope->is_strict()) { + throw ScriptStrictExecutionException(); + } lua_pushboolean(L, false); } @@ -720,6 +903,9 @@ namespace kafe::scripting { "Download failed - %s", e.what() ); + if (scope->is_strict()) { + throw ScriptStrictExecutionException(); + } lua_pushboolean(L, false); } @@ -797,106 +983,13 @@ namespace kafe::scripting { return 1; } - int lua_api_local_within(lua_State *L) { - const auto *scope = get_scope(L); - - if (1 != lua_gettop(L) || !lua_isstring(L, 1)) { - luaL_error(L, "Expected one argument - string"); - } - - auto directory = scope->replace_vars(luaL_checkstring(L, 1)); - auto directory_norm = FileSystem::normalize(directory, scope->get_local_api()->get_chdir()); - - scope->get_context()->get_log_listener()->emit_info( - "Changing local working directory to <%s>", - directory_norm.c_str() - ); - - if (!FileSystem::is_directory(directory_norm)) { - return luaL_error(L, "Not a directory <%s>", directory_norm.c_str()); - } - - scope->get_local_api()->chdir(directory_norm); - - return 0; - } - - int lua_api_local_exec(lua_State *L) { - const auto *scope = get_scope(L); - - if (scope->has_current_api()) { - auto debug = get_lua_debug(L); - scope->get_context()->get_log_listener()->emit_warning( - "Executing local command with active remote scope in %s:%d. " - "Command will be executed for each server in context!", - debug.short_src, - debug.currentline - ); - } - - int n_args = lua_gettop(L); - - if (1 != n_args && 2 != n_args) { - return luaL_error(L, "Expected one or two arguments"); - } - - if (!lua_isstring(L, 1)) { - return luaL_error(L, "Argument one is expected to be string"); - } - - bool print_output = true; - if (n_args == 2) { - if (!lua_isboolean(L, 2)) { - return luaL_error(L, "Argument two is expected to be boolean"); - } - print_output = static_cast(lua_toboolean(L, 2)); - } - - auto command = scope->replace_vars(luaL_checkstring(L, 1)); - auto result = scope->get_local_api()->local_popen(command, print_output); - - lua_pushstring(L, result.get_out().c_str()); - lua_pushinteger(L, result.get_code()); - - return 2; - } - - int lua_api_local_shell(lua_State *L) { - const auto *scope = get_scope(L); - - if (scope->has_current_api()) { - auto debug = get_lua_debug(L); - scope->get_context()->get_log_listener()->emit_warning( - "Executing local command with active remote scope in %s:%d. " - "Command will be executed for each server in context!", - debug.short_src, - debug.currentline - ); - } - - int n_args = lua_gettop(L); - - if (1 != n_args) { - return luaL_error(L, "Expected one argument"); - } - - if (!lua_isstring(L, 1)) { - return luaL_error(L, "Argument one is expected to be string"); - } - - auto command = scope->replace_vars(luaL_checkstring(L, 1)); - auto result = scope->get_local_api()->local_popen(command, true); - - lua_pushboolean(L, 0 == result.get_code()); - - return 1; - } - extern "C" const struct luaL_Reg module_def[] = { {"require_api", lua_api_level_require}, + {"strict", lua_api_strict_mode}, {"task", lua_api_task_define}, {"add_inventory", lua_api_inventory_add}, {"on", lua_api_on_role_invoke}, + {"invoke", lua_api_invoke_func}, {"within", lua_api_remote_within}, {"exec", lua_api_remote_exec}, {"shell", lua_api_remote_shell}, @@ -909,7 +1002,7 @@ namespace kafe::scripting { {"define", lua_api_define}, {"strfvars", lua_api_strfvars}, {"strfenv", lua_api_strfenv}, - {"getenv", lua_api_getenv}, + {"getenv", lua_api_getenv}, {"local_exec", lua_api_local_exec}, {"local_shell", lua_api_local_shell}, {"local_within", lua_api_local_within}, diff --git a/publish.sh b/publish.sh index 8f2b0fb..8a5f8c5 100755 --- a/publish.sh +++ b/publish.sh @@ -1,61 +1,65 @@ #!/usr/bin/env bash # EL -cloudsmith push rpm kafe/libkafe/el/7 build-artifact/centos-7/kafe-cli-1.1.3-1.x86_64.el7.rpm -cloudsmith push rpm kafe/libkafe/el/7 build-artifact/centos-7/libkafe-1.1.3-1.x86_64.el7.rpm -cloudsmith push rpm kafe/libkafe/el/7 build-artifact/centos-7/libkafe-devel-1.1.3-1.x86_64.el7.rpm +cloudsmith push rpm kafe/libkafe/el/7 build-artifact/centos-7/kafe-cli-1.1.5-1.x86_64.el7.rpm +cloudsmith push rpm kafe/libkafe/el/7 build-artifact/centos-7/libkafe-1.1.5-1.x86_64.el7.rpm +cloudsmith push rpm kafe/libkafe/el/7 build-artifact/centos-7/libkafe-devel-1.1.5-1.x86_64.el7.rpm -cloudsmith push rpm kafe/libkafe/el/8 build-artifact/centos-8/kafe-cli-1.1.3-1.x86_64.el8.rpm -cloudsmith push rpm kafe/libkafe/el/8 build-artifact/centos-8/libkafe-1.1.3-1.x86_64.el8.rpm -cloudsmith push rpm kafe/libkafe/el/8 build-artifact/centos-8/libkafe-devel-1.1.3-1.x86_64.el8.rpm +cloudsmith push rpm kafe/libkafe/el/8 build-artifact/almalinux-8/kafe-cli-1.1.5-1.x86_64.el8.rpm +cloudsmith push rpm kafe/libkafe/el/8 build-artifact/almalinux-8/libkafe-1.1.5-1.x86_64.el8.rpm +cloudsmith push rpm kafe/libkafe/el/8 build-artifact/almalinux-8/libkafe-devel-1.1.5-1.x86_64.el8.rpm # Debian -cloudsmith push deb kafe/libkafe/debian/stretch build-artifact/debian-9/kafe-cli_1.1.3_amd64.deb9.deb -cloudsmith push deb kafe/libkafe/debian/stretch build-artifact/debian-9/libkafe_1.1.3_amd64.deb9.deb -cloudsmith push deb kafe/libkafe/debian/stretch build-artifact/debian-9/libkafe-dev_1.1.3_amd64.deb9.deb +cloudsmith push deb kafe/libkafe/debian/stretch build-artifact/debian-9/kafe-cli_1.1.5_amd64.deb9.deb +cloudsmith push deb kafe/libkafe/debian/stretch build-artifact/debian-9/libkafe_1.1.5_amd64.deb9.deb +cloudsmith push deb kafe/libkafe/debian/stretch build-artifact/debian-9/libkafe-dev_1.1.5_amd64.deb9.deb -cloudsmith push deb kafe/libkafe/debian/buster build-artifact/debian-10/kafe-cli_1.1.3_amd64.deb10.deb -cloudsmith push deb kafe/libkafe/debian/buster build-artifact/debian-10/libkafe_1.1.3_amd64.deb10.deb -cloudsmith push deb kafe/libkafe/debian/buster build-artifact/debian-10/libkafe-dev_1.1.3_amd64.deb10.deb +cloudsmith push deb kafe/libkafe/debian/buster build-artifact/debian-10/kafe-cli_1.1.5_amd64.deb10.deb +cloudsmith push deb kafe/libkafe/debian/buster build-artifact/debian-10/libkafe_1.1.5_amd64.deb10.deb +cloudsmith push deb kafe/libkafe/debian/buster build-artifact/debian-10/libkafe-dev_1.1.5_amd64.deb10.deb -cloudsmith push deb kafe/libkafe/debian/bullseye build-artifact/debian-11/kafe-cli_1.1.3_amd64.deb11.deb -cloudsmith push deb kafe/libkafe/debian/bullseye build-artifact/debian-11/libkafe_1.1.3_amd64.deb11.deb -cloudsmith push deb kafe/libkafe/debian/bullseye build-artifact/debian-11/libkafe-dev_1.1.3_amd64.deb11.deb +cloudsmith push deb kafe/libkafe/debian/bullseye build-artifact/debian-11/kafe-cli_1.1.5_amd64.deb11.deb +cloudsmith push deb kafe/libkafe/debian/bullseye build-artifact/debian-11/libkafe_1.1.5_amd64.deb11.deb +cloudsmith push deb kafe/libkafe/debian/bullseye build-artifact/debian-11/libkafe-dev_1.1.5_amd64.deb11.deb # Ubuntu -cloudsmith push deb kafe/libkafe/ubuntu/bionic build-artifact/ubuntu-1804/kafe-cli_1.1.3_amd64.ubu1804.deb -cloudsmith push deb kafe/libkafe/ubuntu/bionic build-artifact/ubuntu-1804/libkafe_1.1.3_amd64.ubu1804.deb -cloudsmith push deb kafe/libkafe/ubuntu/bionic build-artifact/ubuntu-1804/libkafe-dev_1.1.3_amd64.ubu1804.deb +cloudsmith push deb kafe/libkafe/ubuntu/bionic build-artifact/ubuntu-1804/kafe-cli_1.1.5_amd64.ubu1804.deb +cloudsmith push deb kafe/libkafe/ubuntu/bionic build-artifact/ubuntu-1804/libkafe_1.1.5_amd64.ubu1804.deb +cloudsmith push deb kafe/libkafe/ubuntu/bionic build-artifact/ubuntu-1804/libkafe-dev_1.1.5_amd64.ubu1804.deb -cloudsmith push deb kafe/libkafe/ubuntu/eoan build-artifact/ubuntu-1910/kafe-cli_1.1.3_amd64.ubu1910.deb -cloudsmith push deb kafe/libkafe/ubuntu/eoan build-artifact/ubuntu-1910/libkafe_1.1.3_amd64.ubu1910.deb -cloudsmith push deb kafe/libkafe/ubuntu/eoan build-artifact/ubuntu-1910/libkafe-dev_1.1.3_amd64.ubu1910.deb +cloudsmith push deb kafe/libkafe/ubuntu/eoan build-artifact/ubuntu-1910/kafe-cli_1.1.5_amd64.ubu1910.deb +cloudsmith push deb kafe/libkafe/ubuntu/eoan build-artifact/ubuntu-1910/libkafe_1.1.5_amd64.ubu1910.deb +cloudsmith push deb kafe/libkafe/ubuntu/eoan build-artifact/ubuntu-1910/libkafe-dev_1.1.5_amd64.ubu1910.deb -cloudsmith push deb kafe/libkafe/ubuntu/focal build-artifact/ubuntu-2004/kafe-cli_1.1.3_amd64.ubu2004.deb -cloudsmith push deb kafe/libkafe/ubuntu/focal build-artifact/ubuntu-2004/libkafe_1.1.3_amd64.ubu2004.deb -cloudsmith push deb kafe/libkafe/ubuntu/focal build-artifact/ubuntu-2004/libkafe-dev_1.1.3_amd64.ubu2004.deb +cloudsmith push deb kafe/libkafe/ubuntu/focal build-artifact/ubuntu-2004/kafe-cli_1.1.5_amd64.ubu2004.deb +cloudsmith push deb kafe/libkafe/ubuntu/focal build-artifact/ubuntu-2004/libkafe_1.1.5_amd64.ubu2004.deb +cloudsmith push deb kafe/libkafe/ubuntu/focal build-artifact/ubuntu-2004/libkafe-dev_1.1.5_amd64.ubu2004.deb -cloudsmith push deb kafe/libkafe/ubuntu/groovy build-artifact/ubuntu-2010/kafe-cli_1.1.3_amd64.ubu2010.deb -cloudsmith push deb kafe/libkafe/ubuntu/groovy build-artifact/ubuntu-2010/libkafe_1.1.3_amd64.ubu2010.deb -cloudsmith push deb kafe/libkafe/ubuntu/groovy build-artifact/ubuntu-2010/libkafe-dev_1.1.3_amd64.ubu2010.deb +cloudsmith push deb kafe/libkafe/ubuntu/groovy build-artifact/ubuntu-2010/kafe-cli_1.1.5_amd64.ubu2010.deb +cloudsmith push deb kafe/libkafe/ubuntu/groovy build-artifact/ubuntu-2010/libkafe_1.1.5_amd64.ubu2010.deb +cloudsmith push deb kafe/libkafe/ubuntu/groovy build-artifact/ubuntu-2010/libkafe-dev_1.1.5_amd64.ubu2010.deb -cloudsmith push deb kafe/libkafe/ubuntu/hirsute build-artifact/ubuntu-2104/kafe-cli_1.1.3_amd64.ubu2104.deb -cloudsmith push deb kafe/libkafe/ubuntu/hirsute build-artifact/ubuntu-2104/libkafe_1.1.3_amd64.ubu2104.deb -cloudsmith push deb kafe/libkafe/ubuntu/hirsute build-artifact/ubuntu-2104/libkafe-dev_1.1.3_amd64.ubu2104.deb +cloudsmith push deb kafe/libkafe/ubuntu/hirsute build-artifact/ubuntu-2104/kafe-cli_1.1.5_amd64.ubu2104.deb +cloudsmith push deb kafe/libkafe/ubuntu/hirsute build-artifact/ubuntu-2104/libkafe_1.1.5_amd64.ubu2104.deb +cloudsmith push deb kafe/libkafe/ubuntu/hirsute build-artifact/ubuntu-2104/libkafe-dev_1.1.5_amd64.ubu2104.deb # Fedora -cloudsmith push rpm kafe/libkafe/fedora/31 build-artifact/fedora-31/kafe-cli-1.1.3-1.x86_64.f31.rpm -cloudsmith push rpm kafe/libkafe/fedora/31 build-artifact/fedora-31/libkafe-1.1.3-1.x86_64.f31.rpm -cloudsmith push rpm kafe/libkafe/fedora/31 build-artifact/fedora-31/libkafe-devel-1.1.3-1.x86_64.f31.rpm +cloudsmith push rpm kafe/libkafe/fedora/31 build-artifact/fedora-31/kafe-cli-1.1.5-1.x86_64.f31.rpm +cloudsmith push rpm kafe/libkafe/fedora/31 build-artifact/fedora-31/libkafe-1.1.5-1.x86_64.f31.rpm +cloudsmith push rpm kafe/libkafe/fedora/31 build-artifact/fedora-31/libkafe-devel-1.1.5-1.x86_64.f31.rpm -cloudsmith push rpm kafe/libkafe/fedora/32 build-artifact/fedora-32/kafe-cli-1.1.3-1.x86_64.f32.rpm -cloudsmith push rpm kafe/libkafe/fedora/32 build-artifact/fedora-32/libkafe-1.1.3-1.x86_64.f32.rpm -cloudsmith push rpm kafe/libkafe/fedora/32 build-artifact/fedora-32/libkafe-devel-1.1.3-1.x86_64.f32.rpm +cloudsmith push rpm kafe/libkafe/fedora/32 build-artifact/fedora-32/kafe-cli-1.1.5-1.x86_64.f32.rpm +cloudsmith push rpm kafe/libkafe/fedora/32 build-artifact/fedora-32/libkafe-1.1.5-1.x86_64.f32.rpm +cloudsmith push rpm kafe/libkafe/fedora/32 build-artifact/fedora-32/libkafe-devel-1.1.5-1.x86_64.f32.rpm -cloudsmith push rpm kafe/libkafe/fedora/33 build-artifact/fedora-33/kafe-cli-1.1.3-1.x86_64.f33.rpm -cloudsmith push rpm kafe/libkafe/fedora/33 build-artifact/fedora-33/libkafe-1.1.3-1.x86_64.f33.rpm -cloudsmith push rpm kafe/libkafe/fedora/33 build-artifact/fedora-33/libkafe-devel-1.1.3-1.x86_64.f33.rpm +cloudsmith push rpm kafe/libkafe/fedora/33 build-artifact/fedora-33/kafe-cli-1.1.5-1.x86_64.f33.rpm +cloudsmith push rpm kafe/libkafe/fedora/33 build-artifact/fedora-33/libkafe-1.1.5-1.x86_64.f33.rpm +cloudsmith push rpm kafe/libkafe/fedora/33 build-artifact/fedora-33/libkafe-devel-1.1.5-1.x86_64.f33.rpm -cloudsmith push rpm kafe/libkafe/fedora/34 build-artifact/fedora-34/kafe-cli-1.1.3-1.x86_64.f34.rpm -cloudsmith push rpm kafe/libkafe/fedora/34 build-artifact/fedora-34/libkafe-1.1.3-1.x86_64.f34.rpm -cloudsmith push rpm kafe/libkafe/fedora/34 build-artifact/fedora-34/libkafe-devel-1.1.3-1.x86_64.f34.rpm +cloudsmith push rpm kafe/libkafe/fedora/34 build-artifact/fedora-34/kafe-cli-1.1.5-1.x86_64.f34.rpm +cloudsmith push rpm kafe/libkafe/fedora/34 build-artifact/fedora-34/libkafe-1.1.5-1.x86_64.f34.rpm +cloudsmith push rpm kafe/libkafe/fedora/34 build-artifact/fedora-34/libkafe-devel-1.1.5-1.x86_64.f34.rpm + +cloudsmith push rpm kafe/libkafe/fedora/35 build-artifact/fedora-35/kafe-cli-1.1.5-1.x86_64.f35.rpm +cloudsmith push rpm kafe/libkafe/fedora/35 build-artifact/fedora-35/libkafe-1.1.5-1.x86_64.f35.rpm +cloudsmith push rpm kafe/libkafe/fedora/35 build-artifact/fedora-35/libkafe-devel-1.1.5-1.x86_64.f35.rpm