Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
47df448
ci(linux): release only from the Linux branch, under a tag-gated envi…
datlechin Oct 15, 2026
0483013
ci(linux): audit the Linux workflows when only a sibling workflow cha…
datlechin Oct 15, 2026
6c87ecf
fix(linux-storage): read a group name an earlier build stored past th…
datlechin Oct 15, 2026
3a78e92
fix(linux): run connection edits one at a time so secrets match the e…
datlechin Oct 15, 2026
a92dc31
build(linux): ban serde_json maps in the session crate and drop unuse…
datlechin Oct 15, 2026
d4acf50
test(linux): write fake programs from a child so a fork cannot keep t…
datlechin Oct 15, 2026
68bc561
test(linux): run the gtk suite on a session bus of its own
datlechin Oct 15, 2026
bd5b35d
docs(linux): correct the guides, roadmap and ADRs against the code
datlechin Oct 15, 2026
4c7d20e
fix(linux): require the GLES library GTK loads at runtime in the rpm
datlechin Oct 15, 2026
1d5643d
Merge branch 'fix/linux-group4-review' into refactor/linux-distributi…
datlechin Oct 15, 2026
3d45a5b
build(linux): per-profile settings schema, GNOME 51 Flatpak, no custo…
datlechin Oct 15, 2026
b2d33ca
build(linux): PPA source package built with rustc 1.97, CI on GNOME 51
datlechin Oct 15, 2026
c13b38a
build(linux): COPR builds from a release webhook, full license tags
datlechin Oct 15, 2026
c084387
docs(linux): a PPA and COPR first, Flathub later (ADR 0018)
datlechin Oct 15, 2026
63765e7
fix(linux): forms open in their name field, closed views are freed
datlechin Oct 16, 2026
4397e74
Merge remote-tracking branch 'origin/linux' into refactor/linux-distr…
datlechin Oct 16, 2026
a026f8d
fix(linux): free a closed editor's view under IBus on GTK 4.24
datlechin Oct 16, 2026
d822f4f
test(linux): end test apps without activating them, after their windows
datlechin Oct 16, 2026
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
2 changes: 1 addition & 1 deletion .claude/skills/fix-issue/references/verification-linux.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,4 @@ Cargo prints one "test result:" line per test binary and never adds them up, thr
## Packaging

- **`dist` packs the last commit** and refuses a tree with uncommitted changes, so run it after the commit.
- **The Flatpak manifest** under `linux/build-aux/flatpak/` is not built in CI.
- **The wrapper builds neither the Flatpak nor the packages.** CI builds both, and checks the workspace on the minimum Rust version (`linux/docs/testing.md`, CI). Locally: `flatpak-builder` (`linux/README.md`, Flatpak) and `linux/build-aux/packaging/build-packages.sh` (`linux/README.md`, Packages).
2 changes: 1 addition & 1 deletion .claude/skills/fix-issue/scripts/lib/platform-linux.sh
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,7 @@ platform_step() {
# The same file list CI regenerates, compared without writing to the tree.
echo "== POTFILES.in against the tree"
(cd "$LINUX_DIR" && git ls-files 'crates/app/src/*.rs' 'data/resources/*.ui' 'data/*.desktop.in.in' \
'data/*.gschema.xml' 'data/*.metainfo.xml.in.in' | diff - po/POTFILES.in) || code=1
'data/*.gschema.xml.in' 'data/*.metainfo.xml.in.in' | diff - po/POTFILES.in) || code=1
echo "== cargo test -p tablepro --test i18n_catalog"
in_devenv cargo test --locked -p tablepro --test i18n_catalog || code=1
} >> "$log" 2>&1
Expand Down
142 changes: 48 additions & 94 deletions .github/workflows/build-linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ env:
ZIZMOR_VERSION: 1.30.1
LYCHEE_VERSION: 0.24.2
LYCHEE_SHA256: 1f4e0ef7f6554a6ed33dd7ac144fb2e1bbed98598e7af973042fc5cd43951c9a
FLATPAK_BUILDER_TOOLS_REV: 41c20aa10819cdb2a4f3ca171758a96d1955c018

