Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
178 changes: 178 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
# Publishes the release drafted by draft-release.yml: builds and GPG-signs the provider
# with GoReleaser, attaches the artifacts to the pending draft release, and un-drafts it.
# Un-drafting is what creates the git tag, which the Terraform Registry then ingests.
#
# The signing key must be the one registered with the Terraform Registry — the Registry
# verifies SHA256SUMS.sig against it and rejects the version otherwise.
#
# SECRETS (environment `release`):
# GPG_PRIVATE_KEY - ASCII-armored GPG private key registered with the Terraform Registry
# GPG_PASSPHRASE - passphrase for that key
name: Release

on:
workflow_dispatch:
inputs:
dry_run:
description: "Dry run: build and sign, but do not upload or publish"
required: false
type: boolean
default: false

concurrency:
group: ${{ github.workflow }}
cancel-in-progress: false

permissions:
contents: write

jobs:
release:
name: Release terraform-provider-mailtrap
runs-on: ubuntu-latest
# GPG_PRIVATE_KEY / GPG_PASSPHRASE are environment secrets, not repo secrets:
# the signing key is only reachable through this environment's protection rules.
environment: release
steps:
# The provider has no version file: the version lives in the git tag, which GitHub
# creates when the pending draft release is un-drafted at the end of this job. Until
# then the version is resolved from the draft and handed to GoReleaser via a local,
# never-pushed tag.
#
# The draft is always created against the default branch, so releasing from anywhere
# else would tag a tree the release notes were never generated from.
- name: Refuse to release from a non-default branch
env:
REF: ${{ github.ref_name }}
DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
run: |
if [ "$REF" != "$DEFAULT_BRANCH" ]; then
echo "::error::Releases must be dispatched from $DEFAULT_BRANCH, got $REF"
exit 1
fi

- name: Resolve pending draft release
id: version
uses: actions/github-script@v9
Comment thread
Rabsztok marked this conversation as resolved.
with:
script: |
const releases = await github.paginate(github.rest.repos.listReleases, {
...context.repo,
per_page: 100
});
const drafts = releases.filter((release) => release.draft);

if (drafts.length === 0) {
core.setFailed('No draft GitHub Release found to publish; run the Draft Release workflow first');
return;
}

// Publishing to the Registry is irreversible, so never guess between drafts.
if (drafts.length > 1) {
const tags = drafts.map((draft) => draft.tag_name).join(', ');
core.setFailed(`Found ${drafts.length} draft releases (${tags}); leave exactly one`);
return;
}

const tag = drafts[0].tag_name;

core.info(`Draft GitHub Release ${tag} found`);
core.setOutput('tag', tag);
core.setOutput('version', tag.replace(/^v/, ''));

- uses: actions/checkout@v7
with:
# GoReleaser resolves the version from git tags and history.
fetch-depth: 0
Comment thread
Rabsztok marked this conversation as resolved.
persist-credentials: false

# The draft's target_commitish is a branch name, so un-drafting would tag whatever
# the default branch points at *then* — not what was built here. Record the exact
# commit and pin the release to it before publishing, so the signed checksums always
# describe the tag's tree.
- name: Record built commit
id: commit
run: echo "sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"

- name: Create local tag for GoReleaser
env:
TAG: ${{ steps.version.outputs.tag }}
run: git tag "$TAG"

- uses: actions/setup-go@v6
with:
go-version-file: go.mod
cache: false

# Mirrors ci.yml: the acceptance tests need the terraform binary.
- uses: hashicorp/setup-terraform@v3
with:
terraform_wrapper: false

- name: Vet
run: go vet ./...

- name: Test
run: go test -race ./...

# Third-party actions are pinned to a commit: this one receives the release signing
# key, and GoReleaser's archive naming is load-bearing for Registry ingestion.
- name: Import GPG key
id: import_gpg
uses: crazy-max/ghaction-import-gpg@e89d40939c28e39f97cf32126055eeae86ba74ec # v6.3.0
with:
gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
passphrase: ${{ secrets.GPG_PASSPHRASE }}

# Build, archive, checksum and GPG-sign only — publishing means uploading to the
# pending draft release below, not letting GoReleaser create its own release.
- name: Build and sign with GoReleaser
uses: goreleaser/goreleaser-action@e435ccd777264be153ace6237001ef4d979d3a7a # v6.4.0
with:
distribution: goreleaser
version: "2.17.1"
args: release --clean --skip=publish,announce
env:
GPG_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }}

# GoReleaser attaches the Registry protocol manifest only at publish time, which is
# skipped here — copy it into dist under its release asset name instead (same content
# its SHA256SUMS entry was computed from). Not guarded by dry_run: GoReleaser records
# this file's hash in SHA256SUMS under the asset name below, so a dry run must fail
# here if the two ever drift apart.
- name: Stage the Registry protocol manifest
env:
VERSION: ${{ steps.version.outputs.version }}
run: cp terraform-registry-manifest.json "dist/terraform-provider-mailtrap_${VERSION}_manifest.json"

- name: Attach artifacts to GitHub Release
if: ${{ !inputs.dry_run }}
env:
GH_TOKEN: ${{ github.token }}
TAG: ${{ steps.version.outputs.tag }}
VERSION: ${{ steps.version.outputs.version }}
# --clobber so a re-run after a partial upload replaces assets instead of failing.
run: |
gh release upload "$TAG" \
dist/*.zip \
"dist/terraform-provider-mailtrap_${VERSION}_SHA256SUMS" \
"dist/terraform-provider-mailtrap_${VERSION}_SHA256SUMS.sig" \
"dist/terraform-provider-mailtrap_${VERSION}_manifest.json" \
--clobber \
--repo "$GITHUB_REPOSITORY"
Comment thread
coderabbitai[bot] marked this conversation as resolved.

- name: Pin release to the built commit
if: ${{ !inputs.dry_run }}
env:
GH_TOKEN: ${{ github.token }}
TAG: ${{ steps.version.outputs.tag }}
SHA: ${{ steps.commit.outputs.sha }}
run: gh release edit "$TAG" --target "$SHA" --repo "$GITHUB_REPOSITORY"

# Un-drafting creates the git tag; the Terraform Registry then ingests the release.
- name: Publish GitHub Release
if: ${{ !inputs.dry_run }}
env:
GH_TOKEN: ${{ github.token }}
TAG: ${{ steps.version.outputs.tag }}
run: gh release edit "$TAG" --repo "$GITHUB_REPOSITORY" --draft=false
Loading