`scripts/release-mac.sh` still targeted the retired Gitea release flow: it
looked for `.gitea/workflows/release.yml`, keyed off GITEA_SERVER/REPO_OWNER,
and created/uploaded assets with `tea`. Releases are GitHub-only now, so the
script uploads with `gh release upload --clobber` (a locally signed DMG pair
supersedes the CI-built ones, which the next draft publish then ships).
Also corrects the release docs, which contradicted each other: AGENTS.md said
macOS is built manually and never in CI, while README said CI signs it. In
reality the `release.yml` macOS jobs sign + notarize when the APPLE_* secrets
are present and log a `notice` when they are not — so the checklist now says to
read the run output before publishing a draft (verified on v0.8.1: stapler
validate + spctl report `source=Notarized Developer ID`), lists the local
`scripts/release-mac.sh` as the fallback, and documents the cask bump that the
`release: published` event triggers.
The "keep versions in sync" checklist was also incomplete and had already been
the source of drift: it missed `desktop/src-tauri/Cargo.lock` (only the
`gridline` entry — the sqlx family shares 0.8.x), `www/public/landing.js`, the
site changelog, `bundle-config.test.ts`, the two README banners and the
MAINTENANCE comment.
GitHub is the source of truth; Gitea (origin) is a just-in-case copy that had
drifted two commits behind. Rather than require a credential on GitHub's side
or convert the repo to a read-only Gitea mirror, wire it into the local
workflow:
- scripts/mirror-gitea.sh — pushes prod + all tags to the Gitea remote
(--branches for every branch). Refuses to push to a GitHub remote, prints
what it did, exits non-zero when the mirror could not be updated.
- scripts/hooks/pre-push — git has no post-push hook, so the mirror runs before
a push that includes prod/main or a tag; skips other remotes, deletions and
its own recursion.
- scripts/hooks/post-merge — refreshes the mirror after `git pull`/merge on
prod, which is how a PR squash-merged on GitHub reaches the mirror.
- scripts/install-git-hooks.sh — one-time per clone; sets
core.hooksPath=scripts/hooks so the hooks stay versioned with the repo.
Mirror failures are warnings: a Gitea outage never blocks a push to GitHub.
AGENTS.md documents the setup plus the GitHub-only release flow.
Verified: script + hooks exercised for the prod/tag case, the feature-branch
skip, the Gitea-remote skip and the recursion guard; the mirror is idempotent
(no-op when already in sync).
* fix(keychain): repair stale macOS keychain ACLs before reads
macOS records the trusted application of a legacy keychain item as the
executable *path* for an ad-hoc (dev/linker-signed) build, and as a code
requirement + cdhash partition list for a signed bundle. When the path
changes — the monorepo move (`src-tauri/` -> `desktop/src-tauri/`), a
reinstalled app, running the installed app after a dev build — the ACL no
longer admits the running binary, so macOS demands the keychain password on
*every* access: once per secret, every time, with no "Always Allow".
The pin only ran after writes, and writing to an inaccessible item needs the
same authorization, so the ACL was never repaired.
- keychain_acl::is_item_trusted() answers "am I already in this item's ACL?"
with a metadata-only, prompt-free lookup (SecKeychainItemCopyAccess +
SecAccessCopyACLList + comparison against this process's own
SecTrustedApplication representation; a signed bundle's `cdhash:` partition
lists count as trusted and are left alone).
- ensure_item_acl() now no-ops when already trusted (no duplicate ACL entries,
no redundant authorization) and otherwise pins exactly as before.
- New before_read() hook, called from every keychain read path (DB password,
SSH password, SSH passphrase — commands and internal helpers), so a stale
item costs one authorization and is silent from then on.
- ItemSearchOptions reports "nothing matched" as errSecItemNotFound, which is
now handled as "no item" instead of a lookup error — previously every
connection without a stored password logged a spurious failure.
Verified: cargo test --lib (408 passed) plus the ignored live test
(`cargo test --lib -- --ignored keychain_acl`), which asserts a fresh item is
already trusted, repairs are idempotent and the secret still reads back. That
test now finishes in 0.25s instead of ~8s because the redundant
SecKeychainItemSetAccess is gone.
* chore(release): bump 0.8.1 and document GitHub-only releases
Version 0.8.0 -> 0.8.1 across every synced location: desktop/package.json,
desktop/src-tauri/Cargo.toml (+ Cargo.lock gridline entry), tauri.conf.json,
www/src/pages/index.astro, www/public/landing.js, both README download tables
(+ the MAINTENANCE note), and the version/docs-coverage/bundle-config tests.
Adds the 0.8.1 changelog entry (site + README Recent Changes).
AGENTS.md: releases are GitHub-only — tag `prod` and `git push github vN.M.N`
(origin/Gitea is a just-in-case mirror; never push branches, PRs or tags
there), and the keychain row documents the self-healing ACL repair.
Connecting to a managed provider (Neon in the report) failed with
"connection is insecure (try using `sslmode=require`)" because the TLS mode
never reached the backend:
- ConnectionFormData carried no ssl_* fields and both submit handlers
(NewConnectionScreen.buildPayload, EditConnectionModal save + test) omitted
them, so the SSH/SSL tab's mode was silently dropped before IPC.
- SslFields wrote ssl_ca_cert / ssl_client_cert / ssl_client_key — names the
Rust ConnectionInput ignores (it expects ssl_ca_path / ssl_cert_path /
ssl_key_path).
- parseConnectionString ignored query params, so a pasted Neon/Supabase URL's
?sslmode=require was discarded.
Pasting a URI now fills every setting it can (sslmode / ssl-mode, including
MySQL's REQUIRED and VERIFY_IDENTITY spellings) and offers a derived label
(database name, else host/file stem) as a placeholder that Test/Save uses when
the field is left blank. Managed presets pre-select their TLS mode (Supabase /
Neon → Require, PlanetScale → Verify Full).
Backend safety net: db::tls::resolve_ssl_mode defaults *.neon.tech /
*.supabase.co / *.psdb.cloud to `require` when a connection has no ssl_mode, so
connections saved before this fix keep working; an explicit mode (including
disable) always wins.
Tests: +15 frontend (parser, form payload, SSL field names, edit modal),
+4 Rust (resolve_ssl_mode / tls_decision_for).
* docs(www): point the website, docs and badges back at GitHub
GitHub is the release destination again, so every user-facing reference to the
self-hosted Gitea instance now points at github.com/AdrianBonpin:
- www: nav + footer links, Download / Releases buttons, 'View on GitHub' and
'Star on GitHub' CTAs, landing.js download fallback label, the repo
preconnect/dns-prefetch hints, and the ROADMAP link (Gitea's
src/branch/prod path becomes GitHub's blob/prod)
- www/src/lib/seo.test.ts: matching structured-data fixtures
- README: shields.io badge and the tap prose
- ROADMAP: Homebrew tap URL
Deliberately left pointing at Gitea (they are the fallback, not destinations):
.gitea/workflows/release.yml and scripts/release-mac.sh use git.ranio.xyz as
an API host, so rewriting those would break the fallback path.
* fix(www): update the stale FAQ count assertion
faq.test.ts asserted exactly 5 FAQ items but the list has had 6 since
bdf2bb5 (Release 0.7.15), so the www suite has been red since then. No CI
runs the www tests, which is why it went unnoticed. Found while verifying the
docs change above; unrelated to it.
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.
The `release: published` trigger does not fire for a release whose draft was
created by tauri-action's GITHUB_TOKEN — verified on v0.8.0, which produced
zero workflow runs after publishing, so the Homebrew cask was never bumped and
the tap stayed empty.
Adds `bump_cask` to the workflow_dispatch inputs so the cask bump can be run
on demand for any tag, and switches the cask job to RELEASE_TAG (which resolves
for both dispatch and release events) instead of GITHUB_REF_NAME.
Also stops the cask template's header comment from being rewritten with the
substituted version and checksums on every render.
Adds a `tag` input so a release can be re-run without rewriting its tag.
Tag-triggered runs use the workflow file at the tagged commit, so fixing the
workflow and re-pushing the same tag was previously the only way to rebuild —
which rewrites a published release ref and misrepresents what the tag points
at.
RELEASE_TAG resolves to the input on workflow_dispatch and to the ref
otherwise, and drives both the checkout ref and tauri-action's tagName.
Replace the HOMEBREW_TAP_TOKEN PAT with HOMEBREW_TAP_DEPLOY_KEY, a
write-enabled SSH deploy key scoped to only AdrianBonpin/homebrew-gridline.
actions/checkout's `ssh-key` input wires the key into the SSH agent for the
job, so the cask push goes over SSH and no broadly-scoped personal access
token has to be stored as a repository secret.
Also drops the now-wrong claim that the job no-ops until the secret is set.
`gh secret set` strips the trailing newline when storing APPLE_API_KEY_CONTENT,
and Apple's PEM parser expects the key file to end with one. Write it back
explicitly so notarization can load the key.
GitHub is unsuspended, so releases move back from Gitea to GitHub, with the
Gitea mirror kept as a fallback.
- release.yml: re-enable as the active path and wire up Developer-ID signing
+ notarization for the macOS jobs. The Apple steps are guarded on the
secrets being present, so the workflow stays green (ad-hoc signed) until
they are configured.
- release.yml: re-sign the bundled PostgreSQL/MariaDB client binaries with
the Developer ID identity + hardened runtime + timestamp — Apple rejects
ad-hoc-signed nested executables during notarization.
- release.yml: move the Homebrew cask bump to the `release: published` event.
It previously ran on tag push, but the releases/tags API does not serve
drafts, so it could never download the DMGs. It now also skips cleanly
when HOMEBREW_TAP_TOKEN is unset rather than failing the workflow.
- release.yml: drop the stale header note claiming this is not the active
release path.
- README + cask template: point downloads, issues, releases and the Homebrew
tap back at github.com/AdrianBonpin.
Replace the static hero screenshot with a self-contained React mockup of the
full Gridline DB viewer (sidebar, table tree, tab bar, fake SQL editor,
data grid, queries/objects/schema-visualizer views, disabled Tools state).
- Add @astrojs/react and mount the island via client:visible
- 16:9 aspect-ratio mockup, hidden on mobile (static image fallback)
- JSON cell popover: solid surface, anchored within the mockup window,
Escape/outside-click close
- Dependency-free HTML5 drag-to-reorder tabs
- Theme-aware app tokens (canvas, surface-raised, popover bg)
- Site-wide max-w-7xl (was max-w-[1200px]) so sections center on big screens
- Regenerate standalone www/bun.lock for deterministic Dokploy builds
Hide entry-animated elements at first paint (html.anime-ready guard in
global.css) so the landing page no longer flashes visible content before
anime.js loads. Adds a <noscript> fallback and a JS load-error fallback to
guarantee content is never permanently hidden, and reveals the hero
immediately for prefers-reduced-motion users. Includes #macos-hint in the
hero entrance timeline so the macOS command box animates in correctly.
www/package.json declares vitest ^4.1.10 but the standalone www/bun.lock
(Railpack/Dokploy builds from /www with --frozen-lockfile) was stale and
missing it, failing every deployment with 'lockfile had changes, but
lockfile is frozen'. Regenerated www/bun.lock from www/package.json.
Add 'Copy connection URL' (with password, fetched from the OS keychain on
demand) and 'Copy connection URL (no password)' to the connection card kebab
menu. Builds postgresql://, mysql://, sqlite://, redis:// strings with
percent-encoded credentials and the PG sslmode appended. Bump to v0.7.10.
- Add 2-feature_request.md and 3-improvement.md templates (chooser now
lists Bug report, Feature request, Improvement in order)
- Improve bug report template: cross-template pointer, Linux log hint,
Related issues/PRs section, demo-DB reproduction hint
- Auto-assign every newly opened issue to the repo owner:
assignees in template front matter + a workflows/auto-assign.yml
(covers issues opened without a template)
- Add ISSUE_TEMPLATE/config.yml with Roadmap/README contact links
- Update README Contributing section
The bundle contains read-only files (e.g. the bundled OpenSSL dylibs), so a
sudo-less 'xattr -dr com.apple.quarantine' fails with Permission denied for
some users. Standardize on 'sudo xattr -dr com.apple.quarantine' in the
README, ROADMAP, the release.yml SIGNING STATUS comment, and the release
notes; AGENTS.md now requires release notes to carry the macOS first-launch
instructions until a Developer ID cert is secured.
Users at the top table previously saw no mention of the unsigned-app
warnings; the details lived ~200 lines down under Getting Started. Add a
callout right under the Download table linking to the macOS first-launch
instructions (right-click Open / xattr workaround).