github fabriziosalmi/certmate v2.6.10
v2.6.10 - close #207 bug: install bash so #!/bin/bash deploy hooks resolve

latest release: v2.6.11
4 hours ago

Closes the bug part of issue #207 (reported by @tuxpowered).

Root cause

The base image (python:3.12-slim-trixie) ships with /bin/sh (dash) but no /bin/bash. The deployer invokes hook scripts via ['sh', '-c', command] (modules/core/deployer.py:187); dash then execs the script path and the kernel reads the shebang. A script starting with #!/bin/bash causes execve('/bin/bash', ...) to return ENOENT, which surfaces as exit code 127 in the deploy history.

There was also a latent inconsistency: the Dockerfile already declared useradd --create-home --shell /bin/bash certmate, so the certmate user's login shell was bash — but bash itself was never installed.

Fix

One-line change in the Dockerfile: add bash to the apt-get install -y line so /bin/bash resolves. Python deployer code is untouched — operator hooks remain executed via sh -c, the shebang still controls which interpreter runs the script body.

Operator impact

  • Existing deploy hooks with #!/bin/sh keep working (no change).
  • Hooks with #!/bin/bash (the common default for ops scripts) now run instead of failing with 127.
  • Image size delta: roughly +6 MB compressed for bash + its data files.

Verification

Built certmate:v2.6.10-test from the merge commit and ran:

=== bash presence ===
/usr/bin/bash
GNU bash, version 5.2.37(1)-release (aarch64-unknown-linux-gnu)

=== shebang resolution: #!/bin/bash via sh -c (mirrors deployer.py:187-194) ===
shebang fired: 5.2.37(1)-release
CERTMATE_DOMAIN=test.example.com
curl present: /usr/bin/curl
exit=0

=== baseline: what 127 looked like in v2.6.9 ===
sh: 1: /tmp/missing-interp.sh: not found
exit=127 (expected non-zero for missing interp)

/bin/bash and /usr/bin/bash both resolve via Debian's usrmerge symlink (/bin -> usr/bin), so a literal #!/bin/bash shebang now works.

Full Python suite (no source-code changes): 871 passed, 14 skipped, 2 xfailed, 0 failed.

Out of scope (separate follow-ups tracked against #207)

  • Click-through drill-down on Recent Executions to inspect stdout / stderr / payload / response (backend already stores these in data/deploy_history.jsonl; the UI just doesn't surface them).
  • Relabel "Hook name" → "path to script or cmd" + "Description" prefix on the example field.
  • A dedicated webhook configuration flow (URL + method + auth + freeform JSON payload + variable selector), distinct from the shell-script hook flow.

Don't miss a new certmate release

NewReleases is sending notifications on new releases.