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
55 changes: 55 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
name: release

on:
push:
tags:
- "v*"

permissions:
contents: read

jobs:
goreleaser:
# The /v2 module path was published by accident and is permanently
# retracted (see CHANGELOG). The v2.0.1 and v2.0.2 tags have to stay in the
# repository because v2.0.2 is what carries the retract block, so a v2 tag
# can always be re-pushed by accident. Never build a release from one.
if: ${{ !startsWith(github.ref_name, 'v2.') }}
runs-on: ubuntu-latest
permissions:
# goreleaser creates the GitHub release and uploads the archives.
contents: write
steps:
- uses: actions/checkout@v7
with:
# goreleaser builds the changelog from history, so a shallow
# checkout would produce an empty one.
fetch-depth: 0

- uses: actions/setup-go@v7
with:
go-version-file: go.mod

# A missing or empty tap token skips the cask instead of failing the
# release, which is the right trade but is otherwise invisible. Say so in
# the log, because a secret cannot be read back to check it.
- name: Report whether the Homebrew cask will publish
env:
HOMEBREW_TAP_GITHUB_TOKEN: ${{ secrets.HOMEBREW_TAP_GITHUB_TOKEN }}
run: |
if [ -z "$HOMEBREW_TAP_GITHUB_TOKEN" ]; then
echo "::warning::HOMEBREW_TAP_GITHUB_TOKEN is unset or empty. Archives and checksums will publish; the Homebrew cask will be skipped."
else
echo "HOMEBREW_TAP_GITHUB_TOKEN is present. The cask will be pushed to jferrl/homebrew-tap."
fi

- uses: goreleaser/goreleaser-action@v6
with:
version: "~> v2"
args: release --clean
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Pushing the Homebrew cask writes to jferrl/homebrew-tap, which the
# job's GITHUB_TOKEN cannot reach. Without this secret the release
# still publishes; only the cask is skipped.
HOMEBREW_TAP_GITHUB_TOKEN: ${{ secrets.HOMEBREW_TAP_GITHUB_TOKEN }}
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,6 @@ go.work.sum

# Claude Code local settings and scratch worktrees
.claude/

# GoReleaser build output
dist/
105 changes: 105 additions & 0 deletions .goreleaser.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# Release config for the githubauth CLI. The library is consumed with go get and
# is unaffected by anything here.
#
# Run "goreleaser release --snapshot --clean" to build locally without publishing.
version: 2

project_name: githubauth

before:
hooks:
- go mod tidy

builds:
- id: githubauth
main: ./cmd/githubauth
env:
- CGO_ENABLED=0
goos:
- linux
- darwin
- windows
goarch:
- amd64
- arm64
flags:
- -trimpath
ldflags:
# buildVersion is the CLI's own variable; without it a release binary
# would fall back to the build info and report a pseudo-version.
- -s -w -X main.buildVersion={{ .Version }}

archives:
- id: githubauth
formats:
- tar.gz
format_overrides:
- goos: windows
formats:
- zip
name_template: >-
{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}
files:
- README.md
- LICENSE

checksum:
name_template: checksums.txt

snapshot:
version_template: "{{ incpatch .Version }}-next"

changelog:
sort: asc
filters:
exclude:
- "^docs:"
- "^test:"
- "^chore:"
- "^ci:"
- "Merge pull request"

release:
footer: |
## Installing the CLI

Download the archive for your platform above, or:

```bash
go install github.com/jferrl/go-githubauth/cmd/githubauth@{{ .Tag }}
```

Verify a download against `checksums.txt`:

```bash
sha256sum -c checksums.txt --ignore-missing
```

The library is unaffected by these artifacts:

```bash
go get github.com/jferrl/go-githubauth@{{ .Tag }}
```

homebrew_casks:
- name: githubauth
# A failed tap push fails the whole release, binaries included. If the
# token is not set, publish everything else and skip only the cask.
skip_upload: '{{ if eq (index .Env "HOMEBREW_TAP_GITHUB_TOKEN") "" }}true{{ else }}false{{ end }}'
repository:
owner: jferrl
name: homebrew-tap
token: '{{ index .Env "HOMEBREW_TAP_GITHUB_TOKEN" }}'
homepage: https://github.com/jferrl/go-githubauth
description: GitHub App authentication for your shell - installation tokens, JWTs and PATs
license: MIT
commit_author:
name: jferrl
email: jorgeferrerolinacero@gmail.com
hooks:
post:
# The binary is not notarized, so Gatekeeper would quarantine it.
install: |
if system_command("/usr/bin/xattr", args: ["-h"]).exit_status == 0
system_command "/usr/bin/xattr", args: ["-dr", "com.apple.quarantine", "#{staged_path}/githubauth"]
end
19 changes: 19 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
It adds no module dependencies. The CLI is built on the standard library's `flag`, so
the two-dependency footprint is unchanged for anyone importing the library.

- Prebuilt `githubauth` binaries on each release, for Linux, macOS and Windows on amd64
and arm64, with a `checksums.txt` to verify them. A tagged release builds them with
GoReleaser; the library is unaffected and is still consumed with `go get`.

- A Homebrew cask, so the CLI installs without a Go toolchain:

```bash
brew install jferrl/tap/githubauth
```

The cask is generated into [jferrl/homebrew-tap](https://github.com/jferrl/homebrew-tap)
when a release is tagged.

### Fixed

- `githubauth version` reported a pseudo-version for a binary that was not installed with
`go install`. A release build now carries its tag, and a build from a checkout falls
back to the stamped commit.

## [v1.8.0] - 2026-09-18

A correctness release. Two changes alter behaviour callers may depend on; both
Expand Down
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,14 @@ Full list on [pkg.go.dev](https://pkg.go.dev/github.com/jferrl/go-githubauth?tab

The same credentials, without writing Go:

```bash
brew install jferrl/tap/githubauth
```

Or download a binary from the
[latest release](https://github.com/jferrl/go-githubauth/releases/latest) — Linux, macOS
and Windows, on amd64 and arm64 — or build it yourself:

```bash
go install github.com/jferrl/go-githubauth/cmd/githubauth@latest
```
Expand Down
40 changes: 36 additions & 4 deletions cmd/githubauth/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -317,14 +317,46 @@ func envInt64(key string) int64 {
return n
}

// version reports the module version go install stamped, so a built binary
// can say where it came from without a linker flag.
// buildVersion is set by the release build with -ldflags. It stays empty for
// a binary built any other way, which then falls back to the build info.
var buildVersion string

// version reports where the binary came from. A release build carries the tag
// in buildVersion; go install stamps the module version into the build info;
// a local build has neither and reports the revision instead.
func version() string {
if buildVersion != "" {
return buildVersion
}

info, ok := debug.ReadBuildInfo()
if !ok || info.Main.Version == "" {
if !ok {
return "(unknown)"
}
if v := info.Main.Version; v != "" && v != "(devel)" {
return v
}

// A build from a checkout: report the commit Go stamped, if any.
var revision, modified string
for _, s := range info.Settings {
switch s.Key {
case "vcs.revision":
revision = s.Value
case "vcs.modified":
modified = s.Value
}
}
if revision == "" {
return "(devel)"
}
return info.Main.Version
if len(revision) > 12 {
revision = revision[:12]
}
if modified == "true" {
revision += "-dirty"
}
return "(devel) " + revision
}

// nearest suggests a command for a typo, matching on a shared prefix or a
Expand Down
Loading