From 8cf8e5bb97aa13d624caf4b9483010ff0c6644ed Mon Sep 17 00:00:00 2001 From: Heiko Alexander Weber Date: Mon, 30 Nov 2020 17:35:13 +0100 Subject: [PATCH] #9 | + STDIN support for print command | #patch (#10) #9 implementation known bug with CLI backend --- .github/workflows/pipeline.yml | 111 +++++++++++++++++++++++++++++++-- .vscode/launch.json | 3 +- Cargo.lock | 39 ++++-------- Cargo.toml | 2 +- docs/README.md | 3 +- docs/adrs/src/SUMMARY.md | 10 +++ docs/wiki/src/SUMMARY.md | 3 +- docs/wiki/src/adrs/6.md | 8 ++- docs/wiki/src/adrs/7.md | 11 ++++ src/args/mod.rs | 54 ++++++++++++---- src/main.rs | 3 +- src/render/mod.rs | 2 +- 12 files changed, 195 insertions(+), 54 deletions(-) create mode 100644 docs/adrs/src/SUMMARY.md create mode 100644 docs/wiki/src/adrs/7.md diff --git a/.github/workflows/pipeline.yml b/.github/workflows/pipeline.yml index 04e0017..93c87fb 100644 --- a/.github/workflows/pipeline.yml +++ b/.github/workflows/pipeline.yml @@ -16,19 +16,118 @@ jobs: features: --features "backend+ui" --no-default-features steps: - uses: actions/checkout@v1 - - name: Install tools + - name: install tools run: sudo apt-get install build-essential libncurses5-dev libncursesw5-dev - - name: Check formatting + - name: check formatting run: cargo fmt --all -- --check - - name: Check README.md synchronization + - name: check README.md synchronization run: | cargo install --force cargo-sync-readme cargo sync-readme -c - - name: Scan code + - name: scan code run: | rustup component add clippy cargo clippy --all-targets ${{ matrix.features }} -- -D warnings - - name: Execute tests + - name: execute unit tests run: cargo test ${{ matrix.features }} - - name: Build program + - name: build program run: cargo build ${{ matrix.features }} +<<<<<<< HEAD +======= + + tag: + if: github.ref == 'refs/heads/master' + needs: check + name: tag and release + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: tag + id: tag + uses: anothrNick/github-tag-action@1.19.0 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + WITH_V: false + RELEASE_BRANCHES: master + DEFAULT_BUMP: patch + - name: create release + id: create_release + uses: actions/create-release@latest + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ steps.tag.outputs.new_tag}} + release_name: ${{ steps.tag.outputs.new_tag }} + body: | + Release ${{ steps.tag.outputs.new_tag }}. + draft: false + prerelease: false + - run: printf ${{ steps.create_release.outputs.upload_url }} > ${{ env.RELEASE_FILE }} + - name: upload release data + uses: actions/upload-artifact@v1.0.0 + with: + name: RELEASE + path: ${{ env.RELEASE_FILE }} + + publish-cratesio: + needs: tag + name: crates.io + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: get version + id: get_version + run: echo ::set-output name=VERSION::$(git tag --points-at HEAD --sort -version:refname | head -1) + - name: install tools + run: sudo apt-get install build-essential libncurses5-dev libncursesw5-dev + - name: publish + id: publish + run: | + VERSION=${{ steps.get_version.outputs.VERSION }} make update-version && + cargo login ${{ secrets.CRATES_IO_TOKEN }} && + cargo publish --allow-dirty + + publish: + needs: tag + name: publish for ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + matrix: + include: + - os: macos-latest + target: x86_64-apple-darwin + install: printf ok + - os: ubuntu-latest + target: x86_64-unknown-linux-gnu + install: sudo apt-get install build-essential libncurses5-dev libncursesw5-dev + steps: + - uses: actions/checkout@v1 + - name: get version + id: get_version + run: echo ::set-output name=VERSION::$(git tag --points-at HEAD --sort -version:refname | head -1) + - name: download release id + uses: actions/download-artifact@v1.0.0 + with: + name: RELEASE + - name: get release data + id: get_release_data + run: echo ::set-output name=upload_url::$(cat RELEASE/${{ env.RELEASE_FILE }}) + - name: install tools + run: | + ${{ matrix.install }} && + rustup target install ${{ matrix.target }} + - name: build-${{ matrix.target }} + run: | + VERSION=${{ steps.get_version.outputs.VERSION }} make update-version && + cargo build --release --target ${{ matrix.target }} + - name: zip + run: cd ./target/${{ matrix.target }}/release && tar -zcvf ${{ matrix.target }}.tar.gz complate + - name: upload asset + uses: svenstaro/upload-release-action@v1-release + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: ./target/${{ matrix.target }}/release/${{ matrix.target }}.tar.gz + asset_name: ${{ matrix.target }}.tar.gz + tag: ${{ steps.get_version.outputs.VERSION }} + overwrite: true +>>>>>>> 88d6d53 (#9 small changes to pipeline) diff --git a/.vscode/launch.json b/.vscode/launch.json index 32f5fa1..39d9f8c 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -20,7 +20,8 @@ } }, "args": [ - "--shell-trust", "prompt" + "print", + "--shell-trust", "prompt", ], "cwd": "${workspaceFolder}" }, diff --git a/Cargo.lock b/Cargo.lock index 7362f2a..0ac2c86 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -214,18 +214,6 @@ dependencies = [ "vec_map", ] -[[package]] -name = "clicolors-control" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90082ee5dcdd64dc4e9e0d37fbf3ee325419e39c0092191e0393df65518f741e" -dependencies = [ - "atty", - "lazy_static", - "libc", - "winapi", -] - [[package]] name = "clipboard" version = "0.4.6" @@ -270,19 +258,18 @@ dependencies = [ [[package]] name = "console" -version = "0.10.3" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2586208b33573b7f76ccfbe5adb076394c88deaf81b84d7213969805b0a952a7" +checksum = "a50aab2529019abfabfa93f1e6c41ef392f91fbf179b347a7e96abb524884a08" dependencies = [ - "clicolors-control", "encode_unicode", "lazy_static", "libc", "regex 1.3.9", "terminal_size", - "termios", "unicode-width", "winapi", + "winapi-util", ] [[package]] @@ -350,13 +337,14 @@ dependencies = [ [[package]] name = "dialoguer" -version = "0.5.1" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8b5eb0fce3c4f955b8d8d864b131fb8863959138da962026c106ba7a2e3bf7a" +checksum = "70f807b2943dc90f9747497d9d65d7e92472149be0b88bf4ce1201b4ac979c26" dependencies = [ "console", "lazy_static", "tempfile", + "zeroize", ] [[package]] @@ -1286,15 +1274,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "termios" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f0fcee7b24a25675de40d5bb4de6e41b0df07bc9856295e7e2b3a3600c400c2" -dependencies = [ - "libc", -] - [[package]] name = "textwrap" version = "0.11.0" @@ -1473,3 +1452,9 @@ checksum = "39f0c922f1a334134dc2f7a8b67dc5d25f0735263feec974345ff706bcf20b0d" dependencies = [ "linked-hash-map", ] + +[[package]] +name = "zeroize" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45af6a010d13e4cf5b54c94ba5a2b2eba5596b9e46bf5875612d332a1f2b3f86" diff --git a/Cargo.toml b/Cargo.toml index 5feaaef..98ff53e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,7 +27,7 @@ async-trait = "0.1.24" clap = "2.33.0" handlebars = "3.0.1" -dialoguer = { version = "0.5.0", optional = true } +dialoguer = { version = "0.7.1", optional = true } cursive = { version = "0.11.2", optional = true } fui = { version = "1.0", optional = true } diff --git a/docs/README.md b/docs/README.md index 3ce94ce..9600229 100644 --- a/docs/README.md +++ b/docs/README.md @@ -84,7 +84,8 @@ Either one of the `backend+` flags (or both) MUST be enabled for `complate` to w |Name|Short|Long|Description|Remark|Status| |-- |-- |-- |-- |-- |--| -|Config via file path|-c|--config|The path to the configuration file that shall be used. This path can be relative or absolute. The default path is `./.complate/config.yml`.|Can not be used in conjunction with the `pipe` argument.|stable| +|Config via STDIN pipe|-||The indicator that the config get's passed to the program via STDIN pipe.|Can not be used in conjunction with the `config` argument. Only works with UI backend right now (see: mitsuhiko/dialoguer#93).|experimental| +|Config via file path|-c|--config|The path to the configuration file that shall be used. This path can be relative or absolute. The default path is `./complate/config.yml`.|Can not be used in conjunction with the `pipe` argument.|stable| |Shell trust||--shell-trust|Enables the shell value provider for replacing template placeholders. Due to the potential security risk with this option, it is disabled by default. Possible values for this option are `none` (default), `prompt` and `ultimate`||stable| |Backend|-b|--backend|Defines the backend for the user interaction.||`CLI` is stable. `UI` is experimental (feature = "backend+ui"). diff --git a/docs/adrs/src/SUMMARY.md b/docs/adrs/src/SUMMARY.md new file mode 100644 index 0000000..8671c34 --- /dev/null +++ b/docs/adrs/src/SUMMARY.md @@ -0,0 +1,10 @@ +# Summary + +Architecture + +- [ADR 1: Usage of ADRs](./adrs/1.md) +- [ADR 2: Usage of key words](./adrs/2.md) +- [ADR 3: Versioning](./adrs/3.md) +- [ADR 4: Experimental flag](./adrs/4.md) +- [ADR 5: Usage of feature flags](./adrs/5.md) +- [ADR 6: Print command supporting STDIN pipe](./adrs/6.md) diff --git a/docs/wiki/src/SUMMARY.md b/docs/wiki/src/SUMMARY.md index 01c49bb..917fb65 100644 --- a/docs/wiki/src/SUMMARY.md +++ b/docs/wiki/src/SUMMARY.md @@ -8,4 +8,5 @@ - [ADR 3: Versioning](./adrs/3.md) - [ADR 4: Experimental flag](./adrs/4.md) - [ADR 5: Usage of feature flags](./adrs/5.md) - - [ADR 6: Usage of shell scripts instead of Makefiles](./adrs/6.md) + - [ADR 6: Print command supporting STDIN pipe](./adrs/6.md) + - [ADR 7: Usage of shell scripts instead of Makefiles](./adrs/7.md) diff --git a/docs/wiki/src/adrs/6.md b/docs/wiki/src/adrs/6.md index 6948af2..e8ecfc2 100644 --- a/docs/wiki/src/adrs/6.md +++ b/docs/wiki/src/adrs/6.md @@ -1,9 +1,11 @@ -# ADR 6: Usage of shell scripts instead of Makefiles +# ADR 6: Print command supporting STDIN pipe ## Summary -Originally, this project had a Makefile in order to do the build steps and such. Since Makefile still is kind of an alien syntax and has some disadvantages against standard plain shell scripts (bash scripts), I decided to replace the Makefile with a shell script (`./do.sh`).\ -This script now contains the important commands like generating coverage reports and initializing the repository. +The print command MUST support piping the configuration into it via STDIN as alternative to the file based approach.\ +The indicator for the pipe being used SHALL be the "-" character as argument to the pipe function.\ +The configuration file ("-c", "--config") argument MUST NOT be compatible with the pipe argument. Either none, or exactly one of them MAY be explicitly specified at the same time.\ +Since the config file argument has a default value, the pipe command overrides that so that the program behaves as if there was no configuration file specified at all. ## Authors diff --git a/docs/wiki/src/adrs/7.md b/docs/wiki/src/adrs/7.md new file mode 100644 index 0000000..f1d80b6 --- /dev/null +++ b/docs/wiki/src/adrs/7.md @@ -0,0 +1,11 @@ +# ADR 7: Usage of shell scripts instead of Makefiles + +## Summary + +Originally, this project had a Makefile in order to do the build steps and such. Since Makefile still is kind of an alien syntax and has some disadvantages against standard plain shell scripts (bash scripts), I decided to replace the Makefile with a shell script (`./do.sh`).\ +This script now contains the important commands like generating coverage reports and initializing the repository. + +## Authors + +* Heiko Alexander Weber\ +[haw@voidpointergroup.com](mailto:haw@voidpointergroup.com) diff --git a/src/args/mod.rs b/src/args/mod.rs index 45b7f75..0c94f49 100644 --- a/src/args/mod.rs +++ b/src/args/mod.rs @@ -1,4 +1,4 @@ -use std::io::Result; +use std::io::{stdin, Read, Result}; pub struct CallArgs { pub privileges: Privilege, @@ -10,15 +10,18 @@ impl CallArgs { pub async fn validate(&self) -> Result<()> { match self.privileges { Privilege::Normal => match &self.command { - Command::Print(args) => match args.backend { - #[cfg(feature = "backend+cli")] - Backend::CLI => Ok(()), - #[cfg(feature = "backend+ui")] - Backend::UI => Err(std::io::Error::new( - std::io::ErrorKind::Other, - "can not use backend+ui without experimental features being activated", - )), - }, + Command::Print(args) => { + match args.backend { + #[cfg(feature = "backend+ui")] + Backend::UI => return Err(std::io::Error::new( + std::io::ErrorKind::Other, + "can not use backend+ui without experimental features being activated", + )), + #[cfg(feature = "backend+cli")] + Backend::CLI => {} + }; + Ok(()) + } _ => Ok(()), }, Privilege::Experimental => Ok(()), @@ -80,6 +83,12 @@ impl ClapArgumentLoader { .takes_value(false)) .subcommand(clap::App::new("init")) .subcommand(clap::App::new("print") + .arg(clap::Arg::with_name("-") + .short("-") + .help("Pipe indicates to read the config from STDIN") + .multiple(false) + .required(false) + .takes_value(false)) .arg(clap::Arg::with_name("config") .short("c") .long("config") @@ -125,8 +134,29 @@ impl ClapArgumentLoader { match command.subcommand_matches("print") { Some(x) => { - let config_file = x.value_of("config").unwrap().to_owned(); - let config = std::fs::read_to_string(config_file)?; + let config = if x.is_present("-") { + match privileges { + Privilege::Normal => { + return Err(std::io::Error::new( + std::io::ErrorKind::Other, + "can not use pipe argument without experimental features being activated", + )); + } + Privilege::Experimental => {} + } + let mut buffer = String::new(); + let stdin = stdin(); + stdin.lock().read_to_string(&mut buffer)?; + buffer + } else if x.is_present("config") { + let config_file = x.value_of("config").unwrap().to_owned(); + std::fs::read_to_string(config_file)? + } else { + return Err(std::io::Error::new( + std::io::ErrorKind::Other, + "configuration not specified", + )); + }; let shell_trust = match x.value_of("shell-trust") { Some(x) => match x { diff --git a/src/main.rs b/src/main.rs index 8ebf6cf..8713edd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -61,7 +61,8 @@ async fn async_main() -> Result<()> { Ok(()) } crate::args::Command::Print(x) => { - std::io::stdout().write_all(crate::render::select_and_render(x).await?.as_bytes())?; + let res = crate::render::select_and_render(x).await?; + std::io::stdout().write_all(res.as_bytes())?; Ok(()) } } diff --git a/src/render/mod.rs b/src/render/mod.rs index 6008384..dd62560 100644 --- a/src/render/mod.rs +++ b/src/render/mod.rs @@ -192,7 +192,7 @@ mod cli { } async fn check(&self, prompt: &str, options: &[String]) -> Result { - let indices = dialoguer::Checkboxes::new() + let indices = dialoguer::MultiSelect::new() .with_prompt(prompt) .items(options) .interact()