From d92713930e7a5b4b64cc2866b70e0ff6b0eb0035 Mon Sep 17 00:00:00 2001 From: Sven van Heugten Date: Wed, 15 Apr 2026 20:49:16 +0200 Subject: [PATCH 1/6] Wrap assertion checking in a function --- bin/git-check-assertions | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/bin/git-check-assertions b/bin/git-check-assertions index 1195884..5cc842f 100755 --- a/bin/git-check-assertions +++ b/bin/git-check-assertions @@ -118,7 +118,10 @@ for commit_hash in "${commits[@]}"; do block_num=$((block_num + 1)) echo "git-check-assertions block $block_num:" printf '%s' "$block" | sed 's/^/> /' - if ! bash -euo pipefail -c "$block"; then + check_assertions() { + bash -euo pipefail -c "$block" + } + if ! check_assertions; then echo "git-check-assertions block failed in $commit_hash" >&2 restore echo "Returning to $orig_ref" From ad10b2cc4c711a194effe5dfcb4e059bacd4893c Mon Sep 17 00:00:00 2001 From: Sven van Heugten Date: Wed, 15 Apr 2026 21:03:02 +0200 Subject: [PATCH 2/6] Execute the assertion blocks with Python instead --- bin/git-check-assertions | 6 +++++- default.nix | 3 +++ flake.nix | 1 + 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/bin/git-check-assertions b/bin/git-check-assertions index 5cc842f..dcb91be 100755 --- a/bin/git-check-assertions +++ b/bin/git-check-assertions @@ -119,7 +119,11 @@ for commit_hash in "${commits[@]}"; do echo "git-check-assertions block $block_num:" printf '%s' "$block" | sed 's/^/> /' check_assertions() { - bash -euo pipefail -c "$block" + python3 - "$block" <<-EOF + import sys + import subprocess + subprocess.run(sys.argv[1], shell=True, check=True) + EOF } if ! check_assertions; then echo "git-check-assertions block failed in $commit_hash" >&2 diff --git a/default.nix b/default.nix index c260e4c..e3b912d 100644 --- a/default.nix +++ b/default.nix @@ -11,6 +11,7 @@ shfmt, perl, coreutils, + python3, }: let @@ -44,6 +45,7 @@ resholve.mkDerivation { shfmt perl coreutils + python3 ]; checkPhase = '' @@ -72,6 +74,7 @@ resholve.mkDerivation { bash perl coreutils + python3 ]; execer = [ # Not true at all, but ¯\_(ツ)_/¯ diff --git a/flake.nix b/flake.nix index 1fabe89..0dbfbc8 100644 --- a/flake.nix +++ b/flake.nix @@ -25,6 +25,7 @@ pkgs.perl pkgs.shfmt pkgs.coreutils + pkgs.python3 ]; }; } From 4704cc088de59d3565dd63d4c3b352b74173df31 Mon Sep 17 00:00:00 2001 From: Sven van Heugten Date: Thu, 16 Apr 2026 06:25:28 +0200 Subject: [PATCH 3/6] Introduce a DSL to replace the plain bash scripts It's really important for `git-check-assertions` blocks to be 'obviously correct', so that we don't need to... test the tests that test the tests. Let's introduce a little DSL that is less error-prone than a plain bash script. --- README.md | 63 +++++++++------------ bin/git-check-assertions | 100 +++++++++++++++++++-------------- test/git-check-assertions.bats | 94 +++++++++++++++---------------- 3 files changed, 130 insertions(+), 127 deletions(-) diff --git a/README.md b/README.md index 5f0e8da..d3a8fb3 100644 --- a/README.md +++ b/README.md @@ -7,11 +7,11 @@ I recently wrote two blog posts arguing that there might be some value in writin This is a simple verifier for such assertions. -You include a small bash script inside your commit messages, and `git-check-assertions` will then check out every commit (from the point that your branch diverged from `main`), and verify that the script in the commit message runs successfully. +You write assertions in your commit messages, and `git-check-assertions` will then check out every commit (from the point that your branch diverged from `main`), and verify that the assertions in the commit message hold for the version of the code that is in the commit. For a real-world example, check out the commits in [this pull request](https://codeberg.org/svenvanheugten/git-check-assertions/pulls/8), where `git-check-assertions` is used on itself. -⚠️ Only run this on repositories and branches that you trust, since the `bash` scripts in the commit messages can do whatever they want. +⚠️ Only run this on repositories and branches that you trust, since the commands in the commit messages can do whatever they want. ## Installation @@ -56,26 +56,22 @@ If you use Nix with flakes, you can simply add it to your program's devshell ins ## What do you put in your commit messages? -Simply add a bash script enclosed in a `git-check-assertions` block to a commit message, e.g.: +Simply add a `git-check-assertions` block to a commit message, e.g.: ~~~ ```git-check-assertions -dotnet build -dotnet test --no-build +[success] dotnet build +[success] dotnet test --no-build ``` ~~~ -This script will be run with `set -euo pipefail` on the version of the code that is in the commit, and the commit will be considered correct if the script exits successfully. +Each block is parsed line-by-line: -You can technically assert that a command fails by writing `! ... || exit 1`, or write assertions about a command's output by piping it to `grep`, but doing so won't lead to very useful error messages when things go wrong. To make those things easier, there are some helper functions included, which are inspired by [bats](https://github.com/bats-core/bats-core) and [bats-assert](https://github.com/bats-core/bats-assert): +* A line starting with `[success] ` runs the rest of the line as a shell command and asserts that it exits with status `0`. +* A line starting with `[failure] ` runs the rest of the line as a shell command and asserts that it exits with a non-zero status. +* Any following non-empty line belongs to the most recent command and asserts that the combined stdout/stderr from that command contains that string. -* `run `: run a command, capturing its exit status in `status` and combined stdout/stderr in `output`. -* `assert_success`: succeed if `run` produced a zero exit status. -* `assert_failure`: succeed if `run` produced a non-zero exit status. -* `assert_output `: succeed if `output` exactly matches the string. -* `assert_output --partial `: succeed if `output` contains the string. - -I'm considering taking `bats-assert` as a dependency, but for now, this very minimal set of functions with a similar interface should get you on your way. +Blank lines are ignored. A new `[success]` or `[failure]` line starts a new command block. ## Multiple blocks in one commit message @@ -93,7 +89,7 @@ Assert that a commit builds: ~~~ ```git-check-assertions -dotnet build +[success] dotnet build ``` ~~~ @@ -101,9 +97,8 @@ Assert that a commit builds and that the tests succeed: ~~~ ```git-check-assertions -dotnet build -run dotnet test --no-build -assert_success +[success] dotnet build +[success] dotnet test --no-build ``` ~~~ @@ -111,20 +106,18 @@ Assert that a commit builds, but that the tests do not succeed: ~~~ ```git-check-assertions -dotnet build -run dotnet test --no-build -assert_failure +[success] dotnet build +[failure] dotnet test --no-build ``` ~~~ -Assert that a commit builds, and that the tests fail with exactly the error that you expect: +Assert that a commit builds, and that the tests fail with the error that you expect: ~~~ ```git-check-assertions -dotnet build -run dotnet test --no-build -assert_failure -assert_output --partial "Invalid URL" +[success] dotnet build +[failure] dotnet test --no-build +Invalid URL ``` ~~~ @@ -132,12 +125,10 @@ Assert that a specific change breaks the tests (as discussed [here](https://sven ~~~ ```git-check-assertions -run dotnet test -assert_success +[success] dotnet test -sed -i '/crucial code/d' Main.fs -run dotnet test -assert_failure +[success] sed -i '/crucial code/d' Main.fs +[failure] dotnet test ``` ~~~ @@ -145,12 +136,10 @@ Assert that a specific change in a commit is necessary for the tests to succeed: ~~~ ```git-check-assertions -run dotnet test -assert_success +[success] dotnet test -git checkout HEAD~ Main.fs -run dotnet test -assert_failure -assert_output --partial "Invalid URL" +[success] git checkout HEAD~ Main.fs +[failure] dotnet test +Invalid URL ``` ~~~ diff --git a/bin/git-check-assertions b/bin/git-check-assertions index dcb91be..1d31324 100755 --- a/bin/git-check-assertions +++ b/bin/git-check-assertions @@ -24,48 +24,6 @@ set -euo pipefail -# helper functions inspired by bats/bats-assert -run() { - set +e - tmp_output="$(mktemp)" - "$@" 2>&1 | tee "$tmp_output" - status=${PIPESTATUS[0]} - set -e - output="$(cat "$tmp_output")" - rm -f "$tmp_output" - printf '%s\n' "$output" - return 0 -} -assert_success() { - if [ "$status" -ne 0 ]; then - echo "Expected command to succeed, but it failed." - exit 1 - fi -} -assert_failure() { - if [ "$status" -eq 0 ]; then - echo "Expected command to fail, but it succeeded." - exit 1 - fi -} -assert_output() { - if [ "${1:-}" = "--partial" ]; then - local expected="$2" - if [[ "$output" != *"$expected"* ]]; then - echo "Expected output to contain: $expected" - echo "Actual output: $output" - exit 1 - fi - return 0 - fi - if [ "$output" != "$1" ]; then - echo "Expected output to equal: $1" - echo "Actual output: $output" - exit 1 - fi -} -export -f run assert_success assert_failure assert_output - # main flow if [ ! -z "$(git status --porcelain)" ]; then echo "Uncommitted changes. Refusing to run." >&2 @@ -121,8 +79,64 @@ for commit_hash in "${commits[@]}"; do check_assertions() { python3 - "$block" <<-EOF import sys + import re import subprocess - subprocess.run(sys.argv[1], shell=True, check=True) + + block = sys.argv[1] + steps = [] + current_step = None + + for line_number, raw_line in enumerate(block.splitlines(), start=1): + line = raw_line.rstrip("\\r") + if not line.strip(): + continue + + match = re.match(r"^\\[(success|failure)\\]\\s+(.+)$", line) + if match: + current_step = { + "expects_success": match.group(1) == "success", + "command": match.group(2), + "assertions": [], + } + steps.append(current_step) + continue + + if current_step is None: + print( + f"DSL parse error on line {line_number}: " + "assertion text must follow [success] or [failure]." + ) + sys.exit(1) + + current_step["assertions"].append(line) + + for step in steps: + completed = subprocess.run( + ["sh", "-lc", step["command"]], + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + text=True, + ) + output = completed.stdout or "" + + if output: + sys.stdout.write(output) + if not output.endswith("\\n"): + sys.stdout.write("\\n") + + if step["expects_success"]: + if completed.returncode != 0: + print("Expected command to succeed, but it failed.") + sys.exit(1) + elif completed.returncode == 0: + print("Expected command to fail, but it succeeded.") + sys.exit(1) + + for expected in step["assertions"]: + if expected not in output: + print(f"Expected output to contain: {expected}") + print(f"Actual output: {output}") + sys.exit(1) EOF } if ! check_assertions; then diff --git a/test/git-check-assertions.bats b/test/git-check-assertions.bats index be3b275..09beeba 100755 --- a/test/git-check-assertions.bats +++ b/test/git-check-assertions.bats @@ -43,7 +43,7 @@ commit_with_assertion() { } @test "should not run any assertion blocks when on main" { - commit_with_assertion "touch ../test" + commit_with_assertion "[success] touch ../test" run git-check-assertions @@ -87,7 +87,7 @@ commit_with_assertion() { } @test "should not run any assertion blocks from main when on a feature branch" { - commit_with_assertion "touch ../test" + commit_with_assertion "[success] touch ../test" git checkout -b feature git commit --allow-empty -m "feature" @@ -100,7 +100,7 @@ commit_with_assertion() { @test "should not run any assertion blocks from master when on a feature branch" { init_repo "$BATS_TEST_TMPDIR/repo-master" master - commit_with_assertion "touch ../test" + commit_with_assertion "[success] touch ../test" git checkout -b feature git commit --allow-empty -m "feature" @@ -112,8 +112,8 @@ commit_with_assertion() { @test "should run all succeeding assertion blocks on the feature branch and finally return to the original branch" { git checkout -b feature - commit_with_assertion "touch ../test1" - commit_with_assertion "touch ../test2" + commit_with_assertion "[success] touch ../test1" + commit_with_assertion "[success] touch ../test2" run git-check-assertions @@ -130,11 +130,11 @@ commit_with_assertion() { test \`\`\`git-check-assertions - touch ../test1 + [success] touch ../test1 \`\`\` \`\`\`git-check-assertions - touch ../test2 + [success] touch ../test2 \`\`\` EOF @@ -159,11 +159,11 @@ commit_with_assertion() { update \`\`\`git-check-assertions - git checkout HEAD~ + [success] git checkout HEAD~ \`\`\` \`\`\`git-check-assertions - grep new readme + [success] grep new readme \`\`\` EOF @@ -174,8 +174,8 @@ commit_with_assertion() { @test "should stop at the first failing assertion block and return to the original branch" { git checkout -b feature - commit_with_assertion "touch ../test1 && exit 3" - commit_with_assertion "touch ../test2" + commit_with_assertion "[success] touch ../test1 && exit 3" + commit_with_assertion "[success] touch ../test2" run git-check-assertions @@ -188,7 +188,7 @@ commit_with_assertion() { @test "should restore worktree when finished" { git checkout -b feature - commit_with_assertion 'echo blah >> readme' + commit_with_assertion '[success] echo blah >> readme' run git-check-assertions @@ -198,8 +198,8 @@ commit_with_assertion() { @test "should restore worktree between commits" { git checkout -b feature - commit_with_assertion 'echo blah >> readme' - commit_with_assertion 'cp readme ../test' + commit_with_assertion '[success] echo blah >> readme' + commit_with_assertion '[success] cp readme ../test' run git-check-assertions @@ -209,7 +209,7 @@ commit_with_assertion() { @test "should restore worktree after a failure" { git checkout -b feature - commit_with_assertion $'echo blah >> readme\nexit 3' + commit_with_assertion $'[success] echo blah >> readme\n[success] exit 3' run git-check-assertions @@ -219,7 +219,7 @@ commit_with_assertion() { @test "should restore index when finished" { git checkout -b feature - commit_with_assertion $'echo blah >> readme\ngit add readme' + commit_with_assertion $'[success] echo blah >> readme\n[success] git add readme' run git-check-assertions @@ -229,8 +229,8 @@ commit_with_assertion() { @test "should restore index between commits" { git checkout -b feature - commit_with_assertion $'echo blah >> readme\ngit add readme' - commit_with_assertion 'cp readme ../test' + commit_with_assertion $'[success] echo blah >> readme\n[success] git add readme' + commit_with_assertion '[success] cp readme ../test' run git-check-assertions @@ -240,7 +240,7 @@ commit_with_assertion() { @test "should restore index after a failure" { git checkout -b feature - commit_with_assertion $'echo blah >> readme\ngit add readme\nexit 3' + commit_with_assertion $'[success] echo blah >> readme\n[success] git add readme\n[success] exit 3' run git-check-assertions @@ -250,7 +250,7 @@ commit_with_assertion() { @test "should skip cached commits on subsequent runs" { git checkout -b feature - commit_with_assertion "touch ../test" + commit_with_assertion "[success] touch ../test" run git-check-assertions @@ -266,7 +266,7 @@ commit_with_assertion() { @test "should not cache failed commits" { git checkout -b feature - commit_with_assertion "touch ../test && exit 3" + commit_with_assertion "[success] touch ../test && exit 3" run git-check-assertions @@ -282,8 +282,8 @@ commit_with_assertion() { @test "should skip cached commits when multiple commits are cached" { git checkout -b feature - commit_with_assertion "touch ../test1" - commit_with_assertion "touch ../test2" + commit_with_assertion "[success] touch ../test1" + commit_with_assertion "[success] touch ../test2" run git-check-assertions @@ -303,10 +303,10 @@ commit_with_assertion() { git checkout -b feature echo commit1 >readme git add readme - commit_with_assertion 'grep commit1 readme' + commit_with_assertion '[success] grep commit1 readme' echo commit2 >readme git add readme - commit_with_assertion 'grep commit2 readme' + commit_with_assertion '[success] grep commit2 readme' run git-check-assertions @@ -322,7 +322,7 @@ commit_with_assertion() { echo goodbye >src echo 'grep goodbye src' >tests git add src tests - commit_with_assertion $'git checkout HEAD~ src\nrun bash tests\nassert_failure' + commit_with_assertion $'[success] git checkout HEAD~ src\n[failure] bash tests' run git-check-assertions @@ -330,18 +330,18 @@ commit_with_assertion() { assert_file_contains src goodbye } -@test "assert_success should succeed if the executed command succeeded" { +@test "a [success] command should succeed if the executed command succeeded" { git checkout -b feature - commit_with_assertion $'run exit 0\nassert_success' + commit_with_assertion '[success] exit 0' run git-check-assertions assert_success } -@test "assert_success should fail if the executed command failed" { +@test "a [success] command should fail if the executed command failed" { git checkout -b feature - commit_with_assertion $'run exit 1\nassert_success' + commit_with_assertion '[success] exit 1' run git-check-assertions @@ -349,18 +349,18 @@ commit_with_assertion() { assert_output --partial "Expected command to succeed, but it failed." } -@test "assert_failure should succeed if the executed command failed" { +@test "a [failure] command should succeed if the executed command failed" { git checkout -b feature - commit_with_assertion $'run exit 1\nassert_failure' + commit_with_assertion '[failure] exit 1' run git-check-assertions assert_success } -@test "assert_failure should fail if the executed command succeeded" { +@test "a [failure] command should fail if the executed command succeeded" { git checkout -b feature - commit_with_assertion $'run exit 0\nassert_failure' + commit_with_assertion '[failure] exit 0' run git-check-assertions @@ -368,47 +368,47 @@ commit_with_assertion() { assert_output --partial "Expected command to fail, but it succeeded." } -@test "assert_output should succeed if the output matches the given string" { +@test "an output line should succeed if the output contains the given string" { git checkout -b feature - commit_with_assertion $'run echo hello\nassert_output hello' + commit_with_assertion $'[success] echo hello\nhello' run git-check-assertions assert_success } -@test "assert_output should fail if the output does not match the given string" { +@test "an output line should fail if the output does not contain the given string" { git checkout -b feature - commit_with_assertion $'run echo hello\nassert_output goodbye' + commit_with_assertion $'[success] echo hello\ngoodbye' run git-check-assertions assert_failure - assert_output --partial "Expected output to equal: goodbye" + assert_output --partial "Expected output to contain: goodbye" assert_output --partial "Actual output: hello" } -@test "assert_output should also check stderr output" { +@test "an output line should also check stderr output" { git checkout -b feature - commit_with_assertion $'run sh -c "echo err 1>&2"\nassert_output err' + commit_with_assertion $'[success] sh -c "echo err 1>&2"\nerr' run git-check-assertions assert_success } -@test "assert_output --partial should succeed if output contains the given string" { +@test "multiple output lines should all be checked against the same command output" { git checkout -b feature - commit_with_assertion $'run echo hello\nassert_output --partial ell' + commit_with_assertion $'[success] printf "hello\\n"\nhell\nello' run git-check-assertions assert_success } -@test "assert_output --partial should fail if output does not contain the given string" { +@test "an output line should fail with the missing substring" { git checkout -b feature - commit_with_assertion $'run echo hello\nassert_output --partial xyz' + commit_with_assertion $'[success] echo hello\nxyz' run git-check-assertions @@ -417,9 +417,9 @@ commit_with_assertion() { assert_output --partial "Actual output: hello" } -@test "run should print command output" { +@test "executed commands should print command output" { git checkout -b feature - commit_with_assertion $'run sh -c "echo -n hello; echo -n world"\nassert_success' + commit_with_assertion '[success] sh -c "echo -n hello; echo -n world"' run git-check-assertions From 0719fa33c25c3bd5d0547acf2ede2e7acef52b0e Mon Sep 17 00:00:00 2001 From: Sven van Heugten Date: Thu, 16 Apr 2026 17:01:40 +0200 Subject: [PATCH 4/6] =?UTF-8?q?Support=20=E2=9C=93/=E2=9C=97=20as=20altern?= =?UTF-8?q?atives=20to=20[success]/[failure]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 36 +++++++++++++++++----------------- bin/git-check-assertions | 6 +++--- test/git-check-assertions.bats | 28 ++++++++++++++++++++++++++ 3 files changed, 49 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index d3a8fb3..8fffe71 100644 --- a/README.md +++ b/README.md @@ -60,18 +60,18 @@ Simply add a `git-check-assertions` block to a commit message, e.g.: ~~~ ```git-check-assertions -[success] dotnet build -[success] dotnet test --no-build +✓ dotnet build +✓ dotnet test --no-build ``` ~~~ Each block is parsed line-by-line: -* A line starting with `[success] ` runs the rest of the line as a shell command and asserts that it exits with status `0`. -* A line starting with `[failure] ` runs the rest of the line as a shell command and asserts that it exits with a non-zero status. +* A line starting with `[success] ` or `✓ ` runs the rest of the line as a shell command and asserts that it exits with status `0`. +* A line starting with `[failure] ` or `✗ ` runs the rest of the line as a shell command and asserts that it exits with a non-zero status. * Any following non-empty line belongs to the most recent command and asserts that the combined stdout/stderr from that command contains that string. -Blank lines are ignored. A new `[success]` or `[failure]` line starts a new command block. +Blank lines are ignored. A new `[success]`, `[failure]`, `✓`, or `✗` line starts a new command block. ## Multiple blocks in one commit message @@ -89,7 +89,7 @@ Assert that a commit builds: ~~~ ```git-check-assertions -[success] dotnet build +✓ dotnet build ``` ~~~ @@ -97,8 +97,8 @@ Assert that a commit builds and that the tests succeed: ~~~ ```git-check-assertions -[success] dotnet build -[success] dotnet test --no-build +✓ dotnet build +✓ dotnet test --no-build ``` ~~~ @@ -106,8 +106,8 @@ Assert that a commit builds, but that the tests do not succeed: ~~~ ```git-check-assertions -[success] dotnet build -[failure] dotnet test --no-build +✓ dotnet build +✗ dotnet test --no-build ``` ~~~ @@ -115,8 +115,8 @@ Assert that a commit builds, and that the tests fail with the error that you exp ~~~ ```git-check-assertions -[success] dotnet build -[failure] dotnet test --no-build +✓ dotnet build +✗ dotnet test --no-build Invalid URL ``` ~~~ @@ -125,10 +125,10 @@ Assert that a specific change breaks the tests (as discussed [here](https://sven ~~~ ```git-check-assertions -[success] dotnet test +✓ dotnet test -[success] sed -i '/crucial code/d' Main.fs -[failure] dotnet test +✓ sed -i '/crucial code/d' Main.fs +✗ dotnet test ``` ~~~ @@ -136,10 +136,10 @@ Assert that a specific change in a commit is necessary for the tests to succeed: ~~~ ```git-check-assertions -[success] dotnet test +✓ dotnet test -[success] git checkout HEAD~ Main.fs -[failure] dotnet test +✓ git checkout HEAD~ Main.fs +✗ dotnet test Invalid URL ``` ~~~ diff --git a/bin/git-check-assertions b/bin/git-check-assertions index 1d31324..f8ef8c0 100755 --- a/bin/git-check-assertions +++ b/bin/git-check-assertions @@ -91,10 +91,10 @@ for commit_hash in "${commits[@]}"; do if not line.strip(): continue - match = re.match(r"^\\[(success|failure)\\]\\s+(.+)$", line) + match = re.match(r"^(\\[success\\]|\\[failure\\]|✓|✗)\\s+(.+)$", line) if match: current_step = { - "expects_success": match.group(1) == "success", + "expects_success": match.group(1) in ("[success]", "✓"), "command": match.group(2), "assertions": [], } @@ -104,7 +104,7 @@ for commit_hash in "${commits[@]}"; do if current_step is None: print( f"DSL parse error on line {line_number}: " - "assertion text must follow [success] or [failure]." + "assertion text must follow [success], [failure], ✓, or ✗." ) sys.exit(1) diff --git a/test/git-check-assertions.bats b/test/git-check-assertions.bats index 09beeba..ccd0a2f 100755 --- a/test/git-check-assertions.bats +++ b/test/git-check-assertions.bats @@ -368,6 +368,34 @@ commit_with_assertion() { assert_output --partial "Expected command to fail, but it succeeded." } +@test "a ✓ command should succeed if the executed command succeeded" { + git checkout -b feature + commit_with_assertion '✓ exit 0' + + run git-check-assertions + + assert_success +} + +@test "a ✗ command should succeed if the executed command failed" { + git checkout -b feature + commit_with_assertion '✗ exit 1' + + run git-check-assertions + + assert_success +} + +@test "you can mix symbols and bracketed asserts in the same block" { + git checkout -b feature + commit_with_assertion $'✓ echo hello\nhello\n[failure] exit 0' + + run git-check-assertions + + assert_failure + assert_output --partial "Expected command to fail, but it succeeded." +} + @test "an output line should succeed if the output contains the given string" { git checkout -b feature commit_with_assertion $'[success] echo hello\nhello' From fc7c23355a22195587302a01b5fa3718bce3b374 Mon Sep 17 00:00:00 2001 From: Sven van Heugten Date: Thu, 16 Apr 2026 17:13:12 +0200 Subject: [PATCH 5/6] Be less verbose The output is already printed once anyway. ```git-check-assertions [success] ./test/git-check-assertions.bats ``` --- bin/git-check-assertions | 1 - test/git-check-assertions.bats | 2 -- 2 files changed, 3 deletions(-) diff --git a/bin/git-check-assertions b/bin/git-check-assertions index f8ef8c0..6dd5c6e 100755 --- a/bin/git-check-assertions +++ b/bin/git-check-assertions @@ -135,7 +135,6 @@ for commit_hash in "${commits[@]}"; do for expected in step["assertions"]: if expected not in output: print(f"Expected output to contain: {expected}") - print(f"Actual output: {output}") sys.exit(1) EOF } diff --git a/test/git-check-assertions.bats b/test/git-check-assertions.bats index ccd0a2f..476add5 100755 --- a/test/git-check-assertions.bats +++ b/test/git-check-assertions.bats @@ -413,7 +413,6 @@ commit_with_assertion() { assert_failure assert_output --partial "Expected output to contain: goodbye" - assert_output --partial "Actual output: hello" } @test "an output line should also check stderr output" { @@ -442,7 +441,6 @@ commit_with_assertion() { assert_failure assert_output --partial "Expected output to contain: xyz" - assert_output --partial "Actual output: hello" } @test "executed commands should print command output" { From 967bb580e64525bb3e123571b42fef45f3eb1779 Mon Sep 17 00:00:00 2001 From: Sven van Heugten Date: Thu, 16 Apr 2026 17:19:01 +0200 Subject: [PATCH 6/6] Remove some newlines in the README --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 8fffe71..881160e 100644 --- a/README.md +++ b/README.md @@ -126,7 +126,6 @@ Assert that a specific change breaks the tests (as discussed [here](https://sven ~~~ ```git-check-assertions ✓ dotnet test - ✓ sed -i '/crucial code/d' Main.fs ✗ dotnet test ``` @@ -137,7 +136,6 @@ Assert that a specific change in a commit is necessary for the tests to succeed: ~~~ ```git-check-assertions ✓ dotnet test - ✓ git checkout HEAD~ Main.fs ✗ dotnet test Invalid URL