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
53 changes: 41 additions & 12 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -77,23 +77,39 @@ jobs:
strategy:
matrix:
include:
# Built on 22.04 so the binary also runs on distros with an older glibc.
- os: ubuntu-22.04
# Built in an Ubuntu 22.04 container so the binaries also run on distros with an older glibc.
- os: ubuntu-24.04
target: linux
container: ubuntu:22.04
artifact: dist-linux
- os: ubuntu-24.04-arm
target: linux
container: ubuntu:22.04
artifact: dist-linux-arm64
- os: windows-latest
target: windows
artifact: dist-windows
- os: macos-latest
target: macos
artifact: dist-macos
runs-on: ${{ matrix.os }}
# Windows and macOS have none, so they build on the runner itself.
container: ${{ matrix.container }}
steps:
- uses: actions/checkout@v7

- name: Install GPUI build dependencies
# The bare image has no compilers, pkg-config, certificates, Rust or the file command appimagetool runs.
- name: Install the build tools, GPUI's libraries and rustup
if: matrix.target == 'linux'
env:
DEBIAN_FRONTEND: noninteractive
run: |
sudo apt-get update
sudo apt-get install -y clang libfontconfig-dev libfreetype-dev libwayland-dev \
libx11-xcb-dev libxkbcommon-dev libxkbcommon-x11-dev libvulkan-dev libfuse2
set -euo pipefail
apt-get update
apt-get install -y build-essential clang pkg-config curl ca-certificates file libfontconfig-dev libfreetype-dev \
libwayland-dev libx11-xcb-dev libxkbcommon-dev libxkbcommon-x11-dev libvulkan-dev
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal --default-toolchain none
echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"

- name: Install the pinned toolchain
run: rustup show active-toolchain || rustup toolchain install
Expand All @@ -108,13 +124,17 @@ jobs:
key: release
save-if: ${{ github.ref == 'refs/heads/master' }}

- name: Install cargo-deb, cargo-generate-rpm and linuxdeploy
# package-linux.sh makes the deb and the rpm on x86-64 only.
- name: Install cargo-deb and cargo-generate-rpm
if: matrix.target == 'linux' && runner.arch == 'X64'
run: cargo install --locked cargo-deb@3.8.0 cargo-generate-rpm@0.21.1

- name: Install linuxdeploy
if: matrix.target == 'linux'
run: |
set -euo pipefail
cargo install --locked cargo-deb@3.8.0 cargo-generate-rpm@0.21.1
curl -fsSL -o "$RUNNER_TEMP/linuxdeploy" \
https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage
"https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-$(uname -m).AppImage"
chmod +x "$RUNNER_TEMP/linuxdeploy"
echo "LINUXDEPLOY=$RUNNER_TEMP/linuxdeploy" >> "$GITHUB_ENV"

Expand Down Expand Up @@ -186,13 +206,22 @@ jobs:
APPIMAGE_EXTRACT_AND_RUN: 1
run: scripts/package-${{ matrix.target }}.sh

- name: Show the libraries the Linux binary links
# A newer container image would raise the glibc it needs and drop the distros still on 2.35, like Ubuntu 22.04.
- name: Check the libraries the Linux binary needs
if: matrix.target == 'linux'
run: readelf -d target/package/linux/BarSQL | grep NEEDED
run: |
set -euo pipefail
readelf -d target/package/linux/BarSQL | grep NEEDED
glibc="$(objdump -T target/package/linux/BarSQL | grep -o 'GLIBC_[0-9.]*' | sort -uV | tail -n1)"
echo "Needs $glibc"
if [ "$(printf '%s\n' "$glibc" GLIBC_2.35 | sort -V | tail -n1)" != GLIBC_2.35 ]; then
echo "::error::The binary needs $glibc, newer than Ubuntu 22.04's GLIBC_2.35."
exit 1
fi

