diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e139c7..5277301 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [v0.3.1] - Sep 05, 2026 + ### Fixed - Stop and clean up processes that are still running when `mult` exits @@ -33,6 +35,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Change flags for delay, and "stop on first failure" -[unreleased]: https://github.com/dhth/mult/compare/v0.3.0...HEAD +[unreleased]: https://github.com/dhth/mult/compare/v0.3.1...HEAD +[v0.3.1]: https://github.com/dhth/mult/compare/v0.3.0...v0.3.1 [v0.3.0]: https://github.com/dhth/mult/compare/v0.2.0...v0.3.0 [v0.2.0]: https://github.com/dhth/mult/compare/v0.1.3...v0.2.0 diff --git a/README.md b/README.md index 91ca9d7..d3e4412 100644 --- a/README.md +++ b/README.md @@ -11,10 +11,10 @@ Run a command multiple times and glance at the outputs. ```bash -mult -- command --you=want --to=run +mult -- curl -sSif https://flaky.service.example ``` -[![usage](https://asciinema.org/a/E06efxcFkA4RSSTI7keeUtFqp.svg)](https://asciinema.org/a/E06efxcFkA4RSSTI7keeUtFqp) +[![demo](https://asciinema.org/a/E06efxcFkA4RSSTI7keeUtFqp.svg)](https://asciinema.org/a/E06efxcFkA4RSSTI7keeUtFqp) 🧰 Use Cases --- @@ -28,180 +28,206 @@ mult -- command --you=want --to=run 💾 Installation --- -**homebrew**: +### Pre-built binaries -```sh -brew install dhth/tap/mult -``` +Download a pre-built binary from the [latest +release](https://github.com/dhth/mult/releases/latest). See [Verifying release +artifacts](#-verifying-release-artifacts) for instructions on verifying your +download. + +### Install from source -**go**: +You can also install from source using the `go` toolchain: ```sh go install github.com/dhth/mult@latest ``` -**Arch Linux**: +⚡️ Quick start +--- ```sh -yay -S mult +mult -n 10 -- curl -sSif https://flaky.service.example ``` -Or get a binary directly from a -[release](https://github.com/dhth/mult/releases). Read more about verifying the -authenticity of released artifacts [here](#-verifying-release-artifacts). +This runs the command 10 times, shows the status and duration of each run in the +left pane, and displays the selected run's output in the right pane. Press +`` to switch panes and `?` for help. -⚡️ Usage ---- +`mult` runs commands concurrently by default. Without `--num-runs`, it starts 5 +runs. Use `--sequential` to run them one at a time. -```text -Usage: - mult [flags] -- - -Examples: -mult -s -n 10 -d 1000 -- curl -sif -m 5 'https://some.url/that?fails=sometimes' - -Flags: - -d, --delay int time (in ms) to sleep for between runs - -f, --follow start with "follow mode" ON (ie, automatically select the latest command run) - -h, --help help for mult - -i, --interactive accept flag values interactively (takes precendence over -n) - -n, --num-runs int number of times to run the command (default 5) - -s, --sequential whether to invoke the command sequentially - -F, --stop-on-first-failure whether to stop after first failure - -S, --stop-on-first-success whether to stop after first success +The `--` separator prevents command flags from being interpreted as `mult` +flags. `mult` executes the command directly, without invoking a shell, and +captures its combined standard output and standard error. To use shell features +such as pipes, redirects, or variable expansion, invoke a shell explicitly: + +```sh +mult -- sh -c 'curl -sS https://example.com | wc -c' ``` -### Specify number of runs +Each invocation receives a 1-indexed `MULT_RUN_NUM` environment variable. -```bash -mult -n=10 -- yourcommand +⚡️ Common workflows +--- + +### Choose the number of runs + +```sh +mult -n 10 -- yourcommand ``` -### Ask for number of runs +The number of runs must be between 2 and 1000, inclusive. To enter it +interactively instead, use: -```bash +```sh mult -i -- yourcommand ``` ### Run sequentially -By default, `mult` executes all runs concurrently. Use `-s` for sequentially -execution. - -```bash +```sh mult -s -- yourcommand ``` [![sequential-runs](https://asciinema.org/a/1AeDGwgIF7bJ7QcV73Zhgqkb3.svg)](https://asciinema.org/a/1AeDGwgIF7bJ7QcV73Zhgqkb3) -### Add delay (in milliseconds) between runs +### Pause between runs -```bash -mult -s -d=500 -- yourcommand +Use `--delay` to wait between sequential runs. The value is in milliseconds. + +```sh +mult -s -d 500 -- yourcommand ``` [![add-delay-between-runs](https://asciinema.org/a/vdGk7tf5sXYFZyb77PmN7ciaG.svg)](https://asciinema.org/a/vdGk7tf5sXYFZyb77PmN7ciaG) -### Stop at first failure +### Stop on the first failure or success -```bash -mult -s -F -- yourcommand +```sh +mult -s -F -- yourcommand # stop on the first failure +mult -s -S -- yourcommand # stop on the first success ``` -### Stop at first success +[![stop-on-the-first-failure-or-success](https://asciinema.org/a/IeasGG4AVDLlLfTxKqETjskDD.svg)](https://asciinema.org/a/IeasGG4AVDLlLfTxKqETjskDD) -```bash -mult -s -S -- yourcommand +### Follow the latest run + +In sequential mode, follow mode automatically selects the latest run: + +```sh +mult -s -f -- yourcommand ``` -[![stop-on-first-failure-success](https://asciinema.org/a/IeasGG4AVDLlLfTxKqETjskDD.svg)](https://asciinema.org/a/IeasGG4AVDLlLfTxKqETjskDD) +Press `` to toggle follow mode from the run list or output pane. -*Note: `-d`, `-F`, `-S` only apply in sequential run mode.* +The `--delay`, `--follow`, `--stop-on-first-failure`, and +`--stop-on-first-success` flags only apply in sequential mode. 📟 TUI --- ![TUI](https://tools.dhruvs.space/images/mult/v0-3-0/tui.png) -`mult`'s TUI has 3 views: -- Command Run List View -- Output View -- Help View - -### Keyboard Shortcuts - -**General** - -| Key | Action | -|-------------|----------------------------| -| `tab` | Switch focus between panes | -| `?` | Show help view | -| `q` / `Esc` | Go back or quit | -| `ctrl+c` | Quit immediately | - -**Command Run List View** - -| Key | Action | -|-----------|-------------------------------------| -| `j` / `↓` | Go to next run | -| `k` / `↑` | Go to previous run | -| `l` / `→` | Go to next page (if applicable) | -| `h` / `←` | Go to previous page (if applicable) | -| `g` | Go to start of the list | -| `G` | Go to the end of the list | -| `ctrl+r` | Restart all runs | -| `ctrl+f` | Toggle follow mode | - -**Output View** - -| Key | Action | -|-----------|--------------------| -| `j` / `↓` | Scroll output down | -| `k` / `↑` | Scroll output up | -| `l` / `→` | Go to next run | -| `h` / `←` | Go to previous run | -| `ctrl+r` | Restart all runs | -| `ctrl+f` | Toggle follow mode | +`mult` has three views: + +- **Command Run List View** — Shows the status and duration of each run +- **Output View** — Shows the combined output of the selected run +- **Help View** — Shows the available keyboard shortcuts + +### Keyboard shortcuts + +#### General + +| Key | Action | +|-------------------------|----------------------------| +| `` / `` | Switch focus between panes | +| `?` | Show or hide the help view | +| `q` / `` | Go back or quit | +| `` | Quit immediately | + +#### Command Run List View + +| Key | Action | +|------------|-------------------------------------| +| `j` / `↓` | Go to next run | +| `k` / `↑` | Go to previous run | +| `l` / `→` | Go to next page (if applicable) | +| `h` / `←` | Go to previous page (if applicable) | +| `g` | Go to start of the list | +| `G` | Go to the end of the list | +| `` | Restart all runs | +| `` | Toggle follow mode | + +#### Output View + +| Key | Action | +|------------|--------------------| +| `j` / `↓` | Scroll output down | +| `k` / `↑` | Scroll output up | +| `l` / `→` | Go to next run | +| `h` / `←` | Go to previous run | +| `` | Restart all runs | +| `` | Toggle follow mode | + +`>_` CLI reference +--- + +```text +mult [flags] -- +``` + +| Flag | What it does | +|---------------------------------|-----------------------------------------------------------| +| `-n`, `--num-runs ` | Set the number of runs (default: 5) | +| `-i`, `--interactive` | Prompt for the number of runs; takes precedence over `-n` | +| `-s`, `--sequential` | Run commands sequentially | +| `-d`, `--delay ` | Wait between sequential runs | +| `-f`, `--follow` | Start sequential runs with follow mode enabled | +| `-F`, `--stop-on-first-failure` | Stop sequential execution after the first failure | +| `-S`, `--stop-on-first-success` | Stop sequential execution after the first success | +| `-h`, `--help` | Show help | + +Run `mult --help` for the authoritative command-line reference. 🔐 Verifying release artifacts --- -In case you get the `mult` binary directly from a -[release](https://github.com/dhth/mult/releases), you may want to verify its -authenticity. Checksums are applied to all released artifacts, and the resulting -checksum file is signed using -[cosign](https://docs.sigstore.dev/cosign/installation/). +Each release includes checksums for all artifacts. The checksum file is signed +using [cosign](https://docs.sigstore.dev/cosign/installation/) (version +`3.1.3`). -Steps to verify (replace `A.B.C` in the commands listed below with the version -you want): +Replace `x.y.z` below with the release version you want to verify. -1. Download the following files from the release: +1. Get the checksum and cosign signature from the release: - - mult_A.B.C_checksums.txt - - mult_A.B.C_checksums.txt.pem - - mult_A.B.C_checksums.txt.sig + ```shell + curl -sSLO https://github.com/dhth/mult/releases/download/vx.y.z/mult_x.y.z_checksums.txt + curl -sSLO https://github.com/dhth/mult/releases/download/vx.y.z/mult_x.y.z_checksums.txt.sigstore.json + ``` -2. Verify the signature: +2. Verify the checksum file's signature: - ```shell - cosign verify-blob mult_A.B.C_checksums.txt \ - --certificate mult_A.B.C_checksums.txt.pem \ - --signature mult_A.B.C_checksums.txt.sig \ - --certificate-identity-regexp 'https://github\.com/dhth/mult/\.github/workflows/.+' \ - --certificate-oidc-issuer "https://token.actions.githubusercontent.com" - ``` + ```shell + cosign verify-blob \ + --bundle mult_x.y.z_checksums.txt.sigstore.json \ + --certificate-identity-regexp 'https://github\.com/dhth/mult/\.github/workflows/.+' \ + --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \ + mult_x.y.z_checksums.txt + ``` -3. Download the compressed archive you want, and validate its checksum: +3. Download the archive for your platform and validate its checksum. For example, + for Linux x86-64: - ```shell - curl -sSLO https://github.com/dhth/mult/releases/download/vA.B.C/mult_A.B.C_linux_amd64.tar.gz - sha256sum --ignore-missing -c mult_A.B.C_checksums.txt - ``` + ```shell + curl -sSLO https://github.com/dhth/mult/releases/download/vx.y.z/mult_x.y.z_linux_amd64.tar.gz + sha256sum --ignore-missing -c mult_x.y.z_checksums.txt + ``` -3. If checksum validation goes through, uncompress the archive: +4. Once both checks pass, extract the archive: - ```shell - tar -xzf mult_A.B.C_linux_amd64.tar.gz - ./mult -h - # profit! - ``` + ```shell + tar -xzf mult_x.y.z_linux_amd64.tar.gz + ./mult -h + # profit! + ``` diff --git a/internal/cmd/root.go b/internal/cmd/root.go index 0c93613..b3385be 100644 --- a/internal/cmd/root.go +++ b/internal/cmd/root.go @@ -40,8 +40,8 @@ func NewRootCommand() *cobra.Command { rootCmd := &cobra.Command{ Use: "mult [flags] -- ", - Short: "Run a command multiple times and glance the outputs", - Example: `mult -s -n 10 -d 1000 -- curl -sif -m 5 'https://some.url/that?fails=sometimes'`, + Short: "Run a command multiple times and glance at the outputs", + Example: `mult -s -n 10 -d 1000 -- curl -sSif 'https://flaky.service.example'`, SilenceUsage: true, Args: cobra.MinimumNArgs(1), PreRunE: func(_ *cobra.Command, _ []string) error {