Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6d1bbb0fa3 | ||
|
|
df3e3957a2 | ||
|
|
5bc31a14a6 | ||
|
|
38de9b4da1 |
@@ -3,12 +3,15 @@ name: Bug report
|
|||||||
about: Report a bug or unexpected behavior in Gridline so we can fix it
|
about: Report a bug or unexpected behavior in Gridline so we can fix it
|
||||||
title: "[Bug]: "
|
title: "[Bug]: "
|
||||||
labels: ["bug"]
|
labels: ["bug"]
|
||||||
assignees: ''
|
assignees: ['AdrianBonpin']
|
||||||
---
|
---
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Thank you for reporting a bug! A complete report is the fastest way to get it fixed.
|
Thank you for reporting a bug! A complete report is the fastest way to get it fixed.
|
||||||
Please fill in every section you can — the environment table alone often closes issues.
|
Please fill in every section you can — the environment table alone often closes issues.
|
||||||
|
|
||||||
|
Is this a request for something new instead? Use the
|
||||||
|
Feature request or Improvement templates — those get triaged faster.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
## Before you submit
|
## Before you submit
|
||||||
@@ -58,7 +61,9 @@ assignees: ''
|
|||||||
|
|
||||||
<!--
|
<!--
|
||||||
Paste the error text, console output, or a crash report here.
|
Paste the error text, console output, or a crash report here.
|
||||||
On macOS: Console.app → Gridline. On Windows: Event Viewer → Application.
|
- macOS: Console.app → Gridline
|
||||||
|
- Windows: Event Viewer → Application
|
||||||
|
- Linux: run Gridline from a terminal and capture stdout/stderr (or check the app-data dir, e.g. ~/.local/share/…)
|
||||||
Remove anything that looks like a host/user/password/token before pasting.
|
Remove anything that looks like a host/user/password/token before pasting.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
@@ -76,6 +81,10 @@ assignees: ''
|
|||||||
|
|
||||||
<!-- Anything that gets you unstuck today (e.g. "works when I connect without SSH", "restarting the app fixes it"). Even a partial workaround helps others. -->
|
<!-- Anything that gets you unstuck today (e.g. "works when I connect without SSH", "restarting the app fixes it"). Even a partial workaround helps others. -->
|
||||||
|
|
||||||
|
## Related issues / PRs
|
||||||
|
|
||||||
|
<!-- Links to related issues, PRs, or roadmap items. -->
|
||||||
|
|
||||||
## Additional context
|
## Additional context
|
||||||
|
|
||||||
<!-- Anything else: does it reproduce with a fresh connection or only one specific database? Did it work in an earlier version? Frequency (always / sometimes)? -->
|
<!-- Anything else: does it reproduce with a fresh connection or only one specific database? Did it work in an earlier version? Frequency (always / sometimes)? Does it happen on the demo SQLite database? -->
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
---
|
||||||
|
name: Feature request
|
||||||
|
about: Suggest a new feature or capability for Gridline
|
||||||
|
title: "[Feature]: "
|
||||||
|
labels: ["enhancement"]
|
||||||
|
assignees: ['AdrianBonpin']
|
||||||
|
---
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Thank you for suggesting a feature! 🎉
|
||||||
|
Before you submit, check that it isn't already shipped or planned:
|
||||||
|
- README feature list: https://github.com/AdrianBonpin/gridline#features
|
||||||
|
- Roadmap: https://github.com/AdrianBonpin/gridline/blob/prod/ROADMAP.md
|
||||||
|
-->
|
||||||
|
|
||||||
|
## Before you submit
|
||||||
|
|
||||||
|
- [ ] I searched [existing issues](https://github.com/AdrianBonpin/gridline/issues?q=is%3Aissue) and this isn't a duplicate
|
||||||
|
- [ ] I checked the [Roadmap](https://github.com/AdrianBonpin/gridline/blob/prod/ROADMAP.md) — it isn't already planned
|
||||||
|
- [ ] I checked the [README feature list](https://github.com/AdrianBonpin/gridline#features) — it isn't already shipped
|
||||||
|
- [ ] I will **not** paste secrets — redact any credentials as `<REDACTED>`
|
||||||
|
|
||||||
|
## Problem
|
||||||
|
|
||||||
|
<!-- What can't you do today? Describe the workflow that's blocked or painful. The problem matters more than the solution you have in mind. -->
|
||||||
|
|
||||||
|
## Proposed solution
|
||||||
|
|
||||||
|
<!-- How should it work? High-level description. Consider where it lives in the UI (home screen, DB viewer, object explorer, query editor, tools). Mockups welcome — see below. -->
|
||||||
|
|
||||||
|
## Example scenario
|
||||||
|
|
||||||
|
<!-- A concrete walkthrough: "I want to compare row counts across three production schemas, so I open … and …" -->
|
||||||
|
|
||||||
|
## Alternatives considered
|
||||||
|
|
||||||
|
<!-- What you do today instead: other tools, manual SQL, workarounds. This helps us judge how much it's really needed. -->
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
<!-- Rough size so we can prioritize. -->
|
||||||
|
|
||||||
|
- [ ] Small — UI polish / single command
|
||||||
|
- [ ] Medium — a new view or feature area
|
||||||
|
- [ ] Large — new DB type, new driver, multi-week effort
|
||||||
|
|
||||||
|
## Mockups / screenshots
|
||||||
|
|
||||||
|
<!-- Drag & drop sketches or screenshots; ASCII art is fine. If referencing how another tool does it, name the tool (TablePlus, Beekeeper Studio, DB Pro, …). -->
|
||||||
|
|
||||||
|
## Related
|
||||||
|
|
||||||
|
<!-- Roadmap items, related issues/PRs, competitor references, docs links. -->
|
||||||
|
|
||||||
|
## Priority
|
||||||
|
|
||||||
|
<!-- Optional: how important is this to you? -->
|
||||||
|
|
||||||
|
- [ ] Nice to have
|
||||||
|
- [ ] Important — would save me time every day
|
||||||
|
- [ ] Critical — blocks my workflow without it
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
---
|
||||||
|
name: Improvement
|
||||||
|
about: Suggest a refinement or UX polish for an existing Gridline feature
|
||||||
|
title: "[Improvement]: "
|
||||||
|
labels: ["enhancement"]
|
||||||
|
assignees: ['AdrianBonpin']
|
||||||
|
---
|
||||||
|
|
||||||
|
<!--
|
||||||
|
For refinements to something that already exists — not brand-new features
|
||||||
|
(use the Feature request template for those) and not bugs (use Bug report).
|
||||||
|
Examples: a confusing label, a clunky interaction, a missing keyboard shortcut,
|
||||||
|
faster scroll, better default, accessibility fix.
|
||||||
|
-->
|
||||||
|
|
||||||
|
## Before you submit
|
||||||
|
|
||||||
|
- [ ] I searched [existing issues](https://github.com/AdrianBonpin/gridline/issues?q=is%3Aissue) and this isn't a duplicate
|
||||||
|
- [ ] This targets an **existing** feature (not a new one)
|
||||||
|
- [ ] I will **not** paste secrets — redact any credentials as `<REDACTED>`
|
||||||
|
|
||||||
|
## What's lacking today?
|
||||||
|
|
||||||
|
<!-- The pain point in the current behavior. Point at the exact screen/flow. -->
|
||||||
|
|
||||||
|
## Suggested change
|
||||||
|
|
||||||
|
<!-- The refinement. What should it look like / behave like? Specific beats vague. -->
|
||||||
|
|
||||||
|
## Why it matters
|
||||||
|
|
||||||
|
<!-- Who benefits and how (time saved, fewer misclicks, consistency, accessibility, …). -->
|
||||||
|
|
||||||
|
## Screenshots / recordings
|
||||||
|
|
||||||
|
<!-- Current behavior, plus any mock of the desired behavior. -->
|
||||||
|
|
||||||
|
## Related
|
||||||
|
|
||||||
|
<!-- Links to related issues/PRs. -->
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
# Template chooser settings — see
|
||||||
|
# https://docs.github.com/communities/using-templates-to-encourage-useful-issues-and-pull-requests/configuring-issue-templates-for-your-repository
|
||||||
|
blank_issues_enabled: true
|
||||||
|
contact_links:
|
||||||
|
- name: Gridline Roadmap
|
||||||
|
url: https://github.com/AdrianBonpin/gridline/blob/prod/ROADMAP.md
|
||||||
|
about: Check what's already planned before requesting a feature
|
||||||
|
- name: README — feature list
|
||||||
|
url: https://github.com/AdrianBonpin/gridline#features
|
||||||
|
about: Confirm a feature isn't already shipped
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
name: Auto-assign new issues
|
||||||
|
|
||||||
|
# Assigns every newly opened issue to the repository owner so nothing
|
||||||
|
# lands in the inbox untriaged. The `issues` event only fires in the base
|
||||||
|
# repository (not forks) and this job never checks out code — it only
|
||||||
|
# talks to the GitHub API, so there is no fork/pwn-request exposure.
|
||||||
|
|
||||||
|
on:
|
||||||
|
issues:
|
||||||
|
types: [opened]
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
issues: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
auto-assign:
|
||||||
|
name: Assign issue to repo owner
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Assign to ${{ github.repository_owner }}
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
ISSUE_URL: ${{ github.event.issue.html_url }}
|
||||||
|
OWNER: ${{ github.repository_owner }}
|
||||||
|
run: gh issue edit "$ISSUE_URL" --add-assignee "$OWNER"
|
||||||
@@ -11,7 +11,7 @@ name: Release
|
|||||||
# (bundle.macOS.signingIdentity: "-", hardenedRuntime: false). That replaces
|
# (bundle.macOS.signingIdentity: "-", hardenedRuntime: false). That replaces
|
||||||
# the linker-only signature that made macOS report the app as "damaged" and
|
# the linker-only signature that made macOS report the app as "damaged" and
|
||||||
# refuse Finder launches — users now get the standard "Open Anyway" prompt
|
# refuse Finder launches — users now get the standard "Open Anyway" prompt
|
||||||
# (or run `xattr -dr com.apple.quarantine` for the damaged-error case, see
|
# (or run `sudo xattr -dr com.apple.quarantine` for the damaged-error case, see
|
||||||
# README). Still NOT Developer-ID signed / notarized (no certs yet).
|
# README). Still NOT Developer-ID signed / notarized (no certs yet).
|
||||||
# tauri-action automatically signs + notarizes when the signing secrets are
|
# tauri-action automatically signs + notarizes when the signing secrets are
|
||||||
# present, so the moment we add APPLE_CERTIFICATE / APPLE_API_KEY /
|
# present, so the moment we add APPLE_CERTIFICATE / APPLE_API_KEY /
|
||||||
|
|||||||
@@ -168,6 +168,7 @@ Cut a release by tagging the **`prod`** branch once the PR is merged — `git ta
|
|||||||
- `src/lib/version.test.ts` and `src/lib/docs-coverage.test.ts` if they assert the version
|
- `src/lib/version.test.ts` and `src/lib/docs-coverage.test.ts` if they assert the version
|
||||||
- **README download links are static (versioned)** — both download tables (top **Download** section + **Which file should I download?**) link directly to the release-tag assets (`releases/download/v0.7.9/<file>`). tauri-action uses default versioned asset names (`Gridline_<ver>_aarch64.dmg`, `Gridline-<ver>-1.x86_64.rpm`, etc.) — update BOTH tables to the new names on every release (see the MAINTENANCE comment in README.md).
|
- **README download links are static (versioned)** — both download tables (top **Download** section + **Which file should I download?**) link directly to the release-tag assets (`releases/download/v0.7.9/<file>`). tauri-action uses default versioned asset names (`Gridline_<ver>_aarch64.dmg`, `Gridline-<ver>-1.x86_64.rpm`, etc.) — update BOTH tables to the new names on every release (see the MAINTENANCE comment in README.md).
|
||||||
- **Bundled pg tools:** `tauri.conf.json` `bundle.resources` lists `resources/pg_tools/*`; the `release.yml` matrix builds/downloads + checksum-verifies the static binaries before the Tauri build step.
|
- **Bundled pg tools:** `tauri.conf.json` `bundle.resources` lists `resources/pg_tools/*`; the `release.yml` matrix builds/downloads + checksum-verifies the static binaries before the Tauri build step.
|
||||||
|
- **Release notes must include the macOS first-launch instructions** — until a Developer ID signing certificate is secured, releases are ad-hoc signed only (not notarized), so every macOS user hits a Gatekeeper prompt. The draft release body must include: right-click → **Open** → **Open** (or System Settings → Privacy & Security → **Open Anyway**) for "developer cannot be verified", and `sudo xattr -dr com.apple.quarantine /Applications/Gridline.app` (with the re-run-after-every-upgrade note) for "damaged and can't be opened".
|
||||||
|
|
||||||
### Adding a Tauri Command
|
### Adding a Tauri Command
|
||||||
|
|
||||||
@@ -247,7 +248,7 @@ Planned work is prioritized in the [Project Roadmap](./ROADMAP.md) (source of tr
|
|||||||
| Move-to-folder bulk action | ✅ | Selection toolbar → Move to Folder dialog (folder picker, move confirmed via dialog) |
|
| Move-to-folder bulk action | ✅ | Selection toolbar → Move to Folder dialog (folder picker, move confirmed via dialog) |
|
||||||
| Favorites / Recent connections | ✅ | Star toggle in the connection card ⋮ menu (persisted `favorite` flag); Recent connections row (top 8 via `getRecentConnections`) |
|
| Favorites / Recent connections | ✅ | Star toggle in the connection card ⋮ menu (persisted `favorite` flag); Recent connections row (top 8 via `getRecentConnections`) |
|
||||||
| Connection status indicator on cards | ✅ | Kebab menu → Test connection with inline idle/checking/online/offline result, on-demand via keychain + `testConnection`. **Reports real `server_version` + `latency_ms`** (PG/MySQL/SQLite queries + connect timing in the Rust backend); shows `Online · 16.4 · 42ms` or the error, re-check debounced 2s |
|
| Connection status indicator on cards | ✅ | Kebab menu → Test connection with inline idle/checking/online/offline result, on-demand via keychain + `testConnection`. **Reports real `server_version` + `latency_ms`** (PG/MySQL/SQLite queries + connect timing in the Rust backend); shows `Online · 16.4 · 42ms` or the error, re-check debounced 2s |
|
||||||
| Connection card actions menu (⋮) | ✅ | Kebab dropdown: Favorite toggle, Test connection (inline status), Manage submenu (Edit… / Duplicate / Delete…) |
|
| Connection card actions menu (⋮) | ✅ | Kebab dropdown: Favorite toggle, Test connection (inline status), **Copy connection URL** (with password) / **Copy connection URL (no password)** (builds a `postgresql://`/`mysql://`/`sqlite://`/`redis://` string from the saved connection, percent-encoded, sslmode appended for PG; password fetched from keychain on demand), Manage submenu (Edit… / Duplicate / Delete…) |
|
||||||
|
|
||||||
### Database Viewer
|
### Database Viewer
|
||||||
| Feature | Status | Details |
|
| Feature | Status | Details |
|
||||||
|
|||||||
@@ -30,21 +30,21 @@
|
|||||||
|
|
||||||
## Download
|
## Download
|
||||||
|
|
||||||
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.7.9**):
|
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.7.10**):
|
||||||
|
|
||||||
| OS | Architecture | Download |
|
| OS | Architecture | Download |
|
||||||
| :--------------------------- | :--------------------------- | :------------------------------------------------------------------------------------------------------------------------------- |
|
| :--------------------------- | :--------------------------- | :------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| **macOS** | Apple Silicon (M1/M2/M3/M4…) | [Gridline_0.7.9_aarch64.dmg](https://github.com/AdrianBonpin/gridline/releases/download/v0.7.9/Gridline_0.7.9_aarch64.dmg) |
|
| **macOS** | Apple Silicon (M1/M2/M3/M4…) | [Gridline_0.7.10_aarch64.dmg](https://github.com/AdrianBonpin/gridline/releases/download/v0.7.10/Gridline_0.7.10_aarch64.dmg) |
|
||||||
| **macOS** | Intel | [Gridline_0.7.9_x64.dmg](https://github.com/AdrianBonpin/gridline/releases/download/v0.7.9/Gridline_0.7.9_x64.dmg) |
|
| **macOS** | Intel | [Gridline_0.7.10_x64.dmg](https://github.com/AdrianBonpin/gridline/releases/download/v0.7.10/Gridline_0.7.10_x64.dmg) |
|
||||||
| **Windows** | x64 | [Gridline_0.7.9_x64-setup.exe](https://github.com/AdrianBonpin/gridline/releases/download/v0.7.9/Gridline_0.7.9_x64-setup.exe) |
|
| **Windows** | x64 | [Gridline_0.7.10_x64-setup.exe](https://github.com/AdrianBonpin/gridline/releases/download/v0.7.10/Gridline_0.7.10_x64-setup.exe) |
|
||||||
| **Debian / Ubuntu** | amd64 | [Gridline_0.7.9_amd64.deb](https://github.com/AdrianBonpin/gridline/releases/download/v0.7.9/Gridline_0.7.9_amd64.deb) |
|
| **Debian / Ubuntu** | amd64 | [Gridline_0.7.10_amd64.deb](https://github.com/AdrianBonpin/gridline/releases/download/v0.7.10/Gridline_0.7.10_amd64.deb) |
|
||||||
| **Fedora / RHEL / openSUSE** | x86_64 | [Gridline-0.7.9-1.x86_64.rpm](https://github.com/AdrianBonpin/gridline/releases/download/v0.7.9/Gridline-0.7.9-1.x86_64.rpm) |
|
| **Fedora / RHEL / openSUSE** | x86_64 | [Gridline-0.7.10-1.x86_64.rpm](https://github.com/AdrianBonpin/gridline/releases/download/v0.7.10/Gridline-0.7.10-1.x86_64.rpm) |
|
||||||
| **Other Linux** | amd64 | [Gridline_0.7.9_amd64.AppImage](https://github.com/AdrianBonpin/gridline/releases/download/v0.7.9/Gridline_0.7.9_amd64.AppImage) |
|
| **Other Linux** | amd64 | [Gridline_0.7.10_amd64.AppImage](https://github.com/AdrianBonpin/gridline/releases/download/v0.7.10/Gridline_0.7.10_amd64.AppImage) |
|
||||||
|
|
||||||
> Not sure if your Mac is Intel or Apple Silicon? See [Which file should I download?](#which-file-should-i-download) below. All installers are **unsigned** — see the [notes](#which-file-should-i-download) on first-launch warnings.
|
> **macOS first launch:** macOS may say *"Gridline is damaged and can't be opened"* or *"the developer cannot be verified"* — this is expected; the app isn't Developer-ID signed/notarized yet. See the [macOS first-launch instructions](#installers-are-unsigned-for-now) (right-click → Open, or the one-time `xattr` fix). Not sure if your Mac is Intel or Apple Silicon? See [Which file should I download?](#which-file-should-i-download) below.
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
MAINTENANCE: These links are STATIC (versioned) — they point at the v0.7.9
|
MAINTENANCE: These links are STATIC (versioned) — they point at the v0.7.10
|
||||||
release assets, not at a moving "latest" target. On every new release,
|
release assets, not at a moving "latest" target. On every new release,
|
||||||
update BOTH tables here (Download + Which file should I download?) to the
|
update BOTH tables here (Download + Which file should I download?) to the
|
||||||
new version's asset names, which are tauri-action's default naming:
|
new version's asset names, which are tauri-action's default naming:
|
||||||
@@ -89,6 +89,8 @@ Gridline is built for developers and small teams who manage multiple database en
|
|||||||
<details>
|
<details>
|
||||||
<summary>View the full changelog</summary>
|
<summary>View the full changelog</summary>
|
||||||
|
|
||||||
|
- **v0.7.10** — **Copy connection URL**: from a connection card's ⋮ menu, copy the connection string for use in env vars / other tools — **Copy connection URL** (includes the password, fetched from the OS keychain on demand) and **Copy connection URL (no password)** (safe to share). Builds `postgresql://` / `mysql://` / `sqlite://` / `redis://` strings with percent-encoded credentials and the PG `sslmode` appended; version bump.
|
||||||
|
|
||||||
- **v0.7.9** — **macOS install/launch fixes**: the app's local store now opens under the OS app-data directory (a cwd-relative `gridline.db` made Finder-launched copies silently exit with a Rust panic before the UI started); macOS bundles are **ad-hoc signed at build time** (`signingIdentity "-"`), replacing the Xcode linker-only signature that macOS treated as unsigned ("damaged and can't be opened", silent Finder refusals) — users now get a standard one-time Gatekeeper prompt (see the [install notes](#installers-are-unsigned-for-now), incl. the `xattr` workaround); **OpenSSL is vendored** into the `ssh2` build so the release no longer depends on a Homebrew OpenSSL path; version bump.
|
- **v0.7.9** — **macOS install/launch fixes**: the app's local store now opens under the OS app-data directory (a cwd-relative `gridline.db` made Finder-launched copies silently exit with a Rust panic before the UI started); macOS bundles are **ad-hoc signed at build time** (`signingIdentity "-"`), replacing the Xcode linker-only signature that macOS treated as unsigned ("damaged and can't be opened", silent Finder refusals) — users now get a standard one-time Gatekeeper prompt (see the [install notes](#installers-are-unsigned-for-now), incl. the `xattr` workaround); **OpenSSL is vendored** into the `ssh2` build so the release no longer depends on a Homebrew OpenSSL path; version bump.
|
||||||
|
|
||||||
- **v0.7.8** — **SQLite `.dump` backup/restore** plus **backup / restore / sync for MySQL & SQLite** (mysqldump-based, system-first); **Excel (.xlsx) export** in the grid toolbar alongside CSV/JSON/SQL/Markdown; **cancel long-running queries** per connection; **settings export/import** (JSON); **Windows/Linux title bar fix** (macOS-only overlay drag strip); **SQLite table editor** (Create/Edit Table with SQLite-aware type mapping); version bump.
|
- **v0.7.8** — **SQLite `.dump` backup/restore** plus **backup / restore / sync for MySQL & SQLite** (mysqldump-based, system-first); **Excel (.xlsx) export** in the grid toolbar alongside CSV/JSON/SQL/Markdown; **cancel long-running queries** per connection; **settings export/import** (JSON); **Windows/Linux title bar fix** (macOS-only overlay drag strip); **SQLite table editor** (Create/Edit Table with SQLite-aware type mapping); version bump.
|
||||||
@@ -301,10 +303,10 @@ Gridline is currently distributed **unsigned** — it doesn't pay for code-signi
|
|||||||
- **macOS — "Gridline is damaged and can't be opened":** remove the quarantine flag macOS attaches to downloaded apps, then launch normally:
|
- **macOS — "Gridline is damaged and can't be opened":** remove the quarantine flag macOS attaches to downloaded apps, then launch normally:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
xattr -dr com.apple.quarantine /Applications/Gridline.app
|
sudo xattr -dr com.apple.quarantine /Applications/Gridline.app
|
||||||
```
|
```
|
||||||
|
|
||||||
You need to re-run this after **every upgrade** (each freshly-downloaded copy gets re-quarantined).
|
(`sudo` is required — some files inside the bundle are read-only, so removing the flag needs admin rights.) Re-run after **every upgrade** — each freshly-downloaded copy gets re-quarantined.
|
||||||
- **Windows:** on the SmartScreen prompt, click **More info** → **Run anyway**.
|
- **Windows:** on the SmartScreen prompt, click **More info** → **Run anyway**.
|
||||||
- **Linux:** no warning — install and run normally.
|
- **Linux:** no warning — install and run normally.
|
||||||
|
|
||||||
@@ -312,16 +314,16 @@ Code signing **will be added in the future** (Apple Developer Program + a Window
|
|||||||
|
|
||||||
#### Which file should I download?
|
#### Which file should I download?
|
||||||
|
|
||||||
Each release contains **one file per platform** — you only need the one that matches your computer. The links below point at the current release (**v0.7.9**):
|
Each release contains **one file per platform** — you only need the one that matches your computer. The links below point at the current release (**v0.7.10**):
|
||||||
|
|
||||||
| Your system | Download this | Notes |
|
| Your system | Download this | Notes |
|
||||||
| :------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------- |
|
| :------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------- |
|
||||||
| macOS **Apple Silicon** (M1/M2/M3/M4…) | [Gridline_0.7.9_aarch64.dmg](https://github.com/AdrianBonpin/gridline/releases/download/v0.7.9/Gridline_0.7.9_aarch64.dmg) | `aarch64` = Apple's own chip |
|
| macOS **Apple Silicon** (M1/M2/M3/M4…) | [Gridline_0.7.10_aarch64.dmg](https://github.com/AdrianBonpin/gridline/releases/download/v0.7.10/Gridline_0.7.10_aarch64.dmg) | `aarch64` = Apple's own chip |
|
||||||
| macOS **Intel** | [Gridline_0.7.9_x64.dmg](https://github.com/AdrianBonpin/gridline/releases/download/v0.7.9/Gridline_0.7.9_x64.dmg) | `x64` = Intel/AMD |
|
| macOS **Intel** | [Gridline_0.7.10_x64.dmg](https://github.com/AdrianBonpin/gridline/releases/download/v0.7.10/Gridline_0.7.10_x64.dmg) | `x64` = Intel/AMD |
|
||||||
| **Windows** (most PCs) | [Gridline_0.7.9_x64-setup.exe](https://github.com/AdrianBonpin/gridline/releases/download/v0.7.9/Gridline_0.7.9_x64-setup.exe) | The `.msi` is an alternate installer (for enterprises/IT admins) |
|
| **Windows** (most PCs) | [Gridline_0.7.10_x64-setup.exe](https://github.com/AdrianBonpin/gridline/releases/download/v0.7.10/Gridline_0.7.10_x64-setup.exe) | The `.msi` is an alternate installer (for enterprises/IT admins) |
|
||||||
| **Debian / Ubuntu** | [Gridline_0.7.9_amd64.deb](https://github.com/AdrianBonpin/gridline/releases/download/v0.7.9/Gridline_0.7.9_amd64.deb) | Install: `sudo apt install ./Gridline_0.7.9_amd64.deb` |
|
| **Debian / Ubuntu** | [Gridline_0.7.10_amd64.deb](https://github.com/AdrianBonpin/gridline/releases/download/v0.7.10/Gridline_0.7.10_amd64.deb) | Install: `sudo apt install ./Gridline_0.7.10_amd64.deb` |
|
||||||
| **Fedora / RHEL / openSUSE** | [Gridline-0.7.9-1.x86_64.rpm](https://github.com/AdrianBonpin/gridline/releases/download/v0.7.9/Gridline-0.7.9-1.x86_64.rpm) | Install: `sudo dnf install Gridline-0.7.9-1.x86_64.rpm` |
|
| **Fedora / RHEL / openSUSE** | [Gridline-0.7.10-1.x86_64.rpm](https://github.com/AdrianBonpin/gridline/releases/download/v0.7.10/Gridline-0.7.10-1.x86_64.rpm) | Install: `sudo dnf install Gridline-0.7.10-1.x86_64.rpm` |
|
||||||
| **Any other Linux** | [Gridline_0.7.9_amd64.AppImage](https://github.com/AdrianBonpin/gridline/releases/download/v0.7.9/Gridline_0.7.9_amd64.AppImage) | Works on every distro: `chmod +x` the file, then double-click it |
|
| **Any other Linux** | [Gridline_0.7.10_amd64.AppImage](https://github.com/AdrianBonpin/gridline/releases/download/v0.7.10/Gridline_0.7.10_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.
|
||||||
|
|
||||||
@@ -331,8 +333,8 @@ Cutting a release is one command — CI builds everything. **Releases are cut fr
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
git checkout prod && git pull
|
git checkout prod && git pull
|
||||||
git tag v0.7.9
|
git tag v0.7.10
|
||||||
git push origin v0.7.9
|
git push origin v0.7.10
|
||||||
```
|
```
|
||||||
|
|
||||||
GitHub Actions (`.github/workflows/release.yml`) builds installers for **Apple Silicon, Intel Macs, Windows, and Linux**, then opens a **draft release** on the [Releases](https://github.com/adrianbonpin/gridline/releases) page — review it and hit **Publish release**.
|
GitHub Actions (`.github/workflows/release.yml`) builds installers for **Apple Silicon, Intel Macs, Windows, and Linux**, then opens a **draft release** on the [Releases](https://github.com/adrianbonpin/gridline/releases) page — review it and hit **Publish release**.
|
||||||
@@ -416,7 +418,7 @@ gridline/
|
|||||||
|
|
||||||
## Roadmap
|
## Roadmap
|
||||||
|
|
||||||
The full plan — next-up (v0.7.9), queue, and shipped history — lives in **[ROADMAP.md](./ROADMAP.md)**.
|
The full plan — next-up (v0.7.10), queue, and shipped history — lives in **[ROADMAP.md](./ROADMAP.md)**.
|
||||||
|
|
||||||
Highlights of what's next:
|
Highlights of what's next:
|
||||||
|
|
||||||
@@ -452,7 +454,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.
|
||||||
|
|
||||||
- Report a bug via the **[issue template](.github/ISSUE_TEMPLATE/bug_report.md)** — it walks you through environment details (OS, Gridline version, DB type/version, connection method) so we can reproduce issues quickly. Opening a [new issue](https://github.com/AdrianBonpin/gridline/issues/new) pre-fills the template automatically.
|
- 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.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
+6
-1
@@ -6,10 +6,15 @@ This file is the **source of truth** for what Gridline is building. [AGENTS.md](
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## ✅ Shipped (0.7.10)
|
||||||
|
|
||||||
|
- **Copy connection URL** — from a connection card's ⋮ menu, copy the connection string for use in env vars / other tools: **Copy connection URL** (includes the password, fetched from the OS keychain on demand) and **Copy connection URL (no password)** (safe to share). Builds `postgresql://` / `mysql://` / `sqlite://` / `redis://` strings with percent-encoded credentials and the PG `sslmode` appended.
|
||||||
|
- **Version bump** 0.7.9 → **0.7.10**.
|
||||||
|
|
||||||
## ✅ Shipped (0.7.9)
|
## ✅ Shipped (0.7.9)
|
||||||
|
|
||||||
- **macOS Finder launch fix** — the local store opens under the OS app-data directory instead of a cwd-relative `gridline.db` (Finder/LaunchServices launches run with cwd `/`, so the old path made the app silently exit with a Rust panic before the UI started).
|
- **macOS Finder launch fix** — the local store opens under the OS app-data directory instead of a cwd-relative `gridline.db` (Finder/LaunchServices launches run with cwd `/`, so the old path made the app silently exit with a Rust panic before the UI started).
|
||||||
- **macOS ad-hoc code signing** — bundles are signed at build time (`bundle.macOS.signingIdentity "-"`, hardened runtime off), replacing the Xcode linker-only signature that macOS treated as unsigned ("damaged and can't be opened", silent Finder refusal). Users still get a one-time Gatekeeper prompt; README documents the `xattr -dr com.apple.quarantine` workaround.
|
- **macOS ad-hoc code signing** — bundles are signed at build time (`bundle.macOS.signingIdentity "-"`, hardened runtime off), replacing the Xcode linker-only signature that macOS treated as unsigned ("damaged and can't be opened", silent Finder refusal). Users still get a one-time Gatekeeper prompt; README documents the `sudo xattr -dr com.apple.quarantine` workaround.
|
||||||
- **Vendored OpenSSL for `ssh2`** — the release binary no longer links an absolute Homebrew `/opt/homebrew/.../libssl.3.dylib` path (dyld aborted on machines without it).
|
- **Vendored OpenSSL for `ssh2`** — the release binary no longer links an absolute Homebrew `/opt/homebrew/.../libssl.3.dylib` path (dyld aborted on machines without it).
|
||||||
- **Version bump** 0.7.8 → **0.7.9**.
|
- **Version bump** 0.7.8 → **0.7.9**.
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "gridline",
|
"name": "gridline",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.7.9",
|
"version": "0.7.10",
|
||||||
"description": "An open-source, high-performance database GUI client for PostgreSQL and beyond",
|
"description": "An open-source, high-performance database GUI client for PostgreSQL and beyond",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "gridline"
|
name = "gridline"
|
||||||
version = "0.7.9"
|
version = "0.7.10"
|
||||||
description = "An open-source, high-performance database GUI client for PostgreSQL and beyond"
|
description = "An open-source, high-performance database GUI client for PostgreSQL and beyond"
|
||||||
authors = ["you"]
|
authors = ["you"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://schema.tauri.app/config/2",
|
"$schema": "https://schema.tauri.app/config/2",
|
||||||
"productName": "Gridline",
|
"productName": "Gridline",
|
||||||
"version": "0.7.9",
|
"version": "0.7.10",
|
||||||
"identifier": "com.adrianbonpin.gridline",
|
"identifier": "com.adrianbonpin.gridline",
|
||||||
"build": {
|
"build": {
|
||||||
"beforeDevCommand": "bun run dev",
|
"beforeDevCommand": "bun run dev",
|
||||||
|
|||||||
@@ -200,4 +200,41 @@ describe("ConnectionCardMenu", () => {
|
|||||||
await userEvent.keyboard("{Escape}");
|
await userEvent.keyboard("{Escape}");
|
||||||
expect(screen.queryByText("Manage")).not.toBeInTheDocument();
|
expect(screen.queryByText("Manage")).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("Copy connection URL fetches the password and copies the full URL", async () => {
|
||||||
|
vi.mocked(commands.getConnectionPassword).mockResolvedValue("s3cret");
|
||||||
|
const writeText = vi.fn().mockResolvedValue(undefined);
|
||||||
|
Object.defineProperty(navigator, "clipboard", {
|
||||||
|
value: { writeText },
|
||||||
|
configurable: true,
|
||||||
|
});
|
||||||
|
renderMenu();
|
||||||
|
await openMenu();
|
||||||
|
|
||||||
|
await userEvent.click(screen.getByText("Copy connection URL"));
|
||||||
|
|
||||||
|
expect(commands.getConnectionPassword).toHaveBeenCalledWith("c1");
|
||||||
|
expect(writeText).toHaveBeenCalledWith(
|
||||||
|
"postgresql://:s3cret@prod.example.com:5432",
|
||||||
|
);
|
||||||
|
expect(screen.queryByText("Manage")).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Copy connection URL (no password) does not fetch the password", async () => {
|
||||||
|
const writeText = vi.fn().mockResolvedValue(undefined);
|
||||||
|
Object.defineProperty(navigator, "clipboard", {
|
||||||
|
value: { writeText },
|
||||||
|
configurable: true,
|
||||||
|
});
|
||||||
|
renderMenu();
|
||||||
|
await openMenu();
|
||||||
|
|
||||||
|
await userEvent.click(screen.getByText("Copy connection URL (no password)"));
|
||||||
|
|
||||||
|
expect(commands.getConnectionPassword).not.toHaveBeenCalled();
|
||||||
|
expect(writeText).toHaveBeenCalledWith(
|
||||||
|
"postgresql://prod.example.com:5432",
|
||||||
|
);
|
||||||
|
expect(screen.queryByText("Manage")).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
@@ -5,6 +5,7 @@ import {
|
|||||||
CircleCheck,
|
CircleCheck,
|
||||||
CircleX,
|
CircleX,
|
||||||
Copy,
|
Copy,
|
||||||
|
EyeOff,
|
||||||
Loader2,
|
Loader2,
|
||||||
MoreVertical,
|
MoreVertical,
|
||||||
Pencil,
|
Pencil,
|
||||||
@@ -14,6 +15,7 @@ import {
|
|||||||
import type { Connection } from "../../lib/types";
|
import type { Connection } from "../../lib/types";
|
||||||
import { useConnectionStore } from "../../stores/connectionStore";
|
import { useConnectionStore } from "../../stores/connectionStore";
|
||||||
import { buildConfigFromConnection } from "./ConnectionCard";
|
import { buildConfigFromConnection } from "./ConnectionCard";
|
||||||
|
import { buildConnectionUrl } from "../../lib/connectionString";
|
||||||
import { useConnectionStatus } from "./useConnectionStatus";
|
import { useConnectionStatus } from "./useConnectionStatus";
|
||||||
|
|
||||||
interface ConnectionCardMenuProps {
|
interface ConnectionCardMenuProps {
|
||||||
@@ -82,6 +84,19 @@ export function ConnectionCardMenu({
|
|||||||
setManageOpen(false);
|
setManageOpen(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const copyUrl = async (withPassword: boolean) => {
|
||||||
|
let password: string | null = null;
|
||||||
|
if (withPassword) {
|
||||||
|
password = await useConnectionStore
|
||||||
|
.getState()
|
||||||
|
.getConnectionPassword(connection.id)
|
||||||
|
.catch(() => null);
|
||||||
|
}
|
||||||
|
const url = buildConnectionUrl(connection, password);
|
||||||
|
await navigator.clipboard.writeText(url).catch(() => {});
|
||||||
|
close();
|
||||||
|
};
|
||||||
|
|
||||||
const statusLabel =
|
const statusLabel =
|
||||||
state === "checking"
|
state === "checking"
|
||||||
? "Testing…"
|
? "Testing…"
|
||||||
@@ -169,6 +184,28 @@ export function ConnectionCardMenu({
|
|||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => {
|
||||||
|
void copyUrl(true);
|
||||||
|
}}
|
||||||
|
className={menuItemClass}
|
||||||
|
>
|
||||||
|
<Copy size={14} className="text-text-muted" />
|
||||||
|
<span className="truncate">Copy connection URL</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => {
|
||||||
|
void copyUrl(false);
|
||||||
|
}}
|
||||||
|
className={menuItemClass}
|
||||||
|
>
|
||||||
|
<EyeOff size={14} className="text-text-muted" />
|
||||||
|
<span className="truncate">Copy connection URL (no password)</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setManageOpen((m) => !m)}
|
onClick={() => setManageOpen((m) => !m)}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { describe, it, expect } from "vitest";
|
import { describe, it, expect } from "vitest";
|
||||||
import tauriConf from "../../src-tauri/tauri.conf.json";
|
import tauriConf from "../../src-tauri/tauri.conf.json";
|
||||||
|
|
||||||
describe("tauri bundle config (v0.7.9)", () => {
|
describe("tauri bundle config (v0.7.10)", () => {
|
||||||
it("declares bundled pg_tools resources", () => {
|
it("declares bundled pg_tools resources", () => {
|
||||||
expect(tauriConf.bundle.resources).toContain("resources/pg_tools/*");
|
expect(tauriConf.bundle.resources).toContain("resources/pg_tools/*");
|
||||||
});
|
});
|
||||||
it("version is 0.7.9", () => {
|
it("version is 0.7.10", () => {
|
||||||
expect(tauriConf.version).toBe("0.7.9");
|
expect(tauriConf.version).toBe("0.7.10");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -1,5 +1,24 @@
|
|||||||
import { describe, it, expect } from "vitest";
|
import { describe, it, expect } from "vitest";
|
||||||
import { parseConnectionString, looksLikeConnectionString, detectProviderFromHost } from "./connectionString";
|
import { parseConnectionString, looksLikeConnectionString, detectProviderFromHost, buildConnectionUrl } from "./connectionString";
|
||||||
|
import type { Connection } from "./types";
|
||||||
|
|
||||||
|
function makeConn(overrides: Partial<Connection> = {}): Connection {
|
||||||
|
return {
|
||||||
|
id: "c1",
|
||||||
|
name: "Test",
|
||||||
|
db_type: "postgresql",
|
||||||
|
host: "localhost",
|
||||||
|
port: 5432,
|
||||||
|
username: "user",
|
||||||
|
folder_id: null,
|
||||||
|
keychain_ref: null,
|
||||||
|
tag_ids: [],
|
||||||
|
favorite: false,
|
||||||
|
created_at: "",
|
||||||
|
updated_at: "",
|
||||||
|
...overrides,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
describe("parseConnectionString", () => {
|
describe("parseConnectionString", () => {
|
||||||
it("parses a PostgreSQL URL", () => {
|
it("parses a PostgreSQL URL", () => {
|
||||||
@@ -150,3 +169,75 @@ describe("detectProviderFromHost", () => {
|
|||||||
expect(detectProviderFromHost("prod.example.com")).toBeNull();
|
expect(detectProviderFromHost("prod.example.com")).toBeNull();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("buildConnectionUrl", () => {
|
||||||
|
it("builds a PostgreSQL URL with password and sslmode", () => {
|
||||||
|
const url = buildConnectionUrl(
|
||||||
|
makeConn({ db_type: "postgresql", host: "prod.example.com", port: 5432, username: "alice", database: "app", ssl_mode: "require" }),
|
||||||
|
"s3cret",
|
||||||
|
);
|
||||||
|
expect(url).toBe("postgresql://alice:s3cret@prod.example.com:5432/app?sslmode=require");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("omits password when null (no-password variant)", () => {
|
||||||
|
const url = buildConnectionUrl(
|
||||||
|
makeConn({ db_type: "postgresql", host: "prod.example.com", port: 5432, username: "alice", database: "app" }),
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
expect(url).toBe("postgresql://alice@prod.example.com:5432/app");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("omits sslmode when disable or unset", () => {
|
||||||
|
expect(
|
||||||
|
buildConnectionUrl(makeConn({ ssl_mode: "disable" }), null),
|
||||||
|
).toBe("postgresql://user@localhost:5432");
|
||||||
|
expect(
|
||||||
|
buildConnectionUrl(makeConn({ ssl_mode: undefined }), null),
|
||||||
|
).toBe("postgresql://user@localhost:5432");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("percent-encodes special characters in user/password/database", () => {
|
||||||
|
const url = buildConnectionUrl(
|
||||||
|
makeConn({ username: "a b", database: "my db" }),
|
||||||
|
"p@ss w:rd",
|
||||||
|
);
|
||||||
|
expect(url).toBe("postgresql://a%20b:p%40ss%20w%3Ard@localhost:5432/my%20db");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("builds a MySQL URL", () => {
|
||||||
|
const url = buildConnectionUrl(
|
||||||
|
makeConn({ db_type: "mysql", host: "127.0.0.1", port: 3306, username: "root", database: "app" }),
|
||||||
|
"pw",
|
||||||
|
);
|
||||||
|
expect(url).toBe("mysql://root:pw@127.0.0.1:3306/app");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("builds a Redis URL with password but no username", () => {
|
||||||
|
const url = buildConnectionUrl(
|
||||||
|
makeConn({ db_type: "redis", host: "localhost", port: 6379, username: null, database: "0" }),
|
||||||
|
"pw",
|
||||||
|
);
|
||||||
|
expect(url).toBe("redis://:pw@localhost:6379/0");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("builds a SQLite URL from the host file path", () => {
|
||||||
|
const url = buildConnectionUrl(
|
||||||
|
makeConn({ db_type: "sqlite", host: "/Users/me/data.db", port: null, username: null, database: null }),
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
expect(url).toBe("sqlite:///Users/me/data.db");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("round-trips through parseConnectionString", () => {
|
||||||
|
const conn = makeConn({ db_type: "postgresql", host: "localhost", port: 5432, username: "user", database: "mydb" });
|
||||||
|
const url = buildConnectionUrl(conn, "pass");
|
||||||
|
expect(parseConnectionString(url)).toEqual({
|
||||||
|
db_type: "postgresql",
|
||||||
|
host: "localhost",
|
||||||
|
port: 5432,
|
||||||
|
username: "user",
|
||||||
|
password: "pass",
|
||||||
|
database: "mydb",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { DbType } from "./types";
|
import type { Connection, DbType } from "./types";
|
||||||
|
|
||||||
export interface ParsedConnectionString {
|
export interface ParsedConnectionString {
|
||||||
db_type: DbType;
|
db_type: DbType;
|
||||||
@@ -89,3 +89,49 @@ export function detectProviderFromHost(host: string): "supabase" | "neon" | null
|
|||||||
if (h.endsWith(".neon.tech")) return "neon";
|
if (h.endsWith(".neon.tech")) return "neon";
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a connection URL string from a saved connection, for copying into
|
||||||
|
* env vars / other tools. `password` is the decrypted keychain value (or
|
||||||
|
* `null` to omit it — e.g. the "no password" copy variant).
|
||||||
|
*
|
||||||
|
* Formats per DB type:
|
||||||
|
* - postgresql: `postgresql://user:pass@host:port/db?sslmode=...`
|
||||||
|
* - mysql: `mysql://user:pass@host:port/db`
|
||||||
|
* - sqlite: `sqlite:///abs/path` (host stores the file path)
|
||||||
|
* - redis: `redis://user:pass@host:port/db`
|
||||||
|
*
|
||||||
|
* Username/password/database are percent-encoded; the ssl_mode is appended as
|
||||||
|
* a query param for PostgreSQL when set to anything other than `disable`.
|
||||||
|
*/
|
||||||
|
export function buildConnectionUrl(conn: Connection, password: string | null): string {
|
||||||
|
const enc = (s: string) => encodeURIComponent(s);
|
||||||
|
const port = conn.port ? `:${conn.port}` : "";
|
||||||
|
const user = conn.username ? enc(conn.username) : "";
|
||||||
|
const auth = password
|
||||||
|
? user
|
||||||
|
? `${user}:${enc(password)}@`
|
||||||
|
: `:${enc(password)}@`
|
||||||
|
: user
|
||||||
|
? `${user}@`
|
||||||
|
: "";
|
||||||
|
const db = conn.database ? `/${enc(conn.database)}` : "";
|
||||||
|
|
||||||
|
switch (conn.db_type) {
|
||||||
|
case "postgresql": {
|
||||||
|
const ssl =
|
||||||
|
conn.ssl_mode && conn.ssl_mode !== "disable"
|
||||||
|
? `?sslmode=${encodeURIComponent(conn.ssl_mode)}`
|
||||||
|
: "";
|
||||||
|
return `postgresql://${auth}${conn.host}${port}${db}${ssl}`;
|
||||||
|
}
|
||||||
|
case "mysql":
|
||||||
|
return `mysql://${auth}${conn.host}${port}${db}`;
|
||||||
|
case "sqlite":
|
||||||
|
// host stores the file path; `sqlite:///abs/path` round-trips through
|
||||||
|
// parseConnectionString (pathname → host).
|
||||||
|
return `sqlite://${conn.host}`;
|
||||||
|
case "redis":
|
||||||
|
return `redis://${auth}${conn.host}${port}${db}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { describe, it, expect } from "vitest";
|
|||||||
import agents from "../../AGENTS.md?raw";
|
import agents from "../../AGENTS.md?raw";
|
||||||
import readme from "../../README.md?raw";
|
import readme from "../../README.md?raw";
|
||||||
|
|
||||||
describe("v0.7.9 docs coverage", () => {
|
describe("v0.7.10 docs coverage", () => {
|
||||||
it("AGENTS.md marks inline cell editing complete", () => {
|
it("AGENTS.md marks inline cell editing complete", () => {
|
||||||
expect(agents).toContain("Inline cell editing");
|
expect(agents).toContain("Inline cell editing");
|
||||||
expect(agents).toMatch(/Inline cell editing \| ✅/);
|
expect(agents).toMatch(/Inline cell editing \| ✅/);
|
||||||
@@ -24,8 +24,8 @@ describe("v0.7.9 docs coverage", () => {
|
|||||||
expect(agents).toMatch(/Connection status indicator on cards \| ✅/);
|
expect(agents).toMatch(/Connection status indicator on cards \| ✅/);
|
||||||
expect(agents).toMatch(/Move-to-folder bulk action \| ✅/);
|
expect(agents).toMatch(/Move-to-folder bulk action \| ✅/);
|
||||||
});
|
});
|
||||||
it("README declares v0.7.9", () => {
|
it("README declares v0.7.10", () => {
|
||||||
expect(readme).toContain("0.7.9");
|
expect(readme).toContain("0.7.10");
|
||||||
});
|
});
|
||||||
it("AGENTS.md marks schema CRUD complete", () => {
|
it("AGENTS.md marks schema CRUD complete", () => {
|
||||||
expect(agents).toMatch(/Schema CRUD \| ✅/);
|
expect(agents).toMatch(/Schema CRUD \| ✅/);
|
||||||
@@ -64,9 +64,9 @@ describe("v0.7.9 docs coverage", () => {
|
|||||||
expect(agents).toMatch(/Cancel long-running queries \| ✅/);
|
expect(agents).toMatch(/Cancel long-running queries \| ✅/);
|
||||||
expect(agents).toMatch(/Settings export\/import \| ✅/);
|
expect(agents).toMatch(/Settings export\/import \| ✅/);
|
||||||
});
|
});
|
||||||
it("README links to v0.7.9 assets in both download tables", () => {
|
it("README links to v0.7.10 assets in both download tables", () => {
|
||||||
expect(readme).toContain("releases/download/v0.7.9/");
|
expect(readme).toContain("releases/download/v0.7.10/");
|
||||||
expect(readme).toContain("Gridline_0.7.9_aarch64.dmg");
|
expect(readme).toContain("Gridline_0.7.10_aarch64.dmg");
|
||||||
expect(readme).toContain("Gridline-0.7.9-1.x86_64.rpm");
|
expect(readme).toContain("Gridline-0.7.10-1.x86_64.rpm");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -2,7 +2,7 @@ import { describe, it, expect } from "vitest";
|
|||||||
import pkg from "../../package.json";
|
import pkg from "../../package.json";
|
||||||
|
|
||||||
describe("version", () => {
|
describe("version", () => {
|
||||||
it("declares v0.7.9 across the app shell", () => {
|
it("declares v0.7.10 across the app shell", () => {
|
||||||
expect(pkg.version).toBe("0.7.9");
|
expect(pkg.version).toBe("0.7.10");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
Reference in New Issue
Block a user