jobs:
# The same file runs on main, where linux/ does not exist until the
Expand Down Expand Up @@ -113,8 +112,7 @@ jobs:
# install, then the meson toolchain, the -dev packages the
# workspace links, the runtime pieces the test suites need (xvfb,
# dbus, gnome-keyring for the Secret Service step, openssh-client
# for the SSH transport), and jq with the cargo-sources.json
# generator's modules for the Flatpak manifest.
# for the SSH transport).
run: |
apt-get install -y --no-install-recommends \
build-essential \
Expand All @@ -130,7 +128,6 @@ jobs:
libgtk-4-dev \
libadwaita-1-dev \
libgtksourceview-5-dev \
libsecret-1-dev \
libsqlite3-dev \
libkrb5-dev \
clang \
Expand All @@ -141,10 +138,7 @@ jobs:
openssh-client \
xvfb \
xauth \
dbus-daemon \
jq \
python3-aiohttp \
python3-tomlkit
dbus-daemon
- uses: dtolnay/rust-toolchain@e2a55d2ffb04f378e9626c28d38b36d230d1e12f # master
with:
toolchain: "1.98"
Expand Down Expand Up @@ -213,7 +207,7 @@ jobs:
em_dash=$(printf '\342\200\224')
status=0
git grep -nF "$em_dash" -- 'crates/app/src/*.rs' 'data/resources/*.ui' 'data/*.desktop.in.in' \
'data/*.gschema.xml' 'data/*.metainfo.xml.in.in' || status=$?
'data/*.gschema.xml.in' 'data/*.metainfo.xml.in.in' || status=$?
case "$status" in
0)
echo "::error::write a comma, a colon or two sentences instead of an em dash"
Expand Down Expand Up @@ -260,55 +254,44 @@ jobs:
&& meson devenv -C _build cargo test --locked -p tablepro-storage --test secret_service -- --ignored"
- name: Dist
run: meson dist -C _build --no-tests --formats xztar
- name: Render the release-shaped Flatpak manifest
# Flathub keeps the app's manifest in its own repository and builds
# the release tarball with no network, the crates coming from a
# cargo-sources.json written from Cargo.lock. This is that shape,
# made from the Devel manifest. Meson sets its own CARGO_HOME, so
# Cargo finds the vendored crates through -Dcargo_config. The build
# form takes this commit's screenshots, since the release tag's
# do not exist yet; the lint form names the release asset.

