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
3 changes: 2 additions & 1 deletion .claude/rules/ci.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,8 @@ The required check names are in `AGENTS.md` (CI). Reading a red check: `.claude/
- A push to main or `release/**`: the same minus PR title; never cancelled, the SPM cache saved on a miss. Every code push runs both plans in full; a docs-only or changelog-only push skips the suites through the detector. A `release/**` push also runs Release Build; main gets it nightly.
- A `v*` tag runs only `release.yml`, and a `linux-v*` tag only `release-linux.yml`, which calls `linux-packages.yml`. The test workflows never run on a tag.
- `workflow_dispatch` exists on every workflow except `pr-title.yml` and `release.yml`.
- `build-linux.yml` builds and tests the Linux app (`linux/docs/testing.md`, CI). Its `Changes (Linux)` job skips the rest when nothing under `linux/` or the scripts and workflows it reads changed, and its `Linux` job gates on all of them.
- `build-linux.yml` builds and tests the Linux app, and `linux-packages.yml` builds its packages (`linux/docs/testing.md`, CI). What starts each is in its `on:` and job `if:` lines. Flatpak Devel and Minimum Rust version never run on a PR or push, and `linux-packages.yml` never on a push.
- `Changes (Linux)` skips the other build-linux jobs when nothing under `linux/` or the scripts and workflows they read changed, and the `Linux` job gates on all of them.

## Caches and runner budget

Expand Down
59 changes: 27 additions & 32 deletions .github/workflows/build-linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ on:
push:
branches: [main]
schedule:
- cron: "13 14 * * 1"
- cron: "13 14 * * *"
workflow_dispatch:

permissions:
Expand Down Expand Up @@ -105,6 +105,22 @@ jobs:
xvfb \
xauth \
dbus-daemon
# On the bare checkout: the glob ignores .gitignore, so once Cargo
# fills _build it would read every crate's Markdown too.
- name: Install lychee
run: |
curl --fail --silent --show-error --location --output "$RUNNER_TEMP/lychee.tar.gz" \
"https://github.com/lycheeverse/lychee/releases/download/lychee-v$LYCHEE_VERSION/lychee-x86_64-unknown-linux-gnu.tar.gz"
echo "$LYCHEE_SHA256 $RUNNER_TEMP/lychee.tar.gz" | sha256sum --check --strict
mkdir "$RUNNER_TEMP/lychee"
tar -xzf "$RUNNER_TEMP/lychee.tar.gz" -C "$RUNNER_TEMP/lychee" --strip-components=1 \
lychee-x86_64-unknown-linux-gnu/lychee
echo "$RUNNER_TEMP/lychee" >> "$GITHUB_PATH"
- name: Links and anchors in the Linux docs resolve
# Offline, so only links to files and headings in the tree are
# checked: the ones a rename or a move breaks.
working-directory: .
run: lychee --offline --include-fragments --no-progress 'linux/**/*.md'
- uses: dtolnay/rust-toolchain@683b588c487a69d01f6c6ddf40f036ff682cc2c8 # master
with:
toolchain: "1.98"
Expand Down Expand Up @@ -227,8 +243,8 @@ jobs:
# compiler and not with rustup's.
msrv:
name: Minimum Rust version
needs: changes
if: ${{ !cancelled() && needs.changes.outputs.run != 'false' }}
# Nightly and by hand only, to keep a pull request under GitHub Free's 20 concurrent jobs.
if: ${{ github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' }}
runs-on: ubuntu-24.04
timeout-minutes: 60
container:
Expand Down Expand Up @@ -260,30 +276,6 @@ jobs:
"cargo-$RUST_VERSION" check --workspace --all-targets --locked
"cargo-$RUST_VERSION" check --workspace --all-targets --locked --no-default-features

docs:
name: Documentation links
needs: changes
if: ${{ !cancelled() && needs.changes.outputs.run != 'false' }}
runs-on: ubuntu-24.04
timeout-minutes: 10
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Install lychee
run: |
curl --fail --silent --show-error --location --output "$RUNNER_TEMP/lychee.tar.gz" \
"https://github.com/lycheeverse/lychee/releases/download/lychee-v$LYCHEE_VERSION/lychee-x86_64-unknown-linux-gnu.tar.gz"
echo "$LYCHEE_SHA256 $RUNNER_TEMP/lychee.tar.gz" | sha256sum --check --strict
mkdir "$RUNNER_TEMP/lychee"
tar -xzf "$RUNNER_TEMP/lychee.tar.gz" -C "$RUNNER_TEMP/lychee" --strip-components=1 \
lychee-x86_64-unknown-linux-gnu/lychee
echo "$RUNNER_TEMP/lychee" >> "$GITHUB_PATH"
- name: Links and anchors in the Linux docs resolve
# Offline, so only links to files and headings in the tree are
# checked: the ones a rename or a move breaks.
run: lychee --offline --include-fragments --no-progress 'linux/**/*.md'

supply-chain:
name: Supply chain
needs: changes
Expand Down Expand Up @@ -395,8 +387,8 @@ jobs:

flatpak-devel:
name: Flatpak Devel (${{ matrix.arch }})
needs: changes
if: ${{ !cancelled() && needs.changes.outputs.run != 'false' }}
# Nightly and by hand only, to keep a pull request under GitHub Free's 20 concurrent jobs.
if: ${{ github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' }}
strategy:
fail-fast: false
matrix:
Expand Down Expand Up @@ -440,6 +432,8 @@ jobs:
timeout-minutes: 90
strategy:
fail-fast: false
# GitHub Free runs 20 jobs at once across the account.
max-parallel: 3
matrix:
package:
- tablepro-driver-postgres
Expand Down Expand Up @@ -653,11 +647,12 @@ jobs:
name: junit-tablepro
path: linux/target/nextest/ci/junit.xml

# The one check a branch rule requires: a job skipped by a path decision
# reports success, and this fails when any Linux job failed or was cancelled.
# The one check a branch rule requires: a job skipped by a path or event
# decision reports success, and this fails when any Linux job failed or was
# cancelled.
linux:
name: Linux
needs: [changes, fast, msrv, docs, supply-chain, i18n, flatpak-devel, integration, integration-session, integration-app]
needs: [changes, fast, msrv, supply-chain, i18n, flatpak-devel, integration, integration-session, integration-app]
if: always()
runs-on: ubuntu-24.04
timeout-minutes: 5
Expand Down
14 changes: 6 additions & 8 deletions .github/workflows/linux-packages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ name: Linux packages
# fedora:44 with only its BuildRequires. Each package is then installed in
# another fresh container with only its runtime dependencies and started.
# release-linux.yml calls this workflow and uploads the source packages.
# A run by hand can add arm64, which pushes and pull requests leave out.
# A run by hand can add arm64, which the nightly and pull requests leave out.
# Given a release tag, it packages that release's tarballs instead, with
# the packaging from this checkout, for a PPA upload with a new revision.

Expand All @@ -26,13 +26,11 @@ on:
- "linux/data/**/meson.build"
- "linux/po/meson.build"
- ".github/workflows/linux-packages.yml"
# After a merge every change runs, so a failure only one distribution's
# build shows turns up then and not at tag time.
push:
branches: [main]
paths:
- "linux/**"
- ".github/workflows/linux-packages.yml"
# Nightly on main, so a failure only one distribution's build shows turns
# up before tag time. Three hours after build-linux.yml's nightly, so the
# two do not share the account's 20 concurrent jobs.
schedule:
- cron: "13 17 * * *"
workflow_dispatch:
inputs:
arm64:
Expand Down
2 changes: 1 addition & 1 deletion linux/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ meson dist -C _build --no-tests --formats xztar

Inside a desktop session, `dbus-run-session -- meson test -C _build` runs the same suites without xvfb.

The fast job also runs a few source greps and `scripts/check-doc-symbols.sh` from the repository root. The supply-chain, documentation and Docker jobs run these, among others; [docs/testing.md](docs/testing.md#ci) describes every job, and the Docker job runs the last line once per package that has Docker tests:
The fast job also runs a few source greps, `scripts/check-doc-symbols.sh` and the lychee line below, from the repository root. The supply-chain and Docker jobs run the rest, among others; [docs/testing.md](docs/testing.md#ci) describes every job, and the Docker job runs the last line once per package that has Docker tests:

```bash
cargo deny --locked check advisories bans licenses sources
Expand Down
4 changes: 2 additions & 2 deletions linux/docs/decisions/0018-ppa-copr-and-flathub-later.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ tarballs: `tablepro-X.Y.Z.tar.xz` from `meson dist`, and
--versioned-dirs` and the source replacement it printed. They build
offline and run every Meson suite during the build, the gtk suite under
`xvfb-run` and `dbus-run-session`. `linux-packages.yml` builds them the
same way before a release, on pull requests that touch the packaging and
on pushes to `linux` (`docs/testing.md`, CI).
same way before a release and on the events its `on:` lists
(`docs/testing.md`, CI).

`debian/copyright` and the spec's `License` tag come from
`data/licenses/third-party.json`, the cargo-about output the About
Expand Down
13 changes: 6 additions & 7 deletions linux/docs/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -406,19 +406,18 @@ The tests in `crates/app/src/ui/editor/script_runner.rs` drive `run_batches` und

## CI

GitHub Actions (`.github/workflows/build-linux.yml`), Ubuntu runners. It runs on a pull request, and on a push to `main`, `linux`, `linux/**` or `feat/linux-**`, that touches `linux/` or one of the three Linux workflows: `build-linux.yml`, `linux-packages.yml` and `release-linux.yml`. It also runs every Monday and by hand. Every action is pinned by commit SHA and every tool by version.
GitHub Actions (`.github/workflows/build-linux.yml`), Ubuntu runners. Its `on:` lists what starts it. Flatpak Devel and Minimum Rust version run only on its schedule and by hand. Every action is pinned by commit SHA and every tool by version.

1. **Fast checks**: `meson setup`, `cargo fmt --all -- --check`, the source greps (no `thread_local!` in services, no blocking file I/O under `ui`, no action group inserted as `win`, no `set_qdata`, no em dash in user-facing text), `scripts/check-doc-symbols.sh` over the docs that name code, clippy with and without default features, `meson compile`, `xvfb-run -a dbus-run-session -- meson test --print-errorlogs`, a Secret Service step under `gnome-keyring-daemon`, then `meson dist`. Runs in an `ubuntu:26.04` container, which ships GLib 2.88 plus the meson, appstream and desktop-file-utils packages. A failed run keeps `_build/meson-logs/testlog.txt` as the `meson-testlog` artifact. [CONTRIBUTING.md](../CONTRIBUTING.md#fast-job-commands) lists the same commands.
1. **Fast checks**: lychee, offline, over every Markdown file under `linux/`, with heading anchors checked, then `meson setup`, `cargo fmt --all -- --check`, the source greps (no `thread_local!` in services, no blocking file I/O under `ui`, no action group inserted as `win`, no `set_qdata`, no em dash in user-facing text), `scripts/check-doc-symbols.sh` over the docs that name code, clippy with and without default features, `meson compile`, `xvfb-run -a dbus-run-session -- meson test --print-errorlogs`, a Secret Service step under `gnome-keyring-daemon`, then `meson dist`. Runs in an `ubuntu:26.04` container, which ships GLib 2.88 plus the meson, appstream and desktop-file-utils packages. A failed run keeps `_build/meson-logs/testlog.txt` as the `meson-testlog` artifact. [CONTRIBUTING.md](../CONTRIBUTING.md#fast-job-commands) lists the same commands.
2. **Docker tests** (`integration`, `integration-session`, `integration-app`): runs after fast checks pass, and keeps each package's JUnit report as a `junit-<package>` artifact, `junit-<package>-<release>` for a release entry. One matrix entry per package with docker tests (the PostgreSQL driver with PgBouncer, the MySQL driver with MariaDB, the SQL Server and ClickHouse drivers, all four with their TLS suites, `tablepro-ssh` against an OpenSSH server container, and `tablepro-test-fixtures` for the fixtures themselves) runs that package's ignored docker tests through cargo-nextest on the host runner's Docker, one test at a time per package. The driver packages have an entry more for each older or newer server release ([Server releases](#server-releases)). `tablepro-session` builds storage, which needs GLib 2.88, so its job runs in the fast job's `ubuntu:26.04` image with the host's Docker socket mounted. The app's docker tests (`crates/app/tests/tracker_save_postgres.rs`) run the same way, since the app links GTK; that job names its test targets, because the app's `startup` test needs Meson. The SQLite driver needs no container: its suites run in the fast job.
3. **Supply chain**: `cargo deny check` against `deny.toml` (advisories, bans, licenses and sources, with every feature, both Linux targets and the dev-dependencies), `cargo shear` for unused or misplaced dependencies, and `build-aux/update-third-party-licenses.sh`, failing when `data/licenses`, `build-aux/debian/copyright` or the spec's `License` tag differ from what it writes. Then `zizmor` over the three Linux workflows.
4. **Translations**: in `ubuntu:26.10`, whose gettext 1.0 reads path-qualified Rust calls such as `crate::i18n::gettext(...)`. `po/POTFILES.in` must match `git ls-files`, `meson setup --fatal-meson-warnings` must pass, and `po/tablepro.pot` must match a fresh `meson compile -C _build tablepro-pot` in everything but its creation date. Regenerating it locally needs gettext 1.0 or newer (`po/README.md`). A failed run keeps the template it wrote as the `tablepro-pot` artifact.
5. **Documentation links**: lychee, offline, over every Markdown file under `linux/`, with heading anchors checked.
6. **Flatpak Devel**: the Devel manifest built in the `gnome-51` Flatpak image for x86_64 and aarch64, with every Meson suite run inside the build sandbox from a tree with no `.git`. The x86_64 job lints the manifest with `flatpak-builder-lint` and `build-aux/flatpak/lint-exceptions.json`. The repository holds no Flathub manifest ([ADR 0018](decisions/0018-ppa-copr-and-flathub-later.md)).
7. **Minimum Rust version**: in `ubuntu:26.04`, `build-aux/packaging/add-rust-toolchain-ppa.sh` adds `ppa:rust-toolchain/staging`, `apt-get build-dep -P nocheck` installs the .deb's build dependencies without the test-only ones, and `cargo-1.97 check --workspace --all-targets --locked` runs with and without default features: the compiler the PPA builds with, at the workspace `rust-version`.
5. **Flatpak Devel**: the Devel manifest built in the `gnome-51` Flatpak image for x86_64 and aarch64, with every Meson suite run inside the build sandbox from a tree with no `.git`. The x86_64 job lints the manifest with `flatpak-builder-lint` and `build-aux/flatpak/lint-exceptions.json`. The repository holds no Flathub manifest ([ADR 0018](decisions/0018-ppa-copr-and-flathub-later.md)).
6. **Minimum Rust version**: in `ubuntu:26.04`, `build-aux/packaging/add-rust-toolchain-ppa.sh` adds `ppa:rust-toolchain/staging`, `apt-get build-dep -P nocheck` installs the .deb's build dependencies without the test-only ones, and `cargo-1.97 check --workspace --all-targets --locked` runs with and without default features: the compiler the PPA builds with, at the workspace `rust-version`.

A `Changes (Linux)` job runs `scripts/ci/detect-changed-paths.sh linux` and every other job waits for it: a change that touches nothing the Linux jobs read skips them, and a schedule or manual run takes every job. The `Linux` job needs all of them and fails when any failed or was cancelled, so it is the one check a branch rule requires; a skipped job counts as passed.
A `Changes (Linux)` job runs `scripts/ci/detect-changed-paths.sh linux`, and every job but Flatpak Devel and Minimum Rust version waits for it: a change that touches nothing the Linux jobs read skips them, and a schedule or manual run takes every job. The `Linux` job needs all of them and fails when any failed or was cancelled, so it is the one check a branch rule requires; a skipped job counts as passed.

`.github/workflows/linux-packages.yml` builds the packages from the release source and vendor tarballs. For each Ubuntu series the PPA builds for, 26.04 and 26.10, it writes the Debian source package with `build-aux/packaging/build-source.sh`, then `build-deb.sh` builds the binaries from it the way Launchpad does: in a fresh container of that series with only the declared build dependencies, offline, as a user that is not root, running every Meson suite, then lintian. `build-rpm.sh` builds the .rpm in `fedora:44` with only its BuildRequires, running every Meson suite, and checks it with `rpmlint --strict`. Each package is then installed in a fresh container and started. It runs on a pull request that touches the packaging (`build-aux/debian`, `build-aux/rpm`, `build-aux/packaging`), `Cargo.toml`, `Cargo.lock` or a Meson file, and on every push to `linux` that touches `linux/`, on x86_64 only. Run by hand with `arm64` ticked, it builds, tests and installs both packages on an arm64 runner too. `.github/workflows/release-linux.yml` calls it on a `linux-v*` tag before it publishes, on both architectures ([ADR 0018](decisions/0018-ppa-copr-and-flathub-later.md)). Run by hand on a branch with a released tag and a revision, `release-linux.yml` calls it on that release's tarballs with the branch's .deb packaging, then uploads the result to the PPA as a new revision ([releasing.md](releasing.md#a-packaging-fix)). `build-aux/packaging/build-packages.sh` runs the same steps locally.
`.github/workflows/linux-packages.yml` builds the packages from the release source and vendor tarballs. For each Ubuntu series the PPA builds for, 26.04 and 26.10, it writes the Debian source package with `build-aux/packaging/build-source.sh`, then `build-deb.sh` builds the binaries from it the way Launchpad does: in a fresh container of that series with only the declared build dependencies, offline, as a user that is not root, running every Meson suite, then lintian. `build-rpm.sh` builds the .rpm in `fedora:44` with only its BuildRequires, running every Meson suite, and checks it with `rpmlint --strict`. Each package is then installed in a fresh container and started. Its `on:` lists what starts it. A pull request or the nightly run builds x86_64 only. Run by hand with `arm64` ticked, it builds, tests and installs both packages on an arm64 runner too. `.github/workflows/release-linux.yml` calls it on a `linux-v*` tag before it publishes, on both architectures ([ADR 0018](decisions/0018-ppa-copr-and-flathub-later.md)). Run by hand on a branch with a released tag and a revision, `release-linux.yml` calls it on that release's tarballs with the branch's .deb packaging, then uploads the result to the PPA as a new revision ([releasing.md](releasing.md#a-packaging-fix)). `build-aux/packaging/build-packages.sh` runs the same steps locally.

## Coverage

Expand Down
Loading