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
93 changes: 86 additions & 7 deletions .github/workflows/release-cfl.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ permissions:

jobs:
goreleaser:
runs-on: ubuntu-latest
# INT-450: darwin must build with cgo (Keychain backend). cgo+darwin
# cannot cross-compile from Linux, so this job runs on macOS. Pinned
# image (not the moving macos-latest label) for a reproducible release.
runs-on: macos-15
outputs:
version: ${{ steps.get_version.outputs.version }}
steps:
Expand All @@ -29,20 +32,85 @@ jobs:
go-version: '1.24'
cache-dependency-path: tools/cfl/go.sum

- name: Install GoReleaser
uses: goreleaser/goreleaser-action@v6
with:
version: "~> v2"
install-only: true

- name: GoReleaser check
run: goreleaser check -f .goreleaser-cfl.yml

- name: Create temporary semver tag for GoReleaser
run: |
git tag v${{ steps.get_version.outputs.version }}

- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v6
with:
distribution: goreleaser
version: '~> v2'
args: release --clean --config .goreleaser-cfl.yml
- name: Build (snapshot, no publish)
env:
GORELEASER_CURRENT_TAG: v${{ steps.get_version.outputs.version }}
run: goreleaser release --snapshot --clean -f .goreleaser-cfl.yml

# INT-450 pre-publish gate: prove the darwin binaries actually carry
# the Keychain backend BEFORE anything is published. A CGO_ENABLED=0
# darwin build links no Security.framework and fails closed at
# runtime; this gate makes that impossible to ship silently.
- name: Pre-publish gate — darwin Keychain backend present
run: |
set -euo pipefail
art=dist/artifacts.json
arm_bin=$(jq -r '.[]|select(.type=="Binary" and .goos=="darwin" and .goarch=="arm64")|.path' "$art")
amd_bin=$(jq -r '.[]|select(.type=="Binary" and .goos=="darwin" and .goarch=="amd64")|.path' "$art")
[ -n "$arm_bin" ] && [ -n "$amd_bin" ] || { echo "missing a darwin binary in artifacts.json"; exit 1; }
# darwin archives: exactly one per arch, no duplicate names
tot=$(jq '[.[]|select(.type=="Archive" and .goos=="darwin")|.name]|length' "$art")
uniq=$(jq '[.[]|select(.type=="Archive" and .goos=="darwin")|.name]|unique|length' "$art")
[ "$tot" = "$uniq" ] || { echo "duplicate darwin archive names"; exit 1; }
[ "$(jq '[.[]|select(.type=="Archive" and .goos=="darwin" and .goarch=="arm64")]|length' "$art")" = 1 ] || { echo "expected exactly one darwin/arm64 archive"; exit 1; }
[ "$(jq '[.[]|select(.type=="Archive" and .goos=="darwin" and .goarch=="amd64")]|length' "$art")" = 1 ] || { echo "expected exactly one darwin/amd64 archive"; exit 1; }
# Mach-O arch sanity (both slices)
file "$arm_bin" | grep -q 'arm64' || { echo "arm64 binary is not arm64 Mach-O"; exit 1; }
file "$amd_bin" | grep -q 'x86_64' || { echo "amd64 binary is not x86_64 Mach-O"; exit 1; }
lipo -archs "$arm_bin" | grep -qw arm64 || { echo "lipo: arm64 slice missing"; exit 1; }
lipo -archs "$amd_bin" | grep -qw x86_64 || { echo "lipo: x86_64 slice missing"; exit 1; }
# amd64 cannot run on the arm64 runner: assert Security.framework
# is linked. CGO_ENABLED=0 omits it entirely, so its presence is a
# sound *necessary* cgo signal for the slice we can't execute.
otool -L "$amd_bin" | grep -q '/System/Library/Frameworks/Security.framework' \
|| { echo "amd64 binary not linked against Security.framework (cgo missing)"; exit 1; }
# arm64 authoritative functional check: with no backend override
# and isolated HOME/XDG, credstore must auto-select the Keychain.
# cfl config show --output json emits one {"Key": "Value"} per line;
# we grep for the Keyring Backend row containing "keychain (auto)".
tmp=$(mktemp -d)
mkdir -p "$tmp/Library/Application Support/atlassian-cli"
out=$(env -u ATLASSIAN_CLI_KEYRING_BACKEND \
-u CFL_API_TOKEN -u ATLASSIAN_API_TOKEN \
HOME="$tmp" XDG_CONFIG_HOME="$tmp/xdg" \
"$arm_bin" --output json config show 2>/dev/null)
echo "$out"
echo "$out" | grep -q '"Keyring Backend":.*keychain (auto)' \
|| { echo "GATE FAIL: keychain (auto) not found in cfl config show output"; exit 1; }
echo "GATE OK: darwin/arm64 reports keychain backend auto-selected; darwin/amd64 Security.framework linked"

- name: Release notes
run: |
set -euo pipefail
cat > "$RUNNER_TEMP/release-notes.md" <<'EOF'
### macOS Keychain storage restored

Builds since the credential-store migration were compiled without
cgo and failed closed on macOS (no Keychain backend). This release
builds the darwin binaries with cgo enabled, restoring native
macOS Keychain storage. Upgrade and re-run your normal commands;
no other action is required.
EOF

- name: Release (publish)
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAP_GITHUB_TOKEN: ${{ secrets.TAP_GITHUB_TOKEN }}
GORELEASER_CURRENT_TAG: v${{ steps.get_version.outputs.version }}
run: goreleaser release --clean -f .goreleaser-cfl.yml --release-notes="$RUNNER_TEMP/release-notes.md"

- name: Fix release tag
run: |
Expand All @@ -52,6 +120,17 @@ jobs:
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Verify release notes published
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
VERSION=${{ steps.get_version.outputs.version }}
body=$(gh release view "cfl-v${VERSION}" --repo "$GITHUB_REPOSITORY" --json body -q .body)
if ! printf '%s' "$body" | grep -q 'macOS Keychain'; then
gh release edit "cfl-v${VERSION}" --repo "$GITHUB_REPOSITORY" --notes-file "$RUNNER_TEMP/release-notes.md"
fi

# chocolatey-publish-cfl.yml and winget-publish-cfl.yml are dispatched
# automatically by the trigger-publish job below. They remain in separate
# workflow files (workflow_dispatch only) so Microsoft-hosted service
Expand Down
93 changes: 86 additions & 7 deletions .github/workflows/release-jtk.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ permissions:

jobs:
goreleaser:
runs-on: ubuntu-latest
# INT-450: darwin must build with cgo (Keychain backend). cgo+darwin
# cannot cross-compile from Linux, so this job runs on macOS. Pinned
# image (not the moving macos-latest label) for a reproducible release.
runs-on: macos-15
outputs:
version: ${{ steps.get_version.outputs.version }}
steps:
Expand All @@ -29,20 +32,85 @@ jobs:
go-version: '1.24'
cache-dependency-path: tools/jtk/go.sum

- name: Install GoReleaser
uses: goreleaser/goreleaser-action@v6
with:
version: "~> v2"
install-only: true

- name: GoReleaser check
run: goreleaser check -f .goreleaser-jtk.yml

- name: Create temporary semver tag for GoReleaser
run: |
git tag v${{ steps.get_version.outputs.version }}

- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v6
with:
distribution: goreleaser
version: '~> v2'
args: release --clean --config .goreleaser-jtk.yml
- name: Build (snapshot, no publish)
env:
GORELEASER_CURRENT_TAG: v${{ steps.get_version.outputs.version }}
run: goreleaser release --snapshot --clean -f .goreleaser-jtk.yml

# INT-450 pre-publish gate: prove the darwin binaries actually carry
# the Keychain backend BEFORE anything is published. A CGO_ENABLED=0
# darwin build links no Security.framework and fails closed at
# runtime; this gate makes that impossible to ship silently.
- name: Pre-publish gate — darwin Keychain backend present
run: |
set -euo pipefail
art=dist/artifacts.json
arm_bin=$(jq -r '.[]|select(.type=="Binary" and .goos=="darwin" and .goarch=="arm64")|.path' "$art")
amd_bin=$(jq -r '.[]|select(.type=="Binary" and .goos=="darwin" and .goarch=="amd64")|.path' "$art")
[ -n "$arm_bin" ] && [ -n "$amd_bin" ] || { echo "missing a darwin binary in artifacts.json"; exit 1; }
# darwin archives: exactly one per arch, no duplicate names
tot=$(jq '[.[]|select(.type=="Archive" and .goos=="darwin")|.name]|length' "$art")
uniq=$(jq '[.[]|select(.type=="Archive" and .goos=="darwin")|.name]|unique|length' "$art")
[ "$tot" = "$uniq" ] || { echo "duplicate darwin archive names"; exit 1; }
[ "$(jq '[.[]|select(.type=="Archive" and .goos=="darwin" and .goarch=="arm64")]|length' "$art")" = 1 ] || { echo "expected exactly one darwin/arm64 archive"; exit 1; }
[ "$(jq '[.[]|select(.type=="Archive" and .goos=="darwin" and .goarch=="amd64")]|length' "$art")" = 1 ] || { echo "expected exactly one darwin/amd64 archive"; exit 1; }
# Mach-O arch sanity (both slices)
file "$arm_bin" | grep -q 'arm64' || { echo "arm64 binary is not arm64 Mach-O"; exit 1; }
file "$amd_bin" | grep -q 'x86_64' || { echo "amd64 binary is not x86_64 Mach-O"; exit 1; }
lipo -archs "$arm_bin" | grep -qw arm64 || { echo "lipo: arm64 slice missing"; exit 1; }
lipo -archs "$amd_bin" | grep -qw x86_64 || { echo "lipo: x86_64 slice missing"; exit 1; }
# amd64 cannot run on the arm64 runner: assert Security.framework
# is linked. CGO_ENABLED=0 omits it entirely, so its presence is a
# sound *necessary* cgo signal for the slice we can't execute.
otool -L "$amd_bin" | grep -q '/System/Library/Frameworks/Security.framework' \
|| { echo "amd64 binary not linked against Security.framework (cgo missing)"; exit 1; }
# arm64 authoritative functional check: with no backend override
# and isolated HOME/XDG, credstore must auto-select the Keychain.
# jtk config show emits a pipe-delimited table; we grep for the
# keyring_backend row containing "keychain (auto)".
tmp=$(mktemp -d)
mkdir -p "$tmp/Library/Application Support/atlassian-cli"
out=$(env -u ATLASSIAN_CLI_KEYRING_BACKEND \
-u JIRA_API_TOKEN -u ATLASSIAN_API_TOKEN \
HOME="$tmp" XDG_CONFIG_HOME="$tmp/xdg" \
"$arm_bin" config show 2>/dev/null)
echo "$out"
echo "$out" | grep -q 'keychain (auto)' \
|| { echo "GATE FAIL: keychain (auto) not found in jtk config show output"; exit 1; }
echo "GATE OK: darwin/arm64 reports keychain backend auto-selected; darwin/amd64 Security.framework linked"

- name: Release notes
run: |
set -euo pipefail
cat > "$RUNNER_TEMP/release-notes.md" <<'EOF'
### macOS Keychain storage restored

Builds since the credential-store migration were compiled without
cgo and failed closed on macOS (no Keychain backend). This release
builds the darwin binaries with cgo enabled, restoring native
macOS Keychain storage. Upgrade and re-run your normal commands;
no other action is required.
EOF

- name: Release (publish)
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAP_GITHUB_TOKEN: ${{ secrets.TAP_GITHUB_TOKEN }}
GORELEASER_CURRENT_TAG: v${{ steps.get_version.outputs.version }}
run: goreleaser release --clean -f .goreleaser-jtk.yml --release-notes="$RUNNER_TEMP/release-notes.md"

- name: Fix release tag
run: |
Expand All @@ -52,6 +120,17 @@ jobs:
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Verify release notes published
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
VERSION=${{ steps.get_version.outputs.version }}
body=$(gh release view "jtk-v${VERSION}" --repo "$GITHUB_REPOSITORY" --json body -q .body)
if ! printf '%s' "$body" | grep -q 'macOS Keychain'; then
gh release edit "jtk-v${VERSION}" --repo "$GITHUB_REPOSITORY" --notes-file "$RUNNER_TEMP/release-notes.md"
fi

- name: Update jira-ticket-cli alias cask
run: |
VERSION=${{ steps.get_version.outputs.version }}
Expand Down
42 changes: 40 additions & 2 deletions .goreleaser-cfl.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,48 @@ before:
- go mod tidy -C tools/cfl
- go test ./tools/cfl/...

# INT-450: darwin builds with CGO so 99designs/keyring's Keychain backend
# (//go:build darwin && cgo) is compiled in; without it credstore fails
# closed on macOS. linux/windows stay CGO-off static (their keyring
# backends are pure Go; cgo there would regress glibc portability).
# Split is by GOOS only — both darwin amd64 and arm64 are still produced.
builds:
- id: cfl
- id: cfl-darwin
dir: tools/cfl
main: ./cmd/cfl
binary: cfl
env:
- CGO_ENABLED=0
- CGO_ENABLED=1
goos:
- darwin
goarch:
- amd64
- arm64
overrides:
- goos: darwin
goarch: amd64
goamd64: v1
env:
- CGO_ENABLED=1
- "CC=xcrun clang -arch x86_64"
- goos: darwin
goarch: arm64
goarm64: v8.0
env:
- CGO_ENABLED=1
- "CC=xcrun clang -arch arm64"
ldflags:
- -s -w
- -X github.com/open-cli-collective/atlassian-go/version.Version={{.Version}}
- -X github.com/open-cli-collective/atlassian-go/version.Commit={{.Commit}}
- -X github.com/open-cli-collective/atlassian-go/version.BuildDate={{.Date}}
- id: cfl-unix-win
dir: tools/cfl
main: ./cmd/cfl
binary: cfl
env:
- CGO_ENABLED=0
goos:
- linux
- windows
goarch:
Expand Down Expand Up @@ -43,6 +76,11 @@ archives:
# Linux packages (.deb and .rpm)
nfpms:
- id: cfl
# deb/rpm are linux-only: pull the static CGO-off build, never darwin.
# `ids` (the v2 build-id filter) — `builds` is deprecated and fails
# `goreleaser check`.
ids:
- cfl-unix-win
package_name: cfl
vendor: Open CLI Collective
homepage: https://github.com/open-cli-collective/atlassian-cli
Expand Down
42 changes: 40 additions & 2 deletions .goreleaser-jtk.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,48 @@ before:
- go mod tidy -C tools/jtk
- go test ./tools/jtk/...

# INT-450: darwin builds with CGO so 99designs/keyring's Keychain backend
# (//go:build darwin && cgo) is compiled in; without it credstore fails
# closed on macOS. linux/windows stay CGO-off static (their keyring
# backends are pure Go; cgo there would regress glibc portability).
# Split is by GOOS only — both darwin amd64 and arm64 are still produced.
builds:
- id: jtk
- id: jtk-darwin
dir: tools/jtk
main: ./cmd/jtk
binary: jtk
env:
- CGO_ENABLED=0
- CGO_ENABLED=1
goos:
- darwin
goarch:
- amd64
- arm64
overrides:
- goos: darwin
goarch: amd64
goamd64: v1
env:
- CGO_ENABLED=1
- "CC=xcrun clang -arch x86_64"
- goos: darwin
goarch: arm64
goarm64: v8.0
env:
- CGO_ENABLED=1
- "CC=xcrun clang -arch arm64"
ldflags:
- -s -w
- -X github.com/open-cli-collective/atlassian-go/version.Version={{.Version}}
- -X github.com/open-cli-collective/atlassian-go/version.Commit={{.Commit}}
- -X github.com/open-cli-collective/atlassian-go/version.BuildDate={{.Date}}
- id: jtk-unix-win
dir: tools/jtk
main: ./cmd/jtk
binary: jtk
env:
- CGO_ENABLED=0
goos:
- linux
- windows
goarch:
Expand Down Expand Up @@ -43,6 +76,11 @@ archives:
# Linux packages (.deb and .rpm)
nfpms:
- id: jtk
# deb/rpm are linux-only: pull the static CGO-off build, never darwin.
# `ids` (the v2 build-id filter) — `builds` is deprecated and fails
# `goreleaser check`.
ids:
- jtk-unix-win
package_name: jtk
vendor: Open CLI Collective
homepage: https://github.com/open-cli-collective/atlassian-cli
Expand Down
Loading