diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c2d56ff..17bb4d8 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -11,34 +11,63 @@ permissions: jobs: release: runs-on: ubuntu-latest + permissions: + contents: write + packages: write + id-token: write + env: + # Map first: secrets cannot be referenced in steps.if. + CODESIGN_URL: ${{ secrets.CODESIGN_URL }} steps: - - uses: actions/checkout@v7 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 0 - name: Import GPG key id: import_gpg - uses: crazy-max/ghaction-import-gpg@v7 + uses: crazy-max/ghaction-import-gpg@2dc316deee8e90f13e1a351ab510b4d5bc0c82cd # v7.0.0 with: gpg_private_key: ${{ secrets.GPG_SIGNING_KEY }} - name: "Create GPG_SIGNING_KEY file" run: "echo '${{ secrets.GPG_SIGNING_KEY }}' > /tmp/key.gpg" - name: "Save Iteration in Revision" run: echo REVISION=$(git rev-list --count --all || echo 0) > $GITHUB_ENV - - uses: actions/setup-go@v7 + - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: - go-version-file: 'go.mod' + go-version-file: go.mod cache: true - - uses: goreleaser/goreleaser-action@v7 + - name: Install golift codesign + env: + GOBIN: ${{ runner.temp }}/codesign-bin + run: | + set -euo pipefail + mkdir -p "${GOBIN}" + go install golift.io/codesign/cmd/codesign@v1.0.3 + echo "${GOBIN}" >> "${GITHUB_PATH}" + echo "CODESIGN_BIN=${GOBIN}/codesign" >> "${GITHUB_ENV}" + - uses: goreleaser/goreleaser-action@f06c13b6b1a9625abc9e6e439d9c05a8f2190e94 # v7.2.3 with: - version: nightly - args: release --clean + distribution: goreleaser-pro + version: "~> v2" + args: release --clean --timeout 60m env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - # Use a fine grained PAT with Contents: R/W on golift/homebrew-mugs. + GORELEASER_KEY: ${{ secrets.GORELEASER_PRO_KEY }} + # Fine grained PAT with Contents: R/W on golift/homebrew-mugs. HOMEBREW_TAP_GITHUB_TOKEN: ${{ secrets.HOMEBREW_TAP_GITHUB_TOKEN }} + USER: github-actions GPG_SIGNING_KEY: /tmp/key.gpg GPG_SIGNING_KEY_ID: ${{ steps.import_gpg.outputs.keyid }} - - uses: golift/upload-packagecloud@v1 + CODESIGN_URL: ${{ secrets.CODESIGN_URL }} + CODESIGN_CLIENT_CERT: ${{ secrets.CODESIGN_CLIENT_CERT }} + CODESIGN_CLIENT_KEY: ${{ secrets.CODESIGN_CLIENT_KEY }} + CODESIGN_NAME: xt + CODESIGN_WEBSITE: https://unpackerr.zip/xt + MACOS_SIGN_P12: ${{ secrets.MACOS_SIGN_P12 }} + MACOS_SIGN_PASSWORD: ${{ secrets.MACOS_SIGN_PASSWORD }} + MACOS_NOTARY_KEY: ${{ secrets.MACOS_NOTARY_KEY }} + MACOS_NOTARY_KEY_ID: ${{ secrets.MACOS_NOTARY_KEY_ID }} + MACOS_NOTARY_ISSUER_ID: ${{ secrets.MACOS_NOTARY_ISSUER_ID }} + - uses: golift/upload-packagecloud@eb0067369b59340b04dce218010960ebe74720b2 # v1.1.1 with: userrepo: golift/pkgs apitoken: ${{ secrets.PACKAGECLOUD_TOKEN }} diff --git a/.goreleaser.yaml b/.goreleaser.yaml index aa8ee4b..3e8340a 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -1,3 +1,4 @@ +# yaml-language-server: $schema=https://goreleaser.com/static/schema.json version: 2 before: @@ -10,7 +11,8 @@ before: - gzip -9 MANUAL builds: - - env: + - id: xt + env: - CGO_ENABLED=0 - CGO_LDFLAGS=-mmacosx-version-min=10.8 - CGO_CFLAGS=-mmacosx-version-min=10.8 @@ -38,9 +40,31 @@ builds: goarch: arm - goos: darwin goarch: '386' + hooks: + post: + - cmd: bash init/windows/signexe.sh "{{ .Path }}" + if: '{{ eq .Os "windows" }}' universal_binaries: - - replace: true + - id: xt + replace: true + +# Sign + notarize the macOS universal binary (quill; works on Linux CI). +# Enabled only when MACOS_SIGN_P12 is set so local snapshots still work. +notarize: + macos: + - enabled: '{{ isEnvSet "MACOS_SIGN_P12" }}' + ids: + - xt + sign: + certificate: "{{ .Env.MACOS_SIGN_P12 }}" + password: "{{ .Env.MACOS_SIGN_PASSWORD }}" + notarize: + issuer_id: "{{ .Env.MACOS_NOTARY_ISSUER_ID }}" + key_id: "{{ .Env.MACOS_NOTARY_KEY_ID }}" + key: "{{ .Env.MACOS_NOTARY_KEY }}" + wait: true + timeout: 20m archives: - formats: ['tar.gz'] diff --git a/init/windows/signexe.sh b/init/windows/signexe.sh new file mode 100755 index 0000000..2c8808e --- /dev/null +++ b/init/windows/signexe.sh @@ -0,0 +1,54 @@ +#!/usr/bin/env bash + +set -e -o pipefail + +# Authenticode-sign a Windows PE via golift/codesign (YubiKey-backed signerd). +# GoReleaser calls this from builds.hooks.post on windows binaries, after the +# CLI is installed into a temp GOBIN (never /usr/bin/codesign). +# +# Skip when CODESIGN_URL is unset so local snapshots still work. +# Prefer CODESIGN_BIN, then GOBIN/codesign, then GOPATH/bin, then PATH +# entries that are not Apple's /usr/bin/codesign. + +function pick_codesign() { + if [ -n "${CODESIGN_BIN:-}" ]; then + echo "${CODESIGN_BIN}" + return + fi + if [ -n "${GOBIN:-}" ] && [ -x "${GOBIN}/codesign" ]; then + echo "${GOBIN}/codesign" + return + fi + gopath="$(go env GOPATH 2>/dev/null || true)" + if [ -n "${gopath}" ] && [ -x "${gopath}/bin/codesign" ]; then + echo "${gopath}/bin/codesign" + return + fi + while IFS= read -r p; do + case "$p" in + /usr/bin/codesign|/bin/codesign) continue ;; + esac + echo "$p" + return + done < <(type -a -p codesign 2>/dev/null || true) + return 1 +} + +function sign() { + if [ -z "${CODESIGN_URL:-}" ]; then + echo "Skipped signing ${FILE} (CODESIGN_URL unset) .." >&2 + exit 0 + fi + + bin="$(pick_codesign)" || { + echo "CODESIGN_URL is set but golift codesign CLI not found (set CODESIGN_BIN)" >&2 + exit 1 + } + + CODESIGN_NAME="${CODESIGN_NAME:-xt}" \ + CODESIGN_WEBSITE="${CODESIGN_WEBSITE:-https://unpackerr.zip/xt}" \ + "${bin}" -- "${FILE}" + echo "Signed ${FILE} .." >&2 +} + +[ -z "$1" ] || FILE="$1" sign