Add hook scripts to run custom code around OpenTofu commands

What

Adds a hook script mechanism so users can run their own shell code around OpenTofu commands, without having to override a job's script: wholesale — which is currently the only option and is brittle.

Hook points: pre-init/post-init, pre-plan/post-plan, pre-apply/post-apply, pre-destroy/post-destroy.

Configured with a single hooks_dir input (and GITLAB_TOFU_HOOKS_DIR), defaulting to .gitlab/ci/hooks. A missing hook file is a no-op.

Design notes

Hooks are sourced, not executed, so a hook can export variables that affect the following tofu invocation. (For contrast, the equivalent feature in to-be-continuous/terraform executes hooks as subprocesses, which makes exporting impossible.)

post-* only runs on success; errexit means a failing hook fails the job with the hook's exit code. The plan branch was restructured slightly so that post-plan also runs for -detailed-exitcode's exit code 2, since a non-empty plan is not a failure.

gitlab-tofu apply -destroy maps onto the destroy hooks. templates/destroy.yml destroys via gitlab-tofu apply -destroy, so keying hooks on the CLI verb alone would have made pre-destroy/post-destroy unreachable in the actual destroy job.

Backwards compatibility with id_tokens_setup_script

templates/__internal_id_tokens_base_job.yml is byte-for-byte untouched. id_tokens_setup_script keeps working exactly as before and is not deprecated.

The resulting order is id_tokens_setup_scriptpre-inittofu init. Making pre-init run first was considered and rejected: it would require moving the sourcing into gitlab-tofu.sh, where it either stops running for commands that never init (delete-state, fmt, plan-json, bare passthroughs via custom-command) or runs once per gitlab-tofu invocation rather than once per job — and several templates invoke it twice per job (apply + output, plan + show), which would mean a second role assumption.

The chosen order is arguably the better one anyway: a pre-init hook then runs with the cloud credentials the id_tokens script established.

Testing

  • tests/integration-tests/HookScripts.gitlab-ci.yml (+ tests/iac-hooks/ fixture), registered on alpine and debian. Asserts the exact hook sequence per command, that a hook's export reaches tofu, and that id_tokens_setup_script still works.
  • 5 new cases in tests/unit/gitlab-tofu.bats.
  • shellcheck clean; make docs idempotent; go test ./... green.

Known limitation

detect-drift and fetch-mr-plan fire only the init hooks, even though they internally run tofu plan / tofu apply -refresh-only. Deliberate for now — happy to extend if wanted.

Merge request reports

Loading