- uses: actions/upload-artifact@v7
with:
name: dist-${{ matrix.target }}
name: ${{ matrix.artifact }}
path: target/package/dist/*
if-no-files-found: error
retention-days: 7
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,13 @@ Grab the latest build for **Windows**, **macOS**, or **Linux** from the [**Relea
### Recommended assets:

- **Windows**: `BarSQL-windows-amd64.zip` (portable) **or** `BarSQL-amd64-installer.exe` (installer)
- **Linux**: `BarSQL-linux-amd64.tar.gz` (portable)
- **Linux**: `BarSQL-linux-amd64.tar.gz` (x86-64) **or** `BarSQL-linux-arm64.tar.gz` (ARM64), both portable
- **macOS**: `BarSQL-darwin-arm64.zip` (Apple Silicon) **or** `BarSQL-darwin-amd64.zip` (Intel)

### Additional options:

- **Windows**: `BarSQL.exe` (standalone)
- **Linux**: `BarSQL-x86_64.AppImage`, `BarSQL.deb`, `BarSQL.rpm`, `BarSQL.pkg.tar.zst`, `BarSQL` (standalone)
- **Linux**: `BarSQL-x86_64.AppImage` and `BarSQL-aarch64.AppImage` (ARM64), plus `BarSQL.deb`, `BarSQL.rpm`, `BarSQL.pkg.tar.zst` and `BarSQL` (standalone) for x86-64
- **macOS**: `BarSQL-macos-universal.dmg`

No account required - just download and run.
Expand Down
16 changes: 8 additions & 8 deletions RELEASING.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,26 @@

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`, each with a build provenance attestation that `gh attestation verify <file> --repo Bare7a/BarSQL` checks.
3. `.github/workflows/release.yml` checks that the tag matches `Cargo.toml` and isn't already on another commit, then builds for Linux (x86-64 and ARM64, in Ubuntu 22.04 containers), 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.

`cargo xtask package` builds this OS's packages locally, into `target/package/dist` (on Linux, after `cargo install cargo-deb cargo-generate-rpm`). `BARSQL_RELEASE_DIR=<that folder> cargo test -p barsql-app a_packaged_release_stages_its_app` runs the updater over them.

## Packages

| Script | Portable updater asset (one top-level entry) | Other downloads |
| ------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | -------------------------------------------------------------- |
| `scripts/package-macos.sh` | `BarSQL-darwin-arm64.zip`, `BarSQL-darwin-amd64.zip` (a `BarSQL.app` per architecture) | `BarSQL-macos-universal.dmg` |
| `scripts/package-windows.sh` | `BarSQL-windows-amd64.zip` (`BarSQL.exe`) | `BarSQL.exe`, `BarSQL-amd64-installer.exe` |
| `scripts/package-linux.sh` | `BarSQL-linux-amd64.tar.gz` (`BarSQL`) | `BarSQL`, `BarSQL.deb`, `BarSQL.rpm`, `BarSQL-x86_64.AppImage` |
| `packaging/linux/PKGBUILD` (release workflow, in an Arch Linux container) | | `BarSQL.pkg.tar.zst` |
| Script | Portable updater asset (one top-level entry) | Other downloads |
| ------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
| `scripts/package-macos.sh` | `BarSQL-darwin-arm64.zip`, `BarSQL-darwin-amd64.zip` (a `BarSQL.app` per architecture) | `BarSQL-macos-universal.dmg` |
| `scripts/package-windows.sh` | `BarSQL-windows-amd64.zip` (`BarSQL.exe`) | `BarSQL.exe`, `BarSQL-amd64-installer.exe` |
| `scripts/package-linux.sh` | `BarSQL-linux-amd64.tar.gz`, `BarSQL-linux-arm64.tar.gz` (`BarSQL`) | `BarSQL-x86_64.AppImage`, `BarSQL-aarch64.AppImage`, and on x86-64 `BarSQL`, `BarSQL.deb`, `BarSQL.rpm` |
| `packaging/linux/PKGBUILD` (release workflow, in an Arch Linux container) | | `BarSQL.pkg.tar.zst` |

- 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, 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`.
- **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`. Both builds run in an `ubuntu:22.04` container on GitHub's `ubuntu-24.04` and `ubuntu-24.04-arm` runners, so the binaries need glibc 2.35 at most, which the build checks, and run on Ubuntu 22.04, Debian 12, Fedora 36, RHEL 10 and later. The ARM64 build makes only the tarball and the AppImage: every released version picks a `.deb`, `.rpm` or `.pkg.tar.zst` by its extension alone, so an ARM package would reach x86-64 copies too.
- **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/`.

Expand Down
34 changes: 27 additions & 7 deletions crates/barsql-app/src/update/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,15 @@ fn serve(mut stream: TcpStream, routes: &Routes) {
}

// Keep in sync with the assets release.yml publishes.
const RELEASE_ASSETS: [&str; 13] = [
const RELEASE_ASSETS: [&str; 15] = [
"SHA256SUMS",
"BarSQL",
"BarSQL-aarch64.AppImage",
"BarSQL-amd64-installer.exe",
"BarSQL-darwin-amd64.zip",
"BarSQL-darwin-arm64.zip",
"BarSQL-linux-amd64.tar.gz",
"BarSQL-linux-arm64.tar.gz",
"BarSQL-macos-universal.dmg",
"BarSQL-windows-amd64.zip",
"BarSQL-x86_64.AppImage",
Expand Down Expand Up @@ -122,20 +124,38 @@ fn each_platform_gets_its_updater_archive() {
assert_eq!(picked("darwin", "amd64", &RELEASE_ASSETS).as_deref(), Some("BarSQL-darwin-amd64.zip"));
assert_eq!(picked("linux", "amd64", &RELEASE_ASSETS).as_deref(), Some("BarSQL-linux-amd64.tar.gz"));
assert_eq!(picked("windows", "amd64", &RELEASE_ASSETS).as_deref(), Some("BarSQL-windows-amd64.zip"));
assert_eq!(picked("linux", "arm64", &RELEASE_ASSETS), None);
assert_eq!(picked("linux", "arm64", &RELEASE_ASSETS).as_deref(), Some("BarSQL-linux-arm64.tar.gz"));
assert_eq!(picked("windows", "arm64", &RELEASE_ASSETS), None);
assert_eq!(picked("windows", "386", &RELEASE_ASSETS), None);
}

#[test]
fn appimages_and_packages_get_their_own_asset() {
let pick = |kind, arch| pick_asset(kind, "linux", arch, &assets(&RELEASE_ASSETS)).map(|ix| RELEASE_ASSETS[ix]);
assert_eq!(pick(Kind::AppImage, "amd64"), Some("BarSQL-x86_64.AppImage"));
assert_eq!(pick(Kind::AppImage, "arm64"), None);
assert_eq!(pick(Kind::AppImage, "arm64"), Some("BarSQL-aarch64.AppImage"));
assert_eq!(pick(Kind::Deb, "amd64"), Some("BarSQL.deb"));
assert_eq!(pick(Kind::Rpm, "amd64"), Some("BarSQL.rpm"));
assert_eq!(pick(Kind::Pacman, "amd64"), Some("BarSQL.pkg.tar.zst"));
}

// pick_asset takes the first match and GitHub promises no order, so no copy may match two assets. Every release
// so far picks the same way, so this covers the copies already out there too.
#[test]
fn every_copy_matches_one_asset_at_most() {
for kind in [Kind::Archive, Kind::AppImage, Kind::Deb, Kind::Rpm, Kind::Pacman] {
for platform in ["darwin", "windows", "linux"] {
for arch in ["amd64", "arm64"] {
let matches: Vec<_> = RELEASE_ASSETS
.into_iter()
.filter(|name| pick_asset(kind, platform, arch, &assets(&[*name])).is_some())
.collect();
assert!(matches.len() <= 1, "{kind:?} on {platform}/{arch} matches {matches:?}");
}
}
}
}

#[test]
fn installs_are_told_apart() {
let none = |_: &Path| None;
Expand Down Expand Up @@ -227,14 +247,14 @@ fn a_newer_release_comes_with_its_asset_and_digest() {

assert_eq!(check(&base, "v1.6.0", Kind::Archive, "darwin", "arm64"), Ok(None), "up to date");
assert_eq!(
check(&base, "1.5.3", Kind::Archive, "linux", "arm64"),
Err("github: release v1.6.0 has no asset for linux/arm64".into())
check(&base, "1.5.3", Kind::Archive, "linux", "riscv64"),
Err("github: release v1.6.0 has no asset for linux/riscv64".into())
);
let image = check(&base, "1.5.3", Kind::AppImage, "linux", "amd64").unwrap().unwrap();
assert_eq!((image.asset.name.as_str(), image.digest), ("BarSQL-x86_64.AppImage", Some(vec![0xf0, 0x0d])));
assert_eq!(
check(&base, "1.5.3", Kind::AppImage, "linux", "arm64"),
Err("github: release v1.6.0 has no AppImage for linux/arm64".into())
check(&base, "1.5.3", Kind::AppImage, "linux", "riscv64"),
Err("github: release v1.6.0 has no AppImage for linux/riscv64".into())
);
}

Expand Down
1 change: 1 addition & 0 deletions docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -635,6 +635,7 @@ <h3>Linux</h3>
</div>
<p class="dl-desc">A portable x86-64 build, plus packages for Debian, Ubuntu, Fedora, RHEL and Arch.</p>
<a class="btn btn-primary dl-main" href="https://github.com/Bare7a/BarSQL/releases/latest/download/BarSQL-linux-amd64.tar.gz" data-meta="Portable .tar.gz Β· Linux x86-64"><svg class="icon" aria-hidden="true"><use href="icons.svg#i-download"/></svg>Download .tar.gz</a>
<p class="dl-alt">For ARM64: <a class="dl-arm" href="https://github.com/Bare7a/BarSQL/releases/latest/download/BarSQL-linux-arm64.tar.gz" data-meta="Portable .tar.gz Β· Linux ARM64">.tar.gz</a> or <a href="https://github.com/Bare7a/BarSQL/releases/latest/download/BarSQL-aarch64.AppImage">AppImage</a></p>
<div class="dl-more">
<span class="dl-more-label">Other formats</span>
<div class="dl-row">
Expand Down
20 changes: 18 additions & 2 deletions docs/script.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,22 +37,32 @@ function detectOS() {
return null;
}

// Chromium's user agent says x86_64 on every Linux, so its client hints come first.
async function onArm() {
try {
const hints = await navigator.userAgentData?.getHighEntropyValues(["architecture"]);
if (hints) return hints.architecture === "arm";
} catch {}
return /aarch64|arm64/i.test(navigator.userAgent);
}

// Point the hero button at the visitor's own build, highlight its card and pick the modifier key.
// Phones and tablets keep the generic button, which leads to every download, and pages without downloads skip this.
function pointDownloadAtVisitor() {
async function pointDownloadAtVisitor() {
const os = detectOS();
const hero = document.getElementById("hero-download");
const card = os && document.getElementById(`dl-${os}`);
if (!hero || !card) return;

const names = { windows: "Windows", macos: "macOS", linux: "Linux" };
const main = card.querySelector(".dl-main");
const meta = document.getElementById("hero-download-meta");
const icon = hero.querySelector("svg");
hero.href = main.href;
icon.setAttribute("class", "logo");
icon.querySelector("use").setAttribute("href", `logos.svg#l-${os === "macos" ? "apple" : os}`);
document.getElementById("hero-download-label").textContent = `Download for ${names[os]}`;
document.getElementById("hero-download-meta").textContent = main.dataset.meta;
meta.textContent = main.dataset.meta;
card.classList.add("dl-detected");

if (os === "macos") {
Expand All @@ -61,6 +71,12 @@ function pointDownloadAtVisitor() {
key.title = "Command";
}
}

const arm = card.querySelector(".dl-arm");
if (arm && (await onArm())) {
hero.href = arm.href;
meta.textContent = arm.dataset.meta;
}
}

// Lightbox with every screenshot on the page, full size and in order. Arrow keys, swipes and the buttons move between them.
Expand Down
Loading
Loading