# Launchpad builds the PPA package with rustc-X.Y and cargo-X.Y, X.Y being
# the workspace rust-version, so the workspace is checked with that
# compiler and not with rustup's.
msrv:
name: Minimum Rust version
needs: detect
if: needs.detect.outputs.linux == 'true'
runs-on: ubuntu-24.04
timeout-minutes: 60
container:
image: ubuntu:26.04
env:
DEBIAN_FRONTEND: noninteractive
defaults:
run:
working-directory: linux
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Install the package's build dependencies
# The toolchain comes from ppa:rust-toolchain/staging, as on
# Launchpad. The test-only ones are left out: no test runs here.
# Cargo needs the CA certificates to fetch the crates.
id: toolchain
run: |
version="$(sed -n 's/^rust-version = "\(.*\)"$/\1/p' Cargo.toml)"
sh build-aux/packaging/add-rust-toolchain-ppa.sh "$version"
apt-get install -y --no-install-recommends ca-certificates
apt-get build-dep -y --no-install-recommends -P nocheck ./build-aux
echo "version=$version" >> "$GITHUB_OUTPUT"
- name: Check with and without default features
env:
SOURCE_REPOSITORY: ${{ github.event.pull_request.head.repo.full_name || github.repository }}
SOURCE_SHA: ${{ github.event.pull_request.head.sha || github.sha }}
RUST_VERSION: ${{ steps.toolchain.outputs.version }}
run: |
version="$(meson introspect --projectinfo _build | jq -r .version)"
tarball="tablepro-$version.tar.xz"
mkdir -p _flatpak/build _flatpak/lint
cp "_build/meson-dist/$tarball" _flatpak/build/
sha256="$(sha256sum "_flatpak/build/$tarball" | cut -d ' ' -f 1)"
curl --fail --silent --show-error --location --output "$RUNNER_TEMP/flatpak-cargo-generator.py" \
"https://raw.githubusercontent.com/flatpak/flatpak-builder-tools/$FLATPAK_BUILDER_TOOLS_REV/cargo/flatpak-cargo-generator.py"
python3 "$RUNNER_TEMP/flatpak-cargo-generator.py" Cargo.lock -o _flatpak/build/cargo-sources.json
cp _flatpak/build/cargo-sources.json _flatpak/lint/
# $archive and $opts are jq variables, not shell ones.
# shellcheck disable=SC2016
release='
.id = "app.tablepro.TablePro"
| .["build-options"] |= (del(.["build-args"], .["test-args"]) | .env.CARGO_NET_OFFLINE = "true")
| .modules[-1] |= (
if [.sources[].type] != ["dir"] then error("the app module no longer builds one dir source") else . end
| del(.["run-tests"])
| .["config-opts"] = [.["config-opts"][] | sub("^-Dprofile=development$"; "-Dprofile=default")]
+ ["-Dcargo_config=cargo/config"] + $opts
| .sources = [$archive, "cargo-sources.json"]
)'
devel=build-aux/flatpak/app.tablepro.TablePro.Devel.json
jq --argjson archive "$(jq -n --arg path "$tarball" --arg sha256 "$sha256" \
'{type: "archive", path: $path, sha256: $sha256}')" \
--argjson opts "$(jq -n --arg url "https://raw.githubusercontent.com/$SOURCE_REPOSITORY/$SOURCE_SHA/linux/data/screenshots" \
'["-Dscreenshots_base_url=" + $url]')" \
"$release" "$devel" > _flatpak/build/app.tablepro.TablePro.json
jq --argjson archive "$(jq -n --arg url "https://github.com/TableProApp/TablePro/releases/download/linux-v$version/$tarball" \
--arg sha256 "$sha256" '{type: "archive", url: $url, sha256: $sha256}')" \
--argjson opts '[]' \
"$release" "$devel" > _flatpak/lint/app.tablepro.TablePro.json
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: flathub-shape
path: linux/_flatpak
if-no-files-found: error
retention-days: 7
"cargo-$RUST_VERSION" check --workspace --all-targets --locked
"cargo-$RUST_VERSION" check --workspace --all-targets --locked --no-default-features

docs:
name: Documentation links
Expand Down Expand Up @@ -416,7 +399,7 @@ jobs:
- name: POTFILES.in is current
run: |
git ls-files 'crates/app/src/*.rs' 'data/resources/*.ui' 'data/*.desktop.in.in' \
'data/*.gschema.xml' 'data/*.metainfo.xml.in.in' > po/POTFILES.in
'data/*.gschema.xml.in' 'data/*.metainfo.xml.in.in' > po/POTFILES.in
git diff --exit-code po/POTFILES.in
- name: Configure
# Fatal, so an xgettext older than 0.24 fails here.
Expand Down Expand Up @@ -454,7 +437,7 @@ jobs:
runs-on: ${{ matrix.runner }}
timeout-minutes: 180
container:
image: ghcr.io/flathub-infra/flatpak-github-actions:gnome-50
image: ghcr.io/flathub-infra/flatpak-github-actions:gnome-51
options: --privileged
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
Expand All @@ -469,43 +452,14 @@ jobs:
run-tests: true
build-bundle: false
cache-key: flatpak-devel-${{ matrix.arch }}-${{ hashFiles('linux/Cargo.lock') }}

flatpak-flathub:
name: Flatpak release shape
needs: [detect, fast]
if: needs.detect.outputs.linux == 'true'
runs-on: ubuntu-24.04
timeout-minutes: 180
container:
image: ghcr.io/flathub-infra/flatpak-github-actions:gnome-50
options: --privileged
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: flathub-shape
path: linux/_flatpak
# Offline and from the release tarball, as Flathub's builders run it,
# with the screenshots mirrored the way Flathub mirrors them.
- uses: flatpak/flatpak-github-actions/flatpak-builder@79327416609af08178ad73b352877e51450790b3 # v6.8
with:
manifest-path: linux/_flatpak/build/app.tablepro.TablePro.json
arch: x86_64
mirror-screenshots-url: https://dl.flathub.org/media
run-tests: false
build-bundle: false
cache-key: flatpak-flathub-${{ hashFiles('linux/Cargo.lock') }}
# The image is built on the linter's own. Only the two findings in
# lint-exceptions.json may remain.
- name: Lint the manifest and the repository
# The image is built on the linter's own. Only the findings in
# lint-exceptions.json may remain. The repository is not linted: its
# screenshot checks need images mirrored from the release tag.
- name: Lint the manifest
if: ${{ !cancelled() && matrix.arch == 'x86_64' }}
run: |
lint() {
flatpak-builder-lint --exceptions --user-exceptions linux/build-aux/flatpak/lint-exceptions.json "$@"
}
lint manifest linux/_flatpak/lint/app.tablepro.TablePro.json
lint repo repo
flatpak-builder-lint --exceptions --user-exceptions linux/build-aux/flatpak/lint-exceptions.json \
manifest linux/build-aux/flatpak/app.tablepro.TablePro.Devel.json

integration:
name: Docker tests (${{ matrix.package }})
Expand Down
116 changes: 69 additions & 47 deletions .github/workflows/linux-packages.yml
Original file line number Diff line number Diff line change
@@ -1,42 +1,40 @@
name: Linux packages

# Builds the .deb and the .rpm from the release source tarballs, each in a
# fresh container that installs only the build dependencies the package
# declares, then installs each package in another fresh container with only
# its runtime dependencies and starts it. release-linux.yml calls this
# workflow and publishes what it builds.
# Builds the Ubuntu and Fedora packages from the release source tarballs.
# The Debian source package is built once for each Ubuntu series the PPA
# builds for, and its binaries are built from it the way Launchpad builds
# them: offline, in a fresh container with only the declared build
# dependencies, then checked with lintian. The .rpm is built in a fresh
# 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.

on:
pull_request:
paths:
- "linux/build-aux/debian/**"
- "linux/build-aux/rpm/**"
- "linux/build-aux/packaging/**"
- "linux/Cargo.toml"
- "linux/Cargo.lock"
- "linux/meson.build"
- "linux/meson_options.txt"
- "linux/crates/app/meson.build"
- "linux/data/meson.build"
- "linux/crates/**/meson.build"
- "linux/data/**/meson.build"
- "linux/po/meson.build"
- "linux/rust-toolchain.toml"
- ".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: [linux]
paths:
- "linux/build-aux/debian/**"
- "linux/build-aux/rpm/**"
- "linux/build-aux/packaging/**"
- "linux/meson.build"
- "linux/meson_options.txt"
- "linux/crates/app/meson.build"
- "linux/data/meson.build"
- "linux/po/meson.build"
- "linux/rust-toolchain.toml"
- "linux/**"
- ".github/workflows/linux-packages.yml"
workflow_dispatch:
workflow_call:
inputs:
deb-runners:
description: JSON list of runners; one .deb is built per runner architecture.
description: JSON list of runners; each series' .deb is built once per runner architecture.
type: string
default: '["ubuntu-24.04"]'

Expand Down Expand Up @@ -68,17 +66,18 @@ jobs:
path: _packages/sources
if-no-files-found: error

deb:
name: Debian package (${{ matrix.runner }})
source-package:
name: Debian source packages
needs: sources
strategy:
fail-fast: false
matrix:
runner: ${{ fromJSON(inputs.deb-runners || '["ubuntu-24.04"]') }}
runs-on: ${{ matrix.runner }}
timeout-minutes: 300
runs-on: ubuntu-24.04
timeout-minutes: 30
container:
image: ubuntu:26.04
env:
# Every Ubuntu series the PPA builds for.
UBUNTU_SERIES: resolute stonking
outputs:
series: ${{ steps.build.outputs.series }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
Expand All @@ -87,46 +86,69 @@ jobs:
with:
name: linux-sources
path: _packages/sources
- name: Build, test and lint the package
- name: Build an unsigned source package for each series
id: build
run: |
dist="$(find _packages/sources -name 'tablepro-*.tar.xz' ! -name '*-vendor.tar.xz')"
vendor="$(find _packages/sources -name 'tablepro-*-vendor.tar.xz')"
linux/build-aux/packaging/build-deb.sh "$dist" "$vendor" _packages/deb
- name: Keep the Meson test log
if: failure()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: linux-deb-testlog-${{ runner.arch }}
path: _packages/deb/testlog.txt
if-no-files-found: ignore
retention-days: 7
# shellcheck disable=SC2086 # one word per series
linux/build-aux/packaging/build-source.sh --install-deps "$dist" "$vendor" _packages/source $UBUNTU_SERIES
series=""
for name in $UBUNTU_SERIES; do
series="$series${series:+, }\"$name\""
done
echo "series=[$series]" >> "$GITHUB_OUTPUT"
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: linux-deb-${{ runner.arch }}
path: _packages/deb
name: linux-source-package
path: _packages/source
if-no-files-found: error

deb-install:
name: Install the Debian package (${{ matrix.runner }})
needs: deb
# On the runner itself rather than in a job container, so build-deb.sh
# can start the containers it builds in, the build one with no network.
deb:
name: Debian package (${{ matrix.series }}, ${{ matrix.runner }})
needs: source-package
strategy:
fail-fast: false
matrix:
runner: ${{ fromJSON(inputs.deb-runners || '["ubuntu-24.04"]') }}
series: ${{ fromJSON(needs.source-package.outputs.series) }}
runs-on: ${{ matrix.runner }}
timeout-minutes: 20
container:
image: ubuntu:26.04
timeout-minutes: 300
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: linux-deb-${{ runner.arch }}
name: linux-source-package
path: _packages/source
- name: Build offline, test and lint the package
env:
SERIES: ${{ matrix.series }}
run: |
changes="$(grep -l -x "Distribution: $SERIES" _packages/source/*_source.changes)"
linux/build-aux/packaging/build-deb.sh "${changes%_source.changes}.dsc" _packages/deb
- name: Keep the Meson test log
if: failure()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: linux-deb-testlog-${{ matrix.series }}-${{ runner.arch }}
path: _packages/deb/testlog.txt
if-no-files-found: ignore
retention-days: 7
- name: Install, start and validate in a fresh container
env:
SERIES: ${{ matrix.series }}
run: |
docker run --rm -v "$PWD:$PWD" -w "$PWD" "ubuntu:$SERIES" \
linux/build-aux/packaging/smoke-test.sh "$(find _packages/deb -name 'tablepro_*.deb')"
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: linux-deb-${{ matrix.series }}-${{ runner.arch }}
path: _packages/deb
- name: Install, start and validate
run: linux/build-aux/packaging/smoke-test.sh "$(find _packages/deb -name 'tablepro_*.deb')"
if-no-files-found: error

rpm:
name: RPM package
Expand Down
Loading
Loading