From 051fea396ac7222f8edc8f05b67513dc0a0453f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Buli=C5=84ski?= Date: Tue, 3 Feb 2026 15:49:26 +0100 Subject: [PATCH 01/20] save --- .editorconfig | 3 ++ .github/workflows/release.yml | 40 +++++++++++++++++++++ .goreleaser.yaml | 67 +++++++++++++++++++++++++++++++++++ 3 files changed, 110 insertions(+) create mode 100644 .editorconfig create mode 100644 .github/workflows/release.yml create mode 100644 .goreleaser.yaml diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..e57157e --- /dev/null +++ b/.editorconfig @@ -0,0 +1,3 @@ +[*.yml] +indent_style = space +indent_size = 2 \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..ee9ca61 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,40 @@ +name: release + +on: + push: + tags: + - "v1.*.*" + +permissions: + contents: write # needed to write releases + id-token: write # needed for keyless signing + attestations: write # needed for provenance + +jobs: + release: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 # this is important, otherwise it won't checkout the full tree (i.e. no previous tags) + + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version: stable + + - name: Install Cosign for attestation and signing + uses: sigstore/cosign-installer@v3.9.2 + + - name: Install Syft for SBOM generation + uses: anchore/sbom-action/download-syft@v0.20.4 + + - name: Run GoReleaser + uses: goreleaser/goreleaser-action@v6 + with: + version: ~> v2 + args: release --clean + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.goreleaser.yaml b/.goreleaser.yaml new file mode 100644 index 0000000..c4a37ec --- /dev/null +++ b/.goreleaser.yaml @@ -0,0 +1,67 @@ +version: 2 + +project_name: qbee-cli + +builds: + - id: qbee-cli + + env: + - CGO_ENABLED=0 + + gcflags: + - -trimpath + + ldflags: + - -s + - -w + - -X go.qbee.io/client.Version={{.Version}} + + targets: + - darwin_amd64 + - darwin_arm64 + - windows_amd64 + - linux_amd64 + + mod_timestamp: "{{ .CommitTimestamp }}" + +gomod: + proxy: true + +changelog: + use: github-native + +sboms: + - artifacts: source + +checksum: + name_template: checksums.txt + +signs: + - cmd: cosign + certificate: "${artifact}.pem" + args: + - sign-blob + - "--output-certificate=${certificate}" + - "--output-signature=${signature}" + - "${artifact}" + - "--yes" + artifacts: checksum + output: true + +source: + enabled: true + +archives: + - formats: [ 'tar.gz' ] + +notarize: + macos_native: + - enabled: "true" + sign: + keychain: "{{ .Env.KEYCHAIN_PATH }}" + identity: "{{ .Env.MACOS_SIGN_IDENTITY }}" + options: [runtime] + + notarize: + profile_name: "{{ .Env.MACOS_NOTARY_PROFILE_NAME }}" + wait: true \ No newline at end of file From b885ae1438f0f458ec87fd3db0c1482a35331d5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Buli=C5=84ski?= Date: Tue, 3 Feb 2026 15:51:20 +0100 Subject: [PATCH 02/20] save --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ee9ca61..215d2e7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -3,7 +3,7 @@ name: release on: push: tags: - - "v1.*.*" + - "v1" permissions: contents: write # needed to write releases From 8429e49a9d03dc81204f23473a60e04d92a3adcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Buli=C5=84ski?= Date: Tue, 3 Feb 2026 16:02:56 +0100 Subject: [PATCH 03/20] save --- .github/workflows/release.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 215d2e7..1ebdfdb 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -3,8 +3,9 @@ name: release on: push: tags: - - "v1" + - "v1." + permissions: contents: write # needed to write releases id-token: write # needed for keyless signing From b7044367c3597d41ad467b95bb28dcfe0fcf5921 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Buli=C5=84ski?= Date: Tue, 3 Feb 2026 16:03:30 +0100 Subject: [PATCH 04/20] save --- .github/workflows/release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1ebdfdb..9864ece 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -3,9 +3,9 @@ name: release on: push: tags: - - "v1." + - "v1.*" + - permissions: contents: write # needed to write releases id-token: write # needed for keyless signing From 0e230d2614afe5b8e467d2ccd712c039492e94db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Buli=C5=84ski?= Date: Tue, 3 Feb 2026 16:04:46 +0100 Subject: [PATCH 05/20] save --- .github/workflows/release.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9864ece..2e8b356 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -5,7 +5,6 @@ on: tags: - "v1.*" - permissions: contents: write # needed to write releases id-token: write # needed for keyless signing From 27eea860f397f7f823ea455fa7086dc2a961ec1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Buli=C5=84ski?= Date: Tue, 3 Feb 2026 16:08:25 +0100 Subject: [PATCH 06/20] save --- .github/workflows/release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2e8b356..f66fd8f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -2,8 +2,8 @@ name: release on: push: - tags: - - "v1.*" + branches: + - main permissions: contents: write # needed to write releases From fde9094a6d268402d9ac517400e3da11514c7991 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Buli=C5=84ski?= Date: Tue, 3 Feb 2026 16:12:11 +0100 Subject: [PATCH 07/20] save --- .github/workflows/release.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f66fd8f..847669b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,9 +1,8 @@ name: release on: - push: - branches: - - main + release: + types: [prereleased] permissions: contents: write # needed to write releases From 89ff1f376b4a840972311d6270b318e98e2db1e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Buli=C5=84ski?= Date: Tue, 3 Feb 2026 16:16:55 +0100 Subject: [PATCH 08/20] save --- .github/workflows/release.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 847669b..3a5f2ff 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -33,6 +33,7 @@ jobs: - name: Run GoReleaser uses: goreleaser/goreleaser-action@v6 with: + distribution: goreleaser-pro version: ~> v2 args: release --clean env: From 89605876c3af7591ba008eedcd0f6232021e808a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Buli=C5=84ski?= Date: Tue, 3 Feb 2026 16:18:58 +0100 Subject: [PATCH 09/20] save --- .github/workflows/release.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3a5f2ff..402afc1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -38,3 +38,4 @@ jobs: args: release --clean env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }} From 48cdddced3031eeed23ccbed7ddf40afdc924493 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Buli=C5=84ski?= Date: Tue, 3 Feb 2026 16:21:30 +0100 Subject: [PATCH 10/20] save --- .goreleaser.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.goreleaser.yaml b/.goreleaser.yaml index c4a37ec..fd1e761 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -24,8 +24,8 @@ builds: mod_timestamp: "{{ .CommitTimestamp }}" -gomod: - proxy: true +# gomod: + # proxy: true changelog: use: github-native From 67b30358d5afa7f18c541b881c4fbbb861512b70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Buli=C5=84ski?= Date: Tue, 3 Feb 2026 16:25:58 +0100 Subject: [PATCH 11/20] save --- .goreleaser.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.goreleaser.yaml b/.goreleaser.yaml index fd1e761..9db4965 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -5,6 +5,8 @@ project_name: qbee-cli builds: - id: qbee-cli + main: ./cmd/ + env: - CGO_ENABLED=0 From 3917bf4e4674469b5784e7b709d35bf711b6867d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Buli=C5=84ski?= Date: Tue, 3 Feb 2026 16:53:02 +0100 Subject: [PATCH 12/20] save --- .github/workflows/release.yml | 5 +++++ .goreleaser.yaml | 15 ++++++--------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 402afc1..a175c62 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -39,3 +39,8 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }} + 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 }} diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 9db4965..c627f12 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -53,17 +53,14 @@ signs: source: enabled: true -archives: - - formats: [ 'tar.gz' ] - notarize: - macos_native: + macos: - enabled: "true" sign: - keychain: "{{ .Env.KEYCHAIN_PATH }}" - identity: "{{ .Env.MACOS_SIGN_IDENTITY }}" - options: [runtime] + certificate: "{{ .Env.MACOS_SIGN_P12 }}" + password: "{{.Env.MACOS_SIGN_PASSWORD}}" notarize: - profile_name: "{{ .Env.MACOS_NOTARY_PROFILE_NAME }}" - wait: true \ No newline at end of file + issuer_id: "{{.Env.MACOS_NOTARY_ISSUER_ID}}" + key_id: "{{.Env.MACOS_NOTARY_KEY_ID}}" + key: "{{.Env.MACOS_NOTARY_KEY}}" From a19f61892445830ac9bded77aaabf64e2c6bb526 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Buli=C5=84ski?= Date: Sat, 21 Feb 2026 15:52:27 +0100 Subject: [PATCH 13/20] Switch from GoReleaser to own release process. --- .github/utils/entitlements.plist | 9 ++ .github/utils/mac-sign-and-notarize.sh | 104 ++++++++++++++ .github/workflows/release.yml | 184 +++++++++++++++++++++---- .goreleaser.yaml | 66 --------- 4 files changed, 271 insertions(+), 92 deletions(-) create mode 100644 .github/utils/entitlements.plist create mode 100644 .github/utils/mac-sign-and-notarize.sh delete mode 100644 .goreleaser.yaml diff --git a/.github/utils/entitlements.plist b/.github/utils/entitlements.plist new file mode 100644 index 0000000..59cf377 --- /dev/null +++ b/.github/utils/entitlements.plist @@ -0,0 +1,9 @@ + + + + com.apple.security.network.server + + com.apple.security.network.client + + + \ No newline at end of file diff --git a/.github/utils/mac-sign-and-notarize.sh b/.github/utils/mac-sign-and-notarize.sh new file mode 100644 index 0000000..9b61360 --- /dev/null +++ b/.github/utils/mac-sign-and-notarize.sh @@ -0,0 +1,104 @@ +#!/usr/bin/env bash +# +# This script signs and notarizes a binary on macOS. +# It performs the following steps: +# 1. Creates a temporary keychain and imports the signing certificate from a base64-encoded .p12 file provided via environment variable. +# 2. Signs the app using the imported certificate. +# 3. Notarizes the app using Apple's notarytool. +# +# [1] - https://developer.apple.com/help/account/certificates/create-developer-id-certificates/ +# [2] - https://developer.apple.com/documentation/xcode/creating-distribution-signed-code-for-the-mac +# [3] - https://developer.apple.com/documentation/security/customizing-the-notarization-workflow#Upload-your-app-to-the-notarization-service + +set -eu + +# Script Configuration +APP_PATH=qbee-cli +KEYCHAIN_TTL=300 # 5 minutes + +# Temporary working directory for keychain and certificate handling +WORKDIR="${TMPDIR:-/tmp}/mac-sign-and-notarize.$$" + +# Helper functions +die() { echo "Error: $*" >&2; exit 1; } +info(){ echo "==> $*"; } +cleanup(){ [ -d "$WORKDIR" ] && rm -rf "$WORKDIR"; } +trap cleanup EXIT INT TERM + +# Validate required environment variables +if [ -z "${MACOS_SIGN_P12:-}" ]; then + die "MACOS_SIGN_P12 must be set to the base64-encoded .p12 certificate" +fi + +if [ -z "${MACOS_SIGN_PASSWORD:-}" ]; then + die "MACOS_SIGN_PASSWORD must be set to the password for the .p12 certificate" +fi + +if [ -z "${APPLE_CODE_NOTARY_EMAIL:-}" ]; then + die "APPLE_CODE_NOTARY_EMAIL must be set to the Apple ID for notarization" +fi + +if [ -z "${APPLE_CODE_NOTARY_PASSWORD:-}" ]; then + die "APPLE_CODE_NOTARY_PASSWORD must be set to the password for the Apple ID for notarization" +fi + +CERT_P12_PATH=${WORKDIR}/cert.p12 +KEYCHAIN_PATH=${WORKDIR}/keychain-db + +info "Generating random password for temporary keychain" +KEYCHAIN_PASSWORD=$(openssl rand -base64 32) + +info "Creating temporary working directory at $WORKDIR" +mkdir -p "$WORKDIR" || die "Cannot create temp dir" + +info "Decoding .p12 certificate and saving to $CERT_P12_PATH" +echo -n "$MACOS_SIGN_P12" | base64 --decode -o $CERT_P12_PATH + +info "Extracting signing identity from certificate" +MACOS_SIGN_IDENTITY=$( + openssl pkcs12 -legacy -in $CERT_P12_PATH -nokeys -passin env:MACOS_SIGN_PASSWORD -clcerts | \ + openssl x509 -noout -subject -nameopt sep_multiline,lname | \ + grep "commonName" | \ + cut -d= -f2) + +info $MACOS_SIGN_IDENTITY + +info "Extracting Apple Team ID from certificate" +APPLE_TEAM_ID=$( + openssl pkcs12 -legacy -in $CERT_P12_PATH -nokeys -passin env:MACOS_SIGN_PASSWORD -clcerts | \ + openssl x509 -noout -subject -nameopt sep_multiline,lname | \ + grep "organizationalUnitName" | \ + cut -d= -f2) + +info $APPLE_TEAM_ID + +info "Creating temporary keychain at $KEYCHAIN_PATH" +security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH + +info "Setting keychain settings to prevent locking for $KEYCHAIN_TTL seconds" +security set-keychain-settings -lut $KEYCHAIN_TTL $KEYCHAIN_PATH + +info "Unlocking keychain" +security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH + +info "Importing certificate into keychain" +security import $CERT_P12_PATH -k $KEYCHAIN_PATH -P "$MACOS_SIGN_PASSWORD" -T /usr/bin/codesign + +info "Allowing codesign to access the keychain item" +security set-key-partition-list -S apple-tool:,apple: -k "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH + +info "Setting keychain search path to include temporary keychain" +security list-keychains -d user -s $KEYCHAIN_PATH login.keychain + +info "Signing $APP_PATH with $MACOS_SIGN_IDENTITY certificate" +codesign --keychain $KEYCHAIN_PATH --force --options runtime --entitlements .github/utils/entitlements.plist --sign "$MACOS_SIGN_IDENTITY" --timestamp $APP_PATH + +info "Storing notarization credentials in keychain for notarytool" +xcrun notarytool store-credentials "notarytool-password" \ + --keychain "$KEYCHAIN_PATH" \ + --apple-id "$APPLE_CODE_NOTARY_EMAIL" \ + --password "$APPLE_CODE_NOTARY_PASSWORD" \ + --team-id "$APPLE_TEAM_ID" + +info "Notarizing binary with Apple Notary Service" +xcrun notarytool submit --keychain-profile "notarytool-password" --wait "$APP_PATH" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a175c62..9877e6e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,46 +1,178 @@ -name: release - -on: - release: - types: [prereleased] +name: Release qbee-cli on GitHub permissions: contents: write # needed to write releases id-token: write # needed for keyless signing attestations: write # needed for provenance -jobs: +on: release: - runs-on: ubuntu-latest + types: [prereleased] + +env: + # VERSION is set to the release tag name, e.g., "v1.0.0". + #VERSION: ${{ github.ref_name }} + VERSION: v1.2026.8 + + # NAME is the name of the application. + NAME: qbee-cli + + # CGO_ENABLED disables CGO. + CGO_ENABLED: 0 + + # GCFLAGS with -trimpath ensures that the build is reproducible and does not contain file system paths. + GCFLAGS: -trimpath + # LDFLAGS with -s and -w reduces the binary size by omitting symbol table and debug information. + # The -X flag sets the Version variable in the code to the release version. + # DFLAGS: -s -w -X go.qbee.io/client.Version=${{ github.ref_name }} + LDFLAGS: -s -w -X go.qbee.io/client.Version=v1.2026.8 + + # GOPROXY ensures that we use the correct Go module proxy. + # We want our build process to start with populateding the Go proxy + # with the freshly tagged version of the module. + GOPROXY: https://proxy.golang.org + +jobs: + go-proxy: + name: Go Proxy + runs-on: ubuntu-24.04 steps: - - name: Checkout code - uses: actions/checkout@v4 + - name: Populate Go module proxy cache + run: go get go.qbee.io/client@${{ env.VERSION }} > /dev/null + + linux: + name: Linux Binaries + runs-on: ubuntu-24.04 + needs: go-proxy + outputs: + amd64_sha256: ${{ steps.release.outputs.amd64_sha256 }} + steps: + - name: Checkout repository + uses: actions/checkout@v6 with: - fetch-depth: 0 # this is important, otherwise it won't checkout the full tree (i.e. no previous tags) + fetch-depth: 0 - - name: Setup Go - uses: actions/setup-go@v5 + - name: Install stable Go version + uses: actions/setup-go@v6 with: go-version: stable - - name: Install Cosign for attestation and signing - uses: sigstore/cosign-installer@v3.9.2 + - name: Build binary + run: go build $GCFLAGS -ldflags "$LDFLAGS" -o ${{ env.NAME }} ./cmd - - name: Install Syft for SBOM generation - uses: anchore/sbom-action/download-syft@v0.20.4 + - name: Create archive and upload to the release + id: release + env: + GH_TOKEN: ${{ github.token }} + ARCHIVE_NAME: ${{ env.NAME }}_linux_amd64_${{ env.VERSION }}.tar.gz + run: | + tar -czvf $ARCHIVE_NAME ${{ env.NAME }} LICENSE README.md + gh release upload ${{ env.VERSION }} $ARCHIVE_NAME + echo "amd64_sha256=$(sha256sum ${{ env.NAME }})" >> $GITHUB_OUTPUT - - name: Run GoReleaser - uses: goreleaser/goreleaser-action@v6 + windows: + name: Windows Signed Binary + runs-on: windows-2025 + needs: go-proxy + env: + # The following environment variables are needed for the DigiCert Software Trust signing action. + # https://docs.digicert.com/en/digicert-keylocker/ci-cd-integrations-and-deployment-pipelines/plugins/github/binary-signing-using-github-actions.html + # Following credentials are from Qbee GitHub Actions service user account. + SM_HOST: ${{ vars.SM_HOST }} + SM_API_KEY: ${{ secrets.SM_API_KEY }} + SM_CLIENT_CERT_FILE: Certificate_pkcs12.p12 + SM_CLIENT_CERT_PASSWORD: ${{ secrets.SM_CLIENT_CERT_PASSWORD }} + outputs: + amd64_sha256: ${{ steps.release.outputs.amd64_sha256 }} + steps: + - name: Checkout repository + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Install stable Go version + uses: actions/setup-go@v6 with: - distribution: goreleaser-pro - version: ~> v2 - args: release --clean + go-version: stable + + - name: Setup DigiCert KeyLocker + shell: bash + run: | + echo "${{ secrets.SM_CLIENT_CERT_FILE_B64 }}" | base64 --decode > ${{ env.SM_CLIENT_CERT_FILE }} + + - name: Build binary + shell: bash + run: go build $GCFLAGS -ldflags "$LDFLAGS" -o ${{ env.NAME }}.exe ./cmd + + - name: Sign the binary + uses: digicert/code-signing-software-trust-action@v1 + with: + simple-signing-mode: true + keypair-alias: ${{ secrets.SM_KEYPAIR_ALIAS }} + input: ${{ env.NAME }}.exe + + - name: Create archive and upload to the release + id: release + shell: bash env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }} + GH_TOKEN: ${{ github.token }} + ARCHIVE_NAME: ${{ env.NAME }}_windows_amd64_${{ env.VERSION }}.zip + run: | + 7z a $ARCHIVE_NAME ${{ env.NAME }}.exe LICENSE README.md + gh release upload ${{ env.VERSION }} $ARCHIVE_NAME + echo "amd64_sha256=$(sha256sum ${{ env.ARCHIVE_NAME }})" >> $GITHUB_OUTPUT + + macos: + name: MacOS Signed Binary + runs-on: macos-26 + needs: go-proxy + strategy: + matrix: + arch: [amd64, arm64] + outputs: + amd64_sha256: ${{ steps.release.outputs.amd64_sha256 }} + arm64_sha256: ${{ steps.release.outputs.arm64_sha256 }} + steps: + - name: Checkout repository + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Install stable Go version + uses: actions/setup-go@v6 + with: + go-version: stable + + - name: Build binary for ${{ matrix.arch }} + env: + GOARCH: ${{ matrix.arch }} + run: go build $GCFLAGS -ldflags "$LDFLAGS" -o ${{ env.NAME }} ./cmd + + - name: Sign and package + env: + TMPDIR: ${{ runner.temp }} + + # MACOS_SIGN_P12 is the base64-encoded .p12 certificate file for Developer ID Application certificate type. + # The certificate can be created and downloaded from the Apple Developer account, Certificates section. MACOS_SIGN_P12: ${{ secrets.MACOS_SIGN_P12 }} + + # MACOS_SIGN_PASSWORD is the password for the .p12 certificate file. 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 }} + + # APPLE_CODE_NOTARY_EMAIL is the Apple ID used for notarization. + # The user needs to have App Manager role. + APPLE_CODE_NOTARY_EMAIL: ${{ secrets.APPLE_CODE_NOTARY_EMAIL }} + + # APPLE_CODE_NOTARY_PASSWORD is the password for the Apple ID used for notarization + APPLE_CODE_NOTARY_PASSWORD: ${{ secrets.APPLE_CODE_NOTARY_PASSWORD }} + + run: bash ./.github/utils/mac-sign-and-notarize.sh + + - name: Upload DMG to the release + env: + GH_TOKEN: ${{ github.token }} + ARCHIVE_NAME: ${{ env.NAME }}_darwin_${{ matrix.arch }}.zip + run: | + ditto -c -k --keepParent ${{ env.NAME }} ${{ env.ARCHIVE_NAME }} + gh release upload ${{ env.VERSION }} ${{ env.ARCHIVE_NAME }} --clobber diff --git a/.goreleaser.yaml b/.goreleaser.yaml deleted file mode 100644 index c627f12..0000000 --- a/.goreleaser.yaml +++ /dev/null @@ -1,66 +0,0 @@ -version: 2 - -project_name: qbee-cli - -builds: - - id: qbee-cli - - main: ./cmd/ - - env: - - CGO_ENABLED=0 - - gcflags: - - -trimpath - - ldflags: - - -s - - -w - - -X go.qbee.io/client.Version={{.Version}} - - targets: - - darwin_amd64 - - darwin_arm64 - - windows_amd64 - - linux_amd64 - - mod_timestamp: "{{ .CommitTimestamp }}" - -# gomod: - # proxy: true - -changelog: - use: github-native - -sboms: - - artifacts: source - -checksum: - name_template: checksums.txt - -signs: - - cmd: cosign - certificate: "${artifact}.pem" - args: - - sign-blob - - "--output-certificate=${certificate}" - - "--output-signature=${signature}" - - "${artifact}" - - "--yes" - artifacts: checksum - output: true - -source: - enabled: true - -notarize: - macos: - - enabled: "true" - 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}}" From 68af9ae12341f0d304ff1dc793e648af943fd67c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Buli=C5=84ski?= Date: Sat, 21 Feb 2026 15:53:02 +0100 Subject: [PATCH 14/20] trigger release on pull request --- .github/workflows/release.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9877e6e..d465c39 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -8,6 +8,9 @@ permissions: on: release: types: [prereleased] + pull_request: + branches: + - main env: # VERSION is set to the release tag name, e.g., "v1.0.0". From 7ceb5b7a5e8536b5265a0cb7018e91d3f4dfd045 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Buli=C5=84ski?= Date: Sat, 21 Feb 2026 15:56:05 +0100 Subject: [PATCH 15/20] save --- .github/workflows/golangci-lint.yml | 2 +- .github/workflows/pr-test.yml | 4 ++-- .github/workflows/release.yml | 2 +- go.mod | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml index 020f280..0a17a2f 100644 --- a/.github/workflows/golangci-lint.yml +++ b/.github/workflows/golangci-lint.yml @@ -18,7 +18,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: - go-version: 1.25.6 + go-version: 1.25.7 cache: false - name: golangci-lint uses: golangci/golangci-lint-action@v9 \ No newline at end of file diff --git a/.github/workflows/pr-test.yml b/.github/workflows/pr-test.yml index 5fb4205..f544052 100644 --- a/.github/workflows/pr-test.yml +++ b/.github/workflows/pr-test.yml @@ -15,7 +15,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: 1.25.6 + go-version: 1.25.7 - name: Build and test env: QBEE_EMAIL: ${{ secrets.QBEE_API_USER }} @@ -25,7 +25,7 @@ jobs: - id: govulncheck uses: golang/govulncheck-action@v1 with: - go-version-input: 1.25.6 + go-version-input: 1.25.7 go-package: ./... - name: golint run: go run golang.org/x/lint/golint@latest ./... diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d465c39..4480717 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -42,7 +42,7 @@ jobs: runs-on: ubuntu-24.04 steps: - name: Populate Go module proxy cache - run: go get go.qbee.io/client@${{ env.VERSION }} > /dev/null + run: go install go.qbee.io/client@${{ env.VERSION }} > /dev/null linux: name: Linux Binaries diff --git a/go.mod b/go.mod index 7902e7c..628d096 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module go.qbee.io/client -go 1.25.6 +go 1.25.7 require ( github.com/xtaci/smux v1.5.55 From 47c124cb5b020eda31e19c9de3e3401a62c5ad07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Buli=C5=84ski?= Date: Sat, 21 Feb 2026 15:58:07 +0100 Subject: [PATCH 16/20] save --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4480717..22171b7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -42,7 +42,7 @@ jobs: runs-on: ubuntu-24.04 steps: - name: Populate Go module proxy cache - run: go install go.qbee.io/client@${{ env.VERSION }} > /dev/null + run: go list -m go.qbee.io/client@${{ env.VERSION }} linux: name: Linux Binaries From 4f8c6bb415922cbf047beab7d7f33b87fd160469 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Buli=C5=84ski?= Date: Sat, 21 Feb 2026 16:02:42 +0100 Subject: [PATCH 17/20] save --- .github/utils/mac-sign-and-notarize.sh | 12 +++++++++++- .github/workflows/release.yml | 9 +++------ 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/.github/utils/mac-sign-and-notarize.sh b/.github/utils/mac-sign-and-notarize.sh index 9b61360..b81b448 100644 --- a/.github/utils/mac-sign-and-notarize.sh +++ b/.github/utils/mac-sign-and-notarize.sh @@ -14,6 +14,7 @@ set -eu # Script Configuration APP_PATH=qbee-cli +ZIP_PATH=${1} KEYCHAIN_TTL=300 # 5 minutes # Temporary working directory for keychain and certificate handling @@ -42,6 +43,10 @@ if [ -z "${APPLE_CODE_NOTARY_PASSWORD:-}" ]; then die "APPLE_CODE_NOTARY_PASSWORD must be set to the password for the Apple ID for notarization" fi +if [ -z "${ZIP_PATH:-}" ]; then + die "$1 must be set to the path of the zip file (e.g., qbee-cli_darwin_amd64.zip)" +fi + CERT_P12_PATH=${WORKDIR}/cert.p12 KEYCHAIN_PATH=${WORKDIR}/keychain-db @@ -93,6 +98,8 @@ security list-keychains -d user -s $KEYCHAIN_PATH login.keychain info "Signing $APP_PATH with $MACOS_SIGN_IDENTITY certificate" codesign --keychain $KEYCHAIN_PATH --force --options runtime --entitlements .github/utils/entitlements.plist --sign "$MACOS_SIGN_IDENTITY" --timestamp $APP_PATH +ditto -c -k --keepParent $APP_PATH $ZIP_PATH + info "Storing notarization credentials in keychain for notarytool" xcrun notarytool store-credentials "notarytool-password" \ --keychain "$KEYCHAIN_PATH" \ @@ -101,4 +108,7 @@ xcrun notarytool store-credentials "notarytool-password" \ --team-id "$APPLE_TEAM_ID" info "Notarizing binary with Apple Notary Service" -xcrun notarytool submit --keychain-profile "notarytool-password" --wait "$APP_PATH" +xcrun notarytool submit --keychain-profile "notarytool-password" --wait "$ZIP_PATH" + +info "Stapling notarization ticket to Zip file" +xcrun stapler staple "$ZIP_PATH" \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 22171b7..7b8da19 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -170,12 +170,9 @@ jobs: # APPLE_CODE_NOTARY_PASSWORD is the password for the Apple ID used for notarization APPLE_CODE_NOTARY_PASSWORD: ${{ secrets.APPLE_CODE_NOTARY_PASSWORD }} - run: bash ./.github/utils/mac-sign-and-notarize.sh + run: bash ./.github/utils/mac-sign-and-notarize.sh ${{ env.NAME }}_darwin_${{ matrix.arch }}.zip - - name: Upload DMG to the release + - name: Upload Zip archive to the release env: GH_TOKEN: ${{ github.token }} - ARCHIVE_NAME: ${{ env.NAME }}_darwin_${{ matrix.arch }}.zip - run: | - ditto -c -k --keepParent ${{ env.NAME }} ${{ env.ARCHIVE_NAME }} - gh release upload ${{ env.VERSION }} ${{ env.ARCHIVE_NAME }} --clobber + run: gh release upload ${{ env.VERSION }} ${{ env.NAME }}_darwin_${{ matrix.arch }}.zip --clobber From 81c962c7b1dc8e9b5ef77716ad7ebf3e695ef7f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Buli=C5=84ski?= Date: Sat, 21 Feb 2026 16:12:48 +0100 Subject: [PATCH 18/20] save --- .github/utils/mac-sign-and-notarize.sh | 3 --- .github/workflows/release.yml | 19 +++++-------------- 2 files changed, 5 insertions(+), 17 deletions(-) diff --git a/.github/utils/mac-sign-and-notarize.sh b/.github/utils/mac-sign-and-notarize.sh index b81b448..c32858e 100644 --- a/.github/utils/mac-sign-and-notarize.sh +++ b/.github/utils/mac-sign-and-notarize.sh @@ -109,6 +109,3 @@ xcrun notarytool store-credentials "notarytool-password" \ info "Notarizing binary with Apple Notary Service" xcrun notarytool submit --keychain-profile "notarytool-password" --wait "$ZIP_PATH" - -info "Stapling notarization ticket to Zip file" -xcrun stapler staple "$ZIP_PATH" \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7b8da19..9a2fea3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -48,8 +48,6 @@ jobs: name: Linux Binaries runs-on: ubuntu-24.04 needs: go-proxy - outputs: - amd64_sha256: ${{ steps.release.outputs.amd64_sha256 }} steps: - name: Checkout repository uses: actions/checkout@v6 @@ -65,14 +63,13 @@ jobs: run: go build $GCFLAGS -ldflags "$LDFLAGS" -o ${{ env.NAME }} ./cmd - name: Create archive and upload to the release - id: release + id: reease env: GH_TOKEN: ${{ github.token }} ARCHIVE_NAME: ${{ env.NAME }}_linux_amd64_${{ env.VERSION }}.tar.gz run: | tar -czvf $ARCHIVE_NAME ${{ env.NAME }} LICENSE README.md - gh release upload ${{ env.VERSION }} $ARCHIVE_NAME - echo "amd64_sha256=$(sha256sum ${{ env.NAME }})" >> $GITHUB_OUTPUT + gh release upload ${{ env.VERSION }} $ARCHIVE_NAME --clobber windows: name: Windows Signed Binary @@ -86,8 +83,6 @@ jobs: SM_API_KEY: ${{ secrets.SM_API_KEY }} SM_CLIENT_CERT_FILE: Certificate_pkcs12.p12 SM_CLIENT_CERT_PASSWORD: ${{ secrets.SM_CLIENT_CERT_PASSWORD }} - outputs: - amd64_sha256: ${{ steps.release.outputs.amd64_sha256 }} steps: - name: Checkout repository uses: actions/checkout@v6 @@ -116,15 +111,13 @@ jobs: input: ${{ env.NAME }}.exe - name: Create archive and upload to the release - id: release shell: bash env: GH_TOKEN: ${{ github.token }} ARCHIVE_NAME: ${{ env.NAME }}_windows_amd64_${{ env.VERSION }}.zip run: | 7z a $ARCHIVE_NAME ${{ env.NAME }}.exe LICENSE README.md - gh release upload ${{ env.VERSION }} $ARCHIVE_NAME - echo "amd64_sha256=$(sha256sum ${{ env.ARCHIVE_NAME }})" >> $GITHUB_OUTPUT + gh release upload ${{ env.VERSION }} $ARCHIVE_NAME --clobber macos: name: MacOS Signed Binary @@ -133,9 +126,6 @@ jobs: strategy: matrix: arch: [amd64, arm64] - outputs: - amd64_sha256: ${{ steps.release.outputs.amd64_sha256 }} - arm64_sha256: ${{ steps.release.outputs.arm64_sha256 }} steps: - name: Checkout repository uses: actions/checkout@v6 @@ -175,4 +165,5 @@ jobs: - name: Upload Zip archive to the release env: GH_TOKEN: ${{ github.token }} - run: gh release upload ${{ env.VERSION }} ${{ env.NAME }}_darwin_${{ matrix.arch }}.zip --clobber + ARCHIVE_NAME: ${{ env.NAME }}_darwin_${{ matrix.arch }}.zip + run: gh release upload ${{ env.VERSION }} ${{ env.ARCHIVE_NAME }} --clobber From 58143bbca194fa6fed9692eda7aa07132f3e336e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Buli=C5=84ski?= Date: Sat, 21 Feb 2026 16:31:36 +0100 Subject: [PATCH 19/20] save --- .github/utils/mac-sign-and-notarize.sh | 3 ++- .github/workflows/release.yml | 10 +++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/utils/mac-sign-and-notarize.sh b/.github/utils/mac-sign-and-notarize.sh index c32858e..8f9c0d3 100644 --- a/.github/utils/mac-sign-and-notarize.sh +++ b/.github/utils/mac-sign-and-notarize.sh @@ -98,7 +98,8 @@ security list-keychains -d user -s $KEYCHAIN_PATH login.keychain info "Signing $APP_PATH with $MACOS_SIGN_IDENTITY certificate" codesign --keychain $KEYCHAIN_PATH --force --options runtime --entitlements .github/utils/entitlements.plist --sign "$MACOS_SIGN_IDENTITY" --timestamp $APP_PATH -ditto -c -k --keepParent $APP_PATH $ZIP_PATH +info "Creating zip archive for notarization" +zip -r $ZIP_PATH $APP_PATH README.md LICENSE info "Storing notarization credentials in keychain for notarytool" xcrun notarytool store-credentials "notarytool-password" \ diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9a2fea3..2911d5c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -69,7 +69,7 @@ jobs: ARCHIVE_NAME: ${{ env.NAME }}_linux_amd64_${{ env.VERSION }}.tar.gz run: | tar -czvf $ARCHIVE_NAME ${{ env.NAME }} LICENSE README.md - gh release upload ${{ env.VERSION }} $ARCHIVE_NAME --clobber + gh release upload ${{ env.VERSION }} $ARCHIVE_NAME windows: name: Windows Signed Binary @@ -117,7 +117,7 @@ jobs: ARCHIVE_NAME: ${{ env.NAME }}_windows_amd64_${{ env.VERSION }}.zip run: | 7z a $ARCHIVE_NAME ${{ env.NAME }}.exe LICENSE README.md - gh release upload ${{ env.VERSION }} $ARCHIVE_NAME --clobber + gh release upload ${{ env.VERSION }} $ARCHIVE_NAME macos: name: MacOS Signed Binary @@ -160,10 +160,10 @@ jobs: # APPLE_CODE_NOTARY_PASSWORD is the password for the Apple ID used for notarization APPLE_CODE_NOTARY_PASSWORD: ${{ secrets.APPLE_CODE_NOTARY_PASSWORD }} - run: bash ./.github/utils/mac-sign-and-notarize.sh ${{ env.NAME }}_darwin_${{ matrix.arch }}.zip + run: bash ./.github/utils/mac-sign-and-notarize.sh ${{ env.NAME }}_darwin_${{ matrix.arch }}_${{ env.VERSION }}.zip - name: Upload Zip archive to the release env: GH_TOKEN: ${{ github.token }} - ARCHIVE_NAME: ${{ env.NAME }}_darwin_${{ matrix.arch }}.zip - run: gh release upload ${{ env.VERSION }} ${{ env.ARCHIVE_NAME }} --clobber + ARCHIVE_NAME: ${{ env.NAME }}_darwin_${{ matrix.arch }}_${{ env.VERSION }}.zip + run: gh release upload ${{ env.VERSION }} ${{ env.ARCHIVE_NAME }} From b72770ac73207c27f49cf7296f36dade75beedab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Buli=C5=84ski?= Date: Sat, 21 Feb 2026 16:36:53 +0100 Subject: [PATCH 20/20] cleanup --- .github/workflows/release.yml | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2911d5c..c8a1cb9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -8,14 +8,10 @@ permissions: on: release: types: [prereleased] - pull_request: - branches: - - main env: # VERSION is set to the release tag name, e.g., "v1.0.0". - #VERSION: ${{ github.ref_name }} - VERSION: v1.2026.8 + VERSION: ${{ github.ref_name }} # NAME is the name of the application. NAME: qbee-cli @@ -28,8 +24,7 @@ env: # LDFLAGS with -s and -w reduces the binary size by omitting symbol table and debug information. # The -X flag sets the Version variable in the code to the release version. - # DFLAGS: -s -w -X go.qbee.io/client.Version=${{ github.ref_name }} - LDFLAGS: -s -w -X go.qbee.io/client.Version=v1.2026.8 + DFLAGS: -s -w -X go.qbee.io/client.Version=${{ github.ref_name }} # GOPROXY ensures that we use the correct Go module proxy. # We want our build process to start with populateding the Go proxy @@ -66,7 +61,7 @@ jobs: id: reease env: GH_TOKEN: ${{ github.token }} - ARCHIVE_NAME: ${{ env.NAME }}_linux_amd64_${{ env.VERSION }}.tar.gz + ARCHIVE_NAME: ${{ env.NAME }}_linux_amd64.tar.gz run: | tar -czvf $ARCHIVE_NAME ${{ env.NAME }} LICENSE README.md gh release upload ${{ env.VERSION }} $ARCHIVE_NAME @@ -114,7 +109,7 @@ jobs: shell: bash env: GH_TOKEN: ${{ github.token }} - ARCHIVE_NAME: ${{ env.NAME }}_windows_amd64_${{ env.VERSION }}.zip + ARCHIVE_NAME: ${{ env.NAME }}_windows_amd64.zip run: | 7z a $ARCHIVE_NAME ${{ env.NAME }}.exe LICENSE README.md gh release upload ${{ env.VERSION }} $ARCHIVE_NAME @@ -160,10 +155,10 @@ jobs: # APPLE_CODE_NOTARY_PASSWORD is the password for the Apple ID used for notarization APPLE_CODE_NOTARY_PASSWORD: ${{ secrets.APPLE_CODE_NOTARY_PASSWORD }} - run: bash ./.github/utils/mac-sign-and-notarize.sh ${{ env.NAME }}_darwin_${{ matrix.arch }}_${{ env.VERSION }}.zip + run: bash ./.github/utils/mac-sign-and-notarize.sh ${{ env.NAME }}_darwin_${{ matrix.arch }}.zip - name: Upload Zip archive to the release env: GH_TOKEN: ${{ github.token }} - ARCHIVE_NAME: ${{ env.NAME }}_darwin_${{ matrix.arch }}_${{ env.VERSION }}.zip + ARCHIVE_NAME: ${{ env.NAME }}_darwin_${{ matrix.arch }}.zip run: gh release upload ${{ env.VERSION }} ${{ env.ARCHIVE_NAME }}