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
4 changes: 4 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Check out every text file with LF on every platform. Shell tests scan their
# own source for "\n"-delimited markers, and viewer-dist/ is sha256-manifested,
# so a CRLF checkout on Windows would fail both.
* text=auto eol=lf
232 changes: 232 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
name: Release

# Builds each package on its own OS from the committed viewer pin, and drafts a
# GitHub release for a v* tag. Publishing the draft stays a manual maintainer
# step. Nothing here restores a cache: every package comes from a clean runner,
# so a poisoned cache can never reach a release. Pull requests that touch the
# pipeline run the build jobs only.
on:
push:
tags: ['v*']
workflow_dispatch:
pull_request:
paths:
- .github/workflows/release.yml
- scripts/release/**
- scripts/sync-viewer.mjs
- src-tauri/tauri*.conf.json
- viewer-dist/manifest.json

concurrency:
group: release-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

permissions:
contents: read

env:
VIEWER_REPOSITORY: https://github.com/zknpr/SQLite-Explorer

jobs:
build:
name: Build ${{ matrix.platform }}
strategy:
fail-fast: false
matrix:
include:
- platform: macos
runner: macos-latest
bundles: app,dmg
# tjs needs glibc 2.38 and the app built here 2.39, so an older build
# host would not widen support much; 24.04 is the release-tested Linux.
- platform: linux
runner: ubuntu-24.04
bundles: deb
- platform: windows
runner: windows-latest
bundles: nsis
runs-on: ${{ matrix.runner }}
timeout-minutes: 60
defaults:
run:
shell: bash
env:
PLATFORM: ${{ matrix.platform }}
BUNDLES: ${{ matrix.bundles }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false

- uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version: 24

- name: Check the version files agree (and match the tag)
env:
REF_TYPE: ${{ github.ref_type }}
REF_NAME: ${{ github.ref_name }}
run: |
if [ "$REF_TYPE" = tag ]; then
node scripts/release/version.mjs --tag "$REF_NAME"
else
node scripts/release/version.mjs
fi

- name: Install Linux build dependencies
if: matrix.platform == 'linux'
run: |
sudo apt-get update
sudo apt-get install -y libwebkit2gtk-4.1-dev libayatana-appindicator3-dev \
librsvg2-dev libxdo-dev libssl-dev libsm-dev libice-dev patchelf file

- name: Install Rust 1.93.0
run: |
rustup toolchain install 1.93.0 --profile minimal
rustup default 1.93.0

- name: Install dependencies
run: npm ci

- name: Fetch the viewer source pin
run: |
pin=$(node -p "require('./viewer-dist/manifest.json').ref")
git init -q ../SQLite-Explorer
git -C ../SQLite-Explorer fetch -q --depth 1 "$VIEWER_REPOSITORY" "$pin"
echo "VIEWER_PIN=$pin" >> "$GITHUB_ENV"

- name: Sync this platform's viewer and native runtime from the pin
run: |
cp viewer-dist/manifest.json "$RUNNER_TEMP/committed-manifest.json"
node scripts/sync-viewer.mjs --source ../SQLite-Explorer --ref "$VIEWER_PIN"
node scripts/sync-viewer.mjs --verify

# The committed viewer-dist/ targets Apple Silicon macOS, so here the
# fresh sync must reproduce it byte for byte.
- name: The committed viewer-dist matches the pin
if: matrix.platform == 'macos'
run: |
node -e '
const fs = require("node:fs");
const files = (p) => JSON.stringify(JSON.parse(fs.readFileSync(p, "utf8")).files);
if (files(process.argv[1]) !== files("viewer-dist/manifest.json")) {
console.error("committed viewer-dist differs from a fresh sync of the pin");
process.exit(1);
}' "$RUNNER_TEMP/committed-manifest.json"

- name: Tests
run: |
npm test
cargo test --locked --manifest-path src-tauri/Cargo.toml --lib

- name: Build
run: npm run tauri build -- --bundles "$BUNDLES"

- name: Check and stage the macOS package
if: matrix.platform == 'macos'
run: |
app="src-tauri/target/release/bundle/macos/SQLite Explorer.app"
codesign --verify --deep --strict --verbose=2 "$app"
test -f "$app/Contents/Resources/THIRD_PARTY_NOTICES.txt"
test -x "$app/Contents/Resources/native/tjs"
dmgs=(src-tauri/target/release/bundle/dmg/*.dmg)
[ "${#dmgs[@]}" -eq 1 ] || { echo "expected one DMG, found ${#dmgs[@]}"; exit 1; }
hdiutil verify "${dmgs[0]}"
mkdir dist
cp "${dmgs[0]}" "dist/$(node scripts/release/assemble.mjs asset-name macos)"

- name: Check and stage the Linux package
if: matrix.platform == 'linux'
run: |
debs=(src-tauri/target/release/bundle/deb/*.deb)
[ "${#debs[@]}" -eq 1 ] || { echo "expected one .deb, found ${#debs[@]}"; exit 1; }
deb="${debs[0]}"
dpkg-deb --info "$deb"
# Listed once into a variable: `grep -q` on a live pipe exits early,
# and under pipefail the writer's SIGPIPE would fail a passing check.
contents=$(dpkg-deb --contents "$deb")
grep -q 'THIRD_PARTY_NOTICES.txt$' <<<"$contents"
grep -q '/native/tjs$' <<<"$contents"
# apt must refuse a system whose glibc is older than any binary in
# the package needs, so the declared floor has to cover them all.
declared=$(dpkg-deb -f "$deb" Depends | grep -oE 'libc6 \(>= [0-9.]+\)' | grep -oE '[0-9]+\.[0-9]+' || true)
[ -n "$declared" ] || { echo "the package declares no libc6 floor"; exit 1; }
root="$RUNNER_TEMP/deb-root"
dpkg-deb -x "$deb" "$root"
required=$(find "$root" -type f -exec file {} + | grep ELF | cut -d: -f1 | while read -r elf; do
strings -a "$elf" | grep -oE '^GLIBC_[0-9]+\.[0-9]+$' || true
done | sed 's/GLIBC_//' | sort -V | tail -1)
echo "declared libc6 >= $declared; binaries need GLIBC_$required"
[ "$(printf '%s\n%s\n' "$required" "$declared" | sort -V | tail -1)" = "$declared" ] \
|| { echo "binaries need glibc $required but the package declares $declared"; exit 1; }
mkdir dist
cp "$deb" "dist/$(node scripts/release/assemble.mjs asset-name linux)"

- name: Check and stage the Windows package
if: matrix.platform == 'windows'
run: |
exes=(src-tauri/target/release/bundle/nsis/*-setup.exe)
[ "${#exes[@]}" -eq 1 ] || { echo "expected one installer, found ${#exes[@]}"; exit 1; }
listing=$(7z l "${exes[0]}")
grep -q 'THIRD_PARTY_NOTICES.txt' <<<"$listing"
grep -q 'tjs.exe' <<<"$listing"
mkdir dist
cp "${exes[0]}" "dist/$(node scripts/release/assemble.mjs asset-name windows)"

- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: package-${{ matrix.platform }}
path: dist/
if-no-files-found: error

release:
name: Draft the release
needs: build
if: github.event_name == 'push' && github.ref_type == 'tag'
runs-on: ubuntu-24.04
timeout-minutes: 30
permissions:
contents: write
id-token: write
attestations: write
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false

- uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version: 24

- name: Install Rust 1.93.0
run: |
rustup toolchain install 1.93.0 --profile minimal
rustup default 1.93.0

- uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
pattern: package-*
merge-multiple: true
path: dist

- name: Add notices and MPL sources, then write the manifest, checksums and notes
env:
REF_NAME: ${{ github.ref_name }}
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
run: |
cp THIRD_PARTY_NOTICES.txt dist/

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Regenerate release-specific third-party notices

For this v0.2.0 release and every future version, copying the static notice ships text headed “0.2.0 private beta” that directs recipients to v0.2.0-beta.1 for the MPL source archive, even though the workflow attaches the corresponding archive to the current tag. Generate or validate the notice against REF_NAME before packaging it so installed copies identify the actual release and source location.

Useful? React with 👍 / 👎.

node scripts/release/mpl-sources.mjs --out dist
node scripts/release/assemble.mjs assemble --dist dist --tag "$REF_NAME" \
--commit "$GITHUB_SHA" --run-url "$RUN_URL" --notes "$RUNNER_TEMP/notes.md"

- uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4.2.2
with:
subject-path: dist/*

- name: Create the draft release
env:
GH_TOKEN: ${{ github.token }}
REF_NAME: ${{ github.ref_name }}
run: |
gh release create "$REF_NAME" dist/* --repo "$GITHUB_REPOSITORY" --draft --verify-tag \

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Mark prerelease tags as prereleases

When a supported tag such as v0.3.0-beta.1 triggers this workflow, the draft is created without --prerelease; publishing it according to the documented procedure therefore exposes the beta as an ordinary release and may let it become Latest. The gh release create manual documents --prerelease as the flag that marks this state, so add it conditionally when the validated version contains a prerelease component.

Useful? React with 👍 / 👎.

--title "SQLite Explorer ${REF_NAME#v}" --notes-file "$RUNNER_TEMP/notes.md"
75 changes: 50 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ Linux and Windows. It shares its viewer with the
and runs in a Tauri desktop shell.

The current desktop version is **0.2.0**, integrating the extension's **1.8.1**
release and desktop-specific fixes. This repository does not publish binary
releases yet; build from a checkout as described below. See
[release readiness](docs/release-readiness.md) for the remaining distribution
gates.
release and desktop-specific fixes. Packages for macOS, Linux and Windows are
attached to each [release](https://github.com/zknpr/SQLite-Explorer-App/releases);
see [Install](#install). See [release readiness](docs/release-readiness.md) for
the remaining distribution gates.

## Working with databases

Expand All @@ -29,17 +29,49 @@ refuse; retain pending work with a separate export before reloading.
| Platform tested | Package | Coverage |
| --- | --- | --- |
| macOS, Apple Silicon | `.app` | Native/WASM workflows; remaining OS checks are recorded in [macOS QA](docs/macos-release-qa.md) |
| Ubuntu 24.04, x86-64, Xfce/X11 | Debian, AppImage | Installed-app checks; shutdown/restart checks used the Debian package |
| Ubuntu 24.04, x86-64, Xfce/X11 | Debian package | Installed-app checks, shutdown/restart |
| Windows 11, x86-64 | NSIS installer | Installed-app checks, shutdown/restart and post-boot saves |

See [Linux and Windows QA](docs/linux-windows-release-qa.md) for tested package
hashes and limits. Other Linux desktops and Wayland remain unverified. Linux
session-end cancellation requires a working inherited XSMP session connection.
Forced termination or power loss cannot preserve unsaved edits.

The Windows build is unsigned. The local macOS build has an ad-hoc signature,
without Developer ID signing or notarization. Downloaded-package trust prompts
have not been signed off.
## Install

Download the package for your system from the
[releases page](https://github.com/zknpr/SQLite-Explorer-App/releases). The
packages are not signed with a publisher identity yet; see the
[code signing policy](docs/code-signing-policy.md). Verify a download before
installing it:

```sh

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Add native checksum commands for macOS and Windows

For users following the new macOS or Windows installation paths on a stock system, sha256sum is unavailable, so the prescribed checksum verification cannot be run as written. Provide platform-specific alternatives such as shasum -a 256 on macOS and Get-FileHash on Windows, or explicitly document the additional GNU tooling required.

Useful? React with 👍 / 👎.

sha256sum --check --ignore-missing SHA256SUMS
gh attestation verify <downloaded file> --repo zknpr/SQLite-Explorer-App
```

**macOS (Apple Silicon).** Open the `.dmg` and drag SQLite Explorer to
Applications. The app has an ad-hoc signature and is not notarized, so macOS
blocks the first launch. Open **System Settings → Privacy & Security** and
choose **Open Anyway** for SQLite Explorer. To uninstall, move the app to the
Trash; settings live in `~/Library/Application Support/xyz.zknpr.sqlite-explorer`.

**Windows (x86-64).** Run the `-setup.exe` installer. It is unsigned, so
SmartScreen shows "Windows protected your PC"; choose **More info → Run
anyway**. If **Smart App Control** is on, Windows blocks unsigned programs and
offers no override, so the installer cannot run there. To uninstall, use
**Settings → Apps → Installed apps → SQLite Explorer → Uninstall**.

**Linux (x86-64, Debian/Ubuntu).** The package needs glibc 2.39 or newer
(Ubuntu 24.04+, Debian 13+):

```sh
sudo apt install ./SQLite-Explorer-<version>-linux-amd64.deb
```

To uninstall, run `sudo apt remove sq-lite-explorer` (the package name Tauri
derives from the product name). AppImage packages are not
provided.

## Build from a checkout

Expand Down Expand Up @@ -74,29 +106,22 @@ npm test
cargo test --locked --manifest-path src-tauri/Cargo.toml --lib
```

On Linux and Windows, create the platform's packages:
Create the platform's packages:

```sh
npm run tauri build
```

On macOS without a configured signing identity, build and seal a local `.app`:

```sh
npm run tauri build -- --bundles app
codesign --force --sign - 'src-tauri/target/release/bundle/macos/SQLite Explorer.app'
codesign --verify --deep --strict 'src-tauri/target/release/bundle/macos/SQLite Explorer.app'
```

The explicit ad-hoc signing step seals the app's resources. The linker's default
signature alone fails strict bundle verification. This local signature does not
provide Developer ID signing or notarization. Distribution packages need their
own [signing and packaging setup](https://v2.tauri.app/distribute/).
Packages appear under `src-tauri/target/release/bundle/`: `.app` and DMG on
macOS, Debian on Linux and NSIS on Windows. `src-tauri/tauri.macos.conf.json`
signs the macOS app ad-hoc with the hardened runtime, which seals its resources
so `codesign --verify --deep --strict` passes; it is not Developer ID signing or
notarization. Selecting another native target in the sync script stages its
resources; it does not cross-compile the app.

Packages appear under `src-tauri/target/release/bundle/`: `.app` for the local
macOS command above, Debian/AppImage on Linux and NSIS on Windows. A configured
macOS distribution build can also produce a DMG. Selecting another native target
in the sync script stages its resources; it does not cross-compile the app.
Release packages are built by the [release workflow](.github/workflows/release.yml),
which runs these steps on each OS from the pinned viewer source and drafts a
release for a `v*` tag.

For development, run `npm run tauri dev`. Packaged-app checks are still required
for native dialogs, OS integration and the custom asset protocol.
Expand Down
Loading
Loading