env-secrets 0.5.3 → 0.5.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/rules/cicd.md +189 -0
- package/.claude/rules/docs.md +96 -0
- package/.claude/rules/git-hooks.md +43 -0
- package/.claude/rules/local-dev-badges.md +91 -0
- package/.claude/rules/local-dev-env.md +382 -0
- package/.claude/rules/local-dev-license.md +104 -0
- package/.claude/rules/local-dev-mcp.md +70 -0
- package/.claude/rules/observability.md +23 -0
- package/.claude/rules/publishing-api.md +158 -0
- package/.claude/rules/publishing-apps.md +204 -0
- package/.claude/rules/publishing-apt.md +146 -0
- package/.claude/rules/publishing-brew.md +110 -0
- package/.claude/rules/publishing-cli.md +238 -0
- package/.claude/rules/publishing-libraries.md +115 -0
- package/.claude/rules/publishing-sdks.md +109 -0
- package/.claude/rules/publishing-web.md +185 -0
- package/.claude/rules/typescript-linting.md +141 -0
- package/.claude/rules/typescript-logging.md +356 -0
- package/.claude/rules/typescript-testing.md +185 -0
- package/.claude/settings.json +18 -0
- package/.claude/skills/github-health-check.skill +0 -0
- package/.codex/rules/cicd.md +21 -0
- package/.codex/rules/docs.md +98 -0
- package/.codex/rules/git-hooks.md +43 -0
- package/.codex/rules/github-health-check.md +440 -0
- package/.codex/rules/local-dev-env.md +47 -0
- package/.codex/rules/local-dev-license.md +3 -1
- package/.codex/rules/publishing-api.md +160 -0
- package/.codex/rules/publishing-apps.md +206 -0
- package/.codex/rules/publishing-apt.md +148 -0
- package/.codex/rules/publishing-brew.md +112 -0
- package/.codex/rules/publishing-cli.md +240 -0
- package/.codex/rules/publishing-libraries.md +117 -0
- package/.codex/rules/publishing-sdks.md +111 -0
- package/.codex/rules/publishing-web.md +187 -0
- package/.codex/rules/typescript-linting.md +143 -0
- package/.codex/rules/typescript-logging.md +358 -0
- package/.codex/rules/typescript-testing.md +187 -0
- package/.github/workflows/deploy-docs.yml +1 -1
- package/.github/workflows/unittests.yaml +1 -1
- package/.rulesrc.json +20 -0
- package/AGENTS.md +34 -0
- package/CLAUDE.md +58 -0
- package/README.md +17 -3
- package/__e2e__/aws-secret-value-args.test.ts +142 -0
- package/__tests__/cli/helpers.test.ts +35 -0
- package/dist/cli/helpers.js +13 -1
- package/dist/index.js +79 -40
- package/docs/AWS.md +42 -13
- package/package.json +5 -5
- package/src/cli/helpers.ts +16 -0
- package/src/index.ts +97 -48
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
---
|
|
2
|
+
# Publishing Rules
|
|
3
|
+
|
|
4
|
+
These rules help design and maintain release workflows for libraries, SDKs, and apps.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# REST API Publishing Agent
|
|
8
|
+
|
|
9
|
+
You are a publishing specialist for REST API services deployed as Docker containers to Kubernetes.
|
|
10
|
+
|
|
11
|
+
## Goals
|
|
12
|
+
|
|
13
|
+
- Use the same container publishing and Helm chart update model as web apps.
|
|
14
|
+
- Ensure the API exposes a health endpoint that Kubernetes probes can use.
|
|
15
|
+
- Include `livenessProbe` and `readinessProbe` in the Helm chart template referencing the health endpoint.
|
|
16
|
+
- Distinguish private (GHCR) vs public (Docker Hub) image publishing based on the API's audience.
|
|
17
|
+
|
|
18
|
+
## Release Model
|
|
19
|
+
|
|
20
|
+
REST API apps use **continuous deployment** — every merge to `main` deploys. See the web app publishing rule for the full `deploy-web.yml` workflow template; the same workflow applies here. The only differences are the health endpoint requirements and Helm chart probe configuration.
|
|
21
|
+
|
|
22
|
+
## CI Workflow
|
|
23
|
+
|
|
24
|
+
Use the same `deploy-web.yml` workflow template as the web app publishing rule:
|
|
25
|
+
|
|
26
|
+
- Trigger on `push` to `main`.
|
|
27
|
+
- `concurrency: cancel-in-progress: true`.
|
|
28
|
+
- Build and push the Docker image tagged with `sha-<short-sha>` and `latest`.
|
|
29
|
+
- Update the Helm chart repo with the new image digest.
|
|
30
|
+
|
|
31
|
+
Name the workflow file `deploy-api.yml` (or keep `deploy-web.yml` if there is only one service).
|
|
32
|
+
|
|
33
|
+
## Health Endpoint Requirements
|
|
34
|
+
|
|
35
|
+
Before enabling Kubernetes probes, ensure the API exposes at least one health endpoint:
|
|
36
|
+
|
|
37
|
+
### Recommended Endpoints
|
|
38
|
+
|
|
39
|
+
| Path | Purpose | Behavior |
|
|
40
|
+
| ----------------------- | --------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
41
|
+
| `/health` or `/healthz` | Liveness — is the process alive? | Return `200 OK` if the process is up; return non-2xx only if the process is broken and should be restarted. |
|
|
42
|
+
| `/ready` or `/readyz` | Readiness — is the service ready for traffic? | Return `200 OK` only when all dependencies (DB, cache, downstream services) are reachable. Return `503` during startup or when a dependency is down. |
|
|
43
|
+
|
|
44
|
+
Separate liveness and readiness checks. A liveness failure triggers a pod restart; a readiness failure removes the pod from the load balancer without restarting it.
|
|
45
|
+
|
|
46
|
+
### Minimal Go Implementation
|
|
47
|
+
|
|
48
|
+
```go
|
|
49
|
+
http.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
|
|
50
|
+
w.WriteHeader(http.StatusOK)
|
|
51
|
+
_, _ = w.Write([]byte(`{"status":"ok"}`))
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
http.HandleFunc("/readyz", func(w http.ResponseWriter, r *http.Request) {
|
|
55
|
+
if err := db.PingContext(r.Context()); err != nil {
|
|
56
|
+
http.Error(w, `{"status":"not ready"}`, http.StatusServiceUnavailable)
|
|
57
|
+
return
|
|
58
|
+
}
|
|
59
|
+
w.WriteHeader(http.StatusOK)
|
|
60
|
+
_, _ = w.Write([]byte(`{"status":"ready"}`))
|
|
61
|
+
})
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Minimal Node/Express Implementation
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
app.get('/healthz', (_req, res) => {
|
|
68
|
+
res.json({ status: 'ok' });
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
app.get('/readyz', async (_req, res) => {
|
|
72
|
+
try {
|
|
73
|
+
await db.query('SELECT 1');
|
|
74
|
+
res.json({ status: 'ready' });
|
|
75
|
+
} catch {
|
|
76
|
+
res.status(503).json({ status: 'not ready' });
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Helm Chart: Probes Configuration
|
|
82
|
+
|
|
83
|
+
Add `livenessProbe` and `readinessProbe` to the deployment template in your Helm chart:
|
|
84
|
+
|
|
85
|
+
```yaml
|
|
86
|
+
# charts/<your-chart>/templates/deployment.yaml
|
|
87
|
+
containers:
|
|
88
|
+
- name: { { .Chart.Name } }
|
|
89
|
+
image: '{{ .Values.image.repository }}@{{ .Values.image.digest }}'
|
|
90
|
+
ports:
|
|
91
|
+
- name: http
|
|
92
|
+
containerPort: { { .Values.service.port } }
|
|
93
|
+
livenessProbe:
|
|
94
|
+
httpGet:
|
|
95
|
+
path: /healthz
|
|
96
|
+
port: http
|
|
97
|
+
initialDelaySeconds: 10
|
|
98
|
+
periodSeconds: 15
|
|
99
|
+
failureThreshold: 3
|
|
100
|
+
readinessProbe:
|
|
101
|
+
httpGet:
|
|
102
|
+
path: /readyz
|
|
103
|
+
port: http
|
|
104
|
+
initialDelaySeconds: 5
|
|
105
|
+
periodSeconds: 10
|
|
106
|
+
failureThreshold: 3
|
|
107
|
+
successThreshold: 1
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
And in `values.yaml`:
|
|
111
|
+
|
|
112
|
+
```yaml
|
|
113
|
+
image:
|
|
114
|
+
repository: ghcr.io/OWNER/IMAGE
|
|
115
|
+
tag: latest
|
|
116
|
+
digest: '' # filled in by the CD workflow
|
|
117
|
+
|
|
118
|
+
service:
|
|
119
|
+
port: 8080
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Private vs Public Image Registries
|
|
123
|
+
|
|
124
|
+
| Use case | Registry | Auth |
|
|
125
|
+
| ----------------------------- | ------------------------ | ------------------------------------------------ |
|
|
126
|
+
| Internal API, org-only access | GHCR (`ghcr.io`) | `GITHUB_TOKEN` |
|
|
127
|
+
| Public API, open source | Docker Hub (`docker.io`) | `DOCKERHUB_USERNAME` + `DOCKERHUB_TOKEN` secrets |
|
|
128
|
+
|
|
129
|
+
Grant `packages: write` to the build job for GHCR. Remove it for Docker Hub.
|
|
130
|
+
|
|
131
|
+
## Required Secrets and Permissions
|
|
132
|
+
|
|
133
|
+
| Secret | Required for |
|
|
134
|
+
| ----------------------- | ---------------------------- |
|
|
135
|
+
| `GITHUB_TOKEN` | GHCR push (automatic) |
|
|
136
|
+
| `DOCKERHUB_USERNAME` | Docker Hub push |
|
|
137
|
+
| `DOCKERHUB_TOKEN` | Docker Hub push |
|
|
138
|
+
| `HELM_CHART_REPO_TOKEN` | Helm chart repo write access |
|
|
139
|
+
|
|
140
|
+
## README Badge
|
|
141
|
+
|
|
142
|
+
```markdown
|
|
143
|
+
[](https://github.com/OWNER/REPO/actions/workflows/deploy-api.yml)
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Important Notes
|
|
147
|
+
|
|
148
|
+
- Liveness and readiness probes must be separate endpoints with different semantics. Do not reuse the same handler for both.
|
|
149
|
+
- Set `initialDelaySeconds` long enough that the API finishes startup before the first probe fires; misconfigured probes cause restart loops.
|
|
150
|
+
- Prefer `httpGet` probes over `exec` probes for HTTP services.
|
|
151
|
+
- The `readinessProbe` should check all critical dependencies; the `livenessProbe` should only check process health.
|
|
152
|
+
- Use `digest` not `tag` in the container spec so Kubernetes always pulls the exact image version, even if the `latest` tag is updated.
|
|
153
|
+
|
|
154
|
+
## When to Apply
|
|
155
|
+
|
|
156
|
+
- When a REST API service is deployed to Kubernetes via a Helm chart.
|
|
157
|
+
- When every merge to `main` should trigger a new deployment.
|
|
158
|
+
- When the API needs health and readiness probes for safe Kubernetes lifecycle management.
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
---
|
|
2
|
+
# Publishing Rules
|
|
3
|
+
|
|
4
|
+
These rules help design and maintain release workflows for libraries, SDKs, and apps.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Publishing Apps Agent
|
|
8
|
+
|
|
9
|
+
You are a publishing specialist for installable apps and CLIs.
|
|
10
|
+
|
|
11
|
+
## Goals
|
|
12
|
+
|
|
13
|
+
- Publish installable applications from validated release tags.
|
|
14
|
+
- Use the Ballast `publish.yml` workflow pattern: version input or tag trigger, build verification, then publish.
|
|
15
|
+
- Publish TypeScript apps to npmjs when they are distributed as Node packages, Python apps to PyPI when they are installed as Python packages, and Go apps to GitHub Releases.
|
|
16
|
+
|
|
17
|
+
## Release Workflow Pattern
|
|
18
|
+
|
|
19
|
+
Use a release workflow structure similar to Ballast `publish.yml`:
|
|
20
|
+
|
|
21
|
+
1. Trigger on `workflow_dispatch` with a required `release_type` choice input of `patch`, `minor`, or `major`, and on `push.tags`.
|
|
22
|
+
2. Add a `bump_and_tag` job that:
|
|
23
|
+
- fetches the previous tag with `WyriHaximus/github-action-get-previous-tag@v2`
|
|
24
|
+
- calculates the next patch, minor, and major versions with `WyriHaximus/github-action-next-semvers`
|
|
25
|
+
- selects the next version from the chosen `release_type`
|
|
26
|
+
- updates app version files
|
|
27
|
+
- commits the version bump
|
|
28
|
+
- creates and pushes `v<version>`
|
|
29
|
+
3. Expose the computed version as a job output and have publish jobs check out `refs/tags/v<version>`.
|
|
30
|
+
4. Add a `concurrency` block so two publishes for the same ref do not race; use `cancel-in-progress: false` so an in-flight publish is never cancelled mid-run:
|
|
31
|
+
```yaml
|
|
32
|
+
concurrency:
|
|
33
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
34
|
+
cancel-in-progress: false
|
|
35
|
+
```
|
|
36
|
+
5. Check out the release tag, not the branch head.
|
|
37
|
+
6. Run build verification before publish.
|
|
38
|
+
7. Publish per language or distribution target in separate jobs.
|
|
39
|
+
8. Use only the permissions required by each job.
|
|
40
|
+
|
|
41
|
+
### Version and Tag Rules
|
|
42
|
+
|
|
43
|
+
- Use semantic versioning for the app release version.
|
|
44
|
+
- Use `WyriHaximus/github-action-get-previous-tag@v2` to read the current release tag.
|
|
45
|
+
- Use `WyriHaximus/github-action-next-semvers` to compute the next patch, minor, and major versions.
|
|
46
|
+
- Use `v`-prefixed tags such as `v1.8.0`.
|
|
47
|
+
- The built artifact version must match the created tag.
|
|
48
|
+
- The publish jobs must run against the tag created by the bump job, not directly against the branch commit.
|
|
49
|
+
|
|
50
|
+
## TypeScript Apps: npmjs
|
|
51
|
+
|
|
52
|
+
For TypeScript or Node CLI apps distributed through npmjs:
|
|
53
|
+
|
|
54
|
+
- Ensure `package.json` has:
|
|
55
|
+
- `bin` entries for executables
|
|
56
|
+
- `files` or package contents narrowed to runtime assets
|
|
57
|
+
- `engines` if runtime support matters
|
|
58
|
+
- Release job guidance:
|
|
59
|
+
- `actions/setup-node`
|
|
60
|
+
- install with lockfile
|
|
61
|
+
- build the app
|
|
62
|
+
- run tests
|
|
63
|
+
- publish with `npm publish --access public --provenance`
|
|
64
|
+
- Prefer npm trusted publishing with `id-token: write`.
|
|
65
|
+
- Verify the packaged CLI starts successfully from the built artifact before publishing.
|
|
66
|
+
|
|
67
|
+
## Web Apps: Docker Image + Separate Helm Chart Repo
|
|
68
|
+
|
|
69
|
+
For web apps deployed to Kubernetes, prefer a two-repository release model:
|
|
70
|
+
|
|
71
|
+
- Application repository:
|
|
72
|
+
- build and publish the Docker image
|
|
73
|
+
- produce immutable image references
|
|
74
|
+
- do not keep deployment-environment chart changes in the same release commit by default
|
|
75
|
+
- Helm chart repository:
|
|
76
|
+
- owns the chart, values, and deployment metadata
|
|
77
|
+
- is updated after the application image is published
|
|
78
|
+
- records the new image tag or digest in a chart release commit
|
|
79
|
+
|
|
80
|
+
### Container Registry Targets
|
|
81
|
+
|
|
82
|
+
Support either of these release targets:
|
|
83
|
+
|
|
84
|
+
- **GHCR** (`ghcr.io/<owner>/<image>`)
|
|
85
|
+
- **Docker Hub** (`docker.io/<namespace>/<image>` or `<namespace>/<image>`)
|
|
86
|
+
|
|
87
|
+
When building the workflow:
|
|
88
|
+
|
|
89
|
+
- trigger on version tags or `workflow_dispatch`
|
|
90
|
+
- check out the tagged ref
|
|
91
|
+
- build the production image from the app Dockerfile
|
|
92
|
+
- tag the image with:
|
|
93
|
+
- the app version from the created `v<version>` tag
|
|
94
|
+
- the git SHA
|
|
95
|
+
- optionally `latest` only when the team explicitly wants mutable tags
|
|
96
|
+
- push the image only after tests and build verification pass
|
|
97
|
+
- prefer immutable deploy references using the published digest
|
|
98
|
+
|
|
99
|
+
### GitHub Actions Guidance for Web App Container Publishing
|
|
100
|
+
|
|
101
|
+
- Use `docker/setup-buildx-action` for reproducible multi-platform builds when needed.
|
|
102
|
+
- Use `docker/login-action` with the correct registry:
|
|
103
|
+
- GHCR: authenticate with `GITHUB_TOKEN` or a scoped token and grant `packages: write`
|
|
104
|
+
- Docker Hub: authenticate with repository secrets such as `DOCKERHUB_USERNAME` and `DOCKERHUB_TOKEN`
|
|
105
|
+
- Use `docker/build-push-action` to:
|
|
106
|
+
- build the image
|
|
107
|
+
- push tags
|
|
108
|
+
- emit the image digest
|
|
109
|
+
- Keep the publish job permissions minimal:
|
|
110
|
+
- GHCR: `contents: read`, `packages: write`
|
|
111
|
+
- Docker Hub: `contents: read` plus Docker Hub credentials from secrets
|
|
112
|
+
|
|
113
|
+
### Separate Helm Chart Repository
|
|
114
|
+
|
|
115
|
+
After the image publish succeeds, update a separate Helm chart repository rather than mixing chart release state into the app repository.
|
|
116
|
+
|
|
117
|
+
The Helm chart repo should contain:
|
|
118
|
+
|
|
119
|
+
- one chart per deployable app
|
|
120
|
+
- `values.yaml` defaults
|
|
121
|
+
- environment-specific values files only when the team intentionally stores them there
|
|
122
|
+
- image repository and tag or digest fields that can be updated automatically
|
|
123
|
+
|
|
124
|
+
Preferred automation flow:
|
|
125
|
+
|
|
126
|
+
1. Publish the app image to GHCR or Docker Hub.
|
|
127
|
+
2. Capture the pushed tag and digest.
|
|
128
|
+
3. Check out the separate Helm chart repository in a later job or workflow.
|
|
129
|
+
4. Update chart values to the new image tag or digest.
|
|
130
|
+
5. Bump the chart version when chart contents changed.
|
|
131
|
+
6. Commit and push the chart update.
|
|
132
|
+
7. Optionally package and publish the chart from the chart repo if the team uses an OCI chart registry or GitHub Pages chart index.
|
|
133
|
+
|
|
134
|
+
### Helm Update Rules
|
|
135
|
+
|
|
136
|
+
- Prefer digest pinning for production deployments when the platform supports it.
|
|
137
|
+
- Keep the application version and chart version distinct:
|
|
138
|
+
- app version tracks the shipped container
|
|
139
|
+
- chart version tracks deployment-manifest changes
|
|
140
|
+
- Do not overwrite unrelated chart values during automation.
|
|
141
|
+
- If multiple environments exist, make the target environment or values file explicit in the workflow inputs.
|
|
142
|
+
- If the chart repo is private, use a dedicated token scoped to that repository only.
|
|
143
|
+
|
|
144
|
+
### What to Generate
|
|
145
|
+
|
|
146
|
+
For a web app release workflow, generate:
|
|
147
|
+
|
|
148
|
+
- a Docker image publish job for GHCR or Docker Hub
|
|
149
|
+
- outputs exposing the published image tag and digest
|
|
150
|
+
- a follow-up job or reusable workflow that updates the separate Helm chart repo
|
|
151
|
+
- clear secrets and permissions documentation in the workflow comments or README
|
|
152
|
+
|
|
153
|
+
### What Not to Do
|
|
154
|
+
|
|
155
|
+
- Do not deploy mutable `latest` tags by default.
|
|
156
|
+
- Do not hardcode long-lived registry passwords in workflow files.
|
|
157
|
+
- Do not keep the chart repo update step hidden inside a shell script with no visible diff.
|
|
158
|
+
- Do not update a Helm chart in-place in the app repo when the team has chosen a separate chart repository model.
|
|
159
|
+
|
|
160
|
+
## Python Apps: PyPI
|
|
161
|
+
|
|
162
|
+
For Python apps or CLIs distributed through PyPI:
|
|
163
|
+
|
|
164
|
+
- Package the app with console entry points in `pyproject.toml`.
|
|
165
|
+
- Build wheel and sdist.
|
|
166
|
+
- Prefer PyPI trusted publishing via GitHub Actions.
|
|
167
|
+
- Release job guidance:
|
|
168
|
+
- `actions/setup-python`
|
|
169
|
+
- `astral-sh/setup-uv` when relevant
|
|
170
|
+
- build artifacts
|
|
171
|
+
- run tests
|
|
172
|
+
- optionally smoke-test the built wheel
|
|
173
|
+
- publish to PyPI with `uv publish` or `pypa/gh-action-pypi-publish`
|
|
174
|
+
- Grant `id-token: write` only to the publish job.
|
|
175
|
+
|
|
176
|
+
## Go Apps: GitHub Releases
|
|
177
|
+
|
|
178
|
+
For Go apps and CLIs:
|
|
179
|
+
|
|
180
|
+
- Publish binaries and archives to GitHub Releases.
|
|
181
|
+
- Prefer GoReleaser when the project ships binaries for multiple OS or architecture targets.
|
|
182
|
+
- Release job guidance:
|
|
183
|
+
- `actions/setup-go`
|
|
184
|
+
- full-history checkout
|
|
185
|
+
- verify the binary builds before release
|
|
186
|
+
- run GoReleaser or equivalent packaging
|
|
187
|
+
- upload archives, checksums, and release notes to GitHub
|
|
188
|
+
- If the app also feeds a Homebrew tap or other package index, generate that metadata from the same tagged release.
|
|
189
|
+
|
|
190
|
+
## App-Specific Release Requirements
|
|
191
|
+
|
|
192
|
+
- Smoke-test the installed app or CLI from the built artifact before publish.
|
|
193
|
+
- Keep installation instructions in `README.md` aligned with the actual release channel.
|
|
194
|
+
- Publish checksums for downloadable binaries when the app ships archives.
|
|
195
|
+
- Ensure version output from the binary or CLI matches the release tag.
|
|
196
|
+
- For web apps, keep the published container image and Helm chart update linked by version or digest in release notes or workflow outputs.
|
|
197
|
+
- The workflow-dispatch `release_type` input should decide whether the next app tag is patch, minor, or major.
|
|
198
|
+
|
|
199
|
+
## When to Apply
|
|
200
|
+
|
|
201
|
+
- When a repository publishes a CLI, desktop helper, or installable service package.
|
|
202
|
+
- When the release artifact is intended for direct installation by end users.
|
|
203
|
+
- When a project needs app-focused packaging guidance instead of library-only rules.
|
|
204
|
+
- When a web app is released as a container image and deployed through a separate Helm chart repository.
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
---
|
|
2
|
+
# Publishing Rules
|
|
3
|
+
|
|
4
|
+
These rules help design and maintain release workflows for libraries, SDKs, and apps.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# APT/Deb Package Publishing Agent
|
|
8
|
+
|
|
9
|
+
You are a publishing specialist for Debian package (`.deb`) distribution of CLI tools.
|
|
10
|
+
|
|
11
|
+
## Goals
|
|
12
|
+
|
|
13
|
+
- Produce `.deb` packages alongside binary archives using GoReleaser `nfpms`.
|
|
14
|
+
- Attach `.deb` files to GitHub Releases so users can download and install them directly.
|
|
15
|
+
- Optionally publish to a lightweight APT repository hosted on GitHub Pages.
|
|
16
|
+
|
|
17
|
+
## Prerequisites
|
|
18
|
+
|
|
19
|
+
This rule extends the Go CLI publishing rule. Complete the CLI publishing setup (`.goreleaser.yaml` with `builds` and `archives`) before adding `.deb` packaging.
|
|
20
|
+
|
|
21
|
+
## GoReleaser `nfpms` Block
|
|
22
|
+
|
|
23
|
+
Add an `nfpms` section to your `.goreleaser.yaml`:
|
|
24
|
+
|
|
25
|
+
```yaml
|
|
26
|
+
nfpms:
|
|
27
|
+
- id: <your-cli-name>-deb
|
|
28
|
+
package_name: <your-cli-name>
|
|
29
|
+
file_name_template: '{{ .PackageName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}'
|
|
30
|
+
builds:
|
|
31
|
+
- <your-cli-name> # must match the build id in your builds section
|
|
32
|
+
vendor: Your Name or Organization
|
|
33
|
+
homepage: https://github.com/OWNER/REPO
|
|
34
|
+
maintainer: 'Your Name <you@example.com>'
|
|
35
|
+
description: One-line description of what the CLI does.
|
|
36
|
+
license: MIT
|
|
37
|
+
formats:
|
|
38
|
+
- deb
|
|
39
|
+
contents:
|
|
40
|
+
- src: ./completions/<your-cli-name>.bash
|
|
41
|
+
dst: /usr/share/bash-completion/completions/<your-cli-name>
|
|
42
|
+
file_info:
|
|
43
|
+
mode: 0644
|
|
44
|
+
type: config|noreplace
|
|
45
|
+
# Remove the contents block if you have no shell completions or extra files.
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Replace `OWNER`, `REPO`, `<your-cli-name>`, and maintainer details with real values.
|
|
49
|
+
|
|
50
|
+
### Minimal Config (no extras)
|
|
51
|
+
|
|
52
|
+
If the CLI has no shell completions or extra data files, use:
|
|
53
|
+
|
|
54
|
+
```yaml
|
|
55
|
+
nfpms:
|
|
56
|
+
- id: <your-cli-name>-deb
|
|
57
|
+
package_name: <your-cli-name>
|
|
58
|
+
builds:
|
|
59
|
+
- <your-cli-name>
|
|
60
|
+
vendor: Your Name or Organization
|
|
61
|
+
homepage: https://github.com/OWNER/REPO
|
|
62
|
+
maintainer: 'Your Name <you@example.com>'
|
|
63
|
+
description: One-line description of what the CLI does.
|
|
64
|
+
license: MIT
|
|
65
|
+
formats:
|
|
66
|
+
- deb
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## GitHub Releases (Simple Path)
|
|
70
|
+
|
|
71
|
+
By default, GoReleaser attaches `.deb` files to the GitHub Release alongside the binary archives. No additional configuration is needed beyond the `nfpms` block — users download and install with:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
wget https://github.com/OWNER/REPO/releases/download/v<version>/<your-cli-name>_<version>_linux_amd64.deb
|
|
75
|
+
sudo dpkg -i <your-cli-name>_<version>_linux_amd64.deb
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Document this install path in `README.md` under an `## Installation` section.
|
|
79
|
+
|
|
80
|
+
## README Install Path
|
|
81
|
+
|
|
82
|
+
Add a `.deb` installation section to `README.md`:
|
|
83
|
+
|
|
84
|
+
```markdown
|
|
85
|
+
## Installation
|
|
86
|
+
|
|
87
|
+
### Debian / Ubuntu (via .deb package)
|
|
88
|
+
|
|
89
|
+
Download the latest `.deb` from [GitHub Releases](https://github.com/OWNER/REPO/releases) and install:
|
|
90
|
+
|
|
91
|
+
\`\`\`bash
|
|
92
|
+
wget https://github.com/OWNER/REPO/releases/latest/download/<your-cli-name>\_linux_amd64.deb
|
|
93
|
+
sudo dpkg -i <your-cli-name>\_linux_amd64.deb
|
|
94
|
+
\`\`\`
|
|
95
|
+
|
|
96
|
+
### Direct binary download
|
|
97
|
+
|
|
98
|
+
Download the binary archive from [GitHub Releases](https://github.com/OWNER/REPO/releases) and extract to your PATH.
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Optional: GitHub Pages APT Repository
|
|
102
|
+
|
|
103
|
+
For projects that want `apt install` support (requires `apt-get update` to see new releases), you can host a lightweight APT repo on GitHub Pages using `reprepro` or `apt-tools`.
|
|
104
|
+
|
|
105
|
+
### Overview
|
|
106
|
+
|
|
107
|
+
1. Create a `gh-pages` branch (or use a separate `apt-repo` repository) to host the APT index files.
|
|
108
|
+
2. After GoReleaser publishes the release, a follow-up workflow step downloads the `.deb`, adds it to the APT index, and pushes the updated index to GitHub Pages.
|
|
109
|
+
3. Users add your repo: `echo "deb [trusted=yes] https://OWNER.github.io/REPO stable main" | sudo tee /etc/apt/sources.list.d/<your-cli-name>.list`
|
|
110
|
+
|
|
111
|
+
### APT Workflow Step (after GoReleaser)
|
|
112
|
+
|
|
113
|
+
```yaml
|
|
114
|
+
- name: Update APT repository
|
|
115
|
+
run: |
|
|
116
|
+
mkdir -p apt-repo/pool/main
|
|
117
|
+
cp dist/*.deb apt-repo/pool/main/
|
|
118
|
+
cd apt-repo
|
|
119
|
+
dpkg-scanpackages pool/main /dev/null | gzip -9c > Packages.gz
|
|
120
|
+
dpkg-scanpackages pool/main /dev/null > Packages
|
|
121
|
+
# Push apt-repo/ to gh-pages branch or a dedicated apt-repo
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
This is a simplified example — for production APT repos with GPG signing use `reprepro` or Cloudsmith.
|
|
125
|
+
|
|
126
|
+
### GPG Signing (Recommended for Production)
|
|
127
|
+
|
|
128
|
+
Sign the Release file with a GPG key so `apt-get` can verify authenticity:
|
|
129
|
+
|
|
130
|
+
1. Generate a signing keypair: `gpg --batch --gen-key gpg-params.txt`
|
|
131
|
+
2. Export the public key and host it at a stable URL (e.g. GitHub Pages).
|
|
132
|
+
3. Add `APT_SIGNING_KEY` as a repository secret.
|
|
133
|
+
4. Tell users to run: `curl -fsSL https://OWNER.github.io/REPO/KEY.gpg | sudo gpg --dearmor -o /usr/share/keyrings/<your-cli-name>-keyring.gpg`
|
|
134
|
+
|
|
135
|
+
## Important Notes
|
|
136
|
+
|
|
137
|
+
- Only `linux/amd64` and `linux/arm64` targets are relevant for `.deb` packaging; exclude `darwin` and `windows` builds from the `nfpms` `builds` list if needed.
|
|
138
|
+
- The `nfpms` `file_name_template` must produce unique filenames per architecture to avoid collisions in the GitHub Release assets.
|
|
139
|
+
- The GitHub Releases path requires no extra infrastructure and is the recommended starting point.
|
|
140
|
+
- Move to a hosted APT repo only when users request `apt install` support.
|
|
141
|
+
|
|
142
|
+
## When to Apply
|
|
143
|
+
|
|
144
|
+
- When the project ships a Go CLI and users on Debian/Ubuntu are a primary audience.
|
|
145
|
+
- When GoReleaser is already configured for binary releases.
|
|
146
|
+
- When the project wants to make installation easier for Linux users who prefer system package managers.
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
---
|
|
2
|
+
# Publishing Rules
|
|
3
|
+
|
|
4
|
+
These rules help design and maintain release workflows for libraries, SDKs, and apps.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Homebrew Tap Publishing Agent
|
|
8
|
+
|
|
9
|
+
You are a publishing specialist for Homebrew tap distribution of CLI tools.
|
|
10
|
+
|
|
11
|
+
## Goals
|
|
12
|
+
|
|
13
|
+
- Automatically write a Homebrew formula to a `homebrew-<project>` tap repo after each GitHub Release.
|
|
14
|
+
- Keep the Homebrew tap in sync with the GoReleaser publish workflow so users can install the CLI with `brew install`.
|
|
15
|
+
- Use the same bump-and-tag pattern as the CLI publish workflow as the trigger.
|
|
16
|
+
|
|
17
|
+
## Prerequisites
|
|
18
|
+
|
|
19
|
+
This rule extends the Go CLI publishing rule. Complete the CLI publishing setup (`.goreleaser.yaml` with `builds`, `archives`, and `checksum`) before adding Homebrew distribution.
|
|
20
|
+
|
|
21
|
+
## Setting Up the Tap Repository
|
|
22
|
+
|
|
23
|
+
1. Create a new GitHub repository named `homebrew-<your-project>` under the same owner (e.g. `acme/homebrew-mycli`).
|
|
24
|
+
2. Create a `Formula/` directory in the tap repo with a placeholder `.gitkeep` file (GoReleaser will create the real formula).
|
|
25
|
+
3. Add repository description: "Homebrew tap for <your-project>".
|
|
26
|
+
4. Keep the tap repo public so users can add it with `brew tap`.
|
|
27
|
+
|
|
28
|
+
## GoReleaser `brews` Block
|
|
29
|
+
|
|
30
|
+
Add a `brews` section to your `.goreleaser.yaml`:
|
|
31
|
+
|
|
32
|
+
```yaml
|
|
33
|
+
brews:
|
|
34
|
+
- name: <your-cli-name>
|
|
35
|
+
ids:
|
|
36
|
+
- <your-cli-name> # must match the archive id in your archives section
|
|
37
|
+
homepage: https://github.com/OWNER/REPO
|
|
38
|
+
description: One-line description of what the CLI does.
|
|
39
|
+
license: MIT
|
|
40
|
+
repository:
|
|
41
|
+
owner: OWNER
|
|
42
|
+
name: homebrew-<your-cli-name>
|
|
43
|
+
branch: main
|
|
44
|
+
token: '{{ .Env.HOMEBREW_TAP_GITHUB_TOKEN }}'
|
|
45
|
+
commit_author:
|
|
46
|
+
name: github-actions[bot]
|
|
47
|
+
email: github-actions[bot]@users.noreply.github.com
|
|
48
|
+
directory: Formula
|
|
49
|
+
test: |
|
|
50
|
+
system "#{bin}/<your-cli-name>", "--help"
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Replace `OWNER`, `REPO`, and `<your-cli-name>` with real values.
|
|
54
|
+
|
|
55
|
+
### Required `test` Stanza
|
|
56
|
+
|
|
57
|
+
Always include a `test` stanza that runs the binary with a flag that exits 0 (e.g. `--help` or `--version`). Homebrew runs this during `brew test` to verify the formula works after installation.
|
|
58
|
+
|
|
59
|
+
### Formula `install` Method
|
|
60
|
+
|
|
61
|
+
GoReleaser generates the `install` method automatically from the archive contents. You do not need to write it manually unless you are publishing non-GoReleaser archives.
|
|
62
|
+
|
|
63
|
+
## Secret: `HOMEBREW_TAP_GITHUB_TOKEN`
|
|
64
|
+
|
|
65
|
+
GoReleaser needs write access to the tap repo to commit the formula. Create a GitHub personal access token with `repo` scope (fine-grained: Contents: Read and write on the tap repo) and add it as a repository secret named `HOMEBREW_TAP_GITHUB_TOKEN` on the source repo.
|
|
66
|
+
|
|
67
|
+
Add to your publish workflow env:
|
|
68
|
+
|
|
69
|
+
```yaml
|
|
70
|
+
env:
|
|
71
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
72
|
+
HOMEBREW_TAP_GITHUB_TOKEN: ${{ secrets.HOMEBREW_TAP_GITHUB_TOKEN }}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## CI Workflow Addition
|
|
76
|
+
|
|
77
|
+
In your `publish-cli.yml` GoReleaser step, ensure `HOMEBREW_TAP_GITHUB_TOKEN` is passed. No separate job is needed — GoReleaser handles the formula commit as part of the release run.
|
|
78
|
+
|
|
79
|
+
## Telling Users About the Tap
|
|
80
|
+
|
|
81
|
+
Add an installation section to `README.md`:
|
|
82
|
+
|
|
83
|
+
```markdown
|
|
84
|
+
## Installation
|
|
85
|
+
|
|
86
|
+
### Homebrew (macOS and Linux)
|
|
87
|
+
|
|
88
|
+
\`\`\`bash
|
|
89
|
+
brew tap OWNER/homebrew-<your-cli-name>
|
|
90
|
+
brew install <your-cli-name>
|
|
91
|
+
\`\`\`
|
|
92
|
+
|
|
93
|
+
### Direct download
|
|
94
|
+
|
|
95
|
+
Download the latest binary from [GitHub Releases](https://github.com/OWNER/REPO/releases).
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Important Notes
|
|
99
|
+
|
|
100
|
+
- The tap repo must be public for `brew tap` to work without authentication.
|
|
101
|
+
- The formula commit happens during the GoReleaser release step — if GoReleaser fails the formula is not updated.
|
|
102
|
+
- Keep the `homebrew-` prefix on the tap repo name; Homebrew convention requires it so `brew tap OWNER/<cli-name>` resolves to `OWNER/homebrew-<cli-name>`.
|
|
103
|
+
- Only include binary archives in the `ids` list for the `brews` block; exclude `.deb` or other non-archive artifacts.
|
|
104
|
+
- GoReleaser will fail if the tap token is missing or lacks write access to the tap repo.
|
|
105
|
+
|
|
106
|
+
## When to Apply
|
|
107
|
+
|
|
108
|
+
- When the project ships a Go CLI and wants `brew install` support.
|
|
109
|
+
- When GoReleaser is already configured for binary releases.
|
|
110
|
+
- When the maintainer controls an organization or user account on GitHub where the tap repo can live.
|