diff --git a/.github/workflows/release-binaries.yml b/.github/workflows/release-binaries.yml index 5e52421..d8de765 100644 --- a/.github/workflows/release-binaries.yml +++ b/.github/workflows/release-binaries.yml @@ -39,11 +39,13 @@ jobs: asset_name: codedb-linux-x86_64 zig_archive: zig-x86_64-linux-0.15.2.tar.xz zig_dir: zig-x86_64-linux-0.15.2 + - runner: ubuntu-24.04 + zig_target: aarch64-linux + asset_name: codedb-linux-arm64 + zig_archive: zig-x86_64-linux-0.15.2.tar.xz + zig_dir: zig-x86_64-linux-0.15.2 env: RELEASE_TAG: ${{ github.event_name == 'workflow_dispatch' && inputs.tag || github.event.release.tag_name }} - APPLE_CERTIFICATE_P12: ${{ secrets.APPLE_CERTIFICATE_P12 }} - APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} - CODEDB_CODESIGN_IDENTITY: ${{ secrets.APPLE_CODESIGN_IDENTITY }} steps: - uses: actions/checkout@v4 with: @@ -58,41 +60,89 @@ jobs: tar -xf zig.tar.xz echo "$PWD/${{ matrix.zig_dir }}" >> "$GITHUB_PATH" - - name: Import Apple signing certificate - if: runner.os == 'macOS' && env.APPLE_CERTIFICATE_P12 != '' + - name: Fetch notarization helper from main + if: runner.os == 'macOS' shell: bash + env: + GH_TOKEN: ${{ github.token }} run: | set -euo pipefail - CERT_PATH="$RUNNER_TEMP/codedb-signing.p12" - KEYCHAIN_PATH="$RUNNER_TEMP/codedb-signing.keychain-db" - export CERT_PATH - python3 - <<'PY' - import base64 - import os - import pathlib - pathlib.Path(os.environ["CERT_PATH"]).write_bytes( - base64.b64decode(os.environ["APPLE_CERTIFICATE_P12"]) - ) - PY - security create-keychain -p "" "$KEYCHAIN_PATH" - security set-keychain-settings -lut 21600 "$KEYCHAIN_PATH" - security unlock-keychain -p "" "$KEYCHAIN_PATH" - security import "$CERT_PATH" -P "$APPLE_CERTIFICATE_PASSWORD" -A -t cert -f pkcs12 -k "$KEYCHAIN_PATH" - security list-keychains -d user -s "$KEYCHAIN_PATH" - security default-keychain -s "$KEYCHAIN_PATH" - security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "" "$KEYCHAIN_PATH" - - - name: Build ${{ matrix.asset_name }} + mkdir -p scripts + gh api repos/${{ github.repository }}/contents/scripts/notarize-macos.sh \ + -H "Accept: application/vnd.github.raw" > scripts/notarize-macos.sh + chmod +x scripts/notarize-macos.sh + + - name: Require macOS signing and notarization secrets + if: runner.os == 'macOS' + env: + APPLE_CERTIFICATE_P12: ${{ secrets.APPLE_CERTIFICATE_P12 }} + APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} + APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }} + APPLE_API_ISSUER_ID: ${{ secrets.APPLE_API_ISSUER_ID }} + APPLE_API_KEY_BASE64: ${{ secrets.APPLE_API_KEY_BASE64 }} shell: bash run: | set -euo pipefail - codesign_arg=() - if [[ "${{ runner.os }}" == "macOS" ]] && grep -q 'codesign-identity' build.zig; then - codesign_arg=(-Dcodesign-identity="${CODEDB_CODESIGN_IDENTITY:-"-"}") + missing=() + [[ -n "${APPLE_CERTIFICATE_P12}" ]] || missing+=("APPLE_CERTIFICATE_P12") + [[ -n "${APPLE_CERTIFICATE_PASSWORD}" ]] || missing+=("APPLE_CERTIFICATE_PASSWORD") + [[ -n "${APPLE_API_KEY_ID}" ]] || missing+=("APPLE_API_KEY_ID") + [[ -n "${APPLE_API_ISSUER_ID}" ]] || missing+=("APPLE_API_ISSUER_ID") + [[ -n "${APPLE_API_KEY_BASE64}" ]] || missing+=("APPLE_API_KEY_BASE64") + if (( ${#missing[@]} > 0 )); then + printf 'Missing required macOS release secrets: %s\n' "${missing[*]}" >&2 + exit 1 fi - zig build -Doptimize=ReleaseFast -Dtarget=${{ matrix.zig_target }} "${codesign_arg[@]}" + + - name: Import Apple signing certificate + if: runner.os == 'macOS' + uses: apple-actions/import-codesign-certs@v5 + with: + p12-file-base64: ${{ secrets.APPLE_CERTIFICATE_P12 }} + p12-password: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} + + - name: Build ${{ matrix.asset_name }} (macOS) + if: runner.os == 'macOS' + env: + CODEDB_CODESIGN_IDENTITY: "${{ secrets.APPLE_CODESIGN_IDENTITY != '' && secrets.APPLE_CODESIGN_IDENTITY || 'Developer ID Application: Rachit Pradhan (WWP9DLJ27P)' }}" + shell: bash + run: | + set -euo pipefail + codesign_args=() + if grep -q 'codesign-identity' build.zig; then + codesign_args=(-Dcodesign-identity="${CODEDB_CODESIGN_IDENTITY}") + fi + zig build -Doptimize=ReleaseFast -Dtarget=${{ matrix.zig_target }} "${codesign_args[@]}" + cp zig-out/bin/codedb "${{ matrix.asset_name }}" + + - name: Re-sign ${{ matrix.asset_name }} for notarization + if: runner.os == 'macOS' + env: + CODEDB_CODESIGN_IDENTITY: "${{ secrets.APPLE_CODESIGN_IDENTITY != '' && secrets.APPLE_CODESIGN_IDENTITY || 'Developer ID Application: Rachit Pradhan (WWP9DLJ27P)' }}" + shell: bash + run: | + set -euo pipefail + codesign -f -s "${CODEDB_CODESIGN_IDENTITY}" --options runtime --timestamp "${{ matrix.asset_name }}" + + - name: Build ${{ matrix.asset_name }} (Linux) + if: runner.os != 'macOS' + shell: bash + run: | + set -euo pipefail + zig build -Doptimize=ReleaseFast -Dtarget=${{ matrix.zig_target }} cp zig-out/bin/codedb "${{ matrix.asset_name }}" + - name: Notarize ${{ matrix.asset_name }} + if: runner.os == 'macOS' + env: + APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }} + APPLE_API_ISSUER_ID: ${{ secrets.APPLE_API_ISSUER_ID }} + APPLE_API_KEY_BASE64: ${{ secrets.APPLE_API_KEY_BASE64 }} + shell: bash + run: | + set -euo pipefail + bash scripts/notarize-macos.sh "${{ matrix.asset_name }}" + - name: Upload build artifact uses: actions/upload-artifact@v4 with: diff --git a/build.zig b/build.zig index bd3b3b0..e21a541 100644 --- a/build.zig +++ b/build.zig @@ -34,7 +34,11 @@ pub fn build(b: *std.Build) void { // ── macOS codesign (ad-hoc by default; configurable for release builds) ── if (target.result.os.tag == .macos and builtin.os.tag == .macos) { - const codesign = b.addSystemCommand(&.{ "codesign", "-f", "-s", codesign_identity }); + const codesign_args = if (std.mem.eql(u8, codesign_identity, "-")) + &.{ "codesign", "-f", "-s", codesign_identity } + else + &.{ "codesign", "-f", "-s", codesign_identity, "--options", "runtime", "--timestamp" }; + const codesign = b.addSystemCommand(codesign_args); codesign.addArtifactArg(exe); b.getInstallStep().dependOn(&codesign.step); } diff --git a/scripts/notarize-macos.sh b/scripts/notarize-macos.sh new file mode 100644 index 0000000..468b7b8 --- /dev/null +++ b/scripts/notarize-macos.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash +set -euo pipefail + +if [[ $# -ne 1 ]]; then + echo "usage: $0 " >&2 + exit 1 +fi + +binary_path="$1" + +: "${APPLE_API_KEY_ID:?APPLE_API_KEY_ID is required}" +: "${APPLE_API_ISSUER_ID:?APPLE_API_ISSUER_ID is required}" +: "${APPLE_API_KEY_BASE64:?APPLE_API_KEY_BASE64 is required}" + +tmp_dir="$(mktemp -d "${TMPDIR:-/tmp}/codedb-notary.XXXXXX")" +api_key_path="$tmp_dir/codedb-notary-key.p8" +zip_path="$tmp_dir/$(basename "$binary_path").zip" +export API_KEY_PATH="$api_key_path" + +cleanup() { + rm -rf "$tmp_dir" +} +trap cleanup EXIT + +python3 - <<'PY' +import base64 +import os +import pathlib + +pathlib.Path(os.environ["API_KEY_PATH"]).write_bytes( + base64.b64decode(os.environ["APPLE_API_KEY_BASE64"]) +) +PY + +chmod 600 "$api_key_path" +codesign --verify --verbose=2 "$binary_path" +ditto -c -k --keepParent "$binary_path" "$zip_path" +xcrun notarytool submit "$zip_path" \ + --key "$api_key_path" \ + --key-id "$APPLE_API_KEY_ID" \ + --issuer "$APPLE_API_ISSUER_ID" \ + --wait +xcrun stapler staple "$binary_path" || true +spctl --assess --type execute --verbose=4 "$binary_path"