tauri-action notarizes and staples the .app but never the .dmg wrapping it, so
Gatekeeper rejected the downloaded DMG as "Unnotarized Developer ID" even
though the app inside was fine:
spctl --assess --type open --context context:primary-signature -v \
Gridline_0.8.0_aarch64.dmg
-> rejected, source=Unnotarized Developer ID
Gatekeeper assesses the DMG itself when a user opens the download, so the app
being notarized does not save the mount. Submit each DMG to the same notary
service and staple the ticket into it.
- release.yml: new macOS-only step after tauri-action replaces each uploaded
DMG with its stapled copy. Assets are replaced via the release ID because the
GitHub releases/tags endpoint 404s for drafts, so uploading by tag cannot
work while the release is still a draft.
- release-mac.sh: same notarize + staple pass for the local/Gitea path before
upload, respecting GRIDLINE_SKIP_NOTARIZE.
- README: state that both the .app and the .dmg carry a stapled ticket.
571 lines
28 KiB
YAML
571 lines
28 KiB
YAML
name: Release
|
|
|
|
# Builds Gridline installers for macOS (Apple Silicon + Intel), Windows, and
|
|
# Linux, then uploads them to a draft GitHub Release.
|
|
#
|
|
# Trigger: push a version tag from the `prod` branch (production), e.g.
|
|
# git checkout prod && git pull
|
|
# git tag v0.5.0 && git push origin v0.5.0
|
|
#
|
|
# SIGNING STATUS: this is the ACTIVE release path. macOS is Developer-ID
|
|
# signed + notarized in CI when the Apple secrets below are configured (see
|
|
# README "macOS signing & notarization"). When the secrets are absent the mac
|
|
# jobs still build, but ad-hoc signed / unnotarized. Linux/Windows installers
|
|
# remain unsigned.
|
|
#
|
|
# Required secrets for signed + notarized macOS builds:
|
|
# APPLE_CERTIFICATE base64 of the Developer ID Application .p12
|
|
# APPLE_CERTIFICATE_PASSWORD password for that .p12
|
|
# APPLE_SIGNING_IDENTITY "Developer ID Application: Name (TEAMID)"
|
|
# KEYCHAIN_PASSWORD any throwaway password for the temp keychain
|
|
# APPLE_API_KEY App Store Connect Key ID
|
|
# APPLE_API_ISSUER App Store Connect Issuer ID
|
|
# APPLE_API_KEY_CONTENT contents of the AuthKey_<KeyID>.p8
|
|
#
|
|
# The cask bump runs only after the draft release is PUBLISHED (the GitHub
|
|
# releases/tags API does not serve drafts), so it is wired to the `release:
|
|
# published` event rather than the tag push. It pushes with a write-enabled
|
|
# deploy key scoped to ONLY the tap repo (HOMEBREW_TAP_DEPLOY_KEY), so no
|
|
# broad personal access token is stored — the default GITHUB_TOKEN cannot
|
|
# push to a different repository.
|
|
|
|
on:
|
|
push:
|
|
tags:
|
|
- 'v*'
|
|
release:
|
|
types: [published]
|
|
# Rebuild an EXISTING tag without moving it — e.g. to re-run a release after
|
|
# a workflow fix. Dispatch from `prod` and pass the tag to build; the tag
|
|
# itself is never rewritten, so the release ref stays truthful.
|
|
workflow_dispatch:
|
|
inputs:
|
|
tag:
|
|
description: 'Tag to build or bump the cask for, e.g. v0.8.0'
|
|
required: true
|
|
bump_cask:
|
|
description: 'Only bump the Homebrew cask for `tag` (skip the build)'
|
|
type: boolean
|
|
default: false
|
|
|
|
permissions:
|
|
contents: write
|
|
|
|
env:
|
|
# workflow_dispatch passes the tag as an input; push/release events derive it
|
|
# from the ref. Drives the checkout ref and tauri-action's tagName.
|
|
RELEASE_TAG: ${{ github.event.inputs.tag || github.ref_name }}
|
|
|
|
jobs:
|
|
publish:
|
|
# Build + draft release happen on a tag push or a dispatch that isn't a
|
|
# cask-only run. The `release: published` event exists only to trigger the
|
|
# cask bump below.
|
|
if: github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && inputs.bump_cask != true)
|
|
env:
|
|
# Empty when the secret is unset — the tool steps below test for a
|
|
# non-empty value before re-signing.
|
|
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- platform: macos-latest # Apple Silicon (M1/M2/M3+)
|
|
args: --target aarch64-apple-darwin
|
|
- platform: macos-15-intel # Intel Macs (last Intel runner; retired ~Aug 2027)
|
|
args: --target x86_64-apple-darwin
|
|
- platform: ubuntu-22.04 # Linux x86_64 (.deb / .rpm / .AppImage)
|
|
args: ''
|
|
- platform: windows-latest # Windows x86_64 (NSIS .exe + .msi)
|
|
args: ''
|
|
runs-on: ${{ matrix.platform }}
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
with:
|
|
ref: ${{ env.RELEASE_TAG }}
|
|
|
|
# ---- Apple signing setup (macOS only, skipped when secrets absent) ---
|
|
- name: Detect Apple signing secrets
|
|
id: apple
|
|
shell: bash
|
|
run: |
|
|
if [ -n "${{ secrets.APPLE_CERTIFICATE }}" ]; then
|
|
echo "enabled=true" >> "$GITHUB_OUTPUT"
|
|
else
|
|
echo "enabled=false" >> "$GITHUB_OUTPUT"
|
|
echo "::notice::APPLE_CERTIFICATE not set — macOS build will be ad-hoc signed and NOT notarized."
|
|
fi
|
|
|
|
# The bundled pg/mariadb tools are re-signed in their own steps below, so
|
|
# the identity must be in a keychain BEFORE those steps run. tauri-action
|
|
# is deliberately NOT given APPLE_CERTIFICATE (only the identity), so it
|
|
# does not create a second keychain.
|
|
- name: Import Apple Developer ID certificate
|
|
if: startsWith(matrix.platform, 'macos') && steps.apple.outputs.enabled == 'true'
|
|
shell: bash
|
|
env:
|
|
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
|
|
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
|
|
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
|
|
run: |
|
|
set -euo pipefail
|
|
KEYCHAIN="gridline-signing.keychain-db"
|
|
echo "$APPLE_CERTIFICATE" | base64 --decode > "$RUNNER_TEMP/cert.p12"
|
|
security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN"
|
|
security set-keychain-settings -lut 21600 "$KEYCHAIN"
|
|
security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN"
|
|
security import "$RUNNER_TEMP/cert.p12" -k "$KEYCHAIN" \
|
|
-P "$APPLE_CERTIFICATE_PASSWORD" -T /usr/bin/codesign
|
|
security set-key-partition-list -S apple-tool:,apple:,codesign: \
|
|
-s -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN" >/dev/null
|
|
EXISTING="$(security list-keychain -d user | tr -d ' \"')"
|
|
security list-keychain -d user -s "$KEYCHAIN" $EXISTING
|
|
rm -f "$RUNNER_TEMP/cert.p12"
|
|
security find-identity -v -p codesigning
|
|
|
|
- name: Prepare notarization credentials
|
|
if: startsWith(matrix.platform, 'macos') && steps.apple.outputs.enabled == 'true'
|
|
shell: bash
|
|
env:
|
|
APPLE_API_KEY: ${{ secrets.APPLE_API_KEY }}
|
|
APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
|
|
APPLE_API_KEY_CONTENT: ${{ secrets.APPLE_API_KEY_CONTENT }}
|
|
run: |
|
|
set -euo pipefail
|
|
KEY_PATH="$RUNNER_TEMP/private_keys/AuthKey_${APPLE_API_KEY}.p8"
|
|
mkdir -p "$(dirname "$KEY_PATH")"
|
|
# Apple's PEM parser expects a terminating newline, which
|
|
# `gh secret set` strips from the stored value — so write one
|
|
# explicitly. A doubled trailing newline is harmless to PEM parsers,
|
|
# but a missing one can make notarization fail to load the key.
|
|
printf '%s\n' "$APPLE_API_KEY_CONTENT" > "$KEY_PATH"
|
|
{
|
|
echo "APPLE_API_KEY=${APPLE_API_KEY}"
|
|
echo "APPLE_API_ISSUER=${APPLE_API_ISSUER}"
|
|
echo "APPLE_API_KEY_PATH=${KEY_PATH}"
|
|
} >> "$GITHUB_ENV"
|
|
|
|
- name: Install Linux dependencies
|
|
if: matrix.platform == 'ubuntu-22.04'
|
|
run: |
|
|
sudo apt-get update
|
|
sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf
|
|
|
|
- name: Set up Bun
|
|
uses: oven-sh/setup-bun@v2
|
|
|
|
- name: Set up Rust
|
|
uses: dtolnay/rust-toolchain@stable
|
|
with:
|
|
targets: aarch64-apple-darwin, x86_64-apple-darwin
|
|
|
|
- name: Cache Rust build artifacts
|
|
uses: swatinem/rust-cache@v2
|
|
with:
|
|
workspaces: './desktop/src-tauri -> target'
|
|
|
|
- name: Install frontend dependencies
|
|
run: bun install --frozen-lockfile
|
|
|
|
- name: Build PostgreSQL client tools (bundled)
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
PG_VER="16.4"
|
|
OUT="${GITHUB_WORKSPACE}/desktop/src-tauri/resources/pg_tools"
|
|
mkdir -p "$OUT"
|
|
case "${{ matrix.platform }}" in
|
|
ubuntu-22.04)
|
|
sudo apt-get update -y
|
|
sudo apt-get install -y build-essential libreadline-dev zlib1g-dev flex bison patchelf
|
|
curl -fsSL "https://ftp.postgresql.org/pub/source/v${PG_VER}/postgresql-${PG_VER}.tar.bz2" -o /tmp/pg.tar.bz2
|
|
tar -xf /tmp/pg.tar.bz2 -C /tmp
|
|
cd /tmp/postgresql-${PG_VER}
|
|
./configure --prefix=/tmp/pgbuild --without-readline --without-icu CFLAGS="-O2"
|
|
# Build the shared prerequisites (catalog headers + libpq/common/
|
|
# port archives) SERIALLY FIRST. Parallel `make -C` runs recurse
|
|
# into src/common concurrently and race `rm -f` / `ar crs` on the
|
|
# same .a — intermittently failing with 'ar: libpgcommon_srv.a:
|
|
# file format not recognized'. Once the archives exist, the tools'
|
|
# -j builds find them up to date and those recursions are no-ops.
|
|
make -C src/backend generated-headers
|
|
make -C src/interfaces/libpq all
|
|
make -j"$(nproc)" -C src/bin/pg_dump all
|
|
make -j"$(nproc)" -C src/bin/psql all
|
|
cp src/bin/pg_dump/pg_dump src/bin/pg_dump/pg_restore "$OUT"/
|
|
cp src/bin/psql/psql "$OUT"/
|
|
# pg_dump links shared libpq (PG16 has no --disable-shared for the
|
|
# client tools); bundle libpq.so alongside and point the loader at
|
|
# the app's resource dir via $ORIGIN rpath.
|
|
cp src/interfaces/libpq/libpq.so.5 "$OUT"/libpq.so.5
|
|
for b in pg_dump pg_restore psql; do
|
|
patchelf --set-rpath '$ORIGIN' "$OUT/$b"
|
|
done
|
|
;;
|
|
macos-latest|macos-15-intel)
|
|
curl -fsSL "https://ftp.postgresql.org/pub/source/v${PG_VER}/postgresql-${PG_VER}.tar.bz2" -o /tmp/pg.tar.bz2
|
|
tar -xf /tmp/pg.tar.bz2 -C /tmp
|
|
cd /tmp/postgresql-${PG_VER}
|
|
# ac_cv_func_strchrnul=no: Xcode 16.4's SDK marks strchrnul as
|
|
# introduced in macOS 15.4, so configure detects it and PG's
|
|
# snprintf.c calls the system symbol — which fails to compile
|
|
# (-Werror=unguarded-availability-new) and would crash at runtime
|
|
# on macOS < 15.4. Forcing the check off makes PG use its own
|
|
# inline fallback instead.
|
|
ac_cv_func_strchrnul=no ./configure --prefix=/tmp/pgbuild --without-readline --without-icu CFLAGS="-O2"
|
|
# macOS SDKs always declare strchrnul in <string.h> (Xcode 16.4
|
|
# marks it 'introduced in macOS 15.4'), so even with HAVE_STRCHRNUL
|
|
# disabled PG16's static-inline fallback would collide with the
|
|
# header declaration on newer SDKs. Rename PG's fallback so the
|
|
# tools compile on any SDK and never touch the (15.4+ only)
|
|
# system symbol.
|
|
sed -i '' 's/strchrnul/pg_strchrnul/g' src/port/snprintf.c
|
|
# Serial shared-prerequisite build first (see the Linux block: the
|
|
# archives must exist before the -j tool builds recurse into
|
|
# src/common).
|
|
make -C src/backend generated-headers
|
|
make -C src/interfaces/libpq all
|
|
make -j"$(sysctl -n hw.ncpu)" -C src/bin/pg_dump all
|
|
make -j"$(sysctl -n hw.ncpu)" -C src/bin/psql all
|
|
cp src/bin/pg_dump/pg_dump src/bin/pg_dump/pg_restore "$OUT"/
|
|
cp src/bin/psql/psql "$OUT"/
|
|
cp src/interfaces/libpq/libpq.5.dylib "$OUT"/libpq.5.dylib
|
|
# Rewrite the absolute /tmp/pgbuild libpq install_name to a
|
|
# relative @loader_path so the tools find libpq next to themselves.
|
|
for b in pg_dump pg_restore psql; do
|
|
libpq=$(otool -L "$OUT/$b" | awk '/libpq/ {print $1; exit}')
|
|
install_name_tool -change "$libpq" "@loader_path/libpq.5.dylib" "$OUT/$b"
|
|
done
|
|
# install_name_tool invalidates the linker signature. Re-sign with
|
|
# the Developer ID identity + hardened runtime + secure timestamp:
|
|
# Apple rejects ad-hoc-signed nested executables at notarization.
|
|
# No-op until the Apple secrets are configured.
|
|
if [ -n "${APPLE_SIGNING_IDENTITY:-}" ]; then
|
|
for f in "$OUT"/pg_dump "$OUT"/pg_restore "$OUT"/psql "$OUT"/libpq.5.dylib; do
|
|
codesign --force --options runtime --timestamp \
|
|
--sign "$APPLE_SIGNING_IDENTITY" "$f"
|
|
done
|
|
fi
|
|
;;
|
|
windows-latest)
|
|
URL="https://get.enterprisedb.com/postgresql/postgresql-${PG_VER}-1-windows-x64-binaries.zip"
|
|
curl -fsSL "$URL" -o /tmp/pg.zip
|
|
EXPECTED="3508d8f085bc3980f38211a82e3f31e5fcae9952105d3dc2f8be67b64a822baa"
|
|
echo "$EXPECTED /tmp/pg.zip" | sha256sum -c -
|
|
sha256sum /tmp/pg.zip
|
|
unzip -o /tmp/pg.zip -d /tmp/pg
|
|
cp /tmp/pg/pgsql/bin/pg_dump.exe /tmp/pg/pgsql/bin/pg_restore.exe /tmp/pg/pgsql/bin/psql.exe "$OUT"/
|
|
# pg_dump.exe needs far more than libpq.dll (libcrypto-3-x64.dll,
|
|
# libssl-3-x64.dll, libiconv-2.dll, libintl-9.dll,
|
|
# libwinpthread-1.dll, liblz4.dll, libzstd.dll, ICU DLLs, ...);
|
|
# copy every DLL next to the tools.
|
|
cp /tmp/pg/pgsql/bin/*.dll "$OUT"/
|
|
;;
|
|
esac
|
|
(cd "$OUT" && sha256sum * | tee checksums.txt)
|
|
# Resolve the per-platform binary suffix WITHOUT a command
|
|
# substitution: `$( [ ... ] && echo .exe )` returns exit 1 when the
|
|
# test is false, and under `set -e` that aborts the whole step.
|
|
if [ "${{ matrix.platform }}" = windows-latest ]; then
|
|
BIN_EXT=".exe"
|
|
else
|
|
BIN_EXT=""
|
|
fi
|
|
for b in pg_dump pg_restore psql; do
|
|
f="$OUT/${b}${BIN_EXT}"
|
|
test -f "$f" || { echo "missing $f"; exit 1; }
|
|
done
|
|
# Sanity: every tool must run (loader path is correct) — this catches
|
|
# a wrong @loader_path / rpath before we ship a broken bundle.
|
|
for b in pg_dump pg_restore psql; do
|
|
"$OUT/${b}${BIN_EXT}" --version >/dev/null 2>&1 || { echo "$b failed to run from resource dir"; exit 1; }
|
|
done
|
|
|
|
- name: Build MariaDB client tools (bundled)
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
MARIADB_VER="11.4.5"
|
|
OUT="${GITHUB_WORKSPACE}/desktop/src-tauri/resources/mysql_tools"
|
|
mkdir -p "$OUT"
|
|
case "${{ matrix.platform }}" in
|
|
ubuntu-22.04)
|
|
sudo apt-get update -y
|
|
sudo apt-get install -y cmake build-essential libssl-dev libzstd-dev pkg-config libgnutls28-dev patchelf
|
|
git clone --depth 1 --branch "mariadb-${MARIADB_VER}" https://github.com/MariaDB/server.git /tmp/mariadb-server
|
|
cd /tmp/mariadb-server
|
|
# Client-only build: wolfSSL + zlib are vendored. The in-tree
|
|
# Connector C still probes GnuTLS on Unix regardless of WITH_SSL,
|
|
# so libgnutls28-dev is required at build time.
|
|
cmake -DCMAKE_BUILD_TYPE=Release \
|
|
-DWITHOUT_SERVER=ON \
|
|
-DWITHOUT_TOKUDB=1 \
|
|
-DWITHOUT_ROCKSDB=1 \
|
|
-DWITHOUT_MROONGA=1 \
|
|
-DWITHOUT_SPIDER=1 \
|
|
-DWITHOUT_SEQUENCE=1 \
|
|
-DWITH_UNIT_TESTS=OFF \
|
|
-DWITH_SSL=bundled \
|
|
-DWITH_ZLIB=bundled .
|
|
make -j"$(nproc)" mariadb-dump mariadb
|
|
# Clients build into client/ (stable across MariaDB 10.x/11.x);
|
|
# fall back to a broad search if a future version moves them.
|
|
for b in mariadb-dump mariadb; do
|
|
src=$(find client -maxdepth 1 -type f -name "$b" -print -quit 2>/dev/null || true)
|
|
test -n "$src" || src=$(find . -type f -name "$b" -print -quit || true)
|
|
test -n "$src" || { echo "build produced no $b binary"; exit 1; }
|
|
cp "$src" "$OUT"/
|
|
done
|
|
# Bundle every non-core shared dependency (GnuTLS + its chain,
|
|
# libzstd) next to the clients and point the loader at $ORIGIN —
|
|
# the same pattern the pg_tools step uses for libpq.
|
|
for b in mariadb-dump mariadb; do
|
|
ldd "$OUT/$b" | awk '/=> \// {print $3}' | sort -u | while read -r lib; do
|
|
case "$(basename "$lib")" in
|
|
ld-linux*|libc.so*|libm.so*|libpthread*|libdl.so*|librt.so*|libgcc_s*|libstdc++*|libcrypt.so*|libresolv*|libutil*)
|
|
;; # core runtime — present on every glibc system
|
|
*)
|
|
cp -n "$lib" "$OUT/$(basename "$lib")" 2>/dev/null || true
|
|
;;
|
|
esac
|
|
done
|
|
patchelf --set-rpath '$ORIGIN' "$OUT/$b"
|
|
done
|
|
;;
|
|
macos-latest|macos-15-intel)
|
|
git clone --depth 1 --branch "mariadb-${MARIADB_VER}" https://github.com/MariaDB/server.git /tmp/mariadb-server
|
|
cd /tmp/mariadb-server
|
|
# macOS connects the in-tree Connector C against brew OpenSSL
|
|
# (proven to build+run on the runners); the Linux-only GnuTLS
|
|
# probe does not fire here. Absolute brew paths are rewritten to
|
|
# @loader_path below so user machines resolve them from the
|
|
# resource dir.
|
|
if [ "$(uname -m)" = arm64 ]; then
|
|
SSL_DIR="/opt/homebrew/opt/openssl"
|
|
else
|
|
SSL_DIR="/usr/local/opt/openssl"
|
|
fi
|
|
[ -d "$SSL_DIR" ] || brew install openssl
|
|
cmake -DCMAKE_BUILD_TYPE=Release \
|
|
-DWITHOUT_SERVER=ON \
|
|
-DWITHOUT_TOKUDB=1 \
|
|
-DWITHOUT_ROCKSDB=1 \
|
|
-DWITHOUT_MROONGA=1 \
|
|
-DWITHOUT_SPIDER=1 \
|
|
-DWITHOUT_SEQUENCE=1 \
|
|
-DWITH_UNIT_TESTS=OFF \
|
|
-DWITH_SSL="$SSL_DIR" \
|
|
-DWITH_ZLIB=bundled .
|
|
make -j"$(sysctl -n hw.ncpu)" mariadb-dump mariadb
|
|
for b in mariadb-dump mariadb; do
|
|
src=$(find client -maxdepth 1 -type f -name "$b" -print -quit 2>/dev/null || true)
|
|
test -n "$src" || src=$(find . -type f -name "$b" -print -quit || true)
|
|
test -n "$src" || { echo "build produced no $b binary"; exit 1; }
|
|
cp "$src" "$OUT"/
|
|
done
|
|
# Rewrite absolute Homebrew dylib deps to @loader_path so the
|
|
# bundled clients (and OpenSSL + their own deps) resolve from the
|
|
# resource dir on user machines. Fixed-point loop: each pass
|
|
# copies newly-seen dylibs and rewrites their parents' refs.
|
|
# Ad-hoc re-sign after each edit — arm64 dyld refuses to load a
|
|
# dylib whose (Homebrew) code signature was invalidated.
|
|
for pass in 1 2 3 4 5 6; do
|
|
for dylib in "$OUT"/*.dylib "$OUT"/mariadb-dump "$OUT"/mariadb; do
|
|
[ -f "$dylib" ] || continue
|
|
# `|| true` — with pipefail, grep exits 1 on zero matches.
|
|
otool -L "$dylib" | awk 'NR>1 {print $1}' | grep -E '^/(usr/local|opt/homebrew)' | while read -r lib; do
|
|
base=$(basename "$lib")
|
|
if [ ! -f "$OUT/$base" ]; then cp "$lib" "$OUT/$base" 2>/dev/null || true; fi
|
|
install_name_tool -change "$lib" "@loader_path/$base" "$dylib" 2>/dev/null || true
|
|
done || true
|
|
if [ -n "${APPLE_SIGNING_IDENTITY:-}" ]; then
|
|
codesign --force --options runtime --timestamp \
|
|
--sign "$APPLE_SIGNING_IDENTITY" "$dylib"
|
|
else
|
|
codesign --force --sign - "$dylib" 2>/dev/null || true
|
|
fi
|
|
done
|
|
done
|
|
;;
|
|
windows-latest)
|
|
URL="https://archive.mariadb.org/mariadb-${MARIADB_VER}/winx64-packages/mariadb-${MARIADB_VER}-winx64.zip"
|
|
curl -fsSL "$URL" -o /tmp/mariadb.zip
|
|
# TODO(RELEASE): pin the sha256 printed on the first CI run, then
|
|
# uncomment the check so later releases verify the download.
|
|
# EXPECTED="<sha256 of mariadb-${MARIADB_VER}-winx64.zip>"
|
|
# echo "$EXPECTED /tmp/mariadb.zip" | sha256sum -c -
|
|
sha256sum /tmp/mariadb.zip
|
|
unzip -o /tmp/mariadb.zip -d /tmp/mariadb
|
|
WINROOT="/tmp/mariadb/mariadb-${MARIADB_VER}-winx64"
|
|
cp "$WINROOT"/bin/mariadb-dump.exe "$WINROOT"/bin/mariadb.exe "$OUT"/
|
|
# The clients' only runtime dependency is the client library
|
|
# (lib/libmariadb.dll); the winx64 package statically links
|
|
# wolfSSL, so there are no OpenSSL DLLs to bundle. bin/*.dll is
|
|
# just the embedded server.dll, which the client tools don't need.
|
|
cp "$WINROOT"/lib/libmariadb.dll "$OUT"/
|
|
;;
|
|
esac
|
|
(cd "$OUT" && sha256sum * | tee checksums.txt)
|
|
# Resolve the per-platform binary suffix WITHOUT a command
|
|
# substitution (see the pg_tools step for why).
|
|
if [ "${{ matrix.platform }}" = windows-latest ]; then
|
|
BIN_EXT=".exe"
|
|
else
|
|
BIN_EXT=""
|
|
fi
|
|
for b in mariadb-dump mariadb; do
|
|
f="$OUT/${b}${BIN_EXT}"
|
|
test -f "$f" || { echo "missing $f"; exit 1; }
|
|
done
|
|
# Sanity: every tool must run (loader path is correct) — this catches
|
|
# a missing shared library before we ship a broken bundle.
|
|
for b in mariadb-dump mariadb; do
|
|
"$OUT/${b}${BIN_EXT}" --version >/dev/null 2>&1 || { echo "$b failed to run from resource dir"; exit 1; }
|
|
done
|
|
|
|
- name: Build and upload to GitHub Release
|
|
uses: tauri-apps/tauri-action@v0
|
|
env:
|
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
with:
|
|
projectPath: desktop
|
|
tagName: ${{ env.RELEASE_TAG }}
|
|
releaseName: 'Gridline ${{ env.RELEASE_TAG }}'
|
|
releaseDraft: true
|
|
args: ${{ matrix.args }}
|
|
# Assets use tauri-action's default versioned naming (e.g.
|
|
# Gridline_0.7.5_aarch64.dmg, Gridline-0.7.5-1.x86_64.rpm) and the
|
|
# README download tables link to them statically per release —
|
|
# remember to update both README tables when cutting a new version.
|
|
|
|
# tauri-action notarizes + staples the .app but NOT the .dmg wrapping it,
|
|
# so Gatekeeper rejects the downloaded DMG as "Unnotarized Developer ID"
|
|
# even though the app inside is fine. Submit the DMG to the same notary
|
|
# service, staple the ticket, and replace the uploaded asset.
|
|
#
|
|
# Assets are replaced via the release ID rather than `gh release upload`:
|
|
# the GitHub releases/tags endpoint returns 404 for drafts, so uploading
|
|
# by tag cannot work while the release is still a draft.
|
|
- name: Notarize and staple the DMGs
|
|
if: startsWith(matrix.platform, 'macos') && steps.apple.outputs.enabled == 'true'
|
|
shell: bash
|
|
env:
|
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
run: |
|
|
set -euo pipefail
|
|
shopt -s nullglob
|
|
dmgs=(desktop/src-tauri/target/*/release/bundle/dmg/*.dmg)
|
|
if [ ${#dmgs[@]} -eq 0 ]; then
|
|
echo "no DMG produced" >&2
|
|
exit 1
|
|
fi
|
|
REL_ID=$(gh api "repos/${GITHUB_REPOSITORY}/releases?per_page=100" \
|
|
--jq ".[] | select(.tag_name == \"${RELEASE_TAG}\") | .id" | head -1)
|
|
if [ -z "$REL_ID" ]; then
|
|
echo "could not resolve a release id for ${RELEASE_TAG}" >&2
|
|
exit 1
|
|
fi
|
|
for dmg in "${dmgs[@]}"; do
|
|
name=$(basename "$dmg")
|
|
echo ">> notarizing $name (this can take a few minutes)"
|
|
xcrun notarytool submit "$dmg" \
|
|
--key "$APPLE_API_KEY_PATH" \
|
|
--key-id "$APPLE_API_KEY" \
|
|
--issuer "$APPLE_API_ISSUER" \
|
|
--wait
|
|
xcrun stapler staple "$dmg"
|
|
xcrun stapler validate "$dmg"
|
|
# Replace the copy tauri-action uploaded, which carries no DMG ticket.
|
|
old=$(gh api "repos/${GITHUB_REPOSITORY}/releases/${REL_ID}/assets" \
|
|
--jq ".[] | select(.name == \"${name}\") | .id" | head -1)
|
|
if [ -n "$old" ]; then
|
|
gh api -X DELETE "repos/${GITHUB_REPOSITORY}/releases/assets/${old}"
|
|
fi
|
|
echo ">> re-uploading stapled $name"
|
|
curl -fsS -X POST \
|
|
-H "Authorization: token ${GH_TOKEN}" \
|
|
-H "Content-Type: application/octet-stream" \
|
|
--data-binary "@${dmg}" \
|
|
"https://uploads.github.com/repos/${GITHUB_REPOSITORY}/releases/${REL_ID}/assets?name=${name}"
|
|
echo
|
|
done
|
|
echo "all DMGs notarized and stapled"
|
|
|
|
# After the installers are uploaded, refresh the Homebrew cask in the
|
|
# AdrianBonpin/homebrew-gridline tap: hash the freshly-published DMGs,
|
|
# render Casks/gridline.rb from the template, and push it. Users then get
|
|
# the new version via `brew upgrade --cask gridline`.
|
|
#
|
|
# Requires the HOMEBREW_TAP_DEPLOY_KEY secret — a write-enabled SSH deploy
|
|
# key on the tap repo (the default GITHUB_TOKEN cannot push to a different
|
|
# repository, and a deploy key avoids storing a broad PAT). The job skips
|
|
# cleanly until that secret is set, so releases stay green before this is
|
|
# configured.
|
|
bump-cask:
|
|
# Normally fires on `release: published`, which requires the DMGs to be
|
|
# downloadable — the GitHub releases/tags API returns 404 for drafts, so
|
|
# this cannot run on the tag push. NOTE: a release whose DRAFT was created
|
|
# by tauri-action's GITHUB_TOKEN does not reliably emit a `release` event,
|
|
# so `workflow_dispatch` with bump_cask=true is the dependable path.
|
|
if: github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && inputs.bump_cask == true)
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
contents: read
|
|
steps:
|
|
- name: Check for tap deploy key
|
|
id: tap
|
|
shell: bash
|
|
run: |
|
|
if [ -n "${{ secrets.HOMEBREW_TAP_DEPLOY_KEY }}" ]; then
|
|
echo "enabled=true" >> "$GITHUB_OUTPUT"
|
|
else
|
|
echo "enabled=false" >> "$GITHUB_OUTPUT"
|
|
echo "::notice::HOMEBREW_TAP_DEPLOY_KEY not set - skipping Homebrew cask bump."
|
|
fi
|
|
|
|
- name: Checkout
|
|
if: steps.tap.outputs.enabled == 'true'
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Resolve version and DMG checksums
|
|
id: meta
|
|
if: steps.tap.outputs.enabled == 'true'
|
|
env:
|
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
run: |
|
|
VERSION="${RELEASE_TAG#v}"
|
|
gh release download "$RELEASE_TAG" -p "Gridline_${VERSION}_aarch64.dmg" -O /tmp/arm.dmg
|
|
gh release download "$RELEASE_TAG" -p "Gridline_${VERSION}_x64.dmg" -O /tmp/intel.dmg
|
|
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
|
|
echo "sha_arm=$(shasum -a 256 /tmp/arm.dmg | cut -d' ' -f1)" >> "$GITHUB_OUTPUT"
|
|
echo "sha_intel=$(shasum -a 256 /tmp/intel.dmg | cut -d' ' -f1)" >> "$GITHUB_OUTPUT"
|
|
|
|
- name: Checkout tap repo
|
|
if: steps.tap.outputs.enabled == 'true'
|
|
uses: actions/checkout@v4
|
|
with:
|
|
repository: AdrianBonpin/homebrew-gridline
|
|
ssh-key: ${{ secrets.HOMEBREW_TAP_DEPLOY_KEY }}
|
|
path: tap
|
|
|
|
- name: Render and push cask
|
|
if: steps.tap.outputs.enabled == 'true'
|
|
env:
|
|
VERSION: ${{ steps.meta.outputs.version }}
|
|
SHA_ARM: ${{ steps.meta.outputs.sha_arm }}
|
|
SHA_INTEL: ${{ steps.meta.outputs.sha_intel }}
|
|
run: |
|
|
mkdir -p tap/Casks
|
|
./scripts/render-cask.sh "$VERSION" "$SHA_ARM" "$SHA_INTEL" > tap/Casks/gridline.rb
|
|
cat tap/Casks/gridline.rb
|
|
cd tap
|
|
git config user.name "github-actions[bot]"
|
|
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
git add Casks/gridline.rb
|
|
if git diff --cached --quiet; then
|
|
echo "cask already up to date"
|
|
else
|
|
git commit -m "gridline $VERSION"
|
|
git push
|
|
fi |