From 442876c525f9873176b6abfcd7c70b46153eb9d5 Mon Sep 17 00:00:00 2001 From: Neil Galvin Date: Sun, 30 Aug 2026 16:02:39 +0100 Subject: [PATCH] feat(opentofu): add init-backend input for credential-free lint jobs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A fmt/validate-only job on a configuration that has a `backend` block cannot run `tofu init` bare: the S3 backend resolves credentials during init, so the job dies with "No valid credential sources found" before `validate` is ever reached. The configuration is fine; the job simply has no business holding state-write credentials to lint HCL. `init-backend: false` passes `-backend=false`, which initialises providers and modules and skips the backend entirely. Default stays `true`, so every existing caller produces a byte-identical command line. Setting it alongside `run-plan: true` is a contradiction — plan needs state — so that combination now fails fast with an explicit error rather than surfacing as a confusing failure two steps later. Prompted by hordialabs/platform moving its OpenTofu state to Cloudflare R2: three stacks that had been linting green on local state went red the moment the backend blocks landed. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_017EDkXRcCrVDfu2e8V9Yy52 --- .github/workflows/opentofu.yml | 23 +++++++++++++++++++++++ CHANGELOG.md | 24 ++++++++++++++++++++++++ examples/README.md | 15 +++++++++++++++ 3 files changed, 62 insertions(+) diff --git a/.github/workflows/opentofu.yml b/.github/workflows/opentofu.yml index d806372..a7a8b0d 100644 --- a/.github/workflows/opentofu.yml +++ b/.github/workflows/opentofu.yml @@ -39,6 +39,18 @@ on: description: "Args passed to `tofu init -backend-config=...` (newline-separated)" type: string default: "" + init-backend: + description: | + Initialise the backend during `tofu init`. Set false to pass + `-backend=false`, which initialises providers and modules only. + + Needed for fmt/validate-only jobs on a configuration whose + `backend` block requires credentials the job has no business + holding — an S3/R2 backend, for example, where a bare `tofu init` + fails with "No valid credential sources found" before `validate` + ever runs. Mutually exclusive with run-plan, which needs state. + type: boolean + default: true var-file: description: "Optional .tfvars file to pass to plan" type: string @@ -99,9 +111,20 @@ jobs: if: inputs.run-fmt run: tofu fmt -check -recursive + # Fail here rather than three steps later with a confusing state + # error: `plan` needs the backend that `init-backend: false` skipped. + - name: Check input combination + if: ${{ !inputs.init-backend && inputs.run-plan }} + run: | + echo "::error::init-backend: false skips backend init, so tofu plan cannot run. Set run-plan: false, or give this job backend credentials." >&2 + exit 1 + - name: tofu init run: | ARGS="-input=false" + if [ "${{ inputs.init-backend }}" != "true" ]; then + ARGS="$ARGS -backend=false" + fi if [ -n "${{ inputs.backend-config }}" ]; then while IFS= read -r line; do [ -z "$line" ] && continue diff --git a/CHANGELOG.md b/CHANGELOG.md index 44052e4..d9bef43 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,30 @@ project uses [SemVer](https://semver.org/) for the `vMAJOR.MINOR.PATCH` tags. ## [Unreleased] +### Added + +- `opentofu.yml` — `init-backend` input (boolean, default `true`). Set it to + `false` to run `tofu init -backend=false`, initialising providers and + modules without touching the backend. + + Needed by any consumer whose configuration has a `backend` block requiring + credentials that a lint-only job has no business holding. A bare + `tofu init` against an S3/R2 backend fails with *"No valid credential + sources found"* before `validate` ever runs, so a fmt/validate job that was + green with local state goes red the moment a backend block lands. That is + exactly what `hordialabs/platform` hit moving its state to Cloudflare R2. + + ```yaml + uses: nkg/github-actions/.github/workflows/opentofu.yml@v3 + with: + run-plan: false + init-backend: false # fmt + validate only; no state credentials in CI + ``` + + Mutually exclusive with `run-plan`, which needs state — setting both now + fails fast with an explicit error rather than a confusing one from `plan`. + Default `true` keeps every existing caller byte-identical. + ## [3.1.0] - 2026-08-30 ### Changed diff --git a/examples/README.md b/examples/README.md index 43abb28..a027114 100644 --- a/examples/README.md +++ b/examples/README.md @@ -156,6 +156,21 @@ jobs: tofu-version: "1.8.0" ``` +Lint-only, on a stack whose `backend` block needs credentials CI does not +hold (an S3/R2 backend, say). Without `init-backend: false`, `tofu init` +fails with *"No valid credential sources found"* before `validate` runs: + +```yaml +jobs: + validate: + uses: nkg/github-actions/.github/workflows/opentofu.yml@v3 + with: + working-directory: tofu/proxmox + use-mise: true + run-plan: false + init-backend: false +``` + ## Docker → GHCR ```yaml