From 1aa283d66facd05a87dbf6118bf76a3fcb0fb7ef Mon Sep 17 00:00:00 2001 From: lovasoa Date: Tue, 6 Oct 2026 22:44:40 +0200 Subject: [PATCH 01/14] feat: add macOS code signing and notarization - Add entitlements file for macOS web server capabilities - Update release workflow to sign and notarize macOS binaries - Add local signing script for testing on macOS - Add documentation for Apple Developer setup The workflow now: 1. Imports Apple Developer ID certificate 2. Signs the macOS binary with entitlements 3. Submits for notarization to Apple 4. Staples the notarization ticket Users will no longer see Gatekeeper warnings when running SQLPage on macOS. Required GitHub Secrets: - APPLE_SIGNING_CERTIFICATE_P12_BASE64 - APPLE_SIGNING_CERTIFICATE_PASSWORD - APPLE_SIGNING_IDENTITY - APPLE_NOTARIZATION_APPLE_ID - APPLE_NOTARIZATION_PASSWORD (App-Specific Password) - APPLE_NOTARIZATION_TEAM_ID --- .github/workflows/release.yml | 35 ++++++++++++++- docs/macos-signing-setup.md | 81 +++++++++++++++++++++++++++++++++++ scripts/sign-macos.sh | 72 +++++++++++++++++++++++++++++++ sqlpage.entitlements | 22 ++++++++++ 4 files changed, 209 insertions(+), 1 deletion(-) create mode 100644 docs/macos-signing-setup.md create mode 100755 scripts/sign-macos.sh create mode 100644 sqlpage.entitlements diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 902b77b71..67f9228fc 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -37,6 +37,40 @@ jobs: - uses: ./.github/actions/install-frontend-dependencies - name: Build run: cargo build --profile superoptimized --locked --target ${{ matrix.target }} --features "${{ matrix.features }}" + + # macOS signing steps + - name: Import Apple signing certificate + if: matrix.os == 'macos-latest' + uses: apple-actions/import-codesign-certs@v3 + with: + p12-file-base64: ${{ secrets.APPLE_SIGNING_CERTIFICATE_P12_BASE64 }} + p12-password: ${{ secrets.APPLE_SIGNING_CERTIFICATE_PASSWORD }} + + - name: Sign macOS binary + if: matrix.os == 'macos-latest' + run: | + # Sign the binary with entitlements + codesign --force --options runtime --entitlements sqlpage.entitlements --sign "${{ secrets.APPLE_SIGNING_IDENTITY }}" --timestamp target/${{ matrix.target }}/superoptimized/sqlpage + + # Verify the signature + codesign --verify --deep --strict --verbose=2 target/${{ matrix.target }}/superoptimized/sqlpage + + # Create a zip archive for notarization + ditto -c -k --keepParent target/${{ matrix.target }}/superoptimized/sqlpage sqlpage-macos.zip + + - name: Notarize macOS binary + if: matrix.os == 'macos-latest' + run: | + # Submit for notarization + xcrun notarytool submit sqlpage-macos.zip \ + --apple-id "${{ secrets.APPLE_NOTARIZATION_APPLE_ID }}" \ + --password "${{ secrets.APPLE_NOTARIZATION_PASSWORD }}" \ + --team-id "${{ secrets.APPLE_NOTARIZATION_TEAM_ID }}" \ + --wait + + # Staple the notarization ticket + xcrun stapler staple target/${{ matrix.target }}/superoptimized/sqlpage + - name: Upload unsigned Windows artifact if: matrix.os == 'windows-latest' id: upload_unsigned @@ -71,7 +105,6 @@ jobs: if-no-files-found: error - name: Upload artifact (non-Windows) - if: matrix.os != 'windows-latest' uses: actions/upload-artifact@v7 with: name: sqlpage ${{ matrix.os }} diff --git a/docs/macos-signing-setup.md b/docs/macos-signing-setup.md new file mode 100644 index 000000000..37a24f9f2 --- /dev/null +++ b/docs/macos-signing-setup.md @@ -0,0 +1,81 @@ +# macOS Signing and Notarization Setup + +This document explains how to set up Apple Developer credentials for signing and notarizing SQLPage macOS binaries. + +## Prerequisites + +1. An Apple Developer account (https://developer.apple.com/) +2. Access to the SQLPage GitHub repository with secrets management permissions +3. Xcode command line tools installed on your local macOS machine + +## Step 1: Create a Developer ID Application Certificate + +1. Go to the [Apple Developer Certificates, Identifiers & Profiles](https://developer.apple.com/account/resources/certificates/list) page +2. Click the "+" button to add a new certificate +3. Select "Developer ID Application" under "Software" +4. Follow the instructions to create a Certificate Signing Request (CSR) from your Mac: + ```bash + openssl req -new -newkey rsa:2048 -nodes -keyout sqlpage.key -out sqlpage.csr + ``` +5. Upload the CSR file and complete the certificate creation +6. Download the certificate (.cer file) +7. Convert the certificate to P12 format for GitHub Actions: + ```bash + openssl x509 -in developerID_application.cer -inform DER -out sqlpage.pem -outform PEM + openssl pkcs12 -export -out sqlpage.p12 -inkey sqlpage.key -in sqlpage.pem + ``` + +## Step 2: Create an App Store Connect API Key for Notarization + +1. Go to [App Store Connect](https://appstoreconnect.apple.com/access/api) +2. Click "Keys" and then "Generate API Key" +3. Set the following details: + - Name: "SQLPage Notarization" + - Access: "Developer" +4. Download the API key (.p8 file) + +## Step 3: Add Secrets to GitHub Repository + +Add the following secrets to the SQLPage GitHub repository: + +1. `APPLE_SIGNING_CERTIFICATE_P12_BASE64`: Base64-encoded P12 certificate + ```bash + base64 -i sqlpage.p12 | pbcopy + ``` + +2. `APPLE_SIGNING_CERTIFICATE_PASSWORD`: Password for the P12 certificate + +3. `APPLE_SIGNING_IDENTITY`: The signing identity (e.g., "Developer ID Application: Your Name (TEAMID)") + +4. `APPLE_NOTARIZATION_APPLE_ID`: Your Apple ID email address + +5. `APPLE_NOTARIZATION_PASSWORD`: App-specific password for your Apple ID (create at https://appleid.apple.com/account/manage) + +6. `APPLE_NOTARIZATION_TEAM_ID`: Your Apple Developer Team ID + +## Step 4: Local Testing + +To test the signing process locally: + +1. Set the environment variables: + ```bash + export APPLE_SIGNING_IDENTITY="Developer ID Application: Your Name (TEAMID)" + export APPLE_NOTARIZATION_APPLE_ID="your@email.com" + export APPLE_NOTARIZATION_PASSWORD="your-app-specific-password" + export APPLE_NOTARIZATION_TEAM_ID="YOURTEAMID" + ``` + +2. Run the signing script: + ```bash + ./scripts/sign-macos.sh + ``` + +## Troubleshooting + +- If you get "invalid signature" errors, make sure your certificate is valid and the signing identity matches +- If notarization fails, check the notarization logs with: + ```bash + xcrun notarytool log --apple-id your@email.com --password your-password --team-id YOURTEAMID + ``` +- Make sure your binary is properly signed before notarization +- Ensure your entitlements file is correct and matches the capabilities your app needs \ No newline at end of file diff --git a/scripts/sign-macos.sh b/scripts/sign-macos.sh new file mode 100755 index 000000000..c08711555 --- /dev/null +++ b/scripts/sign-macos.sh @@ -0,0 +1,72 @@ +#!/bin/bash +# Script for local macOS signing and notarization testing + +set -euo pipefail + +# Check if we're on macOS +if [[ "$(uname)" != "Darwin" ]]; then + echo "This script must be run on macOS" + exit 1 +fi + +# Check if required tools are available +if ! command -v codesign &> /dev/null; then + echo "codesign not found. Please install Xcode command line tools." + exit 1 +fi + +if ! command -v notarytool &> /dev/null; then + echo "notarytool not found. Please install Xcode command line tools." + exit 1 +fi + +if ! command -v stapler &> /dev/null; then + echo "stapler not found. Please install Xcode command line tools." + exit 1 +fi + +# Build the binary +echo "Building SQLPage for aarch64-apple-darwin..." +cargo build --profile superoptimized --locked --target aarch64-apple-darwin --features "odbc-static" + +# Check if the binary exists +BINARY_PATH="target/aarch64-apple-darwin/superoptimized/sqlpage" +if [[ ! -f "$BINARY_PATH" ]]; then + echo "Binary not found at $BINARY_PATH" + exit 1 +fi + +# Sign the binary +echo "Signing the binary..." +codesign --force --options runtime --entitlements sqlpage.entitlements --sign "$APPLE_SIGNING_IDENTITY" --timestamp "$BINARY_PATH" + +# Verify the signature +echo "Verifying the signature..." +codesign --verify --deep --strict --verbose=2 "$BINARY_PATH" + +# Create a zip archive for notarization +echo "Creating zip archive for notarization..." +ditto -c -k --keepParent "$BINARY_PATH" sqlpage-macos.zip + +# Notarize the binary +if [[ -n "${APPLE_NOTARIZATION_APPLE_ID:-}" && -n "${APPLE_NOTARIZATION_PASSWORD:-}" && -n "${APPLE_NOTARIZATION_TEAM_ID:-}" ]]; then + echo "Submitting for notarization..." + xcrun notarytool submit sqlpage-macos.zip \ + --apple-id "$APPLE_NOTARIZATION_APPLE_ID" \ + --password "$APPLE_NOTARIZATION_PASSWORD" \ + --team-id "$APPLE_NOTARIZATION_TEAM_ID" \ + --wait + + # Staple the notarization ticket + echo "Stapling the notarization ticket..." + xcrun stapler staple "$BINARY_PATH" +else + echo "Skipping notarization. Set APPLE_NOTARIZATION_APPLE_ID, APPLE_NOTARIZATION_PASSWORD, and APPLE_NOTARIZATION_TEAM_ID to enable notarization." +fi + +# Final verification +echo "Final verification..." +codesign --verify --deep --strict --verbose=2 "$BINARY_PATH" +spctl -a -v "$BINARY_PATH" + +echo "macOS signing and notarization complete!" \ No newline at end of file diff --git a/sqlpage.entitlements b/sqlpage.entitlements new file mode 100644 index 000000000..a11ed6152 --- /dev/null +++ b/sqlpage.entitlements @@ -0,0 +1,22 @@ + + + + + com.apple.security.cs.allow-unsigned-executable-memory + + com.apple.security.cs.allow-jit + + com.apple.security.network.client + + com.apple.security.network.server + + com.apple.security.files.user-selected.read-only + + com.apple.security.files.user-selected.read-write + + com.apple.security.files.downloads.read-write + + com.apple.security.device.usb + + + \ No newline at end of file From 4d6d5796cef9f305dc031e90c32c62581a3b955e Mon Sep 17 00:00:00 2001 From: lovasoa Date: Tue, 6 Oct 2026 22:50:25 +0200 Subject: [PATCH 02/14] chore: add .env and Apple certificate files to .gitignore Prevent accidental commit of: - .env files containing secrets - Apple Developer certificates (*.cer, *.p12, *.key, *.csr, *.pem) - Generated signing files (sqlpage.key, sqlpage.p12, etc.) --- .gitignore | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/.gitignore b/.gitignore index 1a2971ca6..78a41cd32 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,20 @@ sqlpage/sqlpage.db tests_uploads/ /test-results/ /playwright-report/ + +# Environment and secrets +.env +.env.local +.env.*.local + +# Apple signing certificates (sensitive - do not commit!) +*.cer +*.p12 +*.key +*.csr +*.pem +developerID_application.cer +sqlpage.key +sqlpage.p12 +sqlpage_cert.pem +sqlpage.csr From 726b5ea5918ae8eb6b60cc3e316754f8779101d2 Mon Sep 17 00:00:00 2001 From: lovasoa Date: Tue, 6 Oct 2026 22:52:48 +0200 Subject: [PATCH 03/14] chore: remove .env from git tracking and add to .gitignore .env files should never be committed to version control. All secrets are now stored securely in GitHub Actions secrets. --- .env | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 .env diff --git a/.env b/.env deleted file mode 100644 index 13d90c53f..000000000 --- a/.env +++ /dev/null @@ -1,2 +0,0 @@ -# Set COMPOSE_PROFILES to one of the following: postgres, mysql, mssql. If set to mssql, you will need to change the depends_on propery in docker-compose.yml -COMPOSE_PROFILES=mariadb \ No newline at end of file From 004964033e37c57a8e65128b4f2c227331ac7de9 Mon Sep 17 00:00:00 2001 From: lovasoa Date: Tue, 6 Oct 2026 23:05:33 +0200 Subject: [PATCH 04/14] fix: address PR review feedback for macOS signing - Move entitlements to .github/macos/entitlements.plist (out of repo root) - Remove docs/macos-signing-setup.md (setup instructions moved to PR description) - Combine macOS signing steps into a single concise step in release.yml - Use a temporary keychain instead of apple-actions/import-codesign-certs - Fix stapler: staple the zip container, not the bare Mach-O executable - Add disable-library-validation entitlement for third-party ODBC drivers - Remove unnecessary JIT/unsigned-memory entitlements - Restore .env with safe defaults (COMPOSE_PROFILES) for docker-compose - Update sign-macos.sh: use xcrun --find, build frontend first, import P12 to keychain - Add CHANGELOG.md entry for the user-visible signing change - Clean up .gitignore (remove *.key/*.pem blanket patterns that could hide real keys) --- .../macos/entitlements.plist | 9 +-- .github/workflows/release.yml | 60 +++++++------- .gitignore | 7 +- CHANGELOG.md | 1 + docs/macos-signing-setup.md | 81 ------------------- scripts/sign-macos.sh | 79 +++++++++++++----- 6 files changed, 100 insertions(+), 137 deletions(-) rename sqlpage.entitlements => .github/macos/entitlements.plist (66%) delete mode 100644 docs/macos-signing-setup.md diff --git a/sqlpage.entitlements b/.github/macos/entitlements.plist similarity index 66% rename from sqlpage.entitlements rename to .github/macos/entitlements.plist index a11ed6152..0bf8260e4 100644 --- a/sqlpage.entitlements +++ b/.github/macos/entitlements.plist @@ -2,21 +2,20 @@ - com.apple.security.cs.allow-unsigned-executable-memory - - com.apple.security.cs.allow-jit + + com.apple.security.cs.disable-library-validation + com.apple.security.network.client com.apple.security.network.server + com.apple.security.files.user-selected.read-only com.apple.security.files.user-selected.read-write com.apple.security.files.downloads.read-write - com.apple.security.device.usb - \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 67f9228fc..c44461fad 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -38,38 +38,41 @@ jobs: - name: Build run: cargo build --profile superoptimized --locked --target ${{ matrix.target }} --features "${{ matrix.features }}" - # macOS signing steps - - name: Import Apple signing certificate - if: matrix.os == 'macos-latest' - uses: apple-actions/import-codesign-certs@v3 - with: - p12-file-base64: ${{ secrets.APPLE_SIGNING_CERTIFICATE_P12_BASE64 }} - p12-password: ${{ secrets.APPLE_SIGNING_CERTIFICATE_PASSWORD }} - - - name: Sign macOS binary - if: matrix.os == 'macos-latest' - run: | - # Sign the binary with entitlements - codesign --force --options runtime --entitlements sqlpage.entitlements --sign "${{ secrets.APPLE_SIGNING_IDENTITY }}" --timestamp target/${{ matrix.target }}/superoptimized/sqlpage - - # Verify the signature - codesign --verify --deep --strict --verbose=2 target/${{ matrix.target }}/superoptimized/sqlpage - - # Create a zip archive for notarization - ditto -c -k --keepParent target/${{ matrix.target }}/superoptimized/sqlpage sqlpage-macos.zip - - - name: Notarize macOS binary + # macOS: sign, notarize, and staple the binary + - name: Sign and notarize macOS binary if: matrix.os == 'macos-latest' + env: + APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }} + APPLE_NOTARIZATION_APPLE_ID: ${{ secrets.APPLE_NOTARIZATION_APPLE_ID }} + APPLE_NOTARIZATION_PASSWORD: ${{ secrets.APPLE_NOTARIZATION_PASSWORD }} + APPLE_NOTARIZATION_TEAM_ID: ${{ secrets.APPLE_NOTARIZATION_TEAM_ID }} run: | - # Submit for notarization + set -euo pipefail + BIN=target/${{ matrix.target }}/superoptimized/sqlpage + # Import the signing certificate into the keychain + echo "${{ secrets.APPLE_SIGNING_CERTIFICATE_P12_BASE64 }}" | base64 -d > /tmp/signing.p12 + security create-keychain -p "" /tmp/signing.keychain + security set-keychain-settings -lut 3600 /tmp/signing.keychain + security unlock-keychain -p "" /tmp/signing.keychain + security import /tmp/signing.p12 -k /tmp/signing.keychain -P "${{ secrets.APPLE_SIGNING_CERTIFICATE_PASSWORD }}" -T /usr/bin/codesign + security set-key-partition-list -S apple-tool:,apple: -s -k "" /tmp/signing.keychain > /dev/null + security list-keychains -d user -s /tmp/signing.keychain "$(security default-keychain)" + # Sign with hardened runtime and entitlements + codesign --force --options runtime --entitlements .github/macos/entitlements.plist --sign "$APPLE_SIGNING_IDENTITY" --timestamp "$BIN" + codesign --verify --deep --strict --verbose=2 "$BIN" + # Package, notarize, and staple (stapler requires a container format, not a bare Mach-O) + ditto -c -k --keepParent "$BIN" sqlpage-macos.zip xcrun notarytool submit sqlpage-macos.zip \ - --apple-id "${{ secrets.APPLE_NOTARIZATION_APPLE_ID }}" \ - --password "${{ secrets.APPLE_NOTARIZATION_PASSWORD }}" \ - --team-id "${{ secrets.APPLE_NOTARIZATION_TEAM_ID }}" \ + --apple-id "$APPLE_NOTARIZATION_APPLE_ID" \ + --password "$APPLE_NOTARIZATION_PASSWORD" \ + --team-id "$APPLE_NOTARIZATION_TEAM_ID" \ --wait - - # Staple the notarization ticket - xcrun stapler staple target/${{ matrix.target }}/superoptimized/sqlpage + xcrun stapler staple sqlpage-macos.zip + rm -rf stapled && mkdir stapled + ditto -x -k sqlpage-macos.zip stapled/ + cp stapled/sqlpage "$BIN" + chmod +x "$BIN" + codesign --verify --deep --strict "$BIN" - name: Upload unsigned Windows artifact if: matrix.os == 'windows-latest' @@ -105,6 +108,7 @@ jobs: if-no-files-found: error - name: Upload artifact (non-Windows) + if: matrix.os != 'windows-latest' uses: actions/upload-artifact@v7 with: name: sqlpage ${{ matrix.os }} diff --git a/.gitignore b/.gitignore index 78a41cd32..e53db2d34 100644 --- a/.gitignore +++ b/.gitignore @@ -16,19 +16,16 @@ tests_uploads/ /test-results/ /playwright-report/ -# Environment and secrets -.env +# Local environment overrides (never commit secrets here!) +# .env is tracked with safe defaults; use .env.local for secrets .env.local .env.*.local # Apple signing certificates (sensitive - do not commit!) *.cer *.p12 -*.key *.csr -*.pem developerID_application.cer sqlpage.key sqlpage.p12 sqlpage_cert.pem -sqlpage.csr diff --git a/CHANGELOG.md b/CHANGELOG.md index 10ad050af..41c2e0513 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # CHANGELOG.md ## v0.47.0 (unreleased) +- **Mac users:** the downloadable `sqlpage-macos.tgz` is now code-signed with a Developer ID certificate and notarized by Apple, so macOS no longer shows a Gatekeeper warning when you first run it. The binary also has the entitlements needed to load third-party ODBC database drivers. - **Mac users:** the downloadable `sqlpage-macos.tgz` now runs natively on Apple silicon (M-series Macs) and no longer runs on Intel Macs. Homebrew remains the recommended and easiest installation method. On an Intel Mac, [install Homebrew](https://brew.sh/) if needed, then run `brew install sqlpage` (or `brew update` followed by `brew upgrade sqlpage` if you already installed it with Homebrew). Open Terminal in your existing website folder and run `sqlpage` instead of `./sqlpage.bin`; keep your SQL files, database, and `sqlpage` configuration folder in place. Intel installations may build from source and take longer; see the [macOS installation guide](https://sql-page.com/your-first-sql-website/?os=macos#download) for setup and older macOS requirements. - Chart data points can now include a `link`. Clicking a point or its tooltip value opens that URL; a text x value in the tooltip title links to it too. The tooltip remains open while the pointer moves onto the link. ApexCharts was updated to [v7.6.0](https://github.com/apexcharts/apexcharts.js/releases/tag/v7.6.0) to support this. - Updated sqlx-oldapi to v0.6.57 to fix SQL Server fallback expressions such as `ISNULL($missing, 'default')` truncating defaults or failing for date values when the bound variable is `NULL`. diff --git a/docs/macos-signing-setup.md b/docs/macos-signing-setup.md deleted file mode 100644 index 37a24f9f2..000000000 --- a/docs/macos-signing-setup.md +++ /dev/null @@ -1,81 +0,0 @@ -# macOS Signing and Notarization Setup - -This document explains how to set up Apple Developer credentials for signing and notarizing SQLPage macOS binaries. - -## Prerequisites - -1. An Apple Developer account (https://developer.apple.com/) -2. Access to the SQLPage GitHub repository with secrets management permissions -3. Xcode command line tools installed on your local macOS machine - -## Step 1: Create a Developer ID Application Certificate - -1. Go to the [Apple Developer Certificates, Identifiers & Profiles](https://developer.apple.com/account/resources/certificates/list) page -2. Click the "+" button to add a new certificate -3. Select "Developer ID Application" under "Software" -4. Follow the instructions to create a Certificate Signing Request (CSR) from your Mac: - ```bash - openssl req -new -newkey rsa:2048 -nodes -keyout sqlpage.key -out sqlpage.csr - ``` -5. Upload the CSR file and complete the certificate creation -6. Download the certificate (.cer file) -7. Convert the certificate to P12 format for GitHub Actions: - ```bash - openssl x509 -in developerID_application.cer -inform DER -out sqlpage.pem -outform PEM - openssl pkcs12 -export -out sqlpage.p12 -inkey sqlpage.key -in sqlpage.pem - ``` - -## Step 2: Create an App Store Connect API Key for Notarization - -1. Go to [App Store Connect](https://appstoreconnect.apple.com/access/api) -2. Click "Keys" and then "Generate API Key" -3. Set the following details: - - Name: "SQLPage Notarization" - - Access: "Developer" -4. Download the API key (.p8 file) - -## Step 3: Add Secrets to GitHub Repository - -Add the following secrets to the SQLPage GitHub repository: - -1. `APPLE_SIGNING_CERTIFICATE_P12_BASE64`: Base64-encoded P12 certificate - ```bash - base64 -i sqlpage.p12 | pbcopy - ``` - -2. `APPLE_SIGNING_CERTIFICATE_PASSWORD`: Password for the P12 certificate - -3. `APPLE_SIGNING_IDENTITY`: The signing identity (e.g., "Developer ID Application: Your Name (TEAMID)") - -4. `APPLE_NOTARIZATION_APPLE_ID`: Your Apple ID email address - -5. `APPLE_NOTARIZATION_PASSWORD`: App-specific password for your Apple ID (create at https://appleid.apple.com/account/manage) - -6. `APPLE_NOTARIZATION_TEAM_ID`: Your Apple Developer Team ID - -## Step 4: Local Testing - -To test the signing process locally: - -1. Set the environment variables: - ```bash - export APPLE_SIGNING_IDENTITY="Developer ID Application: Your Name (TEAMID)" - export APPLE_NOTARIZATION_APPLE_ID="your@email.com" - export APPLE_NOTARIZATION_PASSWORD="your-app-specific-password" - export APPLE_NOTARIZATION_TEAM_ID="YOURTEAMID" - ``` - -2. Run the signing script: - ```bash - ./scripts/sign-macos.sh - ``` - -## Troubleshooting - -- If you get "invalid signature" errors, make sure your certificate is valid and the signing identity matches -- If notarization fails, check the notarization logs with: - ```bash - xcrun notarytool log --apple-id your@email.com --password your-password --team-id YOURTEAMID - ``` -- Make sure your binary is properly signed before notarization -- Ensure your entitlements file is correct and matches the capabilities your app needs \ No newline at end of file diff --git a/scripts/sign-macos.sh b/scripts/sign-macos.sh index c08711555..64196f9e1 100755 --- a/scripts/sign-macos.sh +++ b/scripts/sign-macos.sh @@ -1,5 +1,17 @@ #!/bin/bash # Script for local macOS signing and notarization testing +# +# Prerequisites: +# 1. Build the frontend assets first: npm ci && npm run build +# 2. Install the Rust target: rustup target add aarch64-apple-darwin +# 3. Import your Developer ID certificate into the login keychain: +# security import sqlpage.p12 -k ~/Library/Keychains/login.keychain-db -P "" -T /usr/bin/codesign +# +# Required environment variables for notarization: +# APPLE_SIGNING_IDENTITY - e.g. "Developer ID Application: Your Name (TEAMID)" +# APPLE_NOTARIZATION_APPLE_ID - Your Apple ID email +# APPLE_NOTARIZATION_PASSWORD - App-specific password +# APPLE_NOTARIZATION_TEAM_ID - Your 10-character Team ID set -euo pipefail @@ -9,22 +21,45 @@ if [[ "$(uname)" != "Darwin" ]]; then exit 1 fi -# Check if required tools are available -if ! command -v codesign &> /dev/null; then - echo "codesign not found. Please install Xcode command line tools." +# Check if required tools are available (use xcrun --find as tools may not be on PATH) +if ! xcrun --find codesign &> /dev/null; then + echo "codesign not found. Please install Xcode command line tools:" + echo " xcode-select --install" exit 1 fi -if ! command -v notarytool &> /dev/null; then - echo "notarytool not found. Please install Xcode command line tools." +if ! xcrun --find notarytool &> /dev/null; then + echo "notarytool not found. Please install Xcode command line tools:" + echo " xcode-select --install" exit 1 fi -if ! command -v stapler &> /dev/null; then - echo "stapler not found. Please install Xcode command line tools." +if ! xcrun --find stapler &> /dev/null; then + echo "stapler not found. Please install Xcode command line tools:" + echo " xcode-select --install" exit 1 fi +# Check that the signing identity is set +if [[ -z "${APPLE_SIGNING_IDENTITY:-}" ]]; then + echo "APPLE_SIGNING_IDENTITY is not set." + echo "Example: export APPLE_SIGNING_IDENTITY=\"Developer ID Application: Your Name (TEAMID)\"" + exit 1 +fi + +# Check that frontend assets exist (build.rs requires them) +if [[ ! -f frontend/dist/tabler-sprite.svg ]]; then + echo "Frontend assets not found. Building them now..." + npm ci + npm run build +fi + +# Install the ARM target if on Intel Mac +if [[ "$(uname -m)" == "x86_64" ]]; then + echo "Intel Mac detected, ensuring aarch64-apple-darwin target is installed..." + rustup target add aarch64-apple-darwin || true +fi + # Build the binary echo "Building SQLPage for aarch64-apple-darwin..." cargo build --profile superoptimized --locked --target aarch64-apple-darwin --features "odbc-static" @@ -38,7 +73,7 @@ fi # Sign the binary echo "Signing the binary..." -codesign --force --options runtime --entitlements sqlpage.entitlements --sign "$APPLE_SIGNING_IDENTITY" --timestamp "$BINARY_PATH" +codesign --force --options runtime --entitlements .github/macos/entitlements.plist --sign "$APPLE_SIGNING_IDENTITY" --timestamp "$BINARY_PATH" # Verify the signature echo "Verifying the signature..." @@ -48,7 +83,7 @@ codesign --verify --deep --strict --verbose=2 "$BINARY_PATH" echo "Creating zip archive for notarization..." ditto -c -k --keepParent "$BINARY_PATH" sqlpage-macos.zip -# Notarize the binary +# Notarize the binary (if credentials are provided) if [[ -n "${APPLE_NOTARIZATION_APPLE_ID:-}" && -n "${APPLE_NOTARIZATION_PASSWORD:-}" && -n "${APPLE_NOTARIZATION_TEAM_ID:-}" ]]; then echo "Submitting for notarization..." xcrun notarytool submit sqlpage-macos.zip \ @@ -56,17 +91,25 @@ if [[ -n "${APPLE_NOTARIZATION_APPLE_ID:-}" && -n "${APPLE_NOTARIZATION_PASSWORD --password "$APPLE_NOTARIZATION_PASSWORD" \ --team-id "$APPLE_NOTARIZATION_TEAM_ID" \ --wait - - # Staple the notarization ticket + + # Staple the notarization ticket to the zip (stapler requires a container format) echo "Stapling the notarization ticket..." - xcrun stapler staple "$BINARY_PATH" + xcrun stapler staple sqlpage-macos.zip + + # Extract the stapled binary + echo "Extracting stapled binary..." + rm -rf stapled && mkdir stapled + ditto -x -k sqlpage-macos.zip stapled/ + cp stapled/sqlpage "$BINARY_PATH" + chmod +x "$BINARY_PATH" + + # Final verification including Gatekeeper assessment + echo "Final verification..." + codesign --verify --deep --strict --verbose=2 "$BINARY_PATH" + spctl -a -v "$BINARY_PATH" else echo "Skipping notarization. Set APPLE_NOTARIZATION_APPLE_ID, APPLE_NOTARIZATION_PASSWORD, and APPLE_NOTARIZATION_TEAM_ID to enable notarization." + echo "Note: The binary is signed but not notarized. Gatekeeper will still show a warning." fi -# Final verification -echo "Final verification..." -codesign --verify --deep --strict --verbose=2 "$BINARY_PATH" -spctl -a -v "$BINARY_PATH" - -echo "macOS signing and notarization complete!" \ No newline at end of file +echo "macOS signing complete!" From ba7a8cb3ea01fad4f00e9734e29c8af1c8936ef6 Mon Sep 17 00:00:00 2001 From: lovasoa Date: Tue, 6 Oct 2026 23:08:55 +0200 Subject: [PATCH 05/14] docs: mention signed and notarized macOS binary in install guide The macOS install tutorial now links directly to the sqlpage-macos.tgz download and notes that it is code-signed and notarized by Apple, so no Gatekeeper warning appears on first run. --- .../your-first-sql-website/tutorial-install-macos.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/official-site/your-first-sql-website/tutorial-install-macos.md b/examples/official-site/your-first-sql-website/tutorial-install-macos.md index 50daddf8f..77fad9fa3 100644 --- a/examples/official-site/your-first-sql-website/tutorial-install-macos.md +++ b/examples/official-site/your-first-sql-website/tutorial-install-macos.md @@ -25,8 +25,9 @@ Source builds require Apple's Command Line Tools, which you can install with `xc Homebrew classifies Intel Macs as Tier 3 (limited support); older macOS versions also have restrictions. Check [Homebrew's macOS requirements](https://docs.brew.sh/Installation#macos-requirements) if installation fails. > **Note**: Advanced users can alternatively install SQLPage using -> [the precompiled binaries for Apple silicon](https://github.com/sqlpage/SQLPage/releases/latest), -> [docker](https://hub.docker.com/repository/docker/lovasoa/SQLPage/general), +> [the precompiled binary for Apple silicon](https://github.com/sqlpage/SQLPage/releases/latest/download/sqlpage-macos.tgz). +> The download is code-signed and notarized by Apple, so macOS will not show a security warning when you first run it. +> Other options: [docker](https://hub.docker.com/repository/docker/lovasoa/SQLPage/general), > [nix](https://search.nixos.org/packages?channel=unstable&show=sqlpage), > or [cargo](https://crates.io/crates/sqlpage). From b907418ca2b72d8ac79bff4223512b2e1bc15834 Mon Sep 17 00:00:00 2001 From: lovasoa Date: Tue, 6 Oct 2026 23:16:30 +0200 Subject: [PATCH 06/14] fix: restore tracked .env file for docker-compose CI compatibility The .env file with COMPOSE_PROFILES=mariadb must remain tracked in git. CI runs 'docker compose up --wait postgres' which requires the web service's depends_on to resolve correctly. Without .env, COMPOSE_PROFILES is unset and the web service depends on 'postgres' which is profile-gated, causing: service "web" depends on undefined service "postgres": invalid compose project Secrets should go in .env.local (which is gitignored), not .env. --- .env | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .env diff --git a/.env b/.env new file mode 100644 index 000000000..13d90c53f --- /dev/null +++ b/.env @@ -0,0 +1,2 @@ +# Set COMPOSE_PROFILES to one of the following: postgres, mysql, mssql. If set to mssql, you will need to change the depends_on propery in docker-compose.yml +COMPOSE_PROFILES=mariadb \ No newline at end of file From 7abae5fe1150fa646c05e0d132dfe3bcf60cffab Mon Sep 17 00:00:00 2001 From: lovasoa Date: Tue, 6 Oct 2026 23:27:14 +0200 Subject: [PATCH 07/14] chore: trigger CI re-run for transient docker hub failure From ebc0629172886eef6ab6d5b495d26ea34740aa5d Mon Sep 17 00:00:00 2001 From: lovasoa Date: Tue, 6 Oct 2026 23:44:46 +0200 Subject: [PATCH 08/14] fix: address latest PR review feedback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remove stapler step: it does not support bare Mach-O executables or zip archives. Notarization alone is sufficient for standalone binaries — Gatekeeper checks Apple's servers online. - Pass P12 base64 and password through environment variables instead of inline secret interpolation to avoid shell injection issues with special characters. - Update sign-macos.sh to match: remove stapler, add explanatory comment. --- .github/workflows/release.yml | 20 ++++++++++---------- scripts/sign-macos.sh | 14 +++----------- 2 files changed, 13 insertions(+), 21 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c44461fad..334ea7346 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -38,7 +38,7 @@ jobs: - name: Build run: cargo build --profile superoptimized --locked --target ${{ matrix.target }} --features "${{ matrix.features }}" - # macOS: sign, notarize, and staple the binary + # macOS: sign and notarize the binary - name: Sign and notarize macOS binary if: matrix.os == 'macos-latest' env: @@ -46,32 +46,32 @@ jobs: APPLE_NOTARIZATION_APPLE_ID: ${{ secrets.APPLE_NOTARIZATION_APPLE_ID }} APPLE_NOTARIZATION_PASSWORD: ${{ secrets.APPLE_NOTARIZATION_PASSWORD }} APPLE_NOTARIZATION_TEAM_ID: ${{ secrets.APPLE_NOTARIZATION_TEAM_ID }} + P12_BASE64: ${{ secrets.APPLE_SIGNING_CERTIFICATE_P12_BASE64 }} + P12_PASSWORD: ${{ secrets.APPLE_SIGNING_CERTIFICATE_PASSWORD }} run: | set -euo pipefail BIN=target/${{ matrix.target }}/superoptimized/sqlpage - # Import the signing certificate into the keychain - echo "${{ secrets.APPLE_SIGNING_CERTIFICATE_P12_BASE64 }}" | base64 -d > /tmp/signing.p12 + # Import the signing certificate into a temporary keychain + printf '%s' "$P12_BASE64" | base64 -d > /tmp/signing.p12 security create-keychain -p "" /tmp/signing.keychain security set-keychain-settings -lut 3600 /tmp/signing.keychain security unlock-keychain -p "" /tmp/signing.keychain - security import /tmp/signing.p12 -k /tmp/signing.keychain -P "${{ secrets.APPLE_SIGNING_CERTIFICATE_PASSWORD }}" -T /usr/bin/codesign + security import /tmp/signing.p12 -k /tmp/signing.keychain -P "$P12_PASSWORD" -T /usr/bin/codesign security set-key-partition-list -S apple-tool:,apple: -s -k "" /tmp/signing.keychain > /dev/null security list-keychains -d user -s /tmp/signing.keychain "$(security default-keychain)" # Sign with hardened runtime and entitlements codesign --force --options runtime --entitlements .github/macos/entitlements.plist --sign "$APPLE_SIGNING_IDENTITY" --timestamp "$BIN" codesign --verify --deep --strict --verbose=2 "$BIN" - # Package, notarize, and staple (stapler requires a container format, not a bare Mach-O) + # Package in a zip and submit for notarization. + # Note: stapler only supports .app bundles, disk images, and flat packages — + # not bare Mach-O executables or zip archives. For a standalone binary, + # notarization alone is sufficient: Gatekeeper checks Apple's servers online. ditto -c -k --keepParent "$BIN" sqlpage-macos.zip xcrun notarytool submit sqlpage-macos.zip \ --apple-id "$APPLE_NOTARIZATION_APPLE_ID" \ --password "$APPLE_NOTARIZATION_PASSWORD" \ --team-id "$APPLE_NOTARIZATION_TEAM_ID" \ --wait - xcrun stapler staple sqlpage-macos.zip - rm -rf stapled && mkdir stapled - ditto -x -k sqlpage-macos.zip stapled/ - cp stapled/sqlpage "$BIN" - chmod +x "$BIN" codesign --verify --deep --strict "$BIN" - name: Upload unsigned Windows artifact diff --git a/scripts/sign-macos.sh b/scripts/sign-macos.sh index 64196f9e1..a1dc020cc 100755 --- a/scripts/sign-macos.sh +++ b/scripts/sign-macos.sh @@ -84,6 +84,9 @@ echo "Creating zip archive for notarization..." ditto -c -k --keepParent "$BINARY_PATH" sqlpage-macos.zip # Notarize the binary (if credentials are provided) +# Note: stapler does not support bare Mach-O executables or zip archives. +# For a standalone binary, notarization alone is sufficient — Gatekeeper +# checks Apple's notarization servers online when the binary is first run. if [[ -n "${APPLE_NOTARIZATION_APPLE_ID:-}" && -n "${APPLE_NOTARIZATION_PASSWORD:-}" && -n "${APPLE_NOTARIZATION_TEAM_ID:-}" ]]; then echo "Submitting for notarization..." xcrun notarytool submit sqlpage-macos.zip \ @@ -92,17 +95,6 @@ if [[ -n "${APPLE_NOTARIZATION_APPLE_ID:-}" && -n "${APPLE_NOTARIZATION_PASSWORD --team-id "$APPLE_NOTARIZATION_TEAM_ID" \ --wait - # Staple the notarization ticket to the zip (stapler requires a container format) - echo "Stapling the notarization ticket..." - xcrun stapler staple sqlpage-macos.zip - - # Extract the stapled binary - echo "Extracting stapled binary..." - rm -rf stapled && mkdir stapled - ditto -x -k sqlpage-macos.zip stapled/ - cp stapled/sqlpage "$BINARY_PATH" - chmod +x "$BINARY_PATH" - # Final verification including Gatekeeper assessment echo "Final verification..." codesign --verify --deep --strict --verbose=2 "$BINARY_PATH" From 14497a71bc8e113c77d4f2a30bbcc2d1ff8b363f Mon Sep 17 00:00:00 2001 From: Ophir Lojkine Date: Wed, 7 Oct 2026 10:35:08 +0000 Subject: [PATCH 09/14] fix: address macOS signing review feedback --- .github/macos/entitlements.plist | 5 +---- .github/workflows/release.yml | 11 ++++++++--- CHANGELOG.md | 2 +- .../tutorial-install-macos.md | 2 +- scripts/sign-macos.sh | 15 +++++++-------- 5 files changed, 18 insertions(+), 17 deletions(-) diff --git a/.github/macos/entitlements.plist b/.github/macos/entitlements.plist index 0bf8260e4..c53774e6a 100644 --- a/.github/macos/entitlements.plist +++ b/.github/macos/entitlements.plist @@ -2,15 +2,12 @@ - com.apple.security.cs.disable-library-validation - com.apple.security.network.client com.apple.security.network.server - com.apple.security.files.user-selected.read-only com.apple.security.files.user-selected.read-write @@ -18,4 +15,4 @@ com.apple.security.files.downloads.read-write - \ No newline at end of file + diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 334ea7346..f082eb84c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -37,7 +37,7 @@ jobs: - uses: ./.github/actions/install-frontend-dependencies - name: Build run: cargo build --profile superoptimized --locked --target ${{ matrix.target }} --features "${{ matrix.features }}" - + # macOS: sign and notarize the binary - name: Sign and notarize macOS binary if: matrix.os == 'macos-latest' @@ -71,9 +71,14 @@ jobs: --apple-id "$APPLE_NOTARIZATION_APPLE_ID" \ --password "$APPLE_NOTARIZATION_PASSWORD" \ --team-id "$APPLE_NOTARIZATION_TEAM_ID" \ - --wait + --wait --output-format plist > notarization-result.plist + if [[ "$(plutil -extract status raw -o - notarization-result.plist)" != "Accepted" ]]; then + echo "Notarization was not accepted:" >&2 + cat notarization-result.plist >&2 + exit 1 + fi codesign --verify --deep --strict "$BIN" - + - name: Upload unsigned Windows artifact if: matrix.os == 'windows-latest' id: upload_unsigned diff --git a/CHANGELOG.md b/CHANGELOG.md index 41c2e0513..ff6cc86f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ # CHANGELOG.md ## v0.47.0 (unreleased) -- **Mac users:** the downloadable `sqlpage-macos.tgz` is now code-signed with a Developer ID certificate and notarized by Apple, so macOS no longer shows a Gatekeeper warning when you first run it. The binary also has the entitlements needed to load third-party ODBC database drivers. +- **Mac users:** the downloadable `sqlpage-macos.tgz` is now code-signed with a Developer ID certificate and notarized by Apple. macOS may still ask you to confirm opening it the first time. The binary can still load third-party ODBC database drivers. - **Mac users:** the downloadable `sqlpage-macos.tgz` now runs natively on Apple silicon (M-series Macs) and no longer runs on Intel Macs. Homebrew remains the recommended and easiest installation method. On an Intel Mac, [install Homebrew](https://brew.sh/) if needed, then run `brew install sqlpage` (or `brew update` followed by `brew upgrade sqlpage` if you already installed it with Homebrew). Open Terminal in your existing website folder and run `sqlpage` instead of `./sqlpage.bin`; keep your SQL files, database, and `sqlpage` configuration folder in place. Intel installations may build from source and take longer; see the [macOS installation guide](https://sql-page.com/your-first-sql-website/?os=macos#download) for setup and older macOS requirements. - Chart data points can now include a `link`. Clicking a point or its tooltip value opens that URL; a text x value in the tooltip title links to it too. The tooltip remains open while the pointer moves onto the link. ApexCharts was updated to [v7.6.0](https://github.com/apexcharts/apexcharts.js/releases/tag/v7.6.0) to support this. - Updated sqlx-oldapi to v0.6.57 to fix SQL Server fallback expressions such as `ISNULL($missing, 'default')` truncating defaults or failing for date values when the bound variable is `NULL`. diff --git a/examples/official-site/your-first-sql-website/tutorial-install-macos.md b/examples/official-site/your-first-sql-website/tutorial-install-macos.md index 77fad9fa3..043eb3cb6 100644 --- a/examples/official-site/your-first-sql-website/tutorial-install-macos.md +++ b/examples/official-site/your-first-sql-website/tutorial-install-macos.md @@ -26,7 +26,7 @@ Homebrew classifies Intel Macs as Tier 3 (limited support); older macOS versions > **Note**: Advanced users can alternatively install SQLPage using > [the precompiled binary for Apple silicon](https://github.com/sqlpage/SQLPage/releases/latest/download/sqlpage-macos.tgz). -> The download is code-signed and notarized by Apple, so macOS will not show a security warning when you first run it. +> The download is code-signed with a Developer ID certificate and notarized by Apple. macOS may still ask you to confirm opening it the first time. > Other options: [docker](https://hub.docker.com/repository/docker/lovasoa/SQLPage/general), > [nix](https://search.nixos.org/packages?channel=unstable&show=sqlpage), > or [cargo](https://crates.io/crates/sqlpage). diff --git a/scripts/sign-macos.sh b/scripts/sign-macos.sh index a1dc020cc..2d91310ca 100755 --- a/scripts/sign-macos.sh +++ b/scripts/sign-macos.sh @@ -34,12 +34,6 @@ if ! xcrun --find notarytool &> /dev/null; then exit 1 fi -if ! xcrun --find stapler &> /dev/null; then - echo "stapler not found. Please install Xcode command line tools:" - echo " xcode-select --install" - exit 1 -fi - # Check that the signing identity is set if [[ -z "${APPLE_SIGNING_IDENTITY:-}" ]]; then echo "APPLE_SIGNING_IDENTITY is not set." @@ -57,7 +51,7 @@ fi # Install the ARM target if on Intel Mac if [[ "$(uname -m)" == "x86_64" ]]; then echo "Intel Mac detected, ensuring aarch64-apple-darwin target is installed..." - rustup target add aarch64-apple-darwin || true + rustup target add aarch64-apple-darwin fi # Build the binary @@ -93,7 +87,12 @@ if [[ -n "${APPLE_NOTARIZATION_APPLE_ID:-}" && -n "${APPLE_NOTARIZATION_PASSWORD --apple-id "$APPLE_NOTARIZATION_APPLE_ID" \ --password "$APPLE_NOTARIZATION_PASSWORD" \ --team-id "$APPLE_NOTARIZATION_TEAM_ID" \ - --wait + --wait --output-format plist > notarization-result.plist + if [[ "$(plutil -extract status raw -o - notarization-result.plist)" != "Accepted" ]]; then + echo "Notarization was not accepted:" >&2 + cat notarization-result.plist >&2 + exit 1 + fi # Final verification including Gatekeeper assessment echo "Final verification..." From af04acf7f2c4aeb28adfca9368de44e27454e96e Mon Sep 17 00:00:00 2001 From: Ophir Lojkine Date: Wed, 7 Oct 2026 10:43:27 +0000 Subject: [PATCH 10/14] ci: test macOS signing helper on relevant changes --- .github/workflows/macos-signing.yml | 65 +++++++++++++++++++ .../your-first-sql-website/index.sql | 2 +- .../tutorial-install-macos.md | 32 ++++++--- .../your-first-sql-website/tutorial.md | 2 +- 4 files changed, 91 insertions(+), 10 deletions(-) create mode 100644 .github/workflows/macos-signing.yml diff --git a/.github/workflows/macos-signing.yml b/.github/workflows/macos-signing.yml new file mode 100644 index 000000000..6300c6cbf --- /dev/null +++ b/.github/workflows/macos-signing.yml @@ -0,0 +1,65 @@ +name: macOS signing + +on: + pull_request: + branches: [main] + paths: + - scripts/sign-macos.sh + - .github/macos/entitlements.plist + - .github/workflows/macos-signing.yml + push: + branches: [main] + paths: + - scripts/sign-macos.sh + - .github/macos/entitlements.plist + - .github/workflows/macos-signing.yml + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + sign-macos: + # Fork PRs cannot access the Apple credentials. Test them on a repository branch. + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository + runs-on: macos-latest + timeout-minutes: 45 + steps: + - uses: actions/checkout@v7 + - uses: ./.github/actions/install-frontend-dependencies + - name: Set up cargo cache + uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 + env: + NODE_OPTIONS: --no-deprecation + - name: Run the signing helper + env: + APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }} + APPLE_NOTARIZATION_APPLE_ID: ${{ secrets.APPLE_NOTARIZATION_APPLE_ID }} + APPLE_NOTARIZATION_PASSWORD: ${{ secrets.APPLE_NOTARIZATION_PASSWORD }} + APPLE_NOTARIZATION_TEAM_ID: ${{ secrets.APPLE_NOTARIZATION_TEAM_ID }} + P12_BASE64: ${{ secrets.APPLE_SIGNING_CERTIFICATE_P12_BASE64 }} + P12_PASSWORD: ${{ secrets.APPLE_SIGNING_CERTIFICATE_PASSWORD }} + run: | + set -euo pipefail + : "${APPLE_SIGNING_IDENTITY:?Missing APPLE_SIGNING_IDENTITY secret}" + : "${APPLE_NOTARIZATION_APPLE_ID:?Missing APPLE_NOTARIZATION_APPLE_ID secret}" + : "${APPLE_NOTARIZATION_PASSWORD:?Missing APPLE_NOTARIZATION_PASSWORD secret}" + : "${APPLE_NOTARIZATION_TEAM_ID:?Missing APPLE_NOTARIZATION_TEAM_ID secret}" + : "${P12_BASE64:?Missing APPLE_SIGNING_CERTIFICATE_P12_BASE64 secret}" + : "${P12_PASSWORD:?Missing APPLE_SIGNING_CERTIFICATE_PASSWORD secret}" + CERTIFICATE_PATH="$RUNNER_TEMP/signing.p12" + KEYCHAIN_PATH="$RUNNER_TEMP/signing.keychain-db" + trap 'security delete-keychain "$KEYCHAIN_PATH"; rm -f "$CERTIFICATE_PATH"' EXIT + printf '%s' "$P12_BASE64" | base64 -d > "$CERTIFICATE_PATH" + security create-keychain -p "" "$KEYCHAIN_PATH" + security set-keychain-settings -lut 3600 "$KEYCHAIN_PATH" + security unlock-keychain -p "" "$KEYCHAIN_PATH" + security import "$CERTIFICATE_PATH" -k "$KEYCHAIN_PATH" -P "$P12_PASSWORD" -T /usr/bin/codesign + security set-key-partition-list -S apple-tool:,apple: -s -k "" "$KEYCHAIN_PATH" > /dev/null + security list-keychains -d user -s "$KEYCHAIN_PATH" + scripts/sign-macos.sh + - name: Run the signed binary + run: target/aarch64-apple-darwin/superoptimized/sqlpage --version diff --git a/examples/official-site/your-first-sql-website/index.sql b/examples/official-site/your-first-sql-website/index.sql index 9c2460e04..85c68cca8 100644 --- a/examples/official-site/your-first-sql-website/index.sql +++ b/examples/official-site/your-first-sql-website/index.sql @@ -43,7 +43,7 @@ Let''s create a simple website with a database from scratch, to learn SQLPage ba ELSE 'https://github.com/sqlpage/SQLPage/releases' END AS link, CASE $os - WHEN 'macos' THEN CONCAT('Install SQLPage ', $sqlpage_version, ' using Homebrew') + WHEN 'macos' THEN CONCAT('Install SQLPage ', $sqlpage_version, ' for macOS') WHEN 'windows' THEN CONCAT('Download SQLPage ', $sqlpage_version, ' for Windows') WHEN 'linux' THEN CONCAT('Download SQLPage ', $sqlpage_version, ' for Linux') ELSE CONCAT('Download SQLPage ', $sqlpage_version) diff --git a/examples/official-site/your-first-sql-website/tutorial-install-macos.md b/examples/official-site/your-first-sql-website/tutorial-install-macos.md index 043eb3cb6..973c21a9b 100644 --- a/examples/official-site/your-first-sql-website/tutorial-install-macos.md +++ b/examples/official-site/your-first-sql-website/tutorial-install-macos.md @@ -1,7 +1,27 @@ # Install SQLPage on macOS -The recommended and easiest way to install SQLPage on macOS is [Homebrew](https://brew.sh/), including on Intel Macs. -If you do not already have Homebrew, open Terminal and run: +For a quick start on an Apple silicon Mac (M-series), download SQLPage below. +If you already use [Homebrew](https://brew.sh/) or want easier updates, use the Homebrew instructions instead. +On an Intel Mac, use Homebrew. + +## Download SQLPage for Apple silicon + +[Download SQLPage for Apple silicon](https://github.com/sqlpage/SQLPage/releases/latest/download/sqlpage-macos.tgz). +Open the downloaded archive to extract it. Keep `sqlpage.bin` and the `sqlpage` folder together in your website folder. +Open Terminal in that folder and run: + +```sh +./sqlpage.bin +``` + +macOS may ask you to confirm opening SQLPage the first time. +To update later, download the latest version and replace `sqlpage.bin`. Keep your SQL files, database, and `sqlpage` configuration folder. + +Starting with SQLPage v0.47.0, this download is for Apple silicon only. On an Intel Mac, use Homebrew below. + +## Install with Homebrew + +If you do not already have Homebrew, open Terminal and install it: ```sh /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" @@ -16,18 +36,14 @@ brew install sqlpage Open Terminal in your website folder and run `sqlpage` to start your website. To update SQLPage later, run `brew update` followed by `brew upgrade sqlpage`. -**Using an Intel Mac?** Starting with SQLPage v0.47.0, the `sqlpage-macos.tgz` download is for Apple silicon (M-series Macs) only. -Use Homebrew on Intel Macs. If you previously used the downloaded executable, keep your SQL files, database, and `sqlpage` configuration folder in place and run `sqlpage` from the same website folder instead of `./sqlpage.bin`. -If SQLPage is already installed through Homebrew, use the upgrade commands above. +If you previously used the downloaded executable, keep your SQL files, database, and `sqlpage` configuration folder in place and run `sqlpage` from the same website folder instead of `./sqlpage.bin`. Homebrew may compile SQLPage and its dependencies from source on Intel Macs, so installation can take longer. Source builds require Apple's Command Line Tools, which you can install with `xcode-select --install`. Homebrew classifies Intel Macs as Tier 3 (limited support); older macOS versions also have restrictions. Check [Homebrew's macOS requirements](https://docs.brew.sh/Installation#macos-requirements) if installation fails. > **Note**: Advanced users can alternatively install SQLPage using -> [the precompiled binary for Apple silicon](https://github.com/sqlpage/SQLPage/releases/latest/download/sqlpage-macos.tgz). -> The download is code-signed with a Developer ID certificate and notarized by Apple. macOS may still ask you to confirm opening it the first time. -> Other options: [docker](https://hub.docker.com/repository/docker/lovasoa/SQLPage/general), +> [docker](https://hub.docker.com/repository/docker/lovasoa/SQLPage/general), > [nix](https://search.nixos.org/packages?channel=unstable&show=sqlpage), > or [cargo](https://crates.io/crates/sqlpage). diff --git a/examples/official-site/your-first-sql-website/tutorial.md b/examples/official-site/your-first-sql-website/tutorial.md index 22cfc9f7e..761dfaa3b 100644 --- a/examples/official-site/your-first-sql-website/tutorial.md +++ b/examples/official-site/your-first-sql-website/tutorial.md @@ -5,7 +5,7 @@ In the rest of this tutorial, we will call this folder the **root folder** of yo - On **Windows**, place the `sqlpage.exe` you downloaded above at the root of the folder. Then double-click the `sqlpage.exe` file to start the server. - On **Linux**, place `sqlpage.bin` at the root of the folder. Then open a terminal, cd to the root folder of your website, and run `./sqlpage.bin` to start the server. -- On **Mac OS**, if you installed SQLPage using Homebrew, then you do not need to place anything at the root of the folder. Open Terminal, cd to the root folder of your website, and type `sqlpage` to start the server. +- On **Mac OS**, if you installed SQLPage using Homebrew, then you do not need to place anything at the root of the folder. Open Terminal, cd to the root folder of your website, and type `sqlpage` to start the server. If you downloaded SQLPage instead, place `sqlpage.bin` in this folder and run `./sqlpage.bin`. ![screenshot for the sql website setup on linux](first-sql-website-launch.png) From 949038fa00c2f072dcfede196b99e014d754b54c Mon Sep 17 00:00:00 2001 From: Ophir Lojkine Date: Wed, 7 Oct 2026 10:46:10 +0000 Subject: [PATCH 11/14] ci: validate signing certificate before keychain import --- .github/workflows/macos-signing.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/macos-signing.yml b/.github/workflows/macos-signing.yml index 6300c6cbf..9bb02ca52 100644 --- a/.github/workflows/macos-signing.yml +++ b/.github/workflows/macos-signing.yml @@ -54,6 +54,7 @@ jobs: KEYCHAIN_PATH="$RUNNER_TEMP/signing.keychain-db" trap 'security delete-keychain "$KEYCHAIN_PATH"; rm -f "$CERTIFICATE_PATH"' EXIT printf '%s' "$P12_BASE64" | base64 -d > "$CERTIFICATE_PATH" + openssl pkcs12 -in "$CERTIFICATE_PATH" -passin env:P12_PASSWORD -noout security create-keychain -p "" "$KEYCHAIN_PATH" security set-keychain-settings -lut 3600 "$KEYCHAIN_PATH" security unlock-keychain -p "" "$KEYCHAIN_PATH" From 2c1bd1aeb361f79635dd9c4e434306ec15f5c2df Mon Sep 17 00:00:00 2001 From: Ophir Lojkine Date: Wed, 7 Oct 2026 10:49:01 +0000 Subject: [PATCH 12/14] fix: import signing certificates in macOS-compatible format --- .github/workflows/macos-signing.yml | 7 +++++-- .github/workflows/release.yml | 6 +++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/.github/workflows/macos-signing.yml b/.github/workflows/macos-signing.yml index 9bb02ca52..ffc790147 100644 --- a/.github/workflows/macos-signing.yml +++ b/.github/workflows/macos-signing.yml @@ -53,8 +53,11 @@ jobs: CERTIFICATE_PATH="$RUNNER_TEMP/signing.p12" KEYCHAIN_PATH="$RUNNER_TEMP/signing.keychain-db" trap 'security delete-keychain "$KEYCHAIN_PATH"; rm -f "$CERTIFICATE_PATH"' EXIT - printf '%s' "$P12_BASE64" | base64 -d > "$CERTIFICATE_PATH" - openssl pkcs12 -in "$CERTIFICATE_PATH" -passin env:P12_PASSWORD -noout + # Use PKCS12 algorithms supported by macOS Keychain. + printf '%s' "$P12_BASE64" | base64 -d | + openssl pkcs12 -passin env:P12_PASSWORD -nodes | + openssl pkcs12 -export -passout env:P12_PASSWORD \ + -keypbe PBE-SHA1-3DES -certpbe PBE-SHA1-3DES -macalg sha1 -out "$CERTIFICATE_PATH" security create-keychain -p "" "$KEYCHAIN_PATH" security set-keychain-settings -lut 3600 "$KEYCHAIN_PATH" security unlock-keychain -p "" "$KEYCHAIN_PATH" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f082eb84c..202a9488d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -52,7 +52,11 @@ jobs: set -euo pipefail BIN=target/${{ matrix.target }}/superoptimized/sqlpage # Import the signing certificate into a temporary keychain - printf '%s' "$P12_BASE64" | base64 -d > /tmp/signing.p12 + # Use PKCS12 algorithms supported by macOS Keychain. + printf '%s' "$P12_BASE64" | base64 -d | + openssl pkcs12 -passin env:P12_PASSWORD -nodes | + openssl pkcs12 -export -passout env:P12_PASSWORD \ + -keypbe PBE-SHA1-3DES -certpbe PBE-SHA1-3DES -macalg sha1 -out /tmp/signing.p12 security create-keychain -p "" /tmp/signing.keychain security set-keychain-settings -lut 3600 /tmp/signing.keychain security unlock-keychain -p "" /tmp/signing.keychain From 98a04e5d51893a882854a28ba1c81e94e98fd15e Mon Sep 17 00:00:00 2001 From: Ophir Lojkine Date: Wed, 7 Oct 2026 10:50:08 +0000 Subject: [PATCH 13/14] fix: re-export PKCS12 from a temporary PEM file --- .github/workflows/macos-signing.yml | 12 +++++++----- .github/workflows/release.yml | 11 +++++++---- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/.github/workflows/macos-signing.yml b/.github/workflows/macos-signing.yml index ffc790147..8c4868477 100644 --- a/.github/workflows/macos-signing.yml +++ b/.github/workflows/macos-signing.yml @@ -52,12 +52,14 @@ jobs: : "${P12_PASSWORD:?Missing APPLE_SIGNING_CERTIFICATE_PASSWORD secret}" CERTIFICATE_PATH="$RUNNER_TEMP/signing.p12" KEYCHAIN_PATH="$RUNNER_TEMP/signing.keychain-db" - trap 'security delete-keychain "$KEYCHAIN_PATH"; rm -f "$CERTIFICATE_PATH"' EXIT + umask 077 + trap 'security delete-keychain "$KEYCHAIN_PATH"; rm -f "$CERTIFICATE_PATH" "$CERTIFICATE_PATH.pem"' EXIT # Use PKCS12 algorithms supported by macOS Keychain. - printf '%s' "$P12_BASE64" | base64 -d | - openssl pkcs12 -passin env:P12_PASSWORD -nodes | - openssl pkcs12 -export -passout env:P12_PASSWORD \ - -keypbe PBE-SHA1-3DES -certpbe PBE-SHA1-3DES -macalg sha1 -out "$CERTIFICATE_PATH" + printf '%s' "$P12_BASE64" | base64 -d > "$CERTIFICATE_PATH" + openssl pkcs12 -in "$CERTIFICATE_PATH" -passin env:P12_PASSWORD -nodes -out "$CERTIFICATE_PATH.pem" + openssl pkcs12 -export -in "$CERTIFICATE_PATH.pem" -passout env:P12_PASSWORD \ + -keypbe PBE-SHA1-3DES -certpbe PBE-SHA1-3DES -macalg sha1 -out "$CERTIFICATE_PATH" + rm -f "$CERTIFICATE_PATH.pem" security create-keychain -p "" "$KEYCHAIN_PATH" security set-keychain-settings -lut 3600 "$KEYCHAIN_PATH" security unlock-keychain -p "" "$KEYCHAIN_PATH" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 202a9488d..b782adf23 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -52,11 +52,14 @@ jobs: set -euo pipefail BIN=target/${{ matrix.target }}/superoptimized/sqlpage # Import the signing certificate into a temporary keychain + umask 077 + trap 'rm -f /tmp/signing.pem' EXIT # Use PKCS12 algorithms supported by macOS Keychain. - printf '%s' "$P12_BASE64" | base64 -d | - openssl pkcs12 -passin env:P12_PASSWORD -nodes | - openssl pkcs12 -export -passout env:P12_PASSWORD \ - -keypbe PBE-SHA1-3DES -certpbe PBE-SHA1-3DES -macalg sha1 -out /tmp/signing.p12 + printf '%s' "$P12_BASE64" | base64 -d > /tmp/signing.p12 + openssl pkcs12 -in /tmp/signing.p12 -passin env:P12_PASSWORD -nodes -out /tmp/signing.pem + openssl pkcs12 -export -in /tmp/signing.pem -passout env:P12_PASSWORD \ + -keypbe PBE-SHA1-3DES -certpbe PBE-SHA1-3DES -macalg sha1 -out /tmp/signing.p12 + rm -f /tmp/signing.pem security create-keychain -p "" /tmp/signing.keychain security set-keychain-settings -lut 3600 /tmp/signing.keychain security unlock-keychain -p "" /tmp/signing.keychain From 62b8594709af9b50575cb4e78dda734f16b88032 Mon Sep 17 00:00:00 2001 From: Ophir Lojkine Date: Wed, 7 Oct 2026 11:12:46 +0000 Subject: [PATCH 14/14] fix: test PR signing without production credentials --- .github/workflows/macos-signing.yml | 10 +++++++--- .github/workflows/release.yml | 2 +- scripts/sign-macos.sh | 25 ++++++++++++++----------- 3 files changed, 22 insertions(+), 15 deletions(-) diff --git a/.github/workflows/macos-signing.yml b/.github/workflows/macos-signing.yml index 8c4868477..6fb371721 100644 --- a/.github/workflows/macos-signing.yml +++ b/.github/workflows/macos-signing.yml @@ -23,8 +23,6 @@ concurrency: jobs: sign-macos: - # Fork PRs cannot access the Apple credentials. Test them on a repository branch. - if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository runs-on: macos-latest timeout-minutes: 45 steps: @@ -34,7 +32,13 @@ jobs: uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 env: NODE_OPTIONS: --no-deprecation - - name: Run the signing helper + - name: Test the signing helper on pull requests + if: github.event_name == 'pull_request' + env: + APPLE_SIGNING_IDENTITY: "-" # Ad hoc signing requires no Apple credentials. + run: scripts/sign-macos.sh + - name: Run the signing helper with Apple credentials + if: github.event_name == 'push' env: APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }} APPLE_NOTARIZATION_APPLE_ID: ${{ secrets.APPLE_NOTARIZATION_APPLE_ID }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b782adf23..905779ef7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -53,7 +53,7 @@ jobs: BIN=target/${{ matrix.target }}/superoptimized/sqlpage # Import the signing certificate into a temporary keychain umask 077 - trap 'rm -f /tmp/signing.pem' EXIT + trap 'security delete-keychain /tmp/signing.keychain; rm -f /tmp/signing.p12 /tmp/signing.pem' EXIT # Use PKCS12 algorithms supported by macOS Keychain. printf '%s' "$P12_BASE64" | base64 -d > /tmp/signing.p12 openssl pkcs12 -in /tmp/signing.p12 -passin env:P12_PASSWORD -nodes -out /tmp/signing.pem diff --git a/scripts/sign-macos.sh b/scripts/sign-macos.sh index 2d91310ca..15b96d913 100755 --- a/scripts/sign-macos.sh +++ b/scripts/sign-macos.sh @@ -8,7 +8,7 @@ # security import sqlpage.p12 -k ~/Library/Keychains/login.keychain-db -P "" -T /usr/bin/codesign # # Required environment variables for notarization: -# APPLE_SIGNING_IDENTITY - e.g. "Developer ID Application: Your Name (TEAMID)" +# APPLE_SIGNING_IDENTITY - e.g. "Developer ID Application: Your Name (TEAMID)", or "-" for ad hoc signing # APPLE_NOTARIZATION_APPLE_ID - Your Apple ID email # APPLE_NOTARIZATION_PASSWORD - App-specific password # APPLE_NOTARIZATION_TEAM_ID - Your 10-character Team ID @@ -28,12 +28,6 @@ if ! xcrun --find codesign &> /dev/null; then exit 1 fi -if ! xcrun --find notarytool &> /dev/null; then - echo "notarytool not found. Please install Xcode command line tools:" - echo " xcode-select --install" - exit 1 -fi - # Check that the signing identity is set if [[ -z "${APPLE_SIGNING_IDENTITY:-}" ]]; then echo "APPLE_SIGNING_IDENTITY is not set." @@ -67,7 +61,11 @@ fi # Sign the binary echo "Signing the binary..." -codesign --force --options runtime --entitlements .github/macos/entitlements.plist --sign "$APPLE_SIGNING_IDENTITY" --timestamp "$BINARY_PATH" +SIGNING_TIMESTAMP_OPTION=--timestamp +if [[ "$APPLE_SIGNING_IDENTITY" == "-" ]]; then + SIGNING_TIMESTAMP_OPTION=--timestamp=none +fi +codesign --force --options runtime --entitlements .github/macos/entitlements.plist --sign "$APPLE_SIGNING_IDENTITY" "$SIGNING_TIMESTAMP_OPTION" "$BINARY_PATH" # Verify the signature echo "Verifying the signature..." @@ -82,6 +80,12 @@ ditto -c -k --keepParent "$BINARY_PATH" sqlpage-macos.zip # For a standalone binary, notarization alone is sufficient — Gatekeeper # checks Apple's notarization servers online when the binary is first run. if [[ -n "${APPLE_NOTARIZATION_APPLE_ID:-}" && -n "${APPLE_NOTARIZATION_PASSWORD:-}" && -n "${APPLE_NOTARIZATION_TEAM_ID:-}" ]]; then + if ! xcrun --find notarytool &> /dev/null; then + echo "notarytool not found. Please install Xcode command line tools:" + echo " xcode-select --install" + exit 1 + fi + echo "Submitting for notarization..." xcrun notarytool submit sqlpage-macos.zip \ --apple-id "$APPLE_NOTARIZATION_APPLE_ID" \ @@ -94,10 +98,9 @@ if [[ -n "${APPLE_NOTARIZATION_APPLE_ID:-}" && -n "${APPLE_NOTARIZATION_PASSWORD exit 1 fi - # Final verification including Gatekeeper assessment + # Verify the signature and notarization ticket for the standalone binary. echo "Final verification..." - codesign --verify --deep --strict --verbose=2 "$BINARY_PATH" - spctl -a -v "$BINARY_PATH" + codesign --verify --deep --strict --verbose=2 --check-notarization -R=notarized "$BINARY_PATH" else echo "Skipping notarization. Set APPLE_NOTARIZATION_APPLE_ID, APPLE_NOTARIZATION_PASSWORD, and APPLE_NOTARIZATION_TEAM_ID to enable notarization." echo "Note: The binary is signed but not notarized. Gatekeeper will still show a warning."