Skip to content
Merged
Show file tree
Hide file tree
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
24 changes: 21 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: CI

on:
push:
branches: [main]
branches: [main, development]
pull_request:

permissions:
Expand All @@ -15,9 +15,11 @@ jobs:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
# The newest Go release, so that binaries get its security fixes.
- uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version-file: go.mod
go-version: stable
check-latest: true
- run: make vet test race

# Builds every release target with the release config, without signing.
Expand All @@ -27,10 +29,26 @@ jobs:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
# The newest Go release, so that binaries get its security fixes.
- uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version-file: go.mod
go-version: stable
check-latest: true
# GoReleaser makes each binary's SBOM with Syft.
- uses: anchore/sbom-action/download-syft@66cbf4bc1f1c0d2edc94016e65bc221b6bb0ad6c # v0.24.3
with:
syft-version: v1.54.1
- uses: goreleaser/goreleaser-action@f06c13b6b1a9625abc9e6e439d9c05a8f2190e94 # v7.2.3
with:
version: v2.18.3
args: release --snapshot --clean

# Fails when the code calls a function with a known vulnerability, in a
# module or in the Go standard library.
vulncheck:
runs-on: ubuntu-latest
steps:
- uses: golang/govulncheck-action@032d45514ae346b1db93c04b0c90b841c370344f # v1.1.0
with:
go-version-input: stable
check-latest: true
20 changes: 19 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ jobs:
runs-on: ubuntu-latest
permissions:
contents: write
id-token: write # to sign the attestation with Sigstore
attestations: write # to store the attestation with GitHub
steps:
# GoReleaser skips signing when MACOS_SIGN_P12 is empty, and a missing
# secret arrives as an empty string. Fail rather than publish unsigned
Expand Down Expand Up @@ -54,12 +56,18 @@ jobs:
with:
fetch-depth: 0 # the changelog needs the previous tag
persist-credentials: false
# The newest Go release, so that binaries get its security fixes.
- uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version-file: go.mod
go-version: stable
check-latest: true
cache: false # build release binaries from a clean module cache
- run: make vet test race

# GoReleaser makes each binary's SBOM with Syft.
- uses: anchore/sbom-action/download-syft@66cbf4bc1f1c0d2edc94016e65bc221b6bb0ad6c # v0.24.3
with:
syft-version: v1.54.1
- uses: goreleaser/goreleaser-action@f06c13b6b1a9625abc9e6e439d9c05a8f2190e94 # v7.2.3
with:
version: v2.18.3
Expand All @@ -72,3 +80,13 @@ jobs:
MACOS_NOTARY_KEY_ID: ${{ secrets.MACOS_NOTARY_KEY_ID }}
MACOS_NOTARY_KEY: ${{ secrets.MACOS_NOTARY_KEY }}
HOMEBREW_TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }}

# Sign a SLSA build provenance attestation for every file in
# SHA256SUMS, each binary and its SBOM: it records that this workflow
# built the file from the tagged commit. "gh attestation verify"
# checks it; see the README. The release is already published by now,
# so if this step fails, delete the GitHub release (not the tag) and
# re-run the workflow.
- uses: actions/attest@1e69f48acb82d1966a394da916b4c1698aa569d6 # v4.2.2
with:
subject-checksums: dist/SHA256SUMS
11 changes: 11 additions & 0 deletions .goreleaser.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,17 @@ archives:
- formats: [binary]
name_template: "{{ .ProjectName }}-{{ .Os }}-{{ .Arch }}"

# A CycloneDX SBOM for each binary, named like it: rypt-linux-amd64.cdx.json
# and so on. Syft reads the Go version and the modules from the binary's
# build information, so the SBOM lists exactly what was compiled in.
sboms:
- id: binaries
artifacts: binary
cmd: syft
args: ["$artifact", "--output", "cyclonedx-json=$document", "--quiet"]
documents:
- "{{ .ProjectName }}-{{ .Os }}-{{ .Arch }}.cdx.json"

checksum:
name_template: SHA256SUMS
algorithm: sha256
Expand Down
26 changes: 26 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Contributing

## Branches

