Compare commits
7
Commits
v0.8.0
..
f807c22a36
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f807c22a36 | ||
|
|
e4de489f53 | ||
|
|
fdbfd3d0d2 | ||
|
|
228660044a | ||
|
|
c4dc1c1c83 | ||
|
|
f1d9fdae3a | ||
|
|
18157f453f |
+210
-15
@@ -7,22 +7,65 @@ name: Release
|
|||||||
# git checkout prod && git pull
|
# git checkout prod && git pull
|
||||||
# git tag v0.5.0 && git push origin v0.5.0
|
# git tag v0.5.0 && git push origin v0.5.0
|
||||||
#
|
#
|
||||||
# SIGNING STATUS: macOS is Developer-ID signed + notarized via
|
# SIGNING STATUS: this is the ACTIVE release path. macOS is Developer-ID
|
||||||
# scripts/release-mac.sh (built manually on Adrian's machine). NOTE: this
|
# signed + notarized in CI when the Apple secrets below are configured (see
|
||||||
# GitHub workflow is NOT the active release path — Gitea Actions uses
|
# README "macOS signing & notarization"). When the secrets are absent the mac
|
||||||
# .gitea/workflows/release.yml (Linux/Windows only). Linux/Windows installers
|
# jobs still build, but ad-hoc signed / unnotarized. Linux/Windows installers
|
||||||
# remain unsigned.
|
# 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:
|
on:
|
||||||
push:
|
push:
|
||||||
tags:
|
tags:
|
||||||
- 'v*'
|
- '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:
|
permissions:
|
||||||
contents: write
|
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:
|
jobs:
|
||||||
publish:
|
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:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
@@ -39,6 +82,69 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
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
|
- name: Install Linux dependencies
|
||||||
if: matrix.platform == 'ubuntu-22.04'
|
if: matrix.platform == 'ubuntu-22.04'
|
||||||
@@ -131,6 +237,16 @@ jobs:
|
|||||||
libpq=$(otool -L "$OUT/$b" | awk '/libpq/ {print $1; exit}')
|
libpq=$(otool -L "$OUT/$b" | awk '/libpq/ {print $1; exit}')
|
||||||
install_name_tool -change "$libpq" "@loader_path/libpq.5.dylib" "$OUT/$b"
|
install_name_tool -change "$libpq" "@loader_path/libpq.5.dylib" "$OUT/$b"
|
||||||
done
|
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)
|
windows-latest)
|
||||||
URL="https://get.enterprisedb.com/postgresql/postgresql-${PG_VER}-1-windows-x64-binaries.zip"
|
URL="https://get.enterprisedb.com/postgresql/postgresql-${PG_VER}-1-windows-x64-binaries.zip"
|
||||||
@@ -263,7 +379,12 @@ jobs:
|
|||||||
if [ ! -f "$OUT/$base" ]; then cp "$lib" "$OUT/$base" 2>/dev/null || true; fi
|
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
|
install_name_tool -change "$lib" "@loader_path/$base" "$dylib" 2>/dev/null || true
|
||||||
done || true
|
done || true
|
||||||
codesign --force --sign - "$dylib" 2>/dev/null || 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
|
||||||
done
|
done
|
||||||
;;
|
;;
|
||||||
@@ -309,8 +430,8 @@ jobs:
|
|||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
with:
|
with:
|
||||||
projectPath: desktop
|
projectPath: desktop
|
||||||
tagName: ${{ github.ref_name }}
|
tagName: ${{ env.RELEASE_TAG }}
|
||||||
releaseName: 'Gridline ${{ github.ref_name }}'
|
releaseName: 'Gridline ${{ env.RELEASE_TAG }}'
|
||||||
releaseDraft: true
|
releaseDraft: true
|
||||||
args: ${{ matrix.args }}
|
args: ${{ matrix.args }}
|
||||||
# Assets use tauri-action's default versioned naming (e.g.
|
# Assets use tauri-action's default versioned naming (e.g.
|
||||||
@@ -318,44 +439,118 @@ jobs:
|
|||||||
# README download tables link to them statically per release —
|
# README download tables link to them statically per release —
|
||||||
# remember to update both README tables when cutting a new version.
|
# 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
|
# After the installers are uploaded, refresh the Homebrew cask in the
|
||||||
# AdrianBonpin/homebrew-gridline tap: hash the freshly-published DMGs,
|
# AdrianBonpin/homebrew-gridline tap: hash the freshly-published DMGs,
|
||||||
# render Casks/gridline.rb from the template, and push it. Users then get
|
# render Casks/gridline.rb from the template, and push it. Users then get
|
||||||
# the new version via `brew upgrade --cask gridline`.
|
# the new version via `brew upgrade --cask gridline`.
|
||||||
#
|
#
|
||||||
# Requires the HOMEBREW_TAP_TOKEN secret (a PAT with `repo` scope on the tap
|
# Requires the HOMEBREW_TAP_DEPLOY_KEY secret — a write-enabled SSH deploy
|
||||||
# repo — the default GITHUB_TOKEN cannot push to a different repo). The job
|
# key on the tap repo (the default GITHUB_TOKEN cannot push to a different
|
||||||
# no-ops until that secret is set, so releases stay green before this is
|
# 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.
|
# configured.
|
||||||
bump-cask:
|
bump-cask:
|
||||||
needs: publish
|
# 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
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
steps:
|
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
|
- name: Checkout
|
||||||
|
if: steps.tap.outputs.enabled == 'true'
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Resolve version and DMG checksums
|
- name: Resolve version and DMG checksums
|
||||||
id: meta
|
id: meta
|
||||||
|
if: steps.tap.outputs.enabled == 'true'
|
||||||
env:
|
env:
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
run: |
|
run: |
|
||||||
VERSION="${GITHUB_REF_NAME#v}"
|
VERSION="${RELEASE_TAG#v}"
|
||||||
gh release download "$GITHUB_REF_NAME" -p "Gridline_${VERSION}_aarch64.dmg" -O /tmp/arm.dmg
|
gh release download "$RELEASE_TAG" -p "Gridline_${VERSION}_aarch64.dmg" -O /tmp/arm.dmg
|
||||||
gh release download "$GITHUB_REF_NAME" -p "Gridline_${VERSION}_x64.dmg" -O /tmp/intel.dmg
|
gh release download "$RELEASE_TAG" -p "Gridline_${VERSION}_x64.dmg" -O /tmp/intel.dmg
|
||||||
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
|
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
|
||||||
echo "sha_arm=$(shasum -a 256 /tmp/arm.dmg | cut -d' ' -f1)" >> "$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"
|
echo "sha_intel=$(shasum -a 256 /tmp/intel.dmg | cut -d' ' -f1)" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
- name: Checkout tap repo
|
- name: Checkout tap repo
|
||||||
|
if: steps.tap.outputs.enabled == 'true'
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
repository: AdrianBonpin/homebrew-gridline
|
repository: AdrianBonpin/homebrew-gridline
|
||||||
token: ${{ secrets.HOMEBREW_TAP_TOKEN }}
|
ssh-key: ${{ secrets.HOMEBREW_TAP_DEPLOY_KEY }}
|
||||||
path: tap
|
path: tap
|
||||||
|
|
||||||
- name: Render and push cask
|
- name: Render and push cask
|
||||||
|
if: steps.tap.outputs.enabled == 'true'
|
||||||
env:
|
env:
|
||||||
VERSION: ${{ steps.meta.outputs.version }}
|
VERSION: ${{ steps.meta.outputs.version }}
|
||||||
SHA_ARM: ${{ steps.meta.outputs.sha_arm }}
|
SHA_ARM: ${{ steps.meta.outputs.sha_arm }}
|
||||||
|
|||||||
@@ -18,34 +18,34 @@
|
|||||||
<a href="https://react.dev/"><img src="https://img.shields.io/badge/react-%2320232a.svg?style=for-the-badge&logo=react&logoColor=%2361DAFB" alt="React" /></a>
|
<a href="https://react.dev/"><img src="https://img.shields.io/badge/react-%2320232a.svg?style=for-the-badge&logo=react&logoColor=%2361DAFB" alt="React" /></a>
|
||||||
<a href="https://www.typescriptlang.org/"><img src="https://img.shields.io/badge/TypeScript-3178C6?style=for-the-badge&logo=typescript&logoColor=white" alt="TypeScript" /></a>
|
<a href="https://www.typescriptlang.org/"><img src="https://img.shields.io/badge/TypeScript-3178C6?style=for-the-badge&logo=typescript&logoColor=white" alt="TypeScript" /></a>
|
||||||
<a href="./LICENSE"><img src="https://img.shields.io/badge/License-Apache_2.0-D22128.svg?style=for-the-badge" alt="Apache 2.0 License" /></a>
|
<a href="./LICENSE"><img src="https://img.shields.io/badge/License-Apache_2.0-D22128.svg?style=for-the-badge" alt="Apache 2.0 License" /></a>
|
||||||
<a href="https://git.ranio.xyz/adrianbonpin/gridline"><img src="https://img.shields.io/badge/Open_Source-Gitea-4183C4.svg?style=for-the-badge" alt="Open source on Gitea" /></a>
|
<a href="https://github.com/AdrianBonpin/gridline"><img src="https://img.shields.io/badge/Open_Source-GitHub-181717.svg?style=for-the-badge" alt="Open source on GitHub" /></a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<a href="https://git.ranio.xyz/adrianbonpin/gridline/releases"><img src="https://img.shields.io/badge/Download_Latest_Release-2ea44f?style=for-the-badge" alt="Download Latest" /></a>
|
<a href="https://github.com/AdrianBonpin/gridline/releases"><img src="https://img.shields.io/badge/Download_Latest_Release-2ea44f?style=for-the-badge" alt="Download Latest" /></a>
|
||||||
<a href="https://git.ranio.xyz/adrianbonpin/gridline/issues/new"><img src="https://img.shields.io/badge/Open_an_Issue-%23E4405F.svg?style=for-the-badge" alt="Open an Issue" /></a>
|
<a href="https://github.com/AdrianBonpin/gridline/issues/new"><img src="https://img.shields.io/badge/Open_an_Issue-%23E4405F.svg?style=for-the-badge" alt="Open an Issue" /></a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Download
|
## Download
|
||||||
|
|
||||||
Grab the installer for your OS from the [latest release](https://git.ranio.xyz/adrianbonpin/gridline/releases/latest) — the links below point at the current release (**v0.8.0**):
|
Grab the installer for your OS from the [latest release](https://github.com/AdrianBonpin/gridline/releases/latest) — the links below point at the current release (**v0.8.0**):
|
||||||
|
|
||||||
| OS | Architecture | Download |
|
| OS | Architecture | Download |
|
||||||
| :--------------------------- | :--------------------------- | :----------------------------------------------------------------------------------------------------------------------------------- |
|
| :--------------------------- | :--------------------------- | :----------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| **macOS** | Apple Silicon (M1/M2/M3/M4…) | [Gridline_0.8.0_aarch64.dmg](https://git.ranio.xyz/adrianbonpin/gridline/releases/download/v0.8.0/Gridline_0.8.0_aarch64.dmg) |
|
| **macOS** | Apple Silicon (M1/M2/M3/M4…) | [Gridline_0.8.0_aarch64.dmg](https://github.com/AdrianBonpin/gridline/releases/download/v0.8.0/Gridline_0.8.0_aarch64.dmg) |
|
||||||
| **macOS** | Intel | [Gridline_0.8.0_x64.dmg](https://git.ranio.xyz/adrianbonpin/gridline/releases/download/v0.8.0/Gridline_0.8.0_x64.dmg) |
|
| **macOS** | Intel | [Gridline_0.8.0_x64.dmg](https://github.com/AdrianBonpin/gridline/releases/download/v0.8.0/Gridline_0.8.0_x64.dmg) |
|
||||||
| **Windows** | x64 | [Gridline_0.8.0_x64-setup.exe](https://git.ranio.xyz/adrianbonpin/gridline/releases/download/v0.8.0/Gridline_0.8.0_x64-setup.exe) |
|
| **Windows** | x64 | [Gridline_0.8.0_x64-setup.exe](https://github.com/AdrianBonpin/gridline/releases/download/v0.8.0/Gridline_0.8.0_x64-setup.exe) |
|
||||||
| **Debian / Ubuntu** | amd64 | [Gridline_0.8.0_amd64.deb](https://git.ranio.xyz/adrianbonpin/gridline/releases/download/v0.8.0/Gridline_0.8.0_amd64.deb) |
|
| **Debian / Ubuntu** | amd64 | [Gridline_0.8.0_amd64.deb](https://github.com/AdrianBonpin/gridline/releases/download/v0.8.0/Gridline_0.8.0_amd64.deb) |
|
||||||
| **Fedora / RHEL / openSUSE** | x86_64 | [Gridline-0.8.0-1.x86_64.rpm](https://git.ranio.xyz/adrianbonpin/gridline/releases/download/v0.8.0/Gridline-0.8.0-1.x86_64.rpm) |
|
| **Fedora / RHEL / openSUSE** | x86_64 | [Gridline-0.8.0-1.x86_64.rpm](https://github.com/AdrianBonpin/gridline/releases/download/v0.8.0/Gridline-0.8.0-1.x86_64.rpm) |
|
||||||
| **Other Linux** | amd64 | [Gridline_0.8.0_amd64.AppImage](https://git.ranio.xyz/adrianbonpin/gridline/releases/download/v0.8.0/Gridline_0.8.0_amd64.AppImage) |
|
| **Other Linux** | amd64 | [Gridline_0.8.0_amd64.AppImage](https://github.com/AdrianBonpin/gridline/releases/download/v0.8.0/Gridline_0.8.0_amd64.AppImage) |
|
||||||
|
|
||||||
**macOS via Homebrew** (recommended for Mac users):
|
**macOS via Homebrew** (recommended for Mac users):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# one-time setup (the tap is hosted on Gitea, not GitHub):
|
# one-time setup (the tap is hosted on GitHub):
|
||||||
brew tap AdrianBonpin/gridline https://git.ranio.xyz/adrianbonpin/homebrew-gridline.git
|
brew tap AdrianBonpin/gridline https://github.com/AdrianBonpin/homebrew-gridline.git
|
||||||
brew trust adrianbonpin/gridline
|
brew trust adrianbonpin/gridline
|
||||||
|
|
||||||
# install / upgrade:
|
# install / upgrade:
|
||||||
@@ -104,7 +104,7 @@ Gridline is built for developers and small teams who manage multiple database en
|
|||||||
|
|
||||||
- **v0.7.15** — **Support channel on the landing page**: optional coffee via [Ko-fi](https://ko-fi.com/adrianbonpin), never gates a feature. Landing copy refreshed (em dashes dropped, OSS section simplified, funding FAQ); version bump.
|
- **v0.7.15** — **Support channel on the landing page**: optional coffee via [Ko-fi](https://ko-fi.com/adrianbonpin), never gates a feature. Landing copy refreshed (em dashes dropped, OSS section simplified, funding FAQ); version bump.
|
||||||
|
|
||||||
- **v0.7.14** — **No more repeated macOS keychain prompts**: saved connection passwords and SSH secrets are now ACL-pinned to the app's code signature on every save (`keychain_acl`), so app upgrades never re-trigger the macOS keychain access dialog (users upgrading from pre-signing builds see one final prompt — choose **Always Allow**). **Install via Homebrew**: `brew tap AdrianBonpin/gridline https://git.ranio.xyz/adrianbonpin/homebrew-gridline.git` then `brew install --cask gridline`; version bump.
|
- **v0.7.14** — **No more repeated macOS keychain prompts**: saved connection passwords and SSH secrets are now ACL-pinned to the app's code signature on every save (`keychain_acl`), so app upgrades never re-trigger the macOS keychain access dialog (users upgrading from pre-signing builds see one final prompt — choose **Always Allow**). **Install via Homebrew**: `brew tap AdrianBonpin/gridline https://github.com/AdrianBonpin/homebrew-gridline.git` then `brew install --cask gridline`; version bump.
|
||||||
|
|
||||||
- **v0.7.13** — **macOS is now Developer-ID signed and notarized**: the app is properly code-signed with a Developer ID Application certificate and notarized by Apple, so macOS opens it without the Gatekeeper "Open Anyway" prompt or the `xattr` quarantine workaround. The release script (`scripts/release-mac.sh`) auto-detects the signing identity from the keychain, notarizes + staples both architectures, and verifies the result before uploading; version bump.
|
- **v0.7.13** — **macOS is now Developer-ID signed and notarized**: the app is properly code-signed with a Developer ID Application certificate and notarized by Apple, so macOS opens it without the Gatekeeper "Open Anyway" prompt or the `xattr` quarantine workaround. The release script (`scripts/release-mac.sh`) auto-detects the signing identity from the keychain, notarizes + staples both architectures, and verifies the result before uploading; version bump.
|
||||||
|
|
||||||
@@ -312,29 +312,33 @@ _Notes: DB Pro is an Electron app (launched Nov 2025) whose marketing copy overc
|
|||||||
|
|
||||||
### Download a release
|
### Download a release
|
||||||
|
|
||||||
Pre-built installers for macOS, Windows, and Linux are published on the [Releases](https://git.ranio.xyz/adrianbonpin/gridline/releases) page.
|
Pre-built installers for macOS, Windows, and Linux are published on the [Releases](https://github.com/AdrianBonpin/gridline/releases) page.
|
||||||
|
|
||||||
> ⚠️ Gridline is under active development. Expect rough edges and please [open issues](https://git.ranio.xyz/adrianbonpin/gridline/issues/new) when you hit them.
|
> ⚠️ Gridline is under active development. Expect rough edges and please [open issues](https://github.com/AdrianBonpin/gridline/issues/new) when you hit them.
|
||||||
|
|
||||||
#### macOS signing & notarization
|
#### macOS signing & notarization
|
||||||
|
|
||||||
Gridline's macOS builds are **Developer-ID signed and notarized**, so macOS opens them without a Gatekeeper prompt. This is configured on the machine that runs `scripts/release-mac.sh` (macOS is built manually, not in CI).
|
Gridline's macOS builds are **Developer-ID signed and notarized by GitHub Actions**, so macOS opens them without a Gatekeeper prompt. Both the `.app` and the `.dmg` it ships inside are notarized and carry a stapled ticket — notarizing only the `.app` is not enough, because Gatekeeper assesses the downloaded DMG itself and rejects it as "Unnotarized Developer ID" even when the app within is fine.
|
||||||
|
|
||||||
**One-time setup** (only the repo owner needs to do this):
|
**One-time setup** (only the repo owner needs to do this):
|
||||||
|
|
||||||
1. **Create a Developer ID Application certificate** — [developer.apple.com](https://developer.apple.com) → Certificates, Identifiers & Profiles → **+** → **Developer ID Application**. Generate a Certificate Signing Request from Keychain Access (Certificate Assistant → *Request a Certificate From a Certificate Authority*), upload it, download the `.cer`, and double-click to install into your login keychain. Verify with `security find-identity -v -p codesigning`.
|
1. **Create a Developer ID Application certificate** — [developer.apple.com](https://developer.apple.com) → Certificates, Identifiers & Profiles → **+** → **Developer ID Application**. Generate a Certificate Signing Request from Keychain Access (Certificate Assistant → *Request a Certificate From a Certificate Authority*), upload it, download the `.cer`, and double-click to install into your login keychain. Verify with `security find-identity -v -p codesigning`.
|
||||||
2. **Create an App Store Connect API key** for notarization — [appstoreconnect.apple.com](https://appstoreconnect.apple.com) → Users and Access → **Integrations** → **App Store Connect API** → **+** (name it, *Developer* access). Download the `.p8` (shown once) and note the **Key ID** and **Issuer ID**.
|
2. **Create an App Store Connect API key** for notarization — [appstoreconnect.apple.com](https://appstoreconnect.apple.com) → Users and Access → **Integrations** → **App Store Connect API** → **+** (name it, *Developer* access). Download the `.p8` (shown once) and note the **Key ID** and **Issuer ID**.
|
||||||
3. **Save the notarization credentials** to `~/.config/gridline/notarize.env` (never committed):
|
3. **Add the signing secrets** to the repository (**Settings → Secrets and variables → Actions**):
|
||||||
|
|
||||||
```bash
|
| Secret | Value |
|
||||||
APPLE_API_KEY=<Key ID>
|
| :--------------------------- | :------------------------------------------------------------------------------------------------- |
|
||||||
APPLE_API_ISSUER=<Issuer ID>
|
| `APPLE_CERTIFICATE` | base64 of the exported Developer ID `.p12` — `base64 -i DeveloperID.p12 \| pbcopy` |
|
||||||
APPLE_API_KEY_PATH=/absolute/path/to/AuthKey_<KeyID>.p8
|
| `APPLE_CERTIFICATE_PASSWORD` | the password you set when exporting the `.p12` |
|
||||||
```
|
| `APPLE_SIGNING_IDENTITY` | e.g. `Developer ID Application: Your Name (TEAMID)` — from `security find-identity -v -p codesigning` |
|
||||||
|
| `KEYCHAIN_PASSWORD` | any throwaway password for the temporary CI keychain |
|
||||||
|
| `APPLE_API_KEY` | App Store Connect **Key ID** |
|
||||||
|
| `APPLE_API_ISSUER` | App Store Connect **Issuer ID** |
|
||||||
|
| `APPLE_API_KEY_CONTENT` | contents of the downloaded `AuthKey_<KeyID>.p8` |
|
||||||
|
|
||||||
(Alternatively, drop the `.p8` at `~/.appstoreconnect/private_keys/AuthKey_<KeyID>.p8` and omit `APPLE_API_KEY_PATH`.)
|
When these secrets are absent the macOS jobs still build, but the DMGs are ad-hoc signed and **not** notarized (users get the “Open Anyway” prompt). The CI re-signs the bundled PostgreSQL/MariaDB client binaries with the same Developer ID identity, because Apple rejects ad-hoc-signed nested executables during notarization.
|
||||||
|
|
||||||
`scripts/release-mac.sh` auto-detects the Developer ID identity from your keychain, signs both architectures, notarizes + staples them, and verifies the result before uploading. If the certificate or credentials are missing it fails with instructions (set `GRIDLINE_SKIP_NOTARIZE=1` to build signed-but-unnotarized for testing only).
|
`scripts/release-mac.sh` is the **local fallback** path (used before GitHub Actions took over macOS builds, and still handy for a signed build without burning CI minutes). It auto-detects the Developer ID identity from your keychain, reads notarization credentials from `~/.config/gridline/notarize.env` (`APPLE_API_KEY`, `APPLE_API_ISSUER`, `APPLE_API_KEY_PATH`), signs both architectures, notarizes + staples them, and verifies the result before uploading. If the certificate or credentials are missing it fails with instructions (set `GRIDLINE_SKIP_NOTARIZE=1` to build signed-but-unnotarized for testing only).
|
||||||
|
|
||||||
**Windows** still shows a SmartScreen prompt (no Windows signing cert yet); **Linux** installs without a warning.
|
**Windows** still shows a SmartScreen prompt (no Windows signing cert yet); **Linux** installs without a warning.
|
||||||
|
|
||||||
@@ -344,12 +348,12 @@ Each release contains **one file per platform** — you only need the one that m
|
|||||||
|
|
||||||
| Your system | Download this | Notes |
|
| Your system | Download this | Notes |
|
||||||
| :------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------- |
|
| :------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------- |
|
||||||
| macOS **Apple Silicon** (M1/M2/M3/M4…) | [Gridline_0.8.0_aarch64.dmg](https://git.ranio.xyz/adrianbonpin/gridline/releases/download/v0.8.0/Gridline_0.8.0_aarch64.dmg) | `aarch64` = Apple's own chip |
|
| macOS **Apple Silicon** (M1/M2/M3/M4…) | [Gridline_0.8.0_aarch64.dmg](https://github.com/AdrianBonpin/gridline/releases/download/v0.8.0/Gridline_0.8.0_aarch64.dmg) | `aarch64` = Apple's own chip |
|
||||||
| macOS **Intel** | [Gridline_0.8.0_x64.dmg](https://git.ranio.xyz/adrianbonpin/gridline/releases/download/v0.8.0/Gridline_0.8.0_x64.dmg) | `x64` = Intel/AMD |
|
| macOS **Intel** | [Gridline_0.8.0_x64.dmg](https://github.com/AdrianBonpin/gridline/releases/download/v0.8.0/Gridline_0.8.0_x64.dmg) | `x64` = Intel/AMD |
|
||||||
| **Windows** (most PCs) | [Gridline_0.8.0_x64-setup.exe](https://git.ranio.xyz/adrianbonpin/gridline/releases/download/v0.8.0/Gridline_0.8.0_x64-setup.exe) | The `.msi` is an alternate installer (for enterprises/IT admins) |
|
| **Windows** (most PCs) | [Gridline_0.8.0_x64-setup.exe](https://github.com/AdrianBonpin/gridline/releases/download/v0.8.0/Gridline_0.8.0_x64-setup.exe) | The `.msi` is an alternate installer (for enterprises/IT admins) |
|
||||||
| **Debian / Ubuntu** | [Gridline_0.8.0_amd64.deb](https://git.ranio.xyz/adrianbonpin/gridline/releases/download/v0.8.0/Gridline_0.8.0_amd64.deb) | Install: `sudo apt install ./Gridline_0.8.0_amd64.deb` |
|
| **Debian / Ubuntu** | [Gridline_0.8.0_amd64.deb](https://github.com/AdrianBonpin/gridline/releases/download/v0.8.0/Gridline_0.8.0_amd64.deb) | Install: `sudo apt install ./Gridline_0.8.0_amd64.deb` |
|
||||||
| **Fedora / RHEL / openSUSE** | [Gridline-0.8.0-1.x86_64.rpm](https://git.ranio.xyz/adrianbonpin/gridline/releases/download/v0.8.0/Gridline-0.8.0-1.x86_64.rpm) | Install: `sudo dnf install Gridline-0.8.0-1.x86_64.rpm` |
|
| **Fedora / RHEL / openSUSE** | [Gridline-0.8.0-1.x86_64.rpm](https://github.com/AdrianBonpin/gridline/releases/download/v0.8.0/Gridline-0.8.0-1.x86_64.rpm) | Install: `sudo dnf install Gridline-0.8.0-1.x86_64.rpm` |
|
||||||
| **Any other Linux** | [Gridline_0.8.0_amd64.AppImage](https://git.ranio.xyz/adrianbonpin/gridline/releases/download/v0.8.0/Gridline_0.8.0_amd64.AppImage) | Works on every distro: `chmod +x` the file, then double-click it |
|
| **Any other Linux** | [Gridline_0.8.0_amd64.AppImage](https://github.com/AdrianBonpin/gridline/releases/download/v0.8.0/Gridline_0.8.0_amd64.AppImage) | Works on every distro: `chmod +x` the file, then double-click it |
|
||||||
|
|
||||||
**Not sure if your Mac is Intel or Apple Silicon?** Click the **Apple menu** → **About This Mac**. If it shows "Apple M1/M2/M3/M4…" download the `aarch64` file; if it shows an Intel chip, download `x64`. Downloading the wrong one won't run.
|
**Not sure if your Mac is Intel or Apple Silicon?** Click the **Apple menu** → **About This Mac**. If it shows "Apple M1/M2/M3/M4…" download the `aarch64` file; if it shows an Intel chip, download `x64`. Downloading the wrong one won't run.
|
||||||
|
|
||||||
@@ -363,7 +367,9 @@ git tag v0.7.15
|
|||||||
git push origin v0.7.15
|
git push origin v0.7.15
|
||||||
```
|
```
|
||||||
|
|
||||||
Gitea Actions (`.gitea/workflows/release.yml`) builds the **Linux and Windows** installers on the self-hosted runners, then opens a **draft release** on the [Releases](https://git.ranio.xyz/adrianbonpin/gridline/releases) page. **macOS is built manually** on the dev Mac and its DMGs uploaded to the same release via `scripts/release-mac.sh`. Review the draft and hit **Publish release**.
|
GitHub Actions (`.github/workflows/release.yml`) builds the **macOS (Apple Silicon + Intel), Windows, and Linux** installers in parallel and opens a **draft release** on the [Releases](https://github.com/AdrianBonpin/gridline/releases) page. Review the draft and hit **Publish release** — publishing triggers a follow-up job that bumps the Homebrew cask in [`AdrianBonpin/homebrew-gridline`](https://github.com/AdrianBonpin/homebrew-gridline) (pushed via a write-enabled deploy key on the tap, `HOMEBREW_TAP_DEPLOY_KEY`; skipped when unset).
|
||||||
|
|
||||||
|
The self-hosted Gitea mirror (`.gitea/workflows/release.yml`) still builds Linux and Windows for the Gitea release, and `scripts/release-mac.sh` can build macOS locally — both are kept as a fallback if GitHub is unavailable.
|
||||||
|
|
||||||
Before tagging, make sure the version number is in sync across `desktop/package.json`, `desktop/src-tauri/Cargo.toml`, and `desktop/src-tauri/tauri.conf.json`, and update the **README download tables** (Download + Which file should I download?) to the new version's asset names.
|
Before tagging, make sure the version number is in sync across `desktop/package.json`, `desktop/src-tauri/Cargo.toml`, and `desktop/src-tauri/tauri.conf.json`, and update the **README download tables** (Download + Which file should I download?) to the new version's asset names.
|
||||||
|
|
||||||
@@ -371,7 +377,7 @@ Before tagging, make sure the version number is in sync across `desktop/package.
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 1. Clone the repository
|
# 1. Clone the repository
|
||||||
git clone https://git.ranio.xyz/adrianbonpin/gridline.git
|
git clone https://github.com/AdrianBonpin/gridline.git
|
||||||
cd gridline
|
cd gridline
|
||||||
|
|
||||||
# 2. Install all workspace dependencies
|
# 2. Install all workspace dependencies
|
||||||
@@ -487,7 +493,7 @@ Highlights of what's next:
|
|||||||
|
|
||||||
Contributions, bug reports, and feature ideas are welcome. Gridline is Apache 2.0-licensed and intentionally stays open — no paywalled tiers, no bundled proprietary services.
|
Contributions, bug reports, and feature ideas are welcome. Gridline is Apache 2.0-licensed and intentionally stays open — no paywalled tiers, no bundled proprietary services.
|
||||||
|
|
||||||
- Open an issue via the **[template chooser](https://git.ranio.xyz/adrianbonpin/gridline/issues/new)** — pick **Bug report** (with an environment table so we can reproduce issues quickly), **Feature request**, or **Improvement** (UX polish for existing features). New issues are auto-assigned to the maintainer.
|
- Open an issue via the **[template chooser](https://github.com/AdrianBonpin/gridline/issues/new)** — pick **Bug report** (with an environment table so we can reproduce issues quickly), **Feature request**, or **Improvement** (UX polish for existing features). New issues are auto-assigned to the maintainer.
|
||||||
- Submit a pull request. Keep Tauri commands thin, type IPC boundaries explicitly, and follow the existing Rust/React conventions.
|
- Submit a pull request. Keep Tauri commands thin, type IPC boundaries explicitly, and follow the existing Rust/React conventions.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
+2
-2
@@ -104,7 +104,7 @@ Still deferred from 0.7.0, slated for this bucket:
|
|||||||
## ✅ Shipped (0.7.14)
|
## ✅ Shipped (0.7.14)
|
||||||
|
|
||||||
- **No more repeated macOS keychain prompts** — saved connection passwords and SSH secrets are ACL-pinned to the app's code signature when stored, so app upgrades never re-trigger the macOS keychain access dialog (users upgrading from older builds see one final prompt — choose Always Allow)
|
- **No more repeated macOS keychain prompts** — saved connection passwords and SSH secrets are ACL-pinned to the app's code signature when stored, so app upgrades never re-trigger the macOS keychain access dialog (users upgrading from older builds see one final prompt — choose Always Allow)
|
||||||
- **Install via Homebrew** — add the Gitea tap (`brew tap AdrianBonpin/gridline https://git.ranio.xyz/adrianbonpin/homebrew-gridline.git`) and run `brew install --cask gridline`
|
- **Install via Homebrew** — add the Homebrew tap (`brew tap AdrianBonpin/gridline https://github.com/AdrianBonpin/homebrew-gridline.git`) and run `brew install --cask gridline`
|
||||||
- **Version bump** 0.7.13 → **0.7.14**.
|
- **Version bump** 0.7.13 → **0.7.14**.
|
||||||
|
|
||||||
## ✅ Shipped (0.7.13)
|
## ✅ Shipped (0.7.13)
|
||||||
@@ -112,7 +112,7 @@ Still deferred from 0.7.0, slated for this bucket:
|
|||||||
- **Developer-ID signed + notarized macOS** — the app is properly code-signed with a Developer ID Application certificate and notarized by Apple, so macOS opens it without the Gatekeeper "Open Anyway" prompt or the `xattr` quarantine workaround
|
- **Developer-ID signed + notarized macOS** — the app is properly code-signed with a Developer ID Application certificate and notarized by Apple, so macOS opens it without the Gatekeeper "Open Anyway" prompt or the `xattr` quarantine workaround
|
||||||
- **Version bump** 0.7.12 → **0.7.13**.
|
- **Version bump** 0.7.12 → **0.7.13**.
|
||||||
|
|
||||||
## ✅ Shipped (0.7.12) *(includes 0.7.11: release-pipeline/docs refresh — README download links moved from GitHub to the self-hosted Gitea `git.ranio.xyz`)*
|
## ✅ Shipped (0.7.12) *(includes 0.7.11: release-pipeline/docs refresh)*
|
||||||
|
|
||||||
- **Reliable cell editing for non-text columns** — editing a PostgreSQL cell whose type isn't text (integers, booleans, UUIDs, json/jsonb, timestamps, enums, numerics) now works; edited values are sent to PostgreSQL in text wire format so the server parses them into the column's own type
|
- **Reliable cell editing for non-text columns** — editing a PostgreSQL cell whose type isn't text (integers, booleans, UUIDs, json/jsonb, timestamps, enums, numerics) now works; edited values are sent to PostgreSQL in text wire format so the server parses them into the column's own type
|
||||||
- **Recent connections fix** — the Recent strip no longer renders empty on first launch when the connections list hadn't loaded yet
|
- **Recent connections fix** — the Recent strip no longer renders empty on first launch when the connections list hadn't loaded yet
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
# Homebrew Cask formula for Gridline.
|
# Homebrew Cask formula for Gridline.
|
||||||
#
|
#
|
||||||
# This file is the template; `scripts/render-cask.sh` substitutes
|
# This file is the template; `scripts/render-cask.sh` substitutes the version
|
||||||
# @VERSION@ / @ARM_SHA256@ / @INTEL_SHA256@ on each release and pushes the
|
# and the per-arch SHA-256 checksums on each release and pushes the rendered
|
||||||
# rendered file to the `AdrianBonpin/homebrew-gridline` tap as
|
# file to the `AdrianBonpin/homebrew-gridline` tap as `Casks/gridline.rb`.
|
||||||
# `Casks/gridline.rb`. End users then install with
|
# End users then install with
|
||||||
#
|
#
|
||||||
# brew install --cask AdrianBonpin/gridline/gridline
|
# brew install --cask AdrianBonpin/gridline/gridline
|
||||||
#
|
#
|
||||||
@@ -21,10 +21,10 @@ cask "gridline" do
|
|||||||
sha256 arm: "@ARM_SHA256@",
|
sha256 arm: "@ARM_SHA256@",
|
||||||
intel: "@INTEL_SHA256@"
|
intel: "@INTEL_SHA256@"
|
||||||
|
|
||||||
url "https://git.ranio.xyz/adrianbonpin/gridline/releases/download/v#{version}/Gridline_#{version}_#{arch}.dmg"
|
url "https://github.com/AdrianBonpin/gridline/releases/download/v#{version}/Gridline_#{version}_#{arch}.dmg"
|
||||||
name "Gridline"
|
name "Gridline"
|
||||||
desc "Open-source, cross-platform database GUI client for PostgreSQL"
|
desc "Open-source, cross-platform database GUI client for PostgreSQL"
|
||||||
homepage "https://git.ranio.xyz/adrianbonpin/gridline"
|
homepage "https://github.com/AdrianBonpin/gridline"
|
||||||
|
|
||||||
depends_on macos: :monterey
|
depends_on macos: :monterey
|
||||||
|
|
||||||
|
|||||||
@@ -197,6 +197,37 @@ for APP in \
|
|||||||
|| echo " (spctl assessment pending — normal right after notarization; stapler validate passed)"
|
|| echo " (spctl assessment pending — normal right after notarization; stapler validate passed)"
|
||||||
done
|
done
|
||||||
|
|
||||||
|
# ---- notarize + staple the DMGs --------------------------------------------
|
||||||
|
# Notarizing the .app is not enough. When a user opens the downloaded DMG,
|
||||||
|
# Gatekeeper assesses the DMG itself — and an unnotarized DMG is rejected as
|
||||||
|
# "Unnotarized Developer ID" even though the app inside it is fine. Submit each
|
||||||
|
# DMG to the same notary service and staple the ticket into the DMG.
|
||||||
|
if [ "${GRIDLINE_SKIP_NOTARIZE:-0}" = "1" ]; then
|
||||||
|
echo ">> Skipping DMG notarization (GRIDLINE_SKIP_NOTARIZE=1)."
|
||||||
|
else
|
||||||
|
API_KEY_PATH="${APPLE_API_KEY_PATH:-${HOME}/.appstoreconnect/private_keys/AuthKey_${APPLE_API_KEY}.p8}"
|
||||||
|
[ -f "$API_KEY_PATH" ] || {
|
||||||
|
echo "ERROR: notarization API key not found at $API_KEY_PATH" >&2
|
||||||
|
echo " Set APPLE_API_KEY_PATH in ${NOTARIZE_ENV} or place the .p8 at the default location." >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
for DMG in \
|
||||||
|
"${BUNDLE_ARM}/dmg/Gridline_${VERSION}_aarch64.dmg" \
|
||||||
|
"${BUNDLE_INTEL}/dmg/Gridline_${VERSION}_x64.dmg"; do
|
||||||
|
[ -f "$DMG" ] || { echo "missing $DMG" >&2; exit 1; }
|
||||||
|
echo ">> Notarizing $(basename "$DMG") (this can take a few minutes)"
|
||||||
|
xcrun notarytool submit "$DMG" \
|
||||||
|
--key "$API_KEY_PATH" \
|
||||||
|
--key-id "$APPLE_API_KEY" \
|
||||||
|
--issuer "$APPLE_API_ISSUER" \
|
||||||
|
--wait
|
||||||
|
xcrun stapler staple "$DMG" \
|
||||||
|
|| { echo "failed to staple $(basename "$DMG")" >&2; exit 1; }
|
||||||
|
xcrun stapler validate "$DMG" \
|
||||||
|
|| { echo "notarization ticket not stapled to $(basename "$DMG")" >&2; exit 1; }
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
# ---- create/ensure release + upload DMGs -----------------------------------
|
# ---- create/ensure release + upload DMGs -----------------------------------
|
||||||
# Use `tea` for release management — it handles OAuth token refresh, unlike a
|
# Use `tea` for release management — it handles OAuth token refresh, unlike a
|
||||||
# raw curl with a token from the git credential store (which can 401).
|
# raw curl with a token from the git credential store (which can 401).
|
||||||
|
|||||||
@@ -59,12 +59,12 @@
|
|||||||
const isMobile = /Mobi|Android|iPhone|iPad|iPod/i.test(userAgent);
|
const isMobile = /Mobi|Android|iPhone|iPad|iPod/i.test(userAgent);
|
||||||
const keepLabel = downloadBtn.hasAttribute("data-download-label");
|
const keepLabel = downloadBtn.hasAttribute("data-download-label");
|
||||||
|
|
||||||
const releasesUrl = "https://git.ranio.xyz/adrianbonpin/gridline/releases";
|
const releasesUrl = "https://github.com/AdrianBonpin/gridline/releases";
|
||||||
const version = "0.8.0";
|
const version = "0.8.0";
|
||||||
|
|
||||||
if (isMobile) {
|
if (isMobile) {
|
||||||
downloadBtn.href = releasesUrl;
|
downloadBtn.href = releasesUrl;
|
||||||
if (!keepLabel) downloadBtn.textContent = "Get it on Gitea";
|
if (!keepLabel) downloadBtn.textContent = "Get it on GitHub";
|
||||||
} else if (isMac) {
|
} else if (isMac) {
|
||||||
downloadBtn.href = `${releasesUrl}/download/v${version}/Gridline_${version}_aarch64.dmg`;
|
downloadBtn.href = `${releasesUrl}/download/v${version}/Gridline_${version}_aarch64.dmg`;
|
||||||
if (!keepLabel) downloadBtn.textContent = "Download for macOS";
|
if (!keepLabel) downloadBtn.textContent = "Download for macOS";
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ export const changelogEntries: ChangelogEntry[] = [
|
|||||||
date: "2026-09-04",
|
date: "2026-09-04",
|
||||||
highlights: [
|
highlights: [
|
||||||
"No more repeated macOS keychain prompts — saved connection passwords and SSH secrets are now ACL-pinned to the app's code signature when stored, so app upgrades never re-trigger the macOS keychain access dialog (users upgrading from older builds see one final prompt — choose Always Allow).",
|
"No more repeated macOS keychain prompts — saved connection passwords and SSH secrets are now ACL-pinned to the app's code signature when stored, so app upgrades never re-trigger the macOS keychain access dialog (users upgrading from older builds see one final prompt — choose Always Allow).",
|
||||||
"Install via Homebrew — add the Gitea tap (brew tap AdrianBonpin/gridline https://git.ranio.xyz/adrianbonpin/homebrew-gridline.git) and run brew install --cask gridline.",
|
"Install via Homebrew — add the Homebrew tap (brew tap AdrianBonpin/gridline https://github.com/AdrianBonpin/homebrew-gridline.git) and run brew install --cask gridline.",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ import { describe, it, expect } from "vitest";
|
|||||||
import { faqItems } from "./faq";
|
import { faqItems } from "./faq";
|
||||||
|
|
||||||
describe("faqItems", () => {
|
describe("faqItems", () => {
|
||||||
it("has exactly 5 items", () => {
|
it("has exactly 6 items", () => {
|
||||||
expect(faqItems).toHaveLength(5);
|
expect(faqItems).toHaveLength(6);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("every item has a non-empty question and answer", () => {
|
it("every item has a non-empty question and answer", () => {
|
||||||
|
|||||||
@@ -40,9 +40,9 @@ const isProd = import.meta.env.PROD;
|
|||||||
<link rel="manifest" href="/site.webmanifest" />
|
<link rel="manifest" href="/site.webmanifest" />
|
||||||
<link rel="preload" as="image" href="/og.png" />
|
<link rel="preload" as="image" href="/og.png" />
|
||||||
<link rel="preconnect" href="https://stat.ranio.xyz" crossorigin />
|
<link rel="preconnect" href="https://stat.ranio.xyz" crossorigin />
|
||||||
<link rel="preconnect" href="https://git.ranio.xyz" />
|
<link rel="preconnect" href="https://github.com" />
|
||||||
<link rel="dns-prefetch" href="https://stat.ranio.xyz" />
|
<link rel="dns-prefetch" href="https://stat.ranio.xyz" />
|
||||||
<link rel="dns-prefetch" href="https://git.ranio.xyz" />
|
<link rel="dns-prefetch" href="https://github.com" />
|
||||||
|
|
||||||
<meta property="og:title" content={title} />
|
<meta property="og:title" content={title} />
|
||||||
<meta property="og:description" content={description} />
|
<meta property="og:description" content={description} />
|
||||||
@@ -104,7 +104,7 @@ const isProd = import.meta.env.PROD;
|
|||||||
</a>
|
</a>
|
||||||
<div class="hidden md:flex items-center gap-6 text-sm">
|
<div class="hidden md:flex items-center gap-6 text-sm">
|
||||||
<a href="/#features" class="nav-link">Features</a>
|
<a href="/#features" class="nav-link">Features</a>
|
||||||
<a href="https://git.ranio.xyz/adrianbonpin/gridline" target="_blank" rel="noopener" class="nav-link">Gitea</a>
|
<a href="https://github.com/AdrianBonpin/gridline" target="_blank" rel="noopener" class="nav-link">GitHub</a>
|
||||||
<a href="/#download" data-download data-download-label class="nav-link">Download</a>
|
<a href="/#download" data-download data-download-label class="nav-link">Download</a>
|
||||||
<button id="theme-toggle" class="p-2 rounded-full hover:bg-[var(--surface-elevated)] transition-colors" aria-label="Toggle theme">
|
<button id="theme-toggle" class="p-2 rounded-full hover:bg-[var(--surface-elevated)] transition-colors" aria-label="Toggle theme">
|
||||||
<span class="theme-icon-sun hidden">☀</span>
|
<span class="theme-icon-sun hidden">☀</span>
|
||||||
@@ -139,9 +139,9 @@ const isProd = import.meta.env.PROD;
|
|||||||
<div>
|
<div>
|
||||||
<h4 class="font-semibold text-sm mb-4">Project</h4>
|
<h4 class="font-semibold text-sm mb-4">Project</h4>
|
||||||
<ul class="space-y-2 text-sm text-[var(--text-muted)]">
|
<ul class="space-y-2 text-sm text-[var(--text-muted)]">
|
||||||
<li><a href="https://git.ranio.xyz/adrianbonpin/gridline" target="_blank" rel="noopener" class="footer-link">Gitea</a></li>
|
<li><a href="https://github.com/AdrianBonpin/gridline" target="_blank" rel="noopener" class="footer-link">GitHub</a></li>
|
||||||
<li><a href="https://git.ranio.xyz/adrianbonpin/gridline/issues" target="_blank" rel="noopener" class="footer-link">Issues</a></li>
|
<li><a href="https://github.com/AdrianBonpin/gridline/issues" target="_blank" rel="noopener" class="footer-link">Issues</a></li>
|
||||||
<li><a href="https://git.ranio.xyz/adrianbonpin/gridline/src/branch/prod/ROADMAP.md" target="_blank" rel="noopener" class="footer-link">Roadmap</a></li>
|
<li><a href="https://github.com/AdrianBonpin/gridline/blob/prod/ROADMAP.md" target="_blank" rel="noopener" class="footer-link">Roadmap</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ describe("seo builders", () => {
|
|||||||
applicationCategory: "DeveloperApplication",
|
applicationCategory: "DeveloperApplication",
|
||||||
offersPrice: 0,
|
offersPrice: 0,
|
||||||
offersCurrency: "USD",
|
offersCurrency: "USD",
|
||||||
downloadUrl: "https://git.ranio.xyz/adrianbonpin/gridline/releases",
|
downloadUrl: "https://github.com/AdrianBonpin/gridline/releases",
|
||||||
});
|
});
|
||||||
expect(ld["@type"]).toBe("SoftwareApplication");
|
expect(ld["@type"]).toBe("SoftwareApplication");
|
||||||
expect(ld.name).toBe("Gridline");
|
expect(ld.name).toBe("Gridline");
|
||||||
@@ -43,10 +43,10 @@ describe("seo builders", () => {
|
|||||||
name: "Gridline",
|
name: "Gridline",
|
||||||
url: "https://getgridline.app/",
|
url: "https://getgridline.app/",
|
||||||
logo: "https://getgridline.app/gridline-icon.svg",
|
logo: "https://getgridline.app/gridline-icon.svg",
|
||||||
sameAs: ["https://git.ranio.xyz/adrianbonpin/gridline"],
|
sameAs: ["https://github.com/AdrianBonpin/gridline"],
|
||||||
});
|
});
|
||||||
expect(ld["@type"]).toBe("Organization");
|
expect(ld["@type"]).toBe("Organization");
|
||||||
expect(ld.sameAs).toContain("https://git.ranio.xyz/adrianbonpin/gridline");
|
expect(ld.sameAs).toContain("https://github.com/AdrianBonpin/gridline");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("buildWebSiteJsonLd includes SearchAction", () => {
|
it("buildWebSiteJsonLd includes SearchAction", () => {
|
||||||
|
|||||||
@@ -38,8 +38,8 @@ const jsonLd = [
|
|||||||
|
|
||||||
<p class="mt-8 text-sm text-[var(--text-muted)]">
|
<p class="mt-8 text-sm text-[var(--text-muted)]">
|
||||||
Full release history on{" "}
|
Full release history on{" "}
|
||||||
<a href="https://git.ranio.xyz/adrianbonpin/gridline/releases" target="_blank" rel="noopener" class="text-[var(--primary)] hover:text-[var(--primary-hover)]">
|
<a href="https://github.com/AdrianBonpin/gridline/releases" target="_blank" rel="noopener" class="text-[var(--primary)] hover:text-[var(--primary-hover)]">
|
||||||
Gitea Releases
|
GitHub Releases
|
||||||
</a>.
|
</a>.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ const jsonLd = [
|
|||||||
applicationCategory: "DeveloperApplication",
|
applicationCategory: "DeveloperApplication",
|
||||||
offersPrice: 0,
|
offersPrice: 0,
|
||||||
offersCurrency: "USD",
|
offersCurrency: "USD",
|
||||||
downloadUrl: "https://git.ranio.xyz/adrianbonpin/gridline/releases",
|
downloadUrl: "https://github.com/AdrianBonpin/gridline/releases",
|
||||||
}),
|
}),
|
||||||
buildFaqJsonLd(faqItems),
|
buildFaqJsonLd(faqItems),
|
||||||
buildOrganizationJsonLd({
|
buildOrganizationJsonLd({
|
||||||
@@ -36,7 +36,7 @@ const jsonLd = [
|
|||||||
url: `${site}/`,
|
url: `${site}/`,
|
||||||
logo: `${site}/gridline-icon.svg`,
|
logo: `${site}/gridline-icon.svg`,
|
||||||
sameAs: [
|
sameAs: [
|
||||||
"https://git.ranio.xyz/adrianbonpin/gridline",
|
"https://github.com/AdrianBonpin/gridline",
|
||||||
"https://x.com/AdrianBonpin",
|
"https://x.com/AdrianBonpin",
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
@@ -73,11 +73,11 @@ const jsonLd = [
|
|||||||
Query, explore, sync, and edit PostgreSQL, MySQL, SQLite, and Redis, with no paywalls, no tab limits, no account required.
|
Query, explore, sync, and edit PostgreSQL, MySQL, SQLite, and Redis, with no paywalls, no tab limits, no account required.
|
||||||
</p>
|
</p>
|
||||||
<div class="mt-8 flex flex-wrap items-center justify-center gap-3">
|
<div class="mt-8 flex flex-wrap items-center justify-center gap-3">
|
||||||
<a id="download" href="https://git.ranio.xyz/adrianbonpin/gridline/releases" class="primary-btn" data-download>
|
<a id="download" href="https://github.com/AdrianBonpin/gridline/releases" class="primary-btn" data-download>
|
||||||
Download
|
Download
|
||||||
</a>
|
</a>
|
||||||
<a href="https://git.ranio.xyz/adrianbonpin/gridline" target="_blank" rel="noopener" class="ghost-btn">
|
<a href="https://github.com/AdrianBonpin/gridline" target="_blank" rel="noopener" class="ghost-btn">
|
||||||
View on Gitea
|
View on GitHub
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -181,8 +181,8 @@ const jsonLd = [
|
|||||||
Gridline is Apache 2.0 and developed openly. Contributions, issues, and feature requests are welcome.
|
Gridline is Apache 2.0 and developed openly. Contributions, issues, and feature requests are welcome.
|
||||||
</p>
|
</p>
|
||||||
<div class="flex flex-wrap items-center justify-center gap-3">
|
<div class="flex flex-wrap items-center justify-center gap-3">
|
||||||
<a href="https://git.ranio.xyz/adrianbonpin/gridline" target="_blank" rel="noopener" class="ghost-btn">
|
<a href="https://github.com/AdrianBonpin/gridline" target="_blank" rel="noopener" class="ghost-btn">
|
||||||
Star on Gitea
|
Star on GitHub
|
||||||
</a>
|
</a>
|
||||||
<a href="https://ko-fi.com/adrianbonpin" target="_blank" rel="noopener" class="ghost-btn">
|
<a href="https://ko-fi.com/adrianbonpin" target="_blank" rel="noopener" class="ghost-btn">
|
||||||
☕ Support on Ko-fi
|
☕ Support on Ko-fi
|
||||||
|
|||||||
Reference in New Issue
Block a user