From 3f8a2aee55ebb0820627381400f1ff38ea1a0d86 Mon Sep 17 00:00:00 2001 From: Nathan Broadbent Date: Sun, 18 Oct 2026 15:07:32 +1300 Subject: [PATCH] Push the provider sync with a deploy key instead of a PAT The 0.2.0 release's "Push + Tag Provider" job failed: TF_PROVIDER_GITHUB_TOKEN, a PAT last used for v0.1.12 in January, was no longer accepted. The job now pushes over SSH with TF_PROVIDER_DEPLOY_KEY, a deploy key with write access to terraform-provider-logstruct only, which doesn't expire. GitHub's host keys come from api.github.com/meta, and the job stops with a clear error, before changing anything, if the secret is missing or GitHub doesn't accept it. A workflow test checks that. AGENTS.md describes the deploy key and the provider repo's GPG signing key, which the Terraform Registry checks. --- .github/workflows/release.yml | 28 +++++++++++++++++++------ AGENTS.md | 8 +++++-- test/workflows/release_workflow_test.rb | 10 +++++++++ 3 files changed, 38 insertions(+), 8 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 68bc9c6c..11b1633e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -262,18 +262,34 @@ jobs: - name: Commit, push and tag the provider env: - PUSH_TOKEN: ${{ secrets.TF_PROVIDER_GITHUB_TOKEN }} + # A deploy key with write access to terraform-provider-logstruct only. Deploy keys don't expire: the PAT + # this replaced had expired by the 0.2.0 release. + DEPLOY_KEY: ${{ secrets.TF_PROVIDER_DEPLOY_KEY }} run: | + if [[ -z "$DEPLOY_KEY" ]]; then + echo "::error::TF_PROVIDER_DEPLOY_KEY isn't set in the release environment." + exit 1 + fi + mkdir -p ~/.ssh && chmod 700 ~/.ssh + printf '%s\n' "$DEPLOY_KEY" > ~/.ssh/provider_deploy_key + chmod 600 ~/.ssh/provider_deploy_key + # GitHub's published host keys, so the key is only ever offered to github.com + curl -fsS https://api.github.com/meta | jq -r '.ssh_keys[] | "github.com " + .' > ~/.ssh/github_known_hosts + export GIT_SSH_COMMAND="ssh -i $HOME/.ssh/provider_deploy_key -o IdentitiesOnly=yes -o StrictHostKeyChecking=yes -o UserKnownHostsFile=$HOME/.ssh/github_known_hosts" + REMOTE=git@github.com:DocSpring/terraform-provider-logstruct.git + # Stop before changing anything if GitHub doesn't accept the key + if ! git ls-remote "$REMOTE" HEAD >/dev/null; then + echo "::error::GitHub didn't accept TF_PROVIDER_DEPLOY_KEY for terraform-provider-logstruct: check the repo's deploy key (with write access) matches the secret." + exit 1 + fi git config --global user.name "DocSpring Bot" git config --global user.email "docspring-bot@docspring.com" - git clone https://github.com/DocSpring/terraform-provider-logstruct.git terraform-provider-logstruct + git clone "$REMOTE" terraform-provider-logstruct cd terraform-provider-logstruct - # The token is passed to the two pushes only, not stored in the clone's config - AUTH="AUTHORIZATION: basic $(printf 'x-access-token:%s' "$PUSH_TOKEN" | base64 -w0)" if [[ -s ../provider-changes.patch ]]; then git apply --index ../provider-changes.patch git commit -m "Sync catalog from logstruct $TAG_NAME" - git -c "http.https://github.com/.extraheader=$AUTH" push origin HEAD:main + git push origin HEAD:main else echo "No provider changes to commit." fi @@ -282,4 +298,4 @@ jobs: exit 0 fi git tag -a "$TAG_NAME" -m "Release $TAG_NAME (logstruct sync)" - git -c "http.https://github.com/.extraheader=$AUTH" push origin "$TAG_NAME" + git push origin "$TAG_NAME" diff --git a/AGENTS.md b/AGENTS.md index fefd2a7e..db2340c7 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -127,8 +127,12 @@ For template file changes (tests, rake tasks, etc.), just run `scripts/rails_tes ## Required secrets -- `TF_PROVIDER_GITHUB_TOKEN` (a `release` environment secret): PAT with write access to - `DocSpring/terraform-provider-logstruct` for syncing/tagging. +- `TF_PROVIDER_DEPLOY_KEY` (a `release` environment secret): the private half of a deploy key with write access to + `DocSpring/terraform-provider-logstruct`, for syncing and tagging it. To replace it, generate an ed25519 key, add the + public half as a write deploy key on the provider repo and set the private half as this secret. +- The provider repo's own release (GoReleaser, on its `v*` tags) signs with the GPG key in its `GPG_PRIVATE_KEY` + secret. The Terraform Registry only accepts releases signed by a key registered for the DocSpring namespace, so a + new signing key must be added to the registry before the next provider release. - RubyGems needs no secret: the `logstruct` gem has a trusted publisher for this repository's Release workflow and its `release` environment. diff --git a/test/workflows/release_workflow_test.rb b/test/workflows/release_workflow_test.rb index 38d0b512..d3dfd213 100644 --- a/test/workflows/release_workflow_test.rb +++ b/test/workflows/release_workflow_test.rb @@ -42,5 +42,15 @@ def all_needs(job_id) assert_equal({"publish_gem" => "release", "publish_provider" => "release"}, environments) end + + test "the provider push uses the deploy key and checks it before changing anything" do + steps = jobs.fetch("publish_provider").fetch("steps") + env = steps.filter_map { |step| step["env"] }.reduce({}, :merge) + run = steps.map { |step| step["run"].to_s }.join("\n") + + assert_equal "${{ secrets.TF_PROVIDER_DEPLOY_KEY }}", env["DEPLOY_KEY"] + refute_match(/TF_PROVIDER_GITHUB_TOKEN|x-access-token/, run) + assert_operator run.index("git ls-remote"), :<, run.index("git clone") + end end end