Skip to content

tbidne/shrun

Repository files navigation

Shrun

Run Shell Commands Concurrently

GitHub release (latest SemVer) ci MIT

linux apple

demo


Table of Contents

Motivation

shrun was borne of frustration. Suppose we need to run several shell commands on a regular basis e.g. updates after pulling the latest code. These can be run manually in separate terminals:

cmd1
cmd2
cmd3
...

But this can be a lot of repetitive typing, especially when the commands are longer. Thus an alias is born:

alias run_commands="cmd1 && cmd2 && cmd3 ..."

All well and good, but this approach has several deficiencies:

  1. There is no information about how long the commands have been running. If any of the commands are long-lived, how would we know when it has been "too long" and the commands should be cancelled? We could use a wall clock or a stopwatch, but that is imprecise and requires remembering every time the commands are run, which is certainly unsatisfying.

  2. These commands are all run synchronously even though there may be no relation between them. For example, three commands that each take 5 minutes will combine to take 15 minutes. This is usually unnecessary.

  3. Related to above, if any command fails then subsequent ones will not be run. This can be frustrating, especially when a quicker command in the beginning prevents a longer one at the end from even starting.

  4. If the alias is tweaked to run all regardless (cmd1; cmd2; cmd3 ...), then it can be difficult to determine which, if any, failed. Additionally, understanding logs is much harder.

  5. It does not scale. Imagine we have variations of cmd3 we want to run under different circumstances. We could create multiple aliases:

     alias run_commands_cmd3a="cmd1 && cmd2 && cmd3a"
     alias run_commands_cmd3b="cmd1 && cmd2 && cmd3b"
    

    But this is messy and grows exponentially in the number of aliases for each variation.

shrun purports to overcome these limitations.

Introduction

In a nut-shell (😉), shrun is a wrapper around running shell commands. For instance:

$ shrun "some long command" "another command"

Will run some long command and another command concurrently.

A running timer is provided, and stdout will be updated when a command finishes or crashes.

Examples

  1. Runs cmd1, cmd2, cmd3 concurrently.

    $ shrun cmd1 cmd2 cmd3
    [Command][cmd1] cmd1 output...
    [Command][cmd2] cmd2 output...
    [Command][cmd3] cmd3 output...
    [Timer] 5 seconds
    
  2. Uses --edges to specify command dependencies. Commands cmd1 and cmd2 are run concurrently; cmd3 is started after cmd1 and cmd2 finish successfully.

    $ shrun --edges "1 -> 3, 2 -> 3" cmd1 cmd2 cmd3
    [Command][cmd1] cmd1 output...
    [Command][cmd2] cmd2 output...
    [Timer] 5 seconds
    
  3. Using config file aliases i.e. builds frontend, backend, and db concurrently, then runs deploy if those tasks completed successfully.

    # config.toml
    legend = [
      # Aliases for multiple commands
      { key = 'deploy', val = [ 'build', 'ds' ], edges = '1 -> 2' },
      { key = 'build', val = [ 'frontend', 'backend', 'db' ] },
    
      # Aliases to actual commands
      { key = 'frontend', val = 'npm run build' },
      { key = 'backend', val = 'javac ...' },
      { key = 'db', val = 'db.sh' },
      { key = 'ds', val = 'deploy.sh' },
    ]
    $ shrun --config config.toml deploy
    [Command][frontend] Running npm...
    [Command][backend] Running javac...
    [Command][db] Running db.sh...
    [Timer] 5 seconds
    

Installation

The releases page has binaries built for several platforms. If there are no binaries for your platform, it is possible to build shrun yourself.

Building

If you have never built a haskell program before, Cabal is probably the best choice.

Cabal

Prerequisites

The easiest way to install these is generally ghcup.

The current "blessed" version is ghc-9.12.2.

Build Shrun

Once you have cabal and ghc, shrun can be built locally with cabal build or installed globally (e.g. ~/.local/bin/shrun) with cabal install.

Important

shrun requires git information to be available at build time, for the purposes of including some data in the binary (e.g. commit hash). Cabal's vanilla install method interfers with this, though we have a workaround that relies on passing the current directory as an environment variable:

$ export SHRUN_HOME=$(pwd); cabal install exe:shrun

Nix does not require such a workaround, and stack does not seem to require it either.

For further reproducibility, an optional freeze files can be used for the "blessed" compiler.

cabal build --project-file cabal.ghc<XYZ>.project

Stack

Prerequisites

Like cabal and ghc, stack can be installed with ghcup.

Build Shrun

Once you have stack, shrun can be built with stack build or installed globally (i.e. ~/.local/bin/shrun) with stack install.

Nix

Prerequisites

Manually

Building with nix uses flakes. shrun can be built with nix build, which will compile and run the tests.

Nix expression

Because shrun is a flake, it can be built as part of a nix expression. For instance, if you want to add shrun to NixOS, your flake.nix should have:

# flake.nix
{
  inputs.shrun.url = "github:tbidne/shrun/main";
}

Then include this in the systemPackages:

# wherever your global packages are defined
{
  environment.systemPackages = [
    shrun.packages."${system}".default
  ];
}

Configuration

See configuration.md for the available options.

FAQ

See faq.md.

About

A utility program for running shell commands concurrently.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages