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
17 changes: 8 additions & 9 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,7 @@ jobs:
PINS
- uses: actions/setup-go@v7
with:
go-version: "1.26"
check-latest: true
go-version-file: .go-version
cache-dependency-path: src/go.sum
# The module lives in src/: a bare `go test ./...` at the root finds no
# packages and passes, so every command names it.
Expand All @@ -64,7 +63,7 @@ jobs:
- uses: actions/checkout@v7
- uses: actions/setup-go@v7
with:
go-version: "1.26"
go-version-file: .go-version
cache-dependency-path: src/go.sum
- uses: golangci/golangci-lint-action@ba0d7d2ec06a0ea1cb5fa41b2e4a3ab91d21278a # v9.3.0
with:
Expand All @@ -77,7 +76,7 @@ jobs:
- uses: actions/checkout@v7
- uses: actions/setup-go@v7
with:
go-version: "1.26"
go-version-file: .go-version
cache-dependency-path: src/go.sum
# Pinned: govulncheck @latest (v1.4.0) segfaulted under the go1.25 toolchain.
- run: go install golang.org/x/vuln/cmd/govulncheck@v1.1.4
Expand All @@ -90,7 +89,7 @@ jobs:
- uses: actions/checkout@v7
- uses: actions/setup-go@v7
with:
go-version: "1.26"
go-version-file: .go-version
cache-dependency-path: src/go.sum
# Build gosec with the runner's Go — the gosec docker action bundles an
# older Go that cannot load a `go 1.24` module.
Expand All @@ -100,7 +99,7 @@ jobs:
# (G702/G703) stay on, with #nosec on the few benign sites.
# Pinned, not @latest, so a new gosec cannot fail a pull request that
# changed nothing it checks. v2.30.0 needs go >= 1.26, which this job now
# runs; move the pin with the go-version.
# runs; move the pin with .go-version.
- run: go install github.com/securego/gosec/v2/cmd/gosec@v2.30.0
- run: |
cd src && "$(go env GOPATH)/bin/gosec" -exclude=G104,G204,G301,G302,G304,G306 ./...
Expand All @@ -111,7 +110,7 @@ jobs:
- uses: actions/checkout@v7
- uses: actions/setup-go@v7
with:
go-version: "1.26"
go-version-file: .go-version
cache-dependency-path: src/go.sum
# Run gitleaks directly (the gitleaks-action requires a paid license for
# org repos). Scans the working tree and honors .gitleaks.toml. Built from
Expand All @@ -130,7 +129,7 @@ jobs:
- uses: actions/checkout@v7
- uses: actions/setup-go@v7
with:
go-version: "1.26"
go-version-file: .go-version
cache-dependency-path: src/go.sum
# go -C src starts probaci inside src/; it walks up to the git root and
# proves the whole repository, as it did from the root.
Expand Down Expand Up @@ -158,7 +157,7 @@ jobs:
fetch-depth: 0
- uses: actions/setup-go@v7
with:
go-version: "1.26"
go-version-file: .go-version
cache-dependency-path: src/go.sum
- run: make cross
- run: cd build && sha256sum -c checksums.txt
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:
- uses: actions/checkout@v7
- uses: actions/setup-go@v7
with:
go-version: "1.26"
go-version-file: .go-version
cache-dependency-path: src/go.sum
# The module lives in src/, which autobuild does not look for: manual mode
# builds it explicitly, so the scan never reports clean on zero code.
Expand Down
135 changes: 32 additions & 103 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,116 +1,45 @@
name: release

# Every release is tag-driven: GoReleaser cross-compiles the full matrix, builds
# the archives/packages and a reproducible source tarball, generates SBOMs and
# checksums, cosign-signs the checksums, creates the
# GitHub Release (body from the tagged CHANGELOG section), and updates the
# Homebrew/Scoop manifests. A build-provenance attestation is then signed for
# every artifact via the checksums file.
#
# Go releases are git only until the owner says otherwise for this project, so
# nothing here starts on a tag push: pushing a vX.Y.Z tag records a version and
# publishes nothing. A release is dispatched by hand against the tag, with an
# explicit confirmation, and refuses to run without both:
# Release: build probaci's binaries and publish them on its GitHub release, and nowhere else (no
# `go install` path, Homebrew, Scoop, Winget, image or any other channel; owner, 2026-11-13).
# The pipeline is simtabi/.github's shared go-release.yml; this file only says when.
#
# gh workflow run release.yml --repo simtabi/probaci --ref vX.Y.Z -f confirm=release-go
# git push origin vX.Y.Z builds and publishes vX.Y.Z
# gh workflow run release.yml --repo simtabi/probaci -f tag=vX.Y.Z the same, for a tag already pushed
#
# It stays tag-driven: GoReleaser reads the version from the ref it is run
# against, and the first step refuses a ref that is not a v[0-9]* tag.
# Push a version tag only for final code: tags are final, and a pushed tag publishes. The shared
# workflow reads src/.goreleaser.yaml (dist: .cache/goreleaser, cosign-signed checksums), takes the
# notes from the tag's CHANGELOG.md section and pushes the src/vX.Y.Z module tag, which matches no
# trigger here. Go comes from .go-version, the one Go version ci.yml and codeql.yml install too,
# rather than src/go.mod, whose `go 1.26.0` would pin the build to the first 1.26 release.
# src/internal/release's TestReleaseRunsOnAVersionTagOrOnRequest fails if a trigger is added or the
# job stops calling the shared workflow.
name: Release

