Radish alpha
r
rad:zwTxygwuz5LDGBq255RA2CbNGrz8
Radicle CI broker
Radicle
Git
Add Nix Flake
Merged fintohaps opened 1 year ago

The aim of this patch is to add a Nix Flake to the project to allow nix users and developers to build and work on the project.

This required some bash related code to get tests passing. See the commits for further details.

12 files changed +630 -35 25a8d24d da7971ad
added .cargo/audit.toml
@@ -0,0 +1,7 @@
+
[advisories]
+
ignore = [
+
  # heartwood issue bd8af7a5e3fd02c0963d7550a5cc2cab00950775
+
  # TL;DR rsa is vulnerable, but heartwood does not use the rsa crate, even
+
  # through the transitivie dependency
+
  "RUSTSEC-2023-0071",
+
]
added .envrc
@@ -0,0 +1 @@
+
use flake
modified .gitignore
@@ -1,4 +1,5 @@
*.svg
*.html
+
/result
/target
doc/messages.md

\ No newline at end of file
modified README.md
@@ -80,6 +80,7 @@ following programs installed for the test suite:
* `git`
* `sqlite3`
* `rad` (from Radicle)
+
* `git-remote-rad` (from Radicle)

## Configuration

modified ci-broker.md
@@ -105,7 +105,7 @@ the CI broker logs adapter error output, and doesn't harm other uses
of the adapter.

~~~{#dummy.sh .file .sh}
-
#!/bin/bash
+
#!/bin/sh
set -euo pipefail
cat > /dev/null
echo '{"response":"triggered","run_id":{"id":"xyzzy"}}'
@@ -164,7 +164,7 @@ then command is successful
~~~

~~~{#which.sh .file .sh}
-
#!/bin/bash
+
#!/bin/sh
# We use Bash build-in command as that's portable. "which" is not.
command -v "$1"
~~~
@@ -449,7 +449,7 @@ then stdout has one line
~~~

~~~{#create-patch .file .sh}
-
#!/bin/bash
+
#!/bin/sh
set -euo pipefail

touch foo
@@ -510,7 +510,7 @@ then command is successful
~~~

~~~{#adapter-with-url.sh .file .sh}
-
#!/bin/bash
+
#!/bin/sh
set -euo pipefail
echo '{"response":"triggered","run_id":{"id":"xyzzy"},"info_url":"https://ci.example.com/xyzzy"}'
echo '{"response":"finished","result":"success"}'
@@ -664,7 +664,7 @@ This adapter outputs a broken response message, and after that
something to its stderr. The CI broker is meant to read and log both.

~~~{#broken-adapter.sh .file .sh}
-
#!/bin/bash
+
#!/bin/sh
set -euo pipefail
cat > /dev/null
echo '{"response":"Rivendell"}}'
@@ -973,7 +973,7 @@ then stdout contains "OK\n"
~~~

~~~{#count.sh .file .sh}
-
#!/bin/bash
+
#!/bin/sh

set -euo pipefail

@@ -1682,7 +1682,7 @@ when I run ./env.sh bash -x verify-upgrade run-list.txt HEAD
~~~

~~~{#verify-upgrade .file .sh}
-
#!/bin/bash
+
#!/bin/sh
#
# Given a list of CI runs and a CI broker version, build and run that
# version so that it triggers and runs CI on a given change. Then
added deny.toml
@@ -0,0 +1,128 @@
+
# Note that all fields that take a lint level have these possible values:
+
# * deny - An error will be produced and the check will fail
+
# * warn - A warning will be produced, but the check will not fail
+
# * allow - No warning or error will be produced
+

+

+
# The graph table configures how the dependency graph is constructed and thus
+
# which crates the checks are performed against
+
[graph]
+
targets = []
+
# If true, metadata will be collected with `--all-features`. Note that this can't
+
# be toggled off if true, if you want to conditionally enable `--all-features` it
+
# is recommended to pass `--all-features` on the cmd line instead
+
all-features = false
+
# If true, metadata will be collected with `--no-default-features`. The same
+
# caveat with `all-features` applies
+
no-default-features = false
+

+
# The output table provides options for how/if diagnostics are outputted
+
[output]
+
# When outputting inclusion graphs in diagnostics that include features, this
+
# option can be used to specify the depth at which feature edges will be added.
+
# This option is included since the graphs can be quite large and the addition
+
# of features from the crate(s) to all of the graph roots can be far too verbose.
+
# This option can be overridden via `--feature-depth` on the cmd line
+
feature-depth = 1
+

+
# This section is considered when running `cargo deny check advisories`
+
[advisories]
+
# Opt into new defaults since cargo deny 0.14.12 (2024-02-23)
+
version = 2
+
# The path where the advisory database is cloned/fetched into
+
db-path = "~/.cargo/advisory-db"
+
# The url(s) of the advisory databases to use
+
db-urls = ["https://github.com/rustsec/advisory-db"]
+
# The lint level for crates that have been yanked from their source registry.
+
yanked = "warn"
+
# A list of advisory IDs to ignore. Note that ignored advisories will still
+
# output a note when they are encountered.
+
ignore = []
+

+
# This section is considered when running `cargo deny check licenses`
+
[licenses]
+
# Opt into new defaults since cargo deny 0.14.12 (2024-02-23)
+
version = 2
+
# List of explicitly allowed licenses
+
allow = [
+
    "MIT",
+
    "MIT-0",
+
    "Apache-2.0",
+
    "Unicode-DFS-2016",
+
    "Unicode-3.0",
+
    "BSD-3-Clause",
+
    "MPL-2.0"
+
]
+
# The confidence threshold for detecting a license from license text.
+
confidence-threshold = 0.8
+
# Allow 1 or more licenses on a per-crate basis, so that particular licenses
+
# aren't accepted for every possible crate as with the normal allow list
+
exceptions = [
+
    { allow = ["GPL-3.0"], name = "radicle-surf", version = "*" },
+
    { allow = ["GPL-3.0"], name = "radicle-std-ext", version = "*" },
+
    { allow = ["GPL-3.0"], name = "radicle-git-ext", version = "*" },
+
    { allow = ["GPL-3.0"], name = "git-ref-format-core", version = "*" },
+
    { allow = ["GPL-3.0"], name = "git-ref-format-macro", version = "*" },
+
    { allow = ["GPL-3.0"], name = "git-ref-format", version = "*" },
+
]
+

+
[licenses.private]
+
# If true, ignores workspace crates that aren't published, or are only
+
# published to private registries.
+
ignore = false
+
registries = []
+

+
# This section is considered when running `cargo deny check bans`.
+
[bans]
+
# Lint level for when multiple versions of the same crate are detected
+
multiple-versions = "warn"
+
# Lint level for when a crate version requirement is `*`
+
wildcards = "allow"
+
# The graph highlighting used when creating dotgraphs for crates
+
# with multiple versions
+
# * lowest-version - The path to the lowest versioned duplicate is highlighted
+
# * simplest-path - The path to the version with the fewest edges is highlighted
+
# * all - Both lowest-version and simplest-path are used
+
highlight = "all"
+
# The default lint level for `default` features for crates that are members of
+
# the workspace that is being checked. This can be overridden by allowing/denying
+
# `default` on a crate-by-crate basis if desired.
+
workspace-default-features = "allow"
+
# The default lint level for `default` features for external crates that are not
+
# members of the workspace. This can be overridden by allowing/denying `default`
+
# on a crate-by-crate basis if desired.
+
external-default-features = "allow"
+
# List of crates that are allowed. Use with care!
+
allow = []
+
# List of crates to deny
+
deny = []
+

+
# Certain crates/versions that will be skipped when doing duplicate detection.
+
skip = []
+
# Similarly to `skip` allows you to skip certain crates during duplicate
+
# detection. Unlike skip, it also includes the entire tree of transitive
+
# dependencies starting at the specified crate, up to a certain depth, which is
+
# by default infinite.
+
skip-tree = []
+

+
# This section is considered when running `cargo deny check sources`.
+
[sources]
+
# Lint level for what to happen when a crate from a crate registry that is not
+
# in the allow list is encountered
+
unknown-registry = "warn"
+
# Lint level for what to happen when a crate from a git repository that is not
+
# in the allow list is encountered
+
unknown-git = "warn"
+
# List of URLs for allowed crate registries. Defaults to the crates.io index
+
# if not specified. If it is specified but empty, no registries are allowed.
+
allow-registry = ["https://github.com/rust-lang/crates.io-index"]
+
# List of URLs for allowed Git repositories
+
allow-git = []
+

+
[sources.allow-org]
+
# 1 or more github.com organizations to allow git sources for
+
github = []
+
# 1 or more gitlab.com organizations to allow git sources for
+
gitlab = []
+
# 1 or more bitbucket.org organizations to allow git sources for
+
bitbucket = []
added flake.lock
@@ -0,0 +1,245 @@
+
{
+
  "nodes": {
+
    "advisory-db": {
+
      "flake": false,
+
      "locked": {
+
        "lastModified": 1737246984,
+
        "narHash": "sha256-cjWzMwqej9zVoFGV5NkefOspMDJJ+gN3+zkFZcBBAkc=",
+
        "owner": "rustsec",
+
        "repo": "advisory-db",
+
        "rev": "cfd49ce116f12c856a3f3c065d041fd0dd7169dc",
+
        "type": "github"
+
      },
+
      "original": {
+
        "owner": "rustsec",
+
        "repo": "advisory-db",
+
        "type": "github"
+
      }
+
    },
+
    "advisory-db_2": {
+
      "flake": false,
+
      "locked": {
+
        "lastModified": 1722017365,
+
        "narHash": "sha256-9wYR5NZIgI+qzMDlJrUzevR31fvFQRgfjlYp50Xp3Ts=",
+
        "owner": "rustsec",
+
        "repo": "advisory-db",
+
        "rev": "9d024c07ee8c18609b43436bc865abf46636e250",
+
        "type": "github"
+
      },
+
      "original": {
+
        "owner": "rustsec",
+
        "repo": "advisory-db",
+
        "type": "github"
+
      }
+
    },
+
    "crane": {
+
      "locked": {
+
        "lastModified": 1737250794,
+
        "narHash": "sha256-bdIPhvsAKyYQzqAIeay4kOxTHGwLGkhM+IlBIsmMYFI=",
+
        "owner": "ipetkov",
+
        "repo": "crane",
+
        "rev": "c5b7075f4a6d523fe8204618aa9754e56478c0e0",
+
        "type": "github"
+
      },
+
      "original": {
+
        "owner": "ipetkov",
+
        "repo": "crane",
+
        "type": "github"
+
      }
+
    },
+
    "crane_2": {
+
      "inputs": {
+
        "nixpkgs": [
+
          "radicle",
+
          "nixpkgs"
+
        ]
+
      },
+
      "locked": {
+
        "lastModified": 1722907532,
+
        "narHash": "sha256-i/j/dMZPR7uJoQPFTa6UCahLu8kMtozYuR83lIKMERM=",
+
        "owner": "ipetkov",
+
        "repo": "crane",
+
        "rev": "d0c8f4ed8571c533b31c1d68e4ebc534b790cc5c",
+
        "type": "github"
+
      },
+
      "original": {
+
        "owner": "ipetkov",
+
        "repo": "crane",
+
        "type": "github"
+
      }
+
    },
+
    "flake-utils": {
+
      "inputs": {
+
        "systems": "systems"
+
      },
+
      "locked": {
+
        "lastModified": 1731533236,
+
        "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
+
        "owner": "numtide",
+
        "repo": "flake-utils",
+
        "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
+
        "type": "github"
+
      },
+
      "original": {
+
        "owner": "numtide",
+
        "repo": "flake-utils",
+
        "type": "github"
+
      }
+
    },
+
    "flake-utils_2": {
+
      "inputs": {
+
        "systems": "systems_2"
+
      },
+
      "locked": {
+
        "lastModified": 1710146030,
+
        "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
+
        "owner": "numtide",
+
        "repo": "flake-utils",
+
        "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
+
        "type": "github"
+
      },
+
      "original": {
+
        "owner": "numtide",
+
        "repo": "flake-utils",
+
        "type": "github"
+
      }
+
    },
+
    "nixpkgs": {
+
      "locked": {
+
        "lastModified": 1737364209,
+
        "narHash": "sha256-Vwr+QSD8YbonNMl1Xk4sT6yh+L1G+6kwZLUgrx4ibkc=",
+
        "owner": "NixOS",
+
        "repo": "nixpkgs",
+
        "rev": "e67b9736817530ccdd84797d8f3614b7750c74ae",
+
        "type": "github"
+
      },
+
      "original": {
+
        "owner": "NixOS",
+
        "ref": "release-24.11",
+
        "repo": "nixpkgs",
+
        "type": "github"
+
      }
+
    },
+
    "nixpkgs_2": {
+
      "locked": {
+
        "lastModified": 1722940684,
+
        "narHash": "sha256-X8JnSq0ruRWsU4PdYuxV+8W4W66F1lnCcxIZZMWzo4E=",
+
        "owner": "NixOS",
+
        "repo": "nixpkgs",
+
        "rev": "a3103d68517c6ad262ea27c96fc4a38ad81be7a0",
+
        "type": "github"
+
      },
+
      "original": {
+
        "owner": "NixOS",
+
        "ref": "release-24.05",
+
        "repo": "nixpkgs",
+
        "type": "github"
+
      }
+
    },
+
    "radicle": {
+
      "inputs": {
+
        "advisory-db": "advisory-db_2",
+
        "crane": "crane_2",
+
        "flake-utils": "flake-utils_2",
+
        "nixpkgs": "nixpkgs_2",
+
        "rust-overlay": "rust-overlay"
+
      },
+
      "locked": {
+
        "lastModified": 1736859921,
+
        "narHash": "sha256-i1Gn8JHhPKBG5IdP5GDI9NQtSWNWHIhBsOYl8aW+UVs=",
+
        "ref": "refs/heads/master",
+
        "rev": "ee0d19f2eb5744777ed717b8f8ec585e54aed4f0",
+
        "revCount": 2177,
+
        "type": "git",
+
        "url": "https://seed.radicle.xyz/rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5.git?tag=v1.1.0"
+
      },
+
      "original": {
+
        "type": "git",
+
        "url": "https://seed.radicle.xyz/rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5.git?tag=v1.1.0"
+
      }
+
    },
+
    "root": {
+
      "inputs": {
+
        "advisory-db": "advisory-db",
+
        "crane": "crane",
+
        "flake-utils": "flake-utils",
+
        "nixpkgs": "nixpkgs",
+
        "radicle": "radicle",
+
        "rust-overlay": "rust-overlay_2"
+
      }
+
    },
+
    "rust-overlay": {
+
      "inputs": {
+
        "nixpkgs": [
+
          "radicle",
+
          "nixpkgs"
+
        ]
+
      },
+
      "locked": {
+
        "lastModified": 1722910815,
+
        "narHash": "sha256-v6Vk/xlABhw2QzOa6xh3Jx/IvmlbKbOazFM+bDFQlWU=",
+
        "owner": "oxalica",
+
        "repo": "rust-overlay",
+
        "rev": "7df2ac544c203d21b63aac23bfaec7f9b919a733",
+
        "type": "github"
+
      },
+
      "original": {
+
        "owner": "oxalica",
+
        "repo": "rust-overlay",
+
        "type": "github"
+
      }
+
    },
+
    "rust-overlay_2": {
+
      "inputs": {
+
        "nixpkgs": [
+
          "nixpkgs"
+
        ]
+
      },
+
      "locked": {
+
        "lastModified": 1737340068,
+
        "narHash": "sha256-5UciRckNV+YOZ6y6ASBIb01cySB12whDxgFUK+EqT8g=",
+
        "owner": "oxalica",
+
        "repo": "rust-overlay",
+
        "rev": "275c824ed9e90e7fd4f96d187bde3670062e721f",
+
        "type": "github"
+
      },
+
      "original": {
+
        "owner": "oxalica",
+
        "repo": "rust-overlay",
+
        "type": "github"
+
      }
+
    },
+
    "systems": {
+
      "locked": {
+
        "lastModified": 1681028828,
+
        "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
+
        "owner": "nix-systems",
+
        "repo": "default",
+
        "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
+
        "type": "github"
+
      },
+
      "original": {
+
        "owner": "nix-systems",
+
        "repo": "default",
+
        "type": "github"
+
      }
+
    },
+
    "systems_2": {
+
      "locked": {
+
        "lastModified": 1681028828,
+
        "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
+
        "owner": "nix-systems",
+
        "repo": "default",
+
        "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
+
        "type": "github"
+
      },
+
      "original": {
+
        "owner": "nix-systems",
+
        "repo": "default",
+
        "type": "github"
+
      }
+
    }
+
  },
+
  "root": "root",
+
  "version": 7
+
}
added flake.nix
@@ -0,0 +1,220 @@
+
{
+
  description = "Radicle CI Broker";
+

+
  inputs = {
+
    nixpkgs.url = "github:NixOS/nixpkgs/release-24.11";
+

+
    crane = {
+
      url = "github:ipetkov/crane";
+
    };
+

+
    rust-overlay = {
+
      url = "github:oxalica/rust-overlay";
+
      inputs = {
+
        nixpkgs.follows = "nixpkgs";
+
      };
+
    };
+

+
    radicle = {
+
      url = "git+https://seed.radicle.xyz/rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5.git?tag=v1.1.0";
+
    };
+

+
    flake-utils.url = "github:numtide/flake-utils";
+

+
    advisory-db = {
+
      url = "github:rustsec/advisory-db";
+
      flake = false;
+
    };
+
  };
+

+
  nixConfig = {
+
    keepOutputs = true;
+
  };
+

+
  outputs = {
+
    self,
+
    nixpkgs,
+
    crane,
+
    flake-utils,
+
    advisory-db,
+
    radicle,
+
    rust-overlay,
+
    ...
+
  }:
+
    flake-utils.lib.eachDefaultSystem (system: let
+
      pname = "radicle-ci-broker";
+
      lib = nixpkgs.lib;
+
      pkgs = import nixpkgs {
+
        inherit system;
+
        overlays = [(import rust-overlay)];
+
      };
+

+
      rustToolChain = pkgs.rust-bin.fromRustupToolchainFile ./rust-toolchain;
+
      craneLib = (crane.mkLib pkgs).overrideToolchain rustToolChain;
+

+
      srcFilters = path: type:
+
        builtins.any (suffix: lib.hasSuffix suffix path) [
+
          ".subplot" # build and documentation
+
          ".md"
+
          ".yaml"
+
          ".css"
+
        ]
+
        ||
+
        # Default filter from crane (allow .rs files)
+
        (craneLib.filterCargoSources path type);
+

+
      src = lib.cleanSourceWith {
+
        src = ./.;
+
        filter = srcFilters;
+
      };
+

+
      basicArgs = {
+
        inherit src;
+
        inherit pname;
+
        strictDeps = true;
+
      };
+

+
      # Build *just* the cargo dependencies, so we can reuse
+
      # all of that work (e.g. via cachix) when running in CI
+
      cargoArtifacts = craneLib.buildDepsOnly basicArgs;
+

+
      # Common arguments can be set here to avoid repeating them later
+
      commonArgs =
+
        basicArgs
+
        // {
+
          inherit cargoArtifacts;
+

+
          nativeBuildInputs = with pkgs; [
+
            git
+
            # Add additional build inputs here
+
          ];
+
          buildInputs = lib.optionals pkgs.stdenv.buildPlatform.isDarwin (with pkgs; [
+
            darwin.apple_sdk.frameworks.Security
+
          ]);
+
        };
+

+
      radicle-ci-broker = craneLib.buildPackage (commonArgs
+
        // {
+
          inherit (craneLib.crateNameFromCargoToml {cargoToml = ./Cargo.toml;}) pname version;
+
          doCheck = false;
+
        });
+

+
      bins = let
+
        bin = {
+
          name,
+
        }:
+
          craneLib.buildPackage (commonArgs
+
            // {
+
              inherit (craneLib.crateNameFromCargoToml {cargoToml = ./Cargo.toml;}) pname version;
+
              cargoExtraArgs = "--bin ${name}";
+
              doCheck = false;
+
            });
+
        bins = builtins.listToAttrs (map
+
          ({name, ...} @ package: lib.nameValuePair name (bin package))
+
          [
+
            {
+
              name = "cibtool";
+
            }
+
            {
+
              name = "cib";
+
            }
+
            {
+
              name = "synthetic-events";
+
            }
+
          ]);
+
      in
+
        bins
+
        // rec {
+
          default = radicle-ci-broker;
+
          ci-broker = pkgs.buildEnv {
+
            name = "ci-broker";
+
            paths = with bins; [
+
              cibtool
+
              cib
+
              synthetic-events
+
            ];
+
          };
+
        };
+
    in {
+
      # Formatter
+
      formatter = pkgs.alejandra;
+

+
      # Set of checks that are run: `nix flake check`
+
      checks = {
+
        # Run clippy (and deny all warnings) on the crate source,
+
        # again, reusing the dependency artifacts from above.
+
        #
+
        # Note that this is done as a separate derivation so that
+
        # we can block the CI if there are issues here, but not
+
        # prevent downstream consumers from building our crate by itself.
+
        clippy = craneLib.cargoClippy (commonArgs
+
          // {
+
            cargoClippyExtraArgs = "--all-targets -- --deny warnings";
+
          });
+

+
        doc = craneLib.cargoDoc commonArgs;
+
        fmt = craneLib.cargoFmt basicArgs;
+
        deny = craneLib.cargoDeny commonArgs;
+

+
        audit = craneLib.cargoAudit {
+
          inherit src advisory-db;
+
        };
+

+
        # Run tests with cargo-nextest
+
        nextest = craneLib.cargoNextest (commonArgs
+
          // {
+
            # We skip the test since it uses the underlying .git directory,
+
            # which is not available in the Nix sandbox.
+
            # In any case, this test is slow and we expect it to be tested
+
            # before merges (and it can be tested in the devShell)
+
            cargoNextestExtraArgs = "-- --skip acceptance_criteria_for_upgrades";
+
            partitions = 1;
+
            partitionType = "count";
+
            nativeBuildInputs = [
+
              pkgs.bash
+
              pkgs.git
+
              pkgs.jq
+
              pkgs.sqlite
+
              radicle.packages.${system}.radicle-cli
+
              radicle.packages.${system}.radicle-remote-helper
+
            ];
+

+
            # Ensure dev is used since we rely on env variables being
+
            # set in tests.
+
            env.CARGO_PROFILE = "dev";
+
          });
+
      };
+

+
      packages.default = radicle-ci-broker;
+

+
      apps.cibtool = flake-utils.lib.mkApp {
+
        drv = self.bins.${system}.cibtool;
+
      };
+

+
      apps.cib = flake-utils.lib.mkApp {
+
        drv = self.bins.${system}.cib;
+
      };
+

+
      apps.synthetic-events = flake-utils.lib.mkApp {
+
        drv = self.bins.${system}.synthetic-events;
+
      };
+

+
      devShells.default = craneLib.devShell {
+
        # Extra inputs can be added here; cargo and rustc are provided by default.
+
        packages = with pkgs; [
+
          bash
+
          coreutils
+
          cargo-watch
+
          cargo-nextest
+
          git
+
          jq
+
          ripgrep
+
          rust-analyzer
+
          sqlite
+
        ];
+

+
        env.RUST_SRC_PATH = "${rustToolChain}/lib/rustlib/src/rust/library";
+
      };
+
    });
+
}
+

modified src/adapter.rs
@@ -391,7 +391,7 @@ mod test {

    #[test]
    fn adapter_reports_success() -> TestResult<()> {
-
        const ADAPTER: &str = r#"#!/bin/bash
+
        const ADAPTER: &str = r#"#!/bin/sh
read
echo '{"response":"triggered","run_id":{"id":"xyzzy"}}'
echo '{"response":"finished","result":"success"}'
@@ -413,7 +413,7 @@ echo '{"response":"finished","result":"success"}'

    #[test]
    fn adapter_reports_failure() -> TestResult<()> {
-
        const ADAPTER: &str = r#"#!/bin/bash
+
        const ADAPTER: &str = r#"#!/bin/sh
read
echo '{"response":"triggered","run_id":{"id":"xyzzy"}}'
echo '{"response":"finished","result":"failure"}'
@@ -440,7 +440,7 @@ echo '{"response":"finished","result":"failure"}'

    #[test]
    fn adapter_exits_nonzero() -> TestResult<()> {
-
        const ADAPTER: &str = r#"#!/bin/bash
+
        const ADAPTER: &str = r#"#!/bin/sh
read
echo '{"response":"triggered","run_id":{"id":"xyzzy"}}'
echo '{"response":"finished","result":"failure"}'
@@ -466,8 +466,8 @@ exit 1

    #[test]
    fn adapter_is_killed_before_any_messages() -> TestResult<()> {
-
        const ADAPTER: &str = r#"#!/bin/bash
-
kill -9 $BASHPID
+
        const ADAPTER: &str = r#"#!/bin/sh
+
kill -9 $$
"#;

        let tmp = tempdir()?;
@@ -487,10 +487,10 @@ kill -9 $BASHPID

    #[test]
    fn adapter_is_killed_after_first_message() -> TestResult<()> {
-
        const ADAPTER: &str = r#"#!/bin/bash
+
        const ADAPTER: &str = r#"#!/bin/sh
read
echo '{"response":"triggered","run_id":{"id":"xyzzy"}}'
-
kill -9 $BASHPID
+
kill -9 $$
"#;

        let tmp = tempdir()?;
@@ -510,7 +510,7 @@ kill -9 $BASHPID

    #[test]
    fn adapter_ends_ok_before_second_message() -> TestResult<()> {
-
        const ADAPTER: &str = r#"#!/bin/bash
+
        const ADAPTER: &str = r#"#!/bin/sh
read
echo '{"response":"triggered","run_id":{"id":"xyzzy"}}'
"#;
@@ -532,11 +532,11 @@ echo '{"response":"triggered","run_id":{"id":"xyzzy"}}'

    #[test]
    fn adapter_is_killed_after_second_message() -> TestResult<()> {
-
        const ADAPTER: &str = r#"#!/bin/bash
+
        const ADAPTER: &str = r#"#!/bin/sh
read
echo '{"response":"triggered","run_id":{"id":"xyzzy"}}'
echo '{"response":"finished","result":"success"}'
-
kill -9 $BASHPID
+
kill -9 $$
"#;

        let tmp = tempdir()?;
@@ -556,7 +556,7 @@ kill -9 $BASHPID

    #[test]
    fn adapter_produces_as_bad_message() -> TestResult<()> {
-
        const ADAPTER: &str = r#"#!/bin/bash
+
        const ADAPTER: &str = r#"#!/bin/sh
read
echo '{"response":"triggered","run_id":{"id":"xyzzy"}}'
echo '{"response":"finished","result":"success","bad":"field"}'
@@ -582,7 +582,7 @@ echo '{"response":"finished","result":"success","bad":"field"}'

    #[test]
    fn adapter_first_message_isnt_triggered() -> TestResult<()> {
-
        const ADAPTER: &str = r#"#!/bin/bash
+
        const ADAPTER: &str = r#"#!/bin/sh
read
echo '{"response":"finished","result":"success"}'
"#;
@@ -609,7 +609,7 @@ echo '{"response":"finished","result":"success"}'

    #[test]
    fn adapter_outputs_too_many_messages() -> TestResult<()> {
-
        const ADAPTER: &str = r#"#!/bin/bash
+
        const ADAPTER: &str = r#"#!/bin/sh
read
echo '{"response":"triggered","run_id":{"id":"xyzzy"}}'
echo '{"response":"finished","result":"success"}'
@@ -660,7 +660,7 @@ echo '{"response":"finished","result":"success"}'

    #[test]
    fn adapter_is_not_executable() -> TestResult<()> {
-
        const ADAPTER: &str = r#"#!/bin/bash
+
        const ADAPTER: &str = r#"#!/bin/sh
read
echo '{"response":"triggered","run_id":{"id":"xyzzy"}}'
echo '{"response":"finished","result":"success"}'
modified src/broker.rs
@@ -197,7 +197,7 @@ mod test {

    #[test]
    fn executes_adapter() -> TestResult<()> {
-
        const ADAPTER: &str = r#"#!/bin/bash
+
        const ADAPTER: &str = r#"#!/bin/sh
read
echo '{"response":"triggered","run_id":{"id":"xyzzy"}}'
echo '{"response":"finished","result":"success"}'
@@ -227,7 +227,7 @@ echo '{"response":"finished","result":"success"}'

    #[test]
    fn adapter_fails() -> TestResult<()> {
-
        const ADAPTER: &str = r#"#!/bin/bash
+
        const ADAPTER: &str = r#"#!/bin/sh
read
echo '{"response":"triggered","run_id":{"id":"xyzzy"}}'
echo '{"response":"finished","result":"success"}'
modified src/subplot.rs
@@ -114,7 +114,7 @@ fn setup_node(context: &ScenarioContext, config: SubplotDataFile, adapter: Subpl

            println!("create env.sh script");
            {
-
                const SCRIPT: &str = r#"#!/bin/bash
+
                const SCRIPT: &str = r#"#!/bin/sh
echo "env.sh starts"
if [ -e env ]; then . ./env; fi
exec "$@"
modified src/timeoutcmd.rs
@@ -587,7 +587,7 @@ mod tests {
        timeout: Duration,
        stdin: Option<&'static str>,
    ) -> Result<RunningProcess, Box<dyn std::error::Error>> {
-
        let mut cmd = Command::new("bash");
+
        let mut cmd = Command::new("sh");
        cmd.arg("-c").arg(script);
        let mut to = TimeoutCommand::new(timeout);
        if let Some(stdin) = stdin {
@@ -598,11 +598,7 @@ mod tests {

    #[test]
    fn bin_true() -> Result<(), Box<dyn std::error::Error>> {
-
        let running = setup(
-
            "exec /bin/true",
-
            LONG_ENOUGH_THAT_SCRIPT_SURELY_FINISHES,
-
            None,
-
        )?;
+
        let running = setup("exec true", LONG_ENOUGH_THAT_SCRIPT_SURELY_FINISHES, None)?;
        let tor = running.wait()?;
        assert_eq!(tor.status().code(), Some(0));
        assert!(!tor.timed_out());
@@ -611,11 +607,7 @@ mod tests {

    #[test]
    fn bin_false() -> Result<(), Box<dyn std::error::Error>> {
-
        let running = setup(
-
            "exec /bin/false",
-
            LONG_ENOUGH_THAT_SCRIPT_SURELY_FINISHES,
-
            None,
-
        )?;
+
        let running = setup("exec false", LONG_ENOUGH_THAT_SCRIPT_SURELY_FINISHES, None)?;
        let tor = running.wait()?;
        assert_eq!(tor.status().code(), Some(1));
        assert!(!tor.timed_out());