- `main` is what has been released. It changes only through pull requests,
and every release is a tag on it.
- `development` collects the work for the next release.
- Work happens on a short-lived branch from `development`, named for what it
does: `feature/...`, `fix/...`, `docs/...`, `ci/...`. Open a pull request
into `development`, and delete the branch once it is merged.
- A release is a pull request from `development` into `main`, followed by a
tag on `main`; see [docs/releasing.md](docs/releasing.md). If `main` then
has commits `development` lacks, merge `main` back into `development`.
- An urgent fix branches from `main` as `hotfix/...`, merges into `main` and
is released as a patch, and is then merged into `development`.

Both `main` and `development` are protected: a change needs a pull request
whose `test` and `build` checks pass, and nobody, admins included, can push
to them directly or force-push.

Keep commits focused, with messages in the imperative. Rebase a branch onto
`development` rather than merging `development` into it.

## Tests

See [Development](README.md#development) in the README.
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,24 @@ that `go install` records. To check a release or `make dist` output, run
(`shasum -a 256 -c --ignore-missing SHA256SUMS` on macOS) in the directory
that holds it.

### Verify a release

From v0.2.0 on, every release file has a signed build provenance
attestation, which shows that this repository's release workflow built it
from the tagged commit. A checksum only shows that a file matches
`SHA256SUMS`, which is published beside it; the attestation shows where both
came from. With the [GitHub CLI](https://cli.github.com):

```sh
gh attestation verify rypt-linux-amd64 \
--repo x6c-co/rypt-cli \
--signer-workflow x6c-co/rypt-cli/.github/workflows/release.yml
```

The same command checks the `rypt` that Homebrew installed: give it
`"$(command -v rypt)"` instead of a file name. Each binary's SBOM
(`rypt-linux-amd64.cdx.json` and so on) is attested the same way.

## Usage

```
Expand Down
27 changes: 21 additions & 6 deletions docs/releasing.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# Releasing rypt

A release is a pull request from `development` into `main` (see
[CONTRIBUTING.md](../CONTRIBUTING.md)). Once it is merged, tag the merge
commit on `main`:

```sh
git switch main && git pull
git tag -a v1.0.0 -m v1.0.0
git push origin v1.0.0
```

Pushing a tag that starts with `v` runs
[.github/workflows/release.yml](../.github/workflows/release.yml). It runs
`make vet test race`, builds the six binaries with GoReleaser
Expand All @@ -8,20 +18,25 @@ binaries, publishes a GitHub release with the binaries, `SHA256SUMS`, and a
changelog of the commits since the previous tag, and then updates the
Homebrew cask in [x6c-co/homebrew-tap](https://github.com/x6c-co/homebrew-tap).

```sh
git tag -a v1.0.0 -m v1.0.0
git push origin v1.0.0
```

A tag with a pre-release suffix, such as `v1.1.0-rc.1`, is published as a
pre-release and leaves the Homebrew cask alone. `rypt version` prints the tag.

The release binaries are named and checksummed the same way as `make dist`
output: `rypt-linux-amd64`, `rypt-darwin-arm64`, `rypt-windows-amd64.exe`, and
so on.

Each binary has a CycloneDX SBOM beside it, named like it
(`rypt-linux-amd64.cdx.json`), which GoReleaser makes with Syft from the
binary's own build information: the Go version and every module compiled in.
The SBOMs are listed in `SHA256SUMS` too, and the workflow's last step signs a
build provenance attestation for every file in `SHA256SUMS` with
[actions/attest](https://github.com/actions/attest); the README shows how to
verify one. The release workflow builds with
the newest Go release, whatever go.mod's `go` line says, so that binaries
get the standard library's security fixes.

[.github/workflows/ci.yml](../.github/workflows/ci.yml) runs the same tests on
every push to `main` and every pull request, and builds all six targets with
every push to `main` and `development` and every pull request, and builds all six targets with
the release configuration, unsigned.

To build locally with the release configuration, unsigned, install
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/x6c-co/rypt-cli

go 1.27.0
go 1.27.2

require (
github.com/x6c-co/rypt v0.1.0
Expand Down
Loading