on:
push:
tags:
- 'v[0-9]+.[0-9]+.[0-9]+'
- 'v[0-9]+.[0-9]+.[0-9]+-*'
workflow_dispatch:
inputs:
confirm:
description: Type release-go to publish binaries for the tag this run is dispatched on
tag:
description: 'An existing vX.Y.Z tag to build and publish'
required: true
type: string

# A newer tag must never abort an in-flight publish.
permissions: {}

# One run per tag, never cancelled: two runs must not publish the same release.
concurrency:
group: release-${{ github.ref }}
group: release-${{ inputs.tag || github.ref_name }}
cancel-in-progress: false

permissions:
contents: read

# Our own modules are fetched from git, never through proxy.golang.org or
# sum.golang.org, so the build itself is not what feeds the public proxy a tag.
env:
GOPRIVATE: github.com/simtabi/*
GONOSUMDB: github.com/simtabi/*

jobs:
goreleaser:
runs-on: ubuntu-latest
environment: release
timeout-minutes: 30
release:
permissions:
contents: write # create the GitHub Release, upload assets
id-token: write # cosign keyless signing + provenance via OIDC
attestations: write # write the build-provenance attestation
steps:
- name: Refuse without confirmation or a release tag
env:
CONFIRM: ${{ inputs.confirm }}
run: |
if [ "${CONFIRM}" != "release-go" ]; then
echo "::error::confirm must be release-go; nothing was published"
exit 1
fi
case "${GITHUB_REF}" in
refs/tags/v[0-9]*) ;;
*) echo "::error::dispatch this on a vX.Y.Z tag (--ref vX.Y.Z), not ${GITHUB_REF}"; exit 1 ;;
esac

- uses: actions/checkout@v7
with:
fetch-depth: 0
- uses: actions/setup-go@v7
with:
go-version: "1.26"
cache-dependency-path: src/go.sum
- uses: sigstore/cosign-installer@756fe2d284c7292c4e9b62c8a28f1e5629c59532 # v4.1.3
# syft powers GoReleaser's SBOM generation.
- uses: anchore/sbom-action/download-syft@66cbf4bc1f1c0d2edc94016e65bc221b6bb0ad6c # v0.24.3
# `go install` needs a tag named after the module's subdirectory: the
# module is github.com/simtabi/probaci/src, so Go resolves src/vX.Y.Z and
# ignores vX.Y.Z. OSS GoReleaser rejects the prefixed tag as non-semver,
# so the release runs off vX.Y.Z and mirrors src/vX.Y.Z onto the same
# commit here, where it cannot be forgotten. This workflow starts on no
# tag push, so pushing src/... starts no second run. A re-run finds the
# tag already there and leaves it alone: a public tag is never moved.
- name: Mirror the tag Go resolves the module through
run: |
alias_tag="src/${GITHUB_REF_NAME}"
if git rev-parse -q --verify "refs/tags/${alias_tag}" >/dev/null; then
echo "${alias_tag} already exists"
else
git tag "${alias_tag}" "${GITHUB_REF_NAME}^{commit}"
git push origin "refs/tags/${alias_tag}"
echo "pushed ${alias_tag}"
fi
# The notes go to $RUNNER_TEMP, never the checkout: an untracked file in
# the working tree makes GoReleaser refuse to release a dirty tree.
- name: Extract CHANGELOG section for this tag
run: ./src/scripts/extract-changelog.sh "${GITHUB_REF_NAME}" > "$RUNNER_TEMP/release_notes.md"
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@a4f02ab5355a589de60d917a659ce617ed19b3de # v7.3.0
with:
# Pin the GoReleaser series so the config schema and the tool stay in
# sync (avoids `latest` deprecating config keys out from under a release).
version: "~> v2.4"
args: release --clean --config src/.goreleaser.yaml --release-notes=${{ runner.temp }}/release_notes.md
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# PAT with write access to the Homebrew tap + Scoop bucket repos.
TAP_GITHUB_TOKEN: ${{ secrets.TAP_GITHUB_TOKEN }}
# Write deploy keys on simtabi/homebrew-tap and simtabi/scoop-bucket. When set, GoReleaser
# pushes with them and the token is not used for those two.
HOMEBREW_TAP_DEPLOY_KEY: ${{ secrets.HOMEBREW_TAP_DEPLOY_KEY }}
SCOOP_BUCKET_DEPLOY_KEY: ${{ secrets.SCOOP_BUCKET_DEPLOY_KEY }}
# Sign a build-provenance attestation for every released artifact. The
# checksums file lists them all, so `gh attestation verify <file>
# --repo simtabi/probaci` works for any downloaded binary or archive.
- name: Attest build provenance
uses: actions/attest-build-provenance@v4
with:
subject-checksums: .cache/goreleaser/checksums.txt
contents: write
id-token: write
attestations: write
uses: simtabi/.github/.github/workflows/go-release.yml@745880bdb9b35a21eb16824cfc995f16e1e82fd5 # simtabi/.github#27
with:
tag: ${{ inputs.tag || github.ref_name }}
go-version-file: .go-version
1 change: 1 addition & 0 deletions .go-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
1.26.11
27 changes: 20 additions & 7 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,18 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
- Building probaci needs Go 1.26, and CI runs on it. Go 1.25 has ten standard-library
vulnerabilities fixed only in 1.26, which the web UI's `net/http` server reaches; `golang.org/x/net`
moves to v0.60.0 for the same reason, and gosec to v2.30.0.
- The release workflow no longer starts on a tag push. Binary releases are on hold, and a release
runs only when dispatched by hand on a `v[0-9]*` tag with `-f confirm=release-go`; a test fails
if an automatic trigger returns.
- The README and installation guide build probaci from source only: no download, install-script,
package-manager or `go install` path is offered while binary releases are on hold.
- Binaries ship on the GitHub release only. A release builds on a pushed `vX.Y.Z` tag (or a
`vX.Y.Z-rc.N` pre-release), or on request for a tag already pushed
(`gh workflow run release.yml --repo simtabi/probaci -f tag=vX.Y.Z`). `release.yml` is now a thin
caller of simtabi/.github's shared `go-release.yml`, which checks the tag, takes the notes from
this file, mirrors `src/vX.Y.Z`, runs GoReleaser and attests provenance. Go comes from a new
`.go-version` (`1.26.11`), which every `setup-go` in `ci.yml` and `codeql.yml` now reads too, so
CI, CodeQL and the release build on one exact toolchain rather than whatever 1.26 patch a runner
has cached; 1.26.11 fixes GO-2026-6859 in `net/http`. This replaces the dispatch-only,
`-f confirm=release-go` workflow of this cycle, which never shipped.
- The README and installation guide offer two paths, in order: download the binary from the GitHub
release and check it against `checksums.txt`, or build from source. No package-manager or
`go install` path is offered.
- The Go module moved into `src/`, and its path is now `github.com/simtabi/probaci/src`. Importers
of `pkg/probaci` use `github.com/simtabi/probaci/src/pkg/probaci`. A release tags `src/vX.Y.Z`
beside `vX.Y.Z` on the same commit, since that is the tag Go resolves the module through.
Expand All @@ -38,8 +45,11 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
embedded with nothing fetched from the network, an SVG favicon on every page, and `--read-only`.
`third-party-notices.md` carries the embedded licenses and ships in the archives.
- Tests that fail when `build/` holds anything but OS folders, inputs and checksums, when a
container image or docker step returns, and when the docs offer `go install`. The release
trigger test now also requires the refusal to be the workflow's first step.
container image or docker step returns, and when the docs offer `go install`.
- Tests that fail if `release.yml` gains a branch push, schedule, pull-request or release trigger,
stops calling the shared release workflow, switches off the `src/vX.Y.Z` alias, or builds with a
Go other than CI's, and if `src/.goreleaser.yaml` gains any publishing block besides the GitHub
release.
- A CI `snapshot` job that runs `make cross` on every pull request and uploads the binaries.

### Removed
Expand All @@ -48,6 +58,9 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
(`ghcr.io/simtabi/probaci`, linux/amd64 and linux/arm64), the release workflow's QEMU, buildx
and ghcr.io login steps and its `packages: write` permission, and Dependabot's docker ecosystem
are gone. No image was ever published, so nothing on ghcr.io is affected.
- Homebrew cask, Scoop and Winget publishing from `src/.goreleaser.yaml`, and the tap and bucket
deploy keys and token the release workflow passed it. No probaci release ever reached them, so
nothing in `simtabi/homebrew-tap` or `simtabi/scoop-bucket` is affected.

### Fixed

Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
# and .exe on Windows. `make check-build` (TestBuildLayout) fails on anything
# else.
#
# Nothing here publishes. Releases are dispatched by hand (docs/release.md).
# Nothing here publishes. A release builds on a pushed vX.Y.Z tag or on request (docs/release.md).

SHELL := /usr/bin/env bash
.DEFAULT_GOAL := help
Expand Down
18 changes: 14 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,19 @@ probaci is built to catch the common causes automatically:

## Install

probaci is built from source: binary releases are on hold, so there is no
download, install-script, package-manager or `go install` channel to use. You need
Go (the version `src/go.mod` names), `git` and `make`.
Download the binary for your platform from the
[GitHub releases](https://github.com/simtabi/probaci/releases)
(`probaci_<os>_<arch>`, e.g. `probaci_macos_universal`, `probaci_linux_amd64`),
check it against the release's `checksums.txt`, and let it install itself:

```sh
shasum -a 256 -c checksums.txt --ignore-missing
chmod +x probaci_macos_universal && ./probaci_macos_universal install
```

The GitHub release is the only channel: there is no package-manager or
`go install` path. Or build from source, with Go (the version `src/go.mod`
names), `git` and `make`:

```sh
git clone https://github.com/simtabi/probaci ~/src/probaci
Expand All @@ -49,7 +59,7 @@ make -C ~/src/probaci build # -> build/<os>/probaci (macos, linux or
probaci doctor
```

Pinning a version, updating, Windows, and PATH guidance are in
Verifying the attestation, pinning a version, updating, Windows, and PATH guidance are in
[docs/installation.md](docs/installation.md).

## Quick start guide and usage
Expand Down
42 changes: 30 additions & 12 deletions docs/installation.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,29 @@
# Installation

probaci is a single static binary, built from source. Binary releases are on
hold until the maintainer decides otherwise, so this page offers no download,
install-script, package-manager or `go install` path. The only hard runtime
dependencies are a container runtime (Docker, rootless Docker, or Podman) and
your VCS.
probaci is a single static binary. Two ways to get it, in this order: download
it from the GitHub release, or build from source. The GitHub release is the only
channel: this page offers no package-manager or `go install` path. The only hard
runtime dependencies are a container runtime (Docker, rootless Docker, or
Podman) and your VCS.

## Download a release

Each [GitHub release](https://github.com/simtabi/probaci/releases) carries bare
binaries `probaci_<os>_<arch>` (`.exe` on Windows; `macos`, not `darwin`),
archives, Linux `.deb`/`.rpm`/`.apk` packages, and `checksums.txt` (cosign-signed).
Download the one for your platform and the checksums, verify, and install:

```sh
gh release download --repo simtabi/probaci --pattern 'probaci_macos_universal' --pattern checksums.txt
shasum -a 256 -c checksums.txt --ignore-missing
chmod +x probaci_macos_universal && ./probaci_macos_universal install
```

Every asset in `checksums.txt` carries a build-provenance attestation:

```sh
gh attestation verify probaci_macos_universal --repo simtabi/probaci
```

## Build from source

Expand Down Expand Up @@ -52,14 +71,13 @@ make -C ~/src/probaci build
`probaci version` reports the version the build was stamped with (`git describe`),
so a build from a branch is still identifiable.

### Binary releases
### Other channels

No binary release has been published. The release tooling stays in the tree:
the GoReleaser config (`src/.goreleaser.yaml`), and `src/scripts/install.sh` /
`src/scripts/install.ps1`, which
download release archives. The release workflow would also publish Homebrew and
Scoop manifests and deb/rpm packages. probaci no longer builds a container image.
None of those channels is offered while binary releases are on hold; see
The GitHub release is the only channel. The release workflow used to be set up
to publish a Homebrew cask, a Scoop manifest and a Winget manifest as well; no
probaci release ever reached them, and that publishing is gone from
`src/.goreleaser.yaml`. probaci builds no container image. `src/scripts/install.sh`
and `src/scripts/install.ps1` download release archives; see
[release.md](release.md).

## Run it globally
Expand Down
Loading
Loading