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
77 changes: 77 additions & 0 deletions .github/workflows/release-hooks.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
name: Release hooks

# After a Release succeeds, tells the repositories that follow Querya's
# releases:
#
# QueryaHub/extensions re-tests every registered extension against the
# new version and refreshes compat.json
# QueryaHub/extension-template opens a pull request that raises the test
# database image tag and adds the version to the
# CI matrix
#
# The querya-testdb-* images and querya-headless are published by their own
# workflows on the same Release. The dev container Feature does not follow
# releases: it downloads the newest tester when it is installed.
#
# `workflow_run`, not `release`: the release is created with GITHUB_TOKEN, and
# events made by that token do not start other workflows. A repository_dispatch
# into another repository needs a token that can write there, so this needs the
# secret QUERYA_DISPATCH_TOKEN (see docs/extension-registry.md); without it the
# job only says so.
on:
workflow_run:
workflows: [Release]
types: [completed]
workflow_dispatch:
inputs:
version:
description: Querya Desktop release to announce (e.g. 0.5.0)
required: true

permissions:
contents: read

jobs:
dispatch:
name: Tell the registry and the template
runs-on: ubuntu-latest
if: github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success'
steps:
- name: Resolve the version
id: v
shell: bash
run: |
set -euo pipefail
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
VERSION="${{ inputs.version }}"
else
# A tag push has the tag as head_branch.
VERSION="${{ github.event.workflow_run.head_branch }}"
fi
VERSION="${VERSION#v}"
if ! [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "::notice::'$VERSION' is not a release tag, nothing to announce"
echo "announce=false" >> "$GITHUB_OUTPUT"
exit 0
fi
echo "announce=true" >> "$GITHUB_OUTPUT"
echo "version=$VERSION" >> "$GITHUB_OUTPUT"

- name: Dispatch
if: steps.v.outputs.announce == 'true'
shell: bash
env:
GH_TOKEN: ${{ secrets.QUERYA_DISPATCH_TOKEN }}
VERSION: ${{ steps.v.outputs.version }}
run: |
set -euo pipefail
if [ -z "$GH_TOKEN" ]; then
echo "::warning::QUERYA_DISPATCH_TOKEN is not set, so the registry and the template were not told about $VERSION"
exit 0
fi
for repo in QueryaHub/extensions QueryaHub/extension-template; do
gh api "repos/${repo}/dispatches" -X POST \
-f event_type=querya-release \
-f "client_payload[version]=${VERSION}"
echo "told ${repo} about ${VERSION}"
done
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- **`querya-ext-tester --strict`** — warnings fail the run (exit 1), for example a recommended method that errors or an OS sandbox that could not be applied; with `--sandbox` in CI a green run now means the plugin really ran isolated. The `test-extension` action has a matching `strict` input.

- **Extension registry** — `StaticRegistryRepository` reads a static `index.json` published by [QueryaHub/extensions](https://github.com/QueryaHub/extensions) (GitHub Pages): yanked entries, non-GitHub-release URLs and extensions the running version is too old for are hidden, the index is cached, installs keep the SHA-256 / archive / sandbox checks. Not wired into the Marketplace page yet. Also new for extension authors: the `ghcr.io/queryahub/features/querya-ext-tester` Dev Container Feature, `querya-testdb-*` test database images, and the [extension template](https://github.com/QueryaHub/extension-template). See [docs/extension-registry.md](docs/extension-registry.md).
- **Release hooks** — after a Release the registry and the template are told about the new version (`repository_dispatch`); needs the `QUERYA_DISPATCH_TOKEN` secret.

### Fixed

- **In-app update of a package install (AUR, `.deb`, `.rpm`)** — the updater no longer downloads a zip and quits when the app lives in `/opt/querya-desktop` (owned by root): the update dialog says how to update it (`yay -Syu querya-desktop`, `apt`, `dnf`) with a *Copy update command* button, or offers the release page for any other folder the user cannot write. If an in-place replace fails anyway, the app is started again and the reason is logged to `~/.local/state/querya-update.log`, instead of staying closed.
Expand Down
1 change: 1 addition & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Index of Querya Desktop documentation, grouped by audience.
- [Security / local data](security.md) — where metadata and secrets are stored.
- [MCP server](mcp-server.md) — let Claude Desktop, Cursor, VS Code and other AI clients read shared connections (read-only).
- [Testing extensions in CI](extension-testing.md) — `querya-ext-tester`, release binaries and the GitHub Action.
- [Extension registry](extension-registry.md) — from an author's machine to the Marketplace: the template, test databases, the registry's CI and index, and how it follows Querya releases.

## For contributors

Expand Down
10 changes: 5 additions & 5 deletions docs/dev-containers.md
Original file line number Diff line number Diff line change
Expand Up @@ -295,13 +295,13 @@ You do not need this repository to write a database-driver extension. Start from
Built from `docker/<db>/Dockerfile.testdb`; a new `Dockerfile.testdb` adds a
database to the build by itself.

The whole path from the template to the Marketplace is in
[extension-registry.md](extension-registry.md).

## Known gaps

- **The extension registry** (registry CI and `StaticRegistryRepository`) and the
release hooks that publish the Feature and the images are not done yet; they
are tracked in [#1487](https://github.com/QueryaHub/Querya-Desktop/issues/1487).
Until then the images are published by hand with the *Test database images*
workflow.
- **The Marketplace page still uses the mock repository.** The registry and
`StaticRegistryRepository` exist; wiring the page to them is the 0.5.0 work.

## Troubleshooting

Expand Down
114 changes: 114 additions & 0 deletions docs/extension-registry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
# Extension registry

How a database-driver extension gets from an author's machine into Querya
Desktop's Marketplace, and what keeps it safe. There is no Marketplace server:
the catalog is a Git repository and one static file.

```
author ──► extension-template ──► release zip on GitHub ──► record PR to QueryaHub/extensions
│ CI: download, SHA-256, archive,
│ manifest, querya-ext-tester --sandbox
▼
Querya Desktop ◄── index.json (GitHub Pages) ◄── publish (on merge, nightly, on a Querya release)
```

| Piece | Where | What it does |
|-------|-------|--------------|
| `extension-template` | [QueryaHub/extension-template](https://github.com/QueryaHub/extension-template) | A working Rust driver with a dev container, a test database and CI. Click *Use this template*. |
| `querya-ext-tester` | Querya Desktop releases, the Dev Container Feature, the `test-extension` action | Runs the extension like the app does and checks its JSON-RPC lifecycle ([extension-testing.md](extension-testing.md)). |
| `querya-testdb-*` | `ghcr.io/queryahub/querya-testdb-{postgres,mysql,mongo,clickhouse,redis}:<version>` | The test databases, seeds inside. Built from `docker/<db>/Dockerfile.testdb`. |
| Registry | [QueryaHub/extensions](https://github.com/QueryaHub/extensions) | Records, CI, and `index.json` / `compat.json` on GitHub Pages. |
| `StaticRegistryRepository` | `lib/core/market/static_registry_repository.dart` | The app's reader of `index.json`. |

## Publishing an extension

1. Start from the template, or any project that produces a **zip** with
`manifest.json` at its root (and, for a driver, the executable `main` points at).
2. Test it: `scripts/test.sh` in the template's dev container, or
`querya-ext-tester ./dist --connect '{...}' --sandbox` against a
`querya-testdb-*` container.
3. Publish a GitHub release with the zip as an asset.
4. Add `extensions/<id>.json` to the registry in a pull request (the format and
what CI checks are in the registry's README). Everything the Marketplace
shows is read from `manifest.json` **inside the archive**; the record only
holds `id`, `version`, `downloadUrl`, `sha256` and an optional `testEnv`.
5. A new version is a change to `version`, `downloadUrl` and `sha256` in the same
file. `"yanked": true` hides an extension without deleting its record.

The extension's binary runs in the registry's CI, inside the OS sandbox, in a job
with no secrets and a read-only token. Only a Linux x86-64 build is exercised.

## What the app trusts

`StaticRegistryRepository` reads `https://queryahub.github.io/extensions/index.json`:

```json
{
"schema": 1,
"generatedAt": "2026-10-16T18:59:53Z",
"extensions": [
{
"id": "acme.redis-driver", "name": "Redis", "version": "1.0.0", "type": "database_driver",
"engines": { "querya_desktop": ">=0.4.19" }, "main": "bin/driver",
"downloadUrl": "https://github.com/acme/redis-driver/releases/download/v1.0.0/redis-driver.zip",
"sha256": "…", "size": 232353
}
]
}
```

- An index with another `schema` is refused, not half read.
- Hidden from the list: `yanked` entries, entries without a checksum, a
`downloadUrl` that is not `https://github.com/<owner>/<repo>/releases/download/<tag>/<asset>`,
and extensions whose `engines.querya_desktop` range excludes the running
version. One malformed entry does not hide the others.
- The index is cached for ten minutes; if a refresh fails the last good copy is
served.
- Installing is the same pipeline as any marketplace install: the archive's
SHA-256 must equal the index's, the archive is unpacked with path-traversal and
zip-bomb limits, and the sandbox policy must accept the manifest.

It is **not wired into the Marketplace page yet** (that still uses the mock
repository): the live Marketplace is the 0.5.0 work.

`compat.json` lists, per extension and per Querya version in the registry's
`hosts.json`, `pass`, `fail` or `untested`. The app does not read it yet.

## Following Querya's releases

The [Release hooks](../.github/workflows/release-hooks.yml) workflow runs after a
**Release** succeeds and sends `repository_dispatch` `querya-release` with the
version to:

- **the registry**, which tests every record against that version too and adds a
column to `compat.json` (raise `hosts.json` in a pull request to keep the
column);
- **the template**, which pushes a branch `querya-<version>` that moves its test
image tag and CI matrix, starts its Test workflow on it, and opens a pull
request (or an issue pointing at the branch when the organization does not
allow Actions to open pull requests).

The `querya-testdb-*` images and `querya-headless` have their own workflows on
the same Release. The Dev Container Feature does not follow releases: it
downloads the newest tester when it is installed.

### One-time setup

A dispatch into another repository needs a token that can write there. Create a
fine-grained personal access token (or a GitHub App token) with **Contents:
Read and write** on `QueryaHub/extensions` and `QueryaHub/extension-template`
and store it as the secret **`QUERYA_DISPATCH_TOKEN`** in this repository.
Without it the Release hooks only log a warning, and nothing breaks.

To let the template open its pull request itself, enable *Settings → Actions →
General → Allow GitHub Actions to create and approve pull requests* for the
organization. Without that the template opens an issue instead.

## The nightly check

The registry rebuilds the index every night from the records. A record whose
asset disappeared, changed its hash, or whose extension fails its tests on every
Querya version is **left out of the index** and gets an issue
`Registry check failed: <id>` (label `registry-check`) with the reason; the
issue closes itself when the record passes again. The rest of the index is still
published.
4 changes: 4 additions & 0 deletions docs/extension-testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,10 @@ the container with `--security-opt seccomp=unconfined` (and, on hosts that
restrict namespaces with AppArmor, `--security-opt apparmor=unconfined`), or
run the tester directly on the CI runner with the action below.

Starting a new extension? The [extension template](https://github.com/QueryaHub/extension-template)
has all of this wired up, and [extension-registry.md](extension-registry.md)
shows how a tested extension gets into the Marketplace.

## Dev Container Feature

In a dev container the tester can be installed as a Feature. It downloads the
Expand Down
1 change: 1 addition & 0 deletions docs/release-checklist.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ Verify the 0.4.4 motion tokens, smooth animations, and high refresh rate support
- [ ] After merge to `main`, confirm **Auto Version Bump** yields a **0.4.20+…** placeholder.
- [ ] **Tag** `0.4.19` is placed on the **main merge commit that includes `0.4.19+2`** (not the auto-bump commit).
- [ ] Run the **Release** workflow via that tag (see [tags-and-releases.md](tags-and-releases.md)).
- [ ] After the Release workflow: the **Release hooks** run told the registry and the template (`QUERYA_DISPATCH_TOKEN` must be set, see [extension-registry.md](extension-registry.md#one-time-setup)), the `querya-testdb-*` and `querya-headless` images are published for the new version, and the template's *Move to Querya …* pull request or issue exists.
- [ ] Verify **portable** zips (`*-linux.zip`, `*-windows.zip`, `*-macos.zip`), **installable** artifacts (`*.AppImage`, `*.deb`, `*.rpm`, `*.flatpak`, `*-windows-setup.exe`), and `SHA256SUMS.txt` on the GitHub Release.
- [ ] After the release, set the release zip's URL and `sha256` in `packaging/linux/flatpak/com.queryahub.querya_desktop.flathub.yml` (and the `metainfo.xml` release entry is already `0.4.19`); the Flathub submission (flathub/flathub#10606) is separate and has open lint errors (HTTPS on `queryahub.com` or another app id, no `--filesystem=home`, a supported GNOME runtime).

Expand Down
Loading