Skip to content

Commit d8a0019

Browse files
authored
Merge pull request #22 from platform9/chore/registry-publishing
chore(release): GoReleaser + release workflow + Terraform Registry publishing runbook
2 parents 9493c44 + dc3423a commit d8a0019

5 files changed

Lines changed: 281 additions & 0 deletions

File tree

.github/workflows/release.yml

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
name: release
2+
3+
# Publishes a signed release to GitHub (and, via the registry webhook, to the
4+
# Terraform Registry) whenever a semver tag is pushed. See RELEASING.md.
5+
on:
6+
push:
7+
tags:
8+
- "v*"
9+
10+
permissions:
11+
contents: write
12+
13+
jobs:
14+
goreleaser:
15+
runs-on: ubuntu-latest
16+
steps:
17+
- uses: actions/checkout@v4
18+
- uses: actions/setup-go@v5
19+
with:
20+
go-version-file: go.mod
21+
cache: true
22+
- name: Import GPG key
23+
uses: crazy-max/ghaction-import-gpg@v6
24+
id: import_gpg
25+
with:
26+
gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
27+
passphrase: ${{ secrets.PASSPHRASE }}
28+
- name: Run GoReleaser
29+
uses: goreleaser/goreleaser-action@v6
30+
with:
31+
# Pin the GoReleaser line to match the config syntax in .goreleaser.yml.
32+
version: "~> v2.5"
33+
args: release --clean
34+
env:
35+
# The fingerprint of the imported key; GoReleaser passes it to gpg.
36+
GPG_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }}
37+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.github/workflows/test.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,21 @@ jobs:
5858
- name: generate docs
5959
run: go run github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs generate --provider-name pcd
6060

61+
release-config:
62+
name: goreleaser check
63+
runs-on: ubuntu-latest
64+
steps:
65+
- uses: actions/checkout@v4
66+
- uses: actions/setup-go@v5
67+
with:
68+
go-version-file: go.mod
69+
# Validate .goreleaser.yml so release-config drift is caught in PRs, not at
70+
# release time. Must stay in sync with the pinned version in release.yml.
71+
- uses: goreleaser/goreleaser-action@v6
72+
with:
73+
version: "~> v2.5"
74+
args: check
75+
6176
terraform-fmt:
6277
name: terraform fmt (examples)
6378
runs-on: ubuntu-latest

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ crash.*.log
2121
clouds.yaml
2222
*.pem
2323
*.key
24+
*.asc
25+
*.gpg
2426
secrets.*
2527

2628
# Local dev overrides

.goreleaser.yml

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# Copyright (c) Platform9 Systems, Inc.
2+
# SPDX-License-Identifier: MPL-2.0
3+
#
4+
# GoReleaser configuration for publishing terraform-provider-pcd to the Terraform
5+
# Registry. Triggered by the release workflow on a `v*` tag. See RELEASING.md.
6+
version: 2
7+
8+
before:
9+
hooks:
10+
# Verify the module is tidy before building the release.
11+
- go mod tidy
12+
13+
builds:
14+
- env:
15+
- CGO_ENABLED=0
16+
mod_timestamp: "{{ .CommitTimestamp }}"
17+
flags:
18+
- -trimpath
19+
ldflags:
20+
# main.version is consumed by the provider (see main.go) and surfaced to
21+
# Terraform; keep this in sync with the variable name there.
22+
- "-s -w -X main.version={{ .Version }}"
23+
goos:
24+
- freebsd
25+
- windows
26+
- linux
27+
- darwin
28+
goarch:
29+
- amd64
30+
- "386"
31+
- arm
32+
- arm64
33+
ignore:
34+
- goos: darwin
35+
goarch: "386"
36+
binary: "{{ .ProjectName }}_v{{ .Version }}"
37+
38+
archives:
39+
- format: zip
40+
name_template: "{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}"
41+
42+
checksum:
43+
extra_files:
44+
- glob: terraform-registry-manifest.json
45+
name_template: "{{ .ProjectName }}_{{ .Version }}_manifest.json"
46+
name_template: "{{ .ProjectName }}_{{ .Version }}_SHA256SUMS"
47+
algorithm: sha256
48+
49+
signs:
50+
- artifacts: checksum
51+
args:
52+
# GPG_FINGERPRINT is exported by the release workflow after importing the
53+
# signing key. The registry verifies this signature against the public key
54+
# uploaded to the platform9 namespace.
55+
- "--batch"
56+
- "--local-user"
57+
- "{{ .Env.GPG_FINGERPRINT }}"
58+
- "--output"
59+
- "${signature}"
60+
- "--detach-sign"
61+
- "${artifact}"
62+
63+
release:
64+
extra_files:
65+
- glob: terraform-registry-manifest.json
66+
name_template: "{{ .ProjectName }}_{{ .Version }}_manifest.json"
67+
68+
changelog:
69+
# The human-maintained CHANGELOG.md is the source of truth for release notes.
70+
disable: true

RELEASING.md

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
# Releasing `terraform-provider-pcd`
2+
3+
This provider is distributed through the [Terraform Registry](https://registry.terraform.io)
4+
at `platform9/pcd`. Releases are cut by pushing a semver tag; the
5+
[`release` workflow](.github/workflows/release.yml) builds every platform binary
6+
with [GoReleaser](https://goreleaser.com), signs the checksums with the org GPG
7+
key, and publishes a GitHub Release. The registry ingests that release
8+
automatically via its webhook.
9+
10+
The steps below are split into **one-time setup** (done once for the repo) and
11+
**per-release** (done for every version).
12+
13+
---
14+
15+
## One-time setup
16+
17+
These are account/organisation actions that require repo-admin and a
18+
registry-connected GitHub account. They cannot be automated from CI and are
19+
**not** performed by any tooling in this repo — a human runs them once.
20+
21+
### 1. Generate the signing GPG key
22+
23+
The registry verifies every release's `SHA256SUMS` signature against a public
24+
key registered to the `platform9` namespace. Generate a dedicated key (RSA 4096,
25+
no expiry is fine for a service key):
26+
27+
```sh
28+
gpg --full-generate-key # choose RSA/RSA, 4096 bits
29+
gpg --list-secret-keys --keyid-format=long # note the key ID / fingerprint
30+
```
31+
32+
Export both halves:
33+
34+
```sh
35+
# Public key — this is uploaded to the Terraform Registry (step 3).
36+
gpg --armor --export "<KEY_ID>" > pcd-signing-key.pub.asc
37+
38+
# Private key — this becomes the GPG_PRIVATE_KEY GitHub secret (step 2).
39+
gpg --armor --export-secret-keys "<KEY_ID>" > pcd-signing-key.priv.asc
40+
```
41+
42+
Keep the private key and its passphrase in the org secret manager. Do **not**
43+
commit either file (both are covered by the `.gitignore` `*.asc` rule).
44+
45+
### 2. Add the GitHub Actions secrets
46+
47+
In **Settings → Secrets and variables → Actions**, add:
48+
49+
| Secret | Value |
50+
| --- | --- |
51+
| `GPG_PRIVATE_KEY` | contents of `pcd-signing-key.priv.asc` (the full ASCII-armored block) |
52+
| `PASSPHRASE` | the passphrase for that key |
53+
54+
`GITHUB_TOKEN` is provided automatically by Actions — no need to add it.
55+
56+
### 3. Register the provider on the Terraform Registry
57+
58+
1. The repository must be **public** (registry requirement). If it is still
59+
private, change it in **Settings → General → Danger Zone → Change
60+
visibility**. Confirm with the code owners before doing this — it exposes the
61+
full history.
62+
2. Sign in to <https://registry.terraform.io> with a GitHub account that is a
63+
member of the `platform9` org.
64+
3. **Publish → Provider**, authorize the registry GitHub app for the
65+
`platform9` org, and select `terraform-provider-pcd`. The registry naming
66+
convention (`terraform-provider-<name>`) yields the address `platform9/pcd`,
67+
matching `main.go`'s `registry.terraform.io/platform9/pcd`.
68+
4. Under **Settings → GPG Keys** for the namespace, paste the public key from
69+
step 1 (`pcd-signing-key.pub.asc`).
70+
71+
Once connected, the registry installs a webhook so future GitHub Releases are
72+
ingested automatically.
73+
74+
### 4. Sanity-check the release config (optional but recommended)
75+
76+
With [GoReleaser installed](https://goreleaser.com/install/) locally:
77+
78+
```sh
79+
goreleaser check # validates .goreleaser.yml
80+
goreleaser release --snapshot --clean --skip=sign # dry-run a full build (no publish)
81+
```
82+
83+
The snapshot build drops artifacts in `dist/`; confirm it produces one zip per
84+
`goos/goarch` plus a `..._SHA256SUMS` file and the `..._manifest.json`.
85+
86+
---
87+
88+
## Per-release
89+
90+
### 1. Prepare the changelog
91+
92+
Move the accumulated notes under `## [Unreleased]` in
93+
[`CHANGELOG.md`](CHANGELOG.md) into a new `## [X.Y.Z] - <date>` section and open
94+
a PR. Merge it to `main` before tagging.
95+
96+
### 2. Tag and push
97+
98+
From an up-to-date `main`:
99+
100+
```sh
101+
git checkout main && git pull --ff-only
102+
git tag v0.1.0 # semver, MUST start with 'v'
103+
git push origin v0.1.0
104+
```
105+
106+
Pushing the tag triggers the `release` workflow.
107+
108+
### 3. Watch the release workflow
109+
110+
```sh
111+
gh run watch # or: gh run list --workflow=release.yml
112+
```
113+
114+
On success there is a new **GitHub Release** for the tag containing:
115+
116+
- `terraform-provider-pcd_X.Y.Z_<os>_<arch>.zip` for every platform
117+
- `terraform-provider-pcd_X.Y.Z_SHA256SUMS` and its `.sig`
118+
- `terraform-provider-pcd_X.Y.Z_manifest.json`
119+
120+
### 4. Verify on the registry
121+
122+
The registry usually ingests within a few minutes. Confirm the new version
123+
appears at <https://registry.terraform.io/providers/platform9/pcd>, then smoke-test
124+
consumption:
125+
126+
```hcl
127+
terraform {
128+
required_providers {
129+
pcd = {
130+
source = "platform9/pcd"
131+
version = "X.Y.Z"
132+
}
133+
}
134+
}
135+
```
136+
137+
```sh
138+
terraform init # should download platform9/pcd X.Y.Z and verify its signature
139+
```
140+
141+
---
142+
143+
## Versioning
144+
145+
Follow [Semantic Versioning](https://semver.org): patch for fixes, minor for
146+
new (backward-compatible) resources/data sources, major for breaking schema or
147+
behavior changes. Pre-1.0 (`v0.y.z`), breaking changes bump the minor.
148+
149+
## Notes
150+
151+
- The GitHub Actions used here are pinned to major-version tags to match
152+
[`test.yml`](.github/workflows/test.yml). Pin to commit SHAs if the org's
153+
supply-chain policy requires it.
154+
- Only tags matching `v*` release; branch pushes never publish.
155+
- `terraform-registry-manifest.json` declares Terraform **protocol 6.0**
156+
(the provider is built on `terraform-plugin-framework`); it is bundled into
157+
every release by GoReleaser so the registry records the protocol version.

0 commit comments

Comments
 (0)