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
2 changes: 1 addition & 1 deletion .claude/rules/ci.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ The required check names are in `AGENTS.md` (CI). Reading a red check: `.claude/
- `ios` watches `apple/TableProMobile/`, `apple/Libs/`, `apple/Plugins/TableProPluginKit/` (the package target is a symlink to it), and every `../` path it reads out of `apple/TableProMobile/project.yml`.
- `oracle` watches `apple/Packages/TableProOracle/`; `hana` watches `apple/Native/HanaBridge/`. Each suite also watches its own workflow.
- `release` watches what the Release build reads: the app, plugin, package, native, config and libs folders, `apple/project.yml`, the app's `Package.resolved`, `.github/plugin-registry.json`, the release scripts it runs (`build.sh`, `package.sh`, `registry.py`, `version.py`, `notes.py`), the build and CI scripts it runs and the setup actions. It skips `apple/Plugins/*Tests/` and `apple/Packages/*/Tests/`.
- `linux` watches `linux/`, the docs and banned-words scripts the Linux fast job runs, the detector itself and the three Linux workflows.
- `linux` watches `linux/`, the detector itself and the three Linux workflows.
- A path a suite newly reads joins that suite's pattern in the same change, or the suite skips on it.
- `apple/CHANGELOG.md` and `apple/changelog.d/` are on no suite's pattern: the What's New phase writes nothing at `0.0.0-dev`, and Repo Hygiene renders every fragment and section instead.

Expand Down
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 @@ -33,7 +33,7 @@ Cargo prints one "test result:" line per test binary and never adds them up, thr

## Translations and data

- **`l10n` checks the file list and the catalog**, not the template. Extracting the template needs gettext 1.0 or later: 0.24 to 0.26 read Rust but skip path-qualified calls such as `crate::i18n::gettext(...)`, and an older `xgettext` reads it with the C lexer. Neither template may be committed. CI's Translations job is the gate for it.
- **`l10n` checks the file list and the catalog**, not the template. The template is not committed: `meson compile -C _build tablepro-pot` writes it, and needs gettext 1.0 or later: 0.24 to 0.26 read Rust but skip path-qualified calls such as `crate::i18n::gettext(...)`, and an older `xgettext` reads it with the C lexer.
- **`i18n_catalog` needs the `en_US.UTF-8` locale and `msgfmt`**: `localedef -i en_US -f UTF-8 en_US.UTF-8` when the locale is missing.
- **`data` skips a validator whose tool is not installed** (`desktop-file-validate`, `appstreamcli`, `glib-compile-schemas`) and reports it.

Expand Down
1 change: 0 additions & 1 deletion .claude/skills/fix-issue/scripts/lib/platform-linux.sh
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,6 @@ platform_step() {
grep -qE '^[<>] ' "$log" && note " POTFILES.in is stale: list the files above in linux/po/POTFILES.in"
diagnose_environment "$log" && STATUS=INCONCLUSIVE
fi
note "the POT template itself is checked by CI's Translations job, which needs gettext 0.24 or later"
emit "$log" $code
;;

Expand Down
3 changes: 1 addition & 2 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,7 @@ apple/TablePro/Resources/ThirdPartyLicenses/texts/** linguist-vendored
# Joins the SQLite copyright page and the source blessing with a sentence of its own.
apple/TablePro/Resources/ThirdPartyLicenses/texts/sqlite.txt -linguist-vendored

# Generated Linux files (Cargo, xgettext, cargo-about, license_metadata.py)
# Generated Linux files (Cargo, cargo-about, license_metadata.py)
linux/Cargo.lock linguist-generated
linux/po/tablepro.pot linguist-generated
linux/data/licenses/** linguist-generated
linux/build-aux/debian/copyright linguist-generated
31 changes: 1 addition & 30 deletions .github/workflows/build-linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -201,18 +201,6 @@ jobs:
exit "$status"
;;
esac
- name: Docs name paths and types that exist
# A renamed type or a moved file stays named in prose until
# something asks. GTK and GLib names resolve against the GIR
# files the -dev packages install.
run: |
../scripts/check-doc-symbols.sh \
linux/ARCHITECTURE.md \
linux/docs/adding-drivers.md \
linux/docs/state-management.md \
linux/docs/storage.md \
linux/docs/testing.md \
linux/docs/decisions/0012-window-per-session.md
- name: Clippy
run: meson devenv -C _build cargo clippy --locked --workspace --all-targets -- -D warnings
- name: Clippy without default features
Expand Down Expand Up @@ -365,25 +353,8 @@ jobs:
'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.
# Fatal, so an xgettext older than 1.0 fails here.
run: meson setup _build --fatal-meson-warnings
- name: The POT template is current
# The creation date changes on every run, so it is the one line
# allowed to differ.
run: |
meson compile -C _build tablepro-pot
if ! git diff --exit-code --ignore-matching-lines='^"POT-Creation-Date: ' -- po/tablepro.pot; then
echo "::error::po/tablepro.pot does not match the strings in the code: run meson compile -C _build tablepro-pot with gettext 1.0 or newer, or take the tablepro-pot artifact of this run, and commit it"
exit 1
fi
- name: Keep the regenerated template
if: failure()
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: tablepro-pot
path: linux/po/tablepro.pot
if-no-files-found: ignore
retention-days: 7

flatpak-devel:
name: Flatpak Devel (${{ matrix.arch }})
Expand Down
1 change: 1 addition & 0 deletions linux/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ _build/
_packages/
_flatpak/
.flatpak-builder/
/po/tablepro.pot
4 changes: 2 additions & 2 deletions 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, `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:
The fast job also runs a few source greps 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 Expand Up @@ -136,7 +136,7 @@ A user-visible change adds one towncrier fragment, `changelog.d/<PR>.<type>.md`
3. The PR description follows the repository's `.github/pull_request_template.md`.
4. Run the [fast-job commands](#fast-job-commands) locally before pushing.
5. A change to `Cargo.lock` commits what `build-aux/update-third-party-licenses.sh` rewrites: `data/licenses`, `build-aux/debian/copyright` and the spec's `License` tag ([Dependencies](#dependencies)).
6. A new or removed file under `crates/app/src` regenerates `po/POTFILES.in`, and a changed user-facing string regenerates `po/tablepro.pot` ([po/README.md](po/README.md)).
6. A new or removed file under `crates/app/src` regenerates `po/POTFILES.in` ([po/README.md](po/README.md)).
7. A user-visible change has its [changelog](#changelog) fragment.
8. UI changes include before and after screenshots in the PR description, light and dark when color is involved. A change to a window shown in the metainfo retakes its screenshot in `data/screenshots` ([README.md](README.md#screenshots)).

Expand Down
2 changes: 1 addition & 1 deletion linux/ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ The app runs on GNOME 50 ([ADR 0015](docs/decisions/0015-gnome-50-platform-floor
| Flatpak | The Devel manifest builds on GNOME 51 for x86_64 and aarch64 with every Meson suite in the sandbox, and is linted, on every pull request that touches `linux/` |
| .deb and .rpm | Ubuntu 26.04 and 26.10 and Fedora 44, built offline in CI the way Launchpad and COPR build them, linted, then installed and started in fresh containers; a `linux-v*` tag uploads to the PPA and starts the COPR build ([ADR 0018](docs/decisions/0018-ppa-copr-and-flathub-later.md)) |
| Desktop integration | Desktop file with Open With for SQLite files, D-Bus activation, a man page, metainfo with screenshots, and SVG icons |
| Translations | The app's own strings go through gettext, and CI regenerates the template with gettext 1.0 and fails when it or `POTFILES.in` is stale. Some error details from the core crates still reach the user in English (see Release criteria) |
| Translations | The app's own strings go through gettext, and CI fails when `POTFILES.in` is stale. The template is generated on demand with gettext 1.0. Some error details from the core crates still reach the user in English (see Release criteria) |
| Logs | stderr, which the session journals, with JSON lines on request. A panic also writes a crash report under `$XDG_STATE_HOME`, offered with Show Report on the next launch |
| Tests | Unit, gtk (with an accessibility audit of the picker, the connect pages, the workspace, the connect dialog and every preferences page), data and packaging suites; connection, DDL and TLS conformance on every engine; Docker jobs for PostgreSQL with PgBouncer, MySQL and MariaDB, SQL Server, ClickHouse, SSH, the session and the grid save ([testing.md](docs/testing.md)); every critical audit finding has named guards ([regression-tests.md](docs/regression-tests.md)) |

Expand Down
2 changes: 1 addition & 1 deletion linux/docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ The reference for working on the Linux app. Start with [ARCHITECTURE.md](../ARCH
| [troubleshooting.md](troubleshooting.md) | For users: saved passwords, SSH, Kerberos, logs, crash reports, bug reports and downgrades |
| [regression-tests.md](regression-tests.md) | The tests that guard each critical audit finding |
| [releasing.md](releasing.md) | The setup before the first release, a release, a packaging fix and a fix release |
| [po/README.md](../po/README.md) | Marking strings for translation and keeping the template current |
| [po/README.md](../po/README.md) | Marking strings for translation and generating the template |

## Drivers

Expand Down
4 changes: 2 additions & 2 deletions linux/docs/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -408,10 +408,10 @@ The tests in `crates/app/src/ui/editor/script_runner.rs` drive `run_batches` und

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**: 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.
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), 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.
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`, and `meson setup --fatal-meson-warnings` must pass. `po/tablepro.pot` is not committed; `meson compile -C _build tablepro-pot` writes it, with gettext 1.0 or newer (`po/README.md`).
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`.

Expand Down
26 changes: 12 additions & 14 deletions linux/po/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,11 @@ Do not translate symbols, SQL keywords, or the word TablePro.

1. Pick a locale code (e.g. `vi`, `de`, `pt_BR`).
2. Add it on its own line to [`LINGUAS`](LINGUAS).
3. Copy `tablepro.pot` to `xx.po` and translate the entries:
3. Generate the template ([Generating tablepro.pot](#generating-tablepropot)),
start `xx.po` from it and translate the entries:

```sh
meson compile -C _build tablepro-pot
msginit --locale=xx --input=po/tablepro.pot --output=po/xx.po
```

Expand Down Expand Up @@ -61,32 +63,28 @@ git ls-files 'crates/app/src/*.rs' 'data/resources/*.ui' 'data/*.desktop.in.in'
CI runs the same command and fails on a diff, so a new source file cannot
silently drop out of translation.

## Regenerating tablepro.pot
## Generating tablepro.pot

`tablepro.pot` is the master template. A change that adds, edits or
removes a user-facing string regenerates it in the same commit:
`tablepro.pot` is not committed, as in other GNOME apps. Generate it from
the code before running `msginit` or `msgmerge`:

```sh
meson compile -C _build tablepro-pot
```

The template names the file each string is in but not the line, so moving
code leaves it alone. The Translations CI job regenerates it and fails when
the committed file differs in anything but its creation date. It then
uploads the template it wrote as the `tablepro-pot` artifact, which can be
committed as it is.

This needs **gettext 1.0 or newer**, the first release whose xgettext
reads a call through a path such as `crate::i18n::gettext(...)`, the way
most of the app calls it. Ubuntu 26.10 ships 1.0; Fedora 44 ships 0.26 and
Ubuntu 26.04 0.23.2. gettext 0.24 to 0.26 read Rust but skip every
path-qualified call, and anything older falls back to its C lexer, which
misreads lifetimes such as `'static` as character constants. Neither
template should be committed. `meson setup` warns when the xgettext it
found is too old.
misreads lifetimes such as `'static` as character constants. `meson setup`
warns when the xgettext it found is too old.

Use `msgmerge` to fold new strings into existing translations:
Then fold new strings into existing translations:

```sh
for f in po/*.po; do msgmerge --update "$f" po/tablepro.pot; done
```

`meson compile -C _build tablepro-update-po` does both steps for every
locale in `LINGUAS`.
4 changes: 2 additions & 2 deletions linux/po/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ if xgettext.found()
xgettext_version = run_command(xgettext, '--version', check: false).stdout().split('\n')[0].split(' ')[-1]
if xgettext_version.version_compare('< 1.0')
warning(
'xgettext @0@ predates 1.0, the first that reads a call through a path such as crate::i18n::gettext. Regenerating the POT needs 1.0 or newer.'.format(
'xgettext @0@ predates 1.0, the first that reads a call through a path such as crate::i18n::gettext. Generating the POT needs 1.0 or newer.'.format(
xgettext_version,
),
)
Expand All @@ -24,7 +24,7 @@ i18n.gettext(
'--keyword=ngettext_f:1,2',
'--keyword=pgettext_f:1c,2',
'--from-code=UTF-8',
# CI fails when the committed template differs from a fresh one, and
# msgmerge copies these references into the committed .po files, where
# line numbers would make every edit above a string count as a change.
'--add-location=file',
'--add-comments=Translators:',
Expand Down
Loading
Loading