chore: import upstream snapshot with attribution
release / release-please (push) Failing after 1m49s
docs / build (push) Failing after 6m34s
release / build-and-upload (arm64, linux) (push) Has been cancelled
release / build-and-upload (arm64, windows) (push) Has been cancelled
release / build-darwin (amd64, darwin) (push) Has been cancelled
release / checksums (push) Has been cancelled
release / finalize (push) Has been cancelled
release / build-darwin (arm64, darwin) (push) Has been cancelled
release / build-and-upload (amd64, linux) (push) Has been cancelled
release / build-and-upload (amd64, windows) (push) Has been cancelled
docs / deploy (push) Has been cancelled
release / release-please (push) Failing after 1m49s
docs / build (push) Failing after 6m34s
release / build-and-upload (arm64, linux) (push) Has been cancelled
release / build-and-upload (arm64, windows) (push) Has been cancelled
release / build-darwin (amd64, darwin) (push) Has been cancelled
release / checksums (push) Has been cancelled
release / finalize (push) Has been cancelled
release / build-darwin (arm64, darwin) (push) Has been cancelled
release / build-and-upload (amd64, linux) (push) Has been cancelled
release / build-and-upload (amd64, windows) (push) Has been cancelled
docs / deploy (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
check:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
|
||||
- name: Format check
|
||||
run: |
|
||||
output=$(gofmt -l .)
|
||||
if [ -n "$output" ]; then
|
||||
echo "Files not formatted:"
|
||||
echo "$output"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Vet
|
||||
run: go vet ./...
|
||||
|
||||
test:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
timeout-minutes: 25
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
|
||||
- name: Test on Unix
|
||||
if: runner.os != 'Windows'
|
||||
run: go test -race ./...
|
||||
|
||||
- name: Test on Windows
|
||||
if: runner.os == 'Windows'
|
||||
run: go test -v -timeout=15m ./...
|
||||
|
||||
- name: Build
|
||||
run: go build ./cmd/no-mistakes
|
||||
|
||||
e2e:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
|
||||
# The e2e suite drives the real no-mistakes binary against a fake
|
||||
# agent through `git push -> daemon -> pipeline -> push to upstream`
|
||||
# for claude, codex, and opencode. It builds the binary itself, so
|
||||
# no separate build step is needed. Linux-only for now: opencode's
|
||||
# ephemeral HTTP server picks up unused ports via :0, which is
|
||||
# fine on Linux runners but flakes on Windows agent harnesses.
|
||||
- name: End-to-end suite
|
||||
run: make e2e
|
||||
@@ -0,0 +1,54 @@
|
||||
name: docs
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- "docs/**"
|
||||
- ".github/workflows/docs.yml"
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- "docs/**"
|
||||
- ".github/workflows/docs.yml"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pages: write
|
||||
id-token: write
|
||||
|
||||
concurrency:
|
||||
group: pages-${{ github.event_name }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- if: github.event_name != 'pull_request'
|
||||
uses: actions/configure-pages@v5
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22
|
||||
cache: npm
|
||||
cache-dependency-path: docs/package-lock.json
|
||||
- run: npm ci
|
||||
working-directory: docs
|
||||
- run: npm run build
|
||||
working-directory: docs
|
||||
- if: github.event_name != 'pull_request'
|
||||
uses: actions/upload-pages-artifact@v3
|
||||
with:
|
||||
path: docs/dist
|
||||
|
||||
deploy:
|
||||
needs: build
|
||||
if: github.ref == 'refs/heads/main'
|
||||
runs-on: ubuntu-latest
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
steps:
|
||||
- id: deployment
|
||||
uses: actions/deploy-pages@v4
|
||||
@@ -0,0 +1,63 @@
|
||||
name: Guard generated files
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
branches:
|
||||
- main
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: guard-generated-files-${{ github.event.pull_request.number }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
check:
|
||||
name: Generated files must not be hand-edited
|
||||
runs-on: ubuntu-latest
|
||||
# release-please owns CHANGELOG.md and .release-please-manifest.json; its PR
|
||||
# is the one legitimate place those files change, so we exempt its author.
|
||||
if: >-
|
||||
github.event.pull_request.user.login != 'github-actions[bot]' &&
|
||||
github.event.pull_request.user.login != 'release-please[bot]'
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
# Need history on both sides of the merge-base so `git diff base...head`
|
||||
# reflects exactly what GitHub shows as "Files changed".
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Check PR does not modify release-please-generated files
|
||||
env:
|
||||
BASE_SHA: ${{ github.event.pull_request.base.sha }}
|
||||
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
|
||||
run: |
|
||||
set -eu
|
||||
files=$(git diff --name-only "${BASE_SHA}...${HEAD_SHA}")
|
||||
|
||||
violated=""
|
||||
for path in CHANGELOG.md .release-please-manifest.json; do
|
||||
if printf '%s\n' "$files" | grep -qxF -- "$path"; then
|
||||
violated="${violated} ${path}"
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -n "$violated" ]; then
|
||||
{
|
||||
echo "::error::This PR modifies release-please-generated files:${violated}"
|
||||
echo
|
||||
echo "CHANGELOG.md and .release-please-manifest.json are auto-generated by"
|
||||
echo "release-please from conventional commits on main. Do not hand-edit them."
|
||||
echo
|
||||
echo "If you want your change to appear in the next release notes, use a"
|
||||
echo "conventional commit message (feat/fix/docs/...). release-please will"
|
||||
echo "update these files in the next release PR automatically."
|
||||
echo
|
||||
echo "See CONTRIBUTING.md for the full contributor workflow."
|
||||
} >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "No release-please-generated files modified. OK."
|
||||
@@ -0,0 +1,54 @@
|
||||
name: Require no-mistakes
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, edited, synchronize, reopened]
|
||||
branches:
|
||||
- main
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: no-mistakes-required-${{ github.event.pull_request.number }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
check:
|
||||
name: PR must be raised via no-mistakes
|
||||
runs-on: ubuntu-latest
|
||||
# Known automation accounts are exempt so the release pipeline keeps working:
|
||||
# - github-actions[bot] opens the release-please PR via GITHUB_TOKEN
|
||||
# - dependabot[bot] opens dependency update PRs
|
||||
# Other authors (human or bot) must raise PRs through `git push no-mistakes`.
|
||||
if: >-
|
||||
github.event.pull_request.user.login != 'github-actions[bot]' &&
|
||||
github.event.pull_request.user.login != 'dependabot[bot]' &&
|
||||
github.event.pull_request.user.login != 'release-please[bot]'
|
||||
steps:
|
||||
- name: Verify no-mistakes signature in PR body
|
||||
env:
|
||||
PR_BODY: ${{ github.event.pull_request.body }}
|
||||
PR_AUTHOR: ${{ github.event.pull_request.user.login }}
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
run: |
|
||||
set -eu
|
||||
marker='Updates from [git push no-mistakes](https://github.com/kunchenguid/no-mistakes)'
|
||||
if printf '%s' "${PR_BODY:-}" | grep -qF -- "$marker"; then
|
||||
echo "Found no-mistakes signature in PR #${PR_NUMBER} body."
|
||||
exit 0
|
||||
fi
|
||||
{
|
||||
echo "::error::This PR was not raised through no-mistakes."
|
||||
echo
|
||||
echo "Contributions to this repository must be submitted via 'git push no-mistakes'."
|
||||
echo "That pipeline runs the required review/test/lint/CI steps and writes a"
|
||||
echo "deterministic '## Pipeline' section into the PR body containing:"
|
||||
echo
|
||||
echo " $marker"
|
||||
echo
|
||||
echo "See CONTRIBUTING.md for setup and the full workflow."
|
||||
echo
|
||||
echo "PR author: ${PR_AUTHOR}"
|
||||
} >&2
|
||||
exit 1
|
||||
@@ -0,0 +1,347 @@
|
||||
name: release
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
concurrency:
|
||||
group: release-${{ github.ref }}
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
release-please:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
release_created: ${{ steps.release.outputs.release_created }}
|
||||
tag_name: ${{ steps.release.outputs.tag_name }}
|
||||
version: ${{ steps.release.outputs.version }}
|
||||
steps:
|
||||
- uses: googleapis/release-please-action@v4
|
||||
id: release
|
||||
with:
|
||||
config-file: release-please-config.json
|
||||
manifest-file: .release-please-manifest.json
|
||||
|
||||
# macOS artifacts are built and Developer ID signed on a macOS runner (codesign
|
||||
# is macOS-only). Signing happens before archiving and before the checksums job
|
||||
# so the published tarball and its SHA-256 both cover the signed binary. The
|
||||
# signing certificate is gated behind the release-signing environment and never
|
||||
# leaves the runner. The "macOS Release Signing" section of AGENTS.md owns the
|
||||
# permanent-identity invariant and the full signing contract.
|
||||
build-darwin:
|
||||
runs-on: macos-latest
|
||||
environment: release-signing
|
||||
needs: release-please
|
||||
if: needs.release-please.outputs.release_created == 'true'
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- goos: darwin
|
||||
goarch: amd64
|
||||
- goos: darwin
|
||||
goarch: arm64
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ needs.release-please.outputs.tag_name }}
|
||||
|
||||
- uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
|
||||
- name: Build darwin binary
|
||||
env:
|
||||
GOOS: darwin
|
||||
GOARCH: ${{ matrix.goarch }}
|
||||
TAG: ${{ needs.release-please.outputs.tag_name }}
|
||||
UMAMI_HOST: https://a.kunchenguid.com
|
||||
UMAMI_WEBSITE_ID: f959e889-92f5-4121-8a1f-571b10861198
|
||||
run: |
|
||||
set -euo pipefail
|
||||
mkdir -p dist
|
||||
DATE="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
||||
COMMIT="$(git rev-parse --short=7 HEAD)"
|
||||
CGO_ENABLED=0 GOOS="$GOOS" GOARCH="$GOARCH" \
|
||||
go build -ldflags "-X github.com/kunchenguid/no-mistakes/internal/buildinfo.Version=${TAG} -X github.com/kunchenguid/no-mistakes/internal/buildinfo.Commit=${COMMIT} -X github.com/kunchenguid/no-mistakes/internal/buildinfo.Date=${DATE} -X github.com/kunchenguid/no-mistakes/internal/buildinfo.TelemetryHost=${UMAMI_HOST} -X github.com/kunchenguid/no-mistakes/internal/buildinfo.TelemetryWebsiteID=${UMAMI_WEBSITE_ID}" \
|
||||
-o "dist/no-mistakes" ./cmd/no-mistakes
|
||||
|
||||
# Import the Developer ID Application cert into an ephemeral keychain locked
|
||||
# with a runtime-generated password, discover exactly one signing identity
|
||||
# (fail closed otherwise), and sign with a fixed identifier, hardened runtime,
|
||||
# and secure timestamp. The cert (CSC_LINK) is base64 and only ever touches
|
||||
# a RUNNER_TEMP file that is deleted immediately after import.
|
||||
- name: Import Developer ID certificate and sign
|
||||
env:
|
||||
CSC_LINK: ${{ secrets.CSC_LINK }}
|
||||
CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
TEAM_ID="9T2J7MNUP9"
|
||||
if [ -z "${CSC_LINK:-}" ] || [ -z "${CSC_KEY_PASSWORD:-}" ]; then
|
||||
echo "::error::CSC_LINK/CSC_KEY_PASSWORD signing secrets are missing; refusing to publish an unsigned macOS artifact" >&2
|
||||
exit 1
|
||||
fi
|
||||
KEYCHAIN_PATH="$RUNNER_TEMP/nm-signing.keychain-db"
|
||||
KEYCHAIN_PASSWORD="$(openssl rand -base64 24)"
|
||||
CERT_PATH="$RUNNER_TEMP/nm-developer-id.p12"
|
||||
echo "KEYCHAIN_PATH=$KEYCHAIN_PATH" >> "$GITHUB_ENV"
|
||||
|
||||
# Reconstruct the cert from the base64 secret into RUNNER_TEMP only.
|
||||
printf '%s' "$CSC_LINK" | tr -d '[:space:]' | openssl base64 -A -d > "$CERT_PATH"
|
||||
|
||||
# Ephemeral keychain: create, unlock, import scoped to codesign, and add
|
||||
# to the search list so codesign can find the private key.
|
||||
security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
|
||||
security set-keychain-settings -lut 21600 "$KEYCHAIN_PATH"
|
||||
security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
|
||||
security import "$CERT_PATH" -P "$CSC_KEY_PASSWORD" -f pkcs12 \
|
||||
-k "$KEYCHAIN_PATH" -T /usr/bin/codesign
|
||||
security set-key-partition-list -S apple-tool:,apple:,codesign: \
|
||||
-s -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH" >/dev/null
|
||||
# Prepend the ephemeral keychain to the user search list, preserving the
|
||||
# existing (space-free) entries. Word splitting here is intentional.
|
||||
# shellcheck disable=SC2046
|
||||
security list-keychains -d user -s "$KEYCHAIN_PATH" \
|
||||
$(security list-keychains -d user | sed 's/["]//g')
|
||||
rm -f "$CERT_PATH"
|
||||
|
||||
# Fail closed unless exactly one Developer ID Application identity for the
|
||||
# expected Team ID is present.
|
||||
IDENTITIES="$(security find-identity -v -p codesigning "$KEYCHAIN_PATH" \
|
||||
| grep 'Developer ID Application' || true)"
|
||||
COUNT="$(printf '%s\n' "$IDENTITIES" | grep -c 'Developer ID Application' || true)"
|
||||
if [ "$COUNT" -ne 1 ]; then
|
||||
echo "::error::expected exactly one Developer ID Application identity, found $COUNT" >&2
|
||||
exit 1
|
||||
fi
|
||||
if ! printf '%s\n' "$IDENTITIES" | grep -q "($TEAM_ID)"; then
|
||||
echo "::error::signing identity does not belong to Team ID $TEAM_ID" >&2
|
||||
exit 1
|
||||
fi
|
||||
IDENTITY_HASH="$(printf '%s\n' "$IDENTITIES" | awk 'NR==1 {print $2}')"
|
||||
|
||||
codesign --force --timestamp --options runtime \
|
||||
--identifier com.kunchenguid.no-mistakes \
|
||||
--keychain "$KEYCHAIN_PATH" \
|
||||
--sign "$IDENTITY_HASH" \
|
||||
"dist/no-mistakes"
|
||||
|
||||
# Strict verification gate: any missing or ambiguous property fails the
|
||||
# release before the artifact is archived or uploaded.
|
||||
- name: Verify Developer ID signature
|
||||
env:
|
||||
GOARCH: ${{ matrix.goarch }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
TEAM_ID="9T2J7MNUP9"
|
||||
BIN="dist/no-mistakes"
|
||||
|
||||
case "$GOARCH" in
|
||||
amd64) EXPECTED_ARCH="x86_64" ;;
|
||||
arm64) EXPECTED_ARCH="arm64" ;;
|
||||
*) echo "::error::unexpected GOARCH $GOARCH" >&2; exit 1 ;;
|
||||
esac
|
||||
|
||||
# 1. Signature valid and strict.
|
||||
codesign --verify --strict --verbose=2 "$BIN"
|
||||
|
||||
# 2. Developer ID identity + Team ID + permanent identifier, not ad-hoc.
|
||||
SIG="$(codesign -dvvv "$BIN" 2>&1)"
|
||||
printf '%s\n' "$SIG"
|
||||
grep -q 'Authority=Developer ID Application' <<<"$SIG"
|
||||
grep -q "TeamIdentifier=$TEAM_ID" <<<"$SIG"
|
||||
grep -q 'Identifier=com.kunchenguid.no-mistakes' <<<"$SIG"
|
||||
if grep -qi 'adhoc' <<<"$SIG"; then
|
||||
echo "::error::signature is ad-hoc" >&2; exit 1
|
||||
fi
|
||||
|
||||
# 3. Hardened runtime enabled.
|
||||
if ! grep -Eq 'flags=0x[0-9a-f]+\([^)]*runtime[^)]*\)' <<<"$SIG"; then
|
||||
echo "::error::hardened runtime flag not set" >&2; exit 1
|
||||
fi
|
||||
|
||||
# 4. Secure (RFC-3161) timestamp present.
|
||||
TIMESTAMP="$(sed -n 's/^Timestamp=//p' <<<"$SIG")"
|
||||
if [[ -z "$TIMESTAMP" ]] || grep -qi '^none$' <<<"$TIMESTAMP"; then
|
||||
echo "::error::secure timestamp is missing" >&2; exit 1
|
||||
fi
|
||||
|
||||
# 5. Designated requirement is identity-based, never a content hash.
|
||||
DR="$(codesign -d -r- "$BIN" 2>&1)"
|
||||
printf '%s\n' "$DR"
|
||||
grep -q 'anchor apple generic' <<<"$DR"
|
||||
# The Team ID starts with a digit so codesign quotes it, but accept the
|
||||
# unquoted form too so a codesign quirk cannot fail a valid release.
|
||||
grep -Eq "leaf\[subject.OU][[:space:]]*=[[:space:]]*\"?$TEAM_ID\"?" <<<"$DR"
|
||||
grep -q 'identifier "com.kunchenguid.no-mistakes"' <<<"$DR"
|
||||
if grep -q 'cdhash H' <<<"$DR"; then
|
||||
echo "::error::designated requirement is content-based (cdhash), not identity-based" >&2; exit 1
|
||||
fi
|
||||
|
||||
# 6. Correct architecture for this matrix leg.
|
||||
ARCHS="$(lipo -archs "$BIN")"
|
||||
if [ "$ARCHS" != "$EXPECTED_ARCH" ]; then
|
||||
echo "::error::architecture mismatch: got '$ARCHS', want '$EXPECTED_ARCH'" >&2; exit 1
|
||||
fi
|
||||
|
||||
- name: Archive signed binary
|
||||
env:
|
||||
GOOS: darwin
|
||||
GOARCH: ${{ matrix.goarch }}
|
||||
TAG: ${{ needs.release-please.outputs.tag_name }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
ARCHIVE="dist/no-mistakes-${TAG}-${GOOS}-${GOARCH}.tar.gz"
|
||||
tar -C dist -czf "$ARCHIVE" no-mistakes
|
||||
echo "ARCHIVE=$ARCHIVE" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Upload release asset
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
TAG: ${{ needs.release-please.outputs.tag_name }}
|
||||
run: gh release upload "$TAG" "$ARCHIVE" --clobber
|
||||
|
||||
- uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: archive-${{ matrix.goos }}-${{ matrix.goarch }}
|
||||
path: ${{ env.ARCHIVE }}
|
||||
|
||||
# Tear down the ephemeral keychain on success and failure alike.
|
||||
- name: Clean up signing keychain
|
||||
if: always()
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if [ -n "${KEYCHAIN_PATH:-}" ]; then
|
||||
security delete-keychain "$KEYCHAIN_PATH" 2>/dev/null || true
|
||||
fi
|
||||
rm -f "$RUNNER_TEMP/nm-developer-id.p12" 2>/dev/null || true
|
||||
|
||||
build-and-upload:
|
||||
runs-on: ubuntu-latest
|
||||
needs: release-please
|
||||
if: needs.release-please.outputs.release_created == 'true'
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- goos: linux
|
||||
goarch: amd64
|
||||
- goos: linux
|
||||
goarch: arm64
|
||||
- goos: windows
|
||||
goarch: amd64
|
||||
- goos: windows
|
||||
goarch: arm64
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ needs.release-please.outputs.tag_name }}
|
||||
|
||||
- uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
|
||||
- name: Build archive
|
||||
env:
|
||||
GOOS: ${{ matrix.goos }}
|
||||
GOARCH: ${{ matrix.goarch }}
|
||||
TAG: ${{ needs.release-please.outputs.tag_name }}
|
||||
UMAMI_HOST: https://a.kunchenguid.com
|
||||
UMAMI_WEBSITE_ID: f959e889-92f5-4121-8a1f-571b10861198
|
||||
run: |
|
||||
set -euo pipefail
|
||||
mkdir -p dist
|
||||
DATE="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
||||
COMMIT="$(git rev-parse --short=7 HEAD)"
|
||||
BIN="no-mistakes"
|
||||
OUT="dist/${BIN}"
|
||||
if [ "$GOOS" = "windows" ]; then
|
||||
BIN="${BIN}.exe"
|
||||
OUT="dist/${BIN}"
|
||||
fi
|
||||
CGO_ENABLED=0 GOOS="$GOOS" GOARCH="$GOARCH" \
|
||||
go build -ldflags "-X github.com/kunchenguid/no-mistakes/internal/buildinfo.Version=${TAG} -X github.com/kunchenguid/no-mistakes/internal/buildinfo.Commit=${COMMIT} -X github.com/kunchenguid/no-mistakes/internal/buildinfo.Date=${DATE} -X github.com/kunchenguid/no-mistakes/internal/buildinfo.TelemetryHost=${UMAMI_HOST} -X github.com/kunchenguid/no-mistakes/internal/buildinfo.TelemetryWebsiteID=${UMAMI_WEBSITE_ID}" \
|
||||
-o "$OUT" ./cmd/no-mistakes
|
||||
if [ "$GOOS" = "windows" ]; then
|
||||
ARCHIVE="dist/no-mistakes-${TAG}-${GOOS}-${GOARCH}.zip"
|
||||
(
|
||||
cd dist
|
||||
zip -q "$(basename "$ARCHIVE")" "$BIN"
|
||||
)
|
||||
else
|
||||
ARCHIVE="dist/no-mistakes-${TAG}-${GOOS}-${GOARCH}.tar.gz"
|
||||
tar -C dist -czf "$ARCHIVE" "$BIN"
|
||||
fi
|
||||
echo "ARCHIVE=$ARCHIVE" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Upload release asset
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
TAG: ${{ needs.release-please.outputs.tag_name }}
|
||||
run: gh release upload "$TAG" "$ARCHIVE" --clobber
|
||||
|
||||
- uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: archive-${{ matrix.goos }}-${{ matrix.goarch }}
|
||||
path: ${{ env.ARCHIVE }}
|
||||
|
||||
checksums:
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- release-please
|
||||
- build-darwin
|
||||
- build-and-upload
|
||||
if: |
|
||||
!cancelled() &&
|
||||
needs.release-please.result == 'success' &&
|
||||
needs.build-darwin.result == 'success' &&
|
||||
needs.build-and-upload.result == 'success' &&
|
||||
needs.release-please.outputs.release_created == 'true'
|
||||
steps:
|
||||
- uses: actions/download-artifact@v8
|
||||
with:
|
||||
path: dist
|
||||
merge-multiple: true
|
||||
|
||||
- name: Generate checksums
|
||||
run: |
|
||||
set -euo pipefail
|
||||
(
|
||||
cd dist
|
||||
sha256sum no-mistakes-* > ../checksums.txt
|
||||
)
|
||||
|
||||
- name: Upload checksums
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
GH_REPO: ${{ github.repository }}
|
||||
TAG: ${{ needs.release-please.outputs.tag_name }}
|
||||
run: gh release upload "$TAG" checksums.txt --clobber
|
||||
|
||||
finalize:
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- release-please
|
||||
- build-darwin
|
||||
- build-and-upload
|
||||
- checksums
|
||||
if: |
|
||||
!cancelled() &&
|
||||
needs.release-please.result == 'success' &&
|
||||
needs.build-darwin.result == 'success' &&
|
||||
needs.build-and-upload.result == 'success' &&
|
||||
needs.checksums.result == 'success' &&
|
||||
needs.release-please.outputs.release_created == 'true'
|
||||
steps:
|
||||
- name: Publish draft release as prerelease
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
GH_REPO: ${{ github.repository }}
|
||||
TAG: ${{ needs.release-please.outputs.tag_name }}
|
||||
run: gh release edit "$TAG" --draft=false --prerelease=true
|
||||
Reference in New Issue
Block a user