Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 75 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,65 @@ jobs:
if: matrix.target == 'windows'
uses: negrutiu/nsis-install@v3

# The README promises a notarized Mac app, so a release here can't go out without these secrets. A fork or a
# build-only run without them signs the app ad-hoc and skips notarization.
- name: Import the Developer ID certificate
if: matrix.target == 'macos'
env:
RELEASING: ${{ needs.version.outputs.tag != '' && github.repository == 'Bare7a/BarSQL' }}
MACOS_CERTIFICATE: ${{ secrets.MACOS_CERTIFICATE }}
MACOS_CERTIFICATE_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PASSWORD }}
APPLE_API_KEY: ${{ secrets.APPLE_API_KEY }}
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
run: |
set -euo pipefail
missing() {
if [ "$RELEASING" = true ]; then
echo "::error::$1"
exit 1
fi
echo "::warning::$1"
exit 0
}
if [ -z "$MACOS_CERTIFICATE" ]; then
missing "No MACOS_CERTIFICATE secret, so the macOS app would be signed ad-hoc and not notarized."
fi
keychain="$RUNNER_TEMP/signing.keychain-db"
keychain_password="$(openssl rand -base64 24)"
security create-keychain -p "$keychain_password" "$keychain"
# Recorded at once, so the cleanup step deletes it even when the import below fails.
echo "BARSQL_SIGNING_KEYCHAIN=$keychain" >> "$GITHUB_ENV"
security set-keychain-settings -lut 21600 "$keychain"
security unlock-keychain -p "$keychain_password" "$keychain"
# A .p12 from Keychain Access leaves out Apple's intermediate, and the runner image may not have it either.
security import packaging/macos/DeveloperIDG2CA.cer -k "$keychain"
printf '%s' "$MACOS_CERTIFICATE" | base64 --decode > "$RUNNER_TEMP/certificate.p12"
security import "$RUNNER_TEMP/certificate.p12" -k "$keychain" -P "$MACOS_CERTIFICATE_PASSWORD" -T /usr/bin/codesign
rm "$RUNNER_TEMP/certificate.p12"
security set-key-partition-list -S apple-tool:,apple: -s -k "$keychain_password" "$keychain" > /dev/null
security list-keychains -d user -s "$keychain" "$HOME/Library/Keychains/login.keychain-db"
# The SHA-1 hash rather than the name, which codesign refuses when two certificates share it, as after a renewal.
identity="$(security find-identity -v -p codesigning "$keychain" | awk '/"Developer ID Application: /{print $2; exit}')"
if [ -z "$identity" ]; then
echo "::error::MACOS_CERTIFICATE holds no Developer ID Application identity."
exit 1
fi
echo "BARSQL_MACOS_IDENTITY=$identity" >> "$GITHUB_ENV"
if [ -z "$APPLE_API_KEY" ]; then
missing "No APPLE_API_KEY secret, so the macOS app would be signed but not notarized, and Gatekeeper would block it."
fi
if [ -z "$APPLE_API_KEY_ID" ] || [ -z "$APPLE_API_ISSUER" ]; then
echo "::error::APPLE_API_KEY needs the APPLE_API_KEY_ID and APPLE_API_ISSUER secrets too."
exit 1
fi
printf '%s\n' "$APPLE_API_KEY" > "$RUNNER_TEMP/notary.p8"
{
echo "BARSQL_NOTARY_KEY=$RUNNER_TEMP/notary.p8"
echo "BARSQL_NOTARY_KEY_ID=$APPLE_API_KEY_ID"
echo "BARSQL_NOTARY_ISSUER=$APPLE_API_ISSUER"
} >> "$GITHUB_ENV"

- name: Package
env:
APPIMAGE_EXTRACT_AND_RUN: 1
Expand All @@ -138,6 +197,14 @@ jobs:
if-no-files-found: error
retention-days: 7

- name: Delete the signing keychain and the keys
if: always() && matrix.target == 'macos'
run: |
if [ -n "${BARSQL_SIGNING_KEYCHAIN:-}" ]; then
security delete-keychain "$BARSQL_SIGNING_KEYCHAIN"
fi
rm -f "$RUNNER_TEMP/certificate.p12" "$RUNNER_TEMP/notary.p8"

# makepkg needs Arch and refuses to run as root.
arch:
needs: [version, build]
Expand Down Expand Up @@ -182,6 +249,8 @@ jobs:
timeout-minutes: 10
permissions:
contents: write
id-token: write
attestations: write
steps:
- uses: actions/download-artifact@v8
with:
Expand All @@ -196,6 +265,12 @@ jobs:
sha256sum -- * > SHA256SUMS
cat SHA256SUMS

# Every file the release publishes, SHA256SUMS included, so gh attestation verify <file> --repo Bare7a/BarSQL
# confirms any of them came from this workflow.
- uses: actions/attest-build-provenance@v4
with:
subject-path: dist/*

- uses: softprops/action-gh-release@v3
with:
tag_name: ${{ needs.version.outputs.tag }}
Expand Down
17 changes: 17 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,23 @@ For a single screenshot, build with `cargo build -p barsql --features snapshot`,
- the update dialog: `update`, `update-current`, `update-downloading`, `update-ready`, `update-failed`
- other screens: `plan`, `quick`, `quick=<query>`, `suggest=<text>`, `toasts`, `palette` (the command palette), `chart` (the first result as a chart, with `BARSQL_SNAPSHOT_RUN=1`), `er` (the `main` schema's ER diagram), `structure` (a table's Structure tab: the first in `main`, or `structure=<table>`)

## Translations

BarSQL speaks English, Deutsch, Π‘ΡŠΠ»Π³Π°Ρ€ΡΠΊΠΈ and EspaΓ±ol. A new language is mostly JSON and needs little of the rest of the code; [#25](https://github.com/Bare7a/BarSQL/pull/25) added Spanish and touches every step below.

1. Copy `crates/barsql-ui/locales/base/en.json` and `crates/barsql-ui/locales/en.json` to the language's code, such as `fr.json`, and translate the values. Keep the keys and placeholders like `{count}` as they are. Terms developers know in English, such as Commit or Rollback, can stay in English.
2. Add the language's own name under `language` in every `locales/base/*.json`.
3. Register it in `crates/barsql-ui/src`:
- `i18n.rs`: `LANGUAGES`, its two `include_str!` constants and `source`, plus its digit grouping in `format_number`
- `actions.rs`: a `Language…` action and its item in View β†’ Language
- `command_palette.rs` and `workspace.rs`: the same action
- `relative_time.rs`: phrases such as "yesterday" and "in 3 days"
4. Add the language to `locales` in `fixtures/golden/ui/relative_time.json`, with a column for it in each row.
5. Run `cargo test -p barsql-ui`. It names any key the new files miss.
6. List the language in `guide/app.md` and on the landing page's "Instant and polished" card.

Plurals have only two forms, `key_one` and `key_other`. A language that needs more, such as Polish or Russian, needs a change to `t_count` first, so open an issue before translating it.

## Docs

- `README.md` - the front page on GitHub: the pitch, downloads, screenshots and the engines table
Expand Down
27 changes: 24 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
# ⚑ BarSQL

![Rust](https://img.shields.io/badge/Rust-1.99+-B7410E?style=for-the-badge&logo=rust)
![GPUI](https://img.shields.io/badge/GPUI_Kit-0.8-4B275F?style=for-the-badge)
![GPUI](https://img.shields.io/badge/GPUI_Kit-0.9-4B275F?style=for-the-badge)
![License](https://img.shields.io/badge/license-GPL--3.0-blue?style=for-the-badge)
![Status](https://img.shields.io/badge/status-active-success?style=for-the-badge)
[![Release](https://img.shields.io/github/v/release/Bare7a/BarSQL?style=for-the-badge&color=success)](../../releases/latest)

> **A fast, local-first SQL workbench built with Rust and GPUI.**

No cloud. No accounts. No telemetry.
Just a focused, native desktop tool for working with databases.
Just a focused, native desktop tool for **PostgreSQL**, **MySQL / MariaDB**, **SQL Server**, **ClickHouse**, **Turso / libSQL** and **SQLite**, on Windows, macOS and Linux.

🌐 **[barsql.bare7a.eu](https://barsql.bare7a.eu)** Β· πŸ“– **[User Guide](guide/README.md)**

![BarSQL's editor with schema-aware suggestions, a saved query's results in the grid and the focused row as JSON](.github/screenshots/1.png?raw=true)

---

## ⬇️ Download
Expand All @@ -34,6 +36,14 @@ No account required - just download and run.

Every build above updates itself. The installer, the Linux packages and copies in protected folders ask for admin rights first.

### First launch

The macOS app is signed with a Developer ID and notarized by Apple, so it opens like any other app.

The Windows build isn't code-signed yet, so Windows asks once before BarSQL first opens: when SmartScreen says *Windows protected your PC*, click **More info β†’ Run anyway**. Smart App Control, where it's turned on, blocks unsigned apps without an exception, so BarSQL can't run there until it's signed.

Every release lists each file's SHA-256 in `SHA256SUMS`, which the updater checks its downloads against, and every file carries a signed [build provenance attestation](../../attestations) from the GitHub Actions run that built it.

Prefer to build it yourself? See **Build from source** below.

---
Expand Down Expand Up @@ -184,6 +194,17 @@ Requirements, tests and the project layout are in **[CONTRIBUTING.md](CONTRIBUTI

---

## 🀝 Contributing

Issues and pull requests are welcome. Good places to start:

- **[Report a bug or ask for a feature](../../issues/new/choose)** - especially anything that misbehaves on your engine or server version
- **[Add a language](CONTRIBUTING.md#translations)** - mostly JSON, plus a few lines of Rust to register it

If BarSQL is useful to you, a ⭐ helps other people find it.

---

## πŸ“„ License

GPL-3.0-only; see [LICENSE](LICENSE).
24 changes: 21 additions & 3 deletions RELEASING.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

1. The workspace starts at 1.0.0. For later releases, run `cargo xtask bump-version --patch` (or `--minor`, `--major`, or an explicit version such as `1.1.0`), then commit and push. `cargo xtask version` prints the current version.
2. Tag and push (`git tag v1.0.0 && git push origin v1.0.0`), or start the workflow by hand on any branch (Actions β†’ Release BarSQL β†’ Run workflow) with the version, and it tags the commit it runs on.
3. `.github/workflows/release.yml` checks that the tag matches `Cargo.toml` and isn't already on another commit, then builds on Ubuntu 22.04, Windows and macOS while the full test suite runs on all three. Once both pass, it publishes every asset plus `SHA256SUMS`.
3. `.github/workflows/release.yml` checks that the tag matches `Cargo.toml` and isn't already on another commit, then builds on Ubuntu 22.04, Windows and macOS while the full test suite runs on all three. Once both pass, it publishes every asset plus `SHA256SUMS`, each with a build provenance attestation that `gh attestation verify <file> --repo Bare7a/BarSQL` checks.

Run the workflow by hand with the version left empty to build the packages without publishing them; they stay on the run's page for 7 days.

Expand All @@ -19,12 +19,30 @@ Run the workflow by hand with the version left empty to build the packages witho

- Only the portable updater assets name both a platform and an architecture, so portable copies never pick another download. The AppImage and the packages update from their own asset, and the installer and the dmg from the `.zip`.
- Release builds turn on the `production` feature, which enables the update check at launch. The release profile uses thin LTO and strips symbols.
- **macOS:** an `.app` for macOS 12 and later with an ad-hoc signature. Each zip holds its own architecture alone, half the size of a universal app, since the updater downloads the zip for the Mac's own chip, even from an Intel build running under Rosetta. The dmg holds a `lipo` universal app and an `Applications` link, so a copy installed from it keeps only its own architecture after its first update. `BARSQL_NATIVE_ONLY=1` builds this Mac's architecture alone.
- **Windows:** `crates/barsql/build.rs` embeds the icon and the version block. `packaging/windows/installer.nsi` installs the app with the `SQLite Database` file class for `.db`, `.sqlite`, `.sqlite3`, `.s3db` and `.sl3`.
- **macOS:** an `.app` for macOS 12 and later, signed with the Developer ID and notarized through the [signing secrets](#macos-signing-and-notarization), which a release requires. A fork or a build-only run without them signs it ad-hoc. Each zip holds its own architecture alone, half the size of a universal app, since the updater downloads the zip for the Mac's own chip, even from an Intel build running under Rosetta. The dmg holds a `lipo` universal app and an `Applications` link, so a copy installed from it keeps only its own architecture after its first update. `BARSQL_NATIVE_ONLY=1` builds this Mac's architecture alone.
- **Windows:** `crates/barsql/build.rs` embeds the icon and the version block. Neither the `.exe` nor the installer is code-signed, so SmartScreen asks once before the first launch. `packaging/windows/installer.nsi` installs the app with the `SQLite Database` file class for `.db`, `.sqlite`, `.sqlite3`, `.s3db` and `.sl3`.
- **Linux:** [`cargo-deb`](https://crates.io/crates/cargo-deb) and [`cargo-generate-rpm`](https://crates.io/crates/cargo-generate-rpm) build the deb and rpm from `crates/barsql/Cargo.toml`, and `makepkg` builds the Arch package from `packaging/linux/PKGBUILD`. Each installs `/usr/bin/BarSQL` and a desktop entry that opens SQLite files; the deb and rpm work out their library dependencies from the binary. The window's app id is `BarSQL`, so Wayland docks match it to `BarSQL.desktop`.
- **Compression:** the dmg is LZMA (`ULMO`, which macOS 10.15 and later open), the installer uses a 64 MB LZMA dictionary, the rpm an xz payload and the Arch package `zstd --ultra -20`. The deb keeps cargo-deb's xz and the AppImage appimagetool's zstd. The updater assets stay deflate `.zip` and gzip `.tar.gz`, the only formats released versions unpack.
- The icon's source is `packaging/icon.svg`. The macOS `.icns` sets it on Apple's icon grid (an 824px body on a 1024px canvas, with a shadow). Windows and Linux show the stack without its tile, since their taskbars and docks are mostly dark and the dark tile would disappear there, leaving the stack looking small. The title bar shows the stack too, in a dark and a light version (`crates/barsql-ui/images/barsql-mark-*.svg`). `cargo xtask icons` regenerates the set from it: the Linux PNGs, `icon.ico`, `icons.icns` and `docs/icon.png`, each size rendered from the vector, and the SVG copies for the app (the marks and its icon) and `docs/`.

## macOS Signing and Notarization

With these repository secrets, the release workflow signs the apps and the dmg with the Developer ID and the hardened runtime, has Apple notarize them, and staples the tickets, so Gatekeeper opens BarSQL without asking. The README promises a notarized Mac app, so a release of Bare7a/BarSQL fails without them. A fork or a build-only run without them signs the apps ad-hoc and skips notarization.

| Secret | Value |
| ---------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| `MACOS_CERTIFICATE` | The Developer ID Application certificate with its private key, exported from Keychain Access as a `.p12`, in base64: `base64 -i DeveloperID.p12 \| pbcopy` |
| `MACOS_CERTIFICATE_PASSWORD` | The password the `.p12` was exported with |
| `APPLE_API_KEY` | The text of an App Store Connect API key's `.p8` file: a team key with the Developer role, from Users and Access β†’ Integrations β†’ App Store Connect API |
| `APPLE_API_KEY_ID` | That key's Key ID |
| `APPLE_API_ISSUER` | The Issuer ID shown above the keys |

- Only the account holder can create a Developer ID Application certificate: Xcode β†’ Settings β†’ Accounts β†’ Manage Certificates β†’ + β†’ Developer ID Application.
- `packaging/macos/BarSQL.entitlements` turns off library validation, which the hardened runtime would otherwise use to refuse SQLite extensions signed by other developers or ad-hoc.
- `packaging/macos/DeveloperIDG2CA.cer` is Apple's Developer ID intermediate certificate, which a `.p12` export leaves out and the workflow adds to its keychain. It expires in September 2031; replace it from [apple.com/certificateauthority](https://www.apple.com/certificateauthority/) before then.
- To try the secrets without publishing, run the workflow by hand with the version left empty. Unzip `BarSQL-darwin-arm64.zip` from the run's `dist-macos` artifact: `spctl -a -vv BarSQL.app` should report `source=Notarized Developer ID`, and `xcrun stapler validate` should pass for the app and the dmg.
- Locally, `BARSQL_MACOS_IDENTITY` signs with a certificate in the login keychain, named as `"Developer ID Application: …"` or, when two share that name, by the SHA-1 hash `security find-identity -v -p codesigning` lists. `BARSQL_NOTARY_KEY` (the `.p8` file's path), `BARSQL_NOTARY_KEY_ID` and `BARSQL_NOTARY_ISSUER` notarize too.

## How Updates Work

Release builds check [Bare7a/BarSQL](https://github.com/Bare7a/BarSQL)'s releases at launch and offer a newer one with a toast. About β†’ Check for Updates does the same at any time. The code lives in `crates/barsql-app/src/update*` and `crates/barsql-ui/src/update_dialog.rs`.
Expand Down
17 changes: 8 additions & 9 deletions docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,11 @@
<link rel="apple-touch-icon" href="icon.png" />
<link rel="stylesheet" href="styles.css" />
<script>
// The saved theme, else the system's, before the first paint.
(function () {
var theme = null;
try { theme = localStorage.getItem("barsql-theme"); } catch (e) {}
if (theme !== "light" && theme !== "dark") {
theme = window.matchMedia && window.matchMedia("(prefers-color-scheme: light)").matches ? "light" : "dark";
}
document.documentElement.dataset.theme = theme;
})();
// The saved theme, else the system's, before the first paint. script.js saves it under the same key.
let savedTheme = null;
try { savedTheme = localStorage.getItem("barsql-theme"); } catch {}
const systemTheme = matchMedia("(prefers-color-scheme: light)").matches ? "light" : "dark";
document.documentElement.dataset.theme = savedTheme === "light" || savedTheme === "dark" ? savedTheme : systemTheme;
</script>
<script type="application/ld+json">
{
Expand Down Expand Up @@ -656,6 +652,8 @@ <h3>Linux</h3>
<p class="dl-all">
Every build updates itself from inside the app. All of them, with their checksums, are on the
<a href="https://github.com/Bare7a/BarSQL/releases/latest" target="_blank" rel="noopener">latest release</a>.
The Windows build isn't signed yet, so Windows asks once before the first launch:
<a href="https://github.com/Bare7a/BarSQL#first-launch" target="_blank" rel="noopener">here's how</a>.
Prefer to build it? <code>cargo run -p barsql --release</code>, as the
<a href="https://github.com/Bare7a/BarSQL/blob/master/CONTRIBUTING.md" target="_blank" rel="noopener">contributing guide</a> explains.
</p>
Expand Down Expand Up @@ -694,6 +692,7 @@ <h3>Linux</h3>
<a href="https://github.com/Bare7a/BarSQL" target="_blank" rel="noopener">GitHub</a>
<a href="https://github.com/Bare7a/BarSQL/issues" target="_blank" rel="noopener">Issues</a>
<a href="https://github.com/Bare7a/BarSQL/blob/master/LICENSE" target="_blank" rel="noopener">GPL-3.0 License</a>
<a href="privacy.html">Privacy</a>
</div>
</nav>
</div>
Expand Down
Loading
Loading