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
7 changes: 4 additions & 3 deletions .claude/rules/ci.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,23 +119,24 @@ The required check names are in `AGENTS.md` (CI). Reading a red check: `.claude/
- `ios` watches `TableProMobile/`, `Libs/`, `Plugins/TableProPluginKit/` (the package target is a symlink to it), and every `../` path it reads out of `TableProMobile/project.yml`.
- `oracle` watches `Packages/TableProOracle/`; `hana` watches `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, `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 `Plugins/*Tests/` and `Packages/*/Tests/`.
- `linux` watches `linux/`, the docs and banned-words scripts the Linux fast job runs, 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.
- `CHANGELOG.md` and `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.

## What runs where

- A pull request: both changes jobs; macOS Tests and iOS and Package Tests unless their detector says false (Oracle only when its detector is not false); SwiftLint; Repo Hygiene; PR title. `AllPlugins` only when the `plugins` detector is not false.
- A push to main or `release/**`: the same minus PR title, plus Release Build; never cancelled, `AllPlugins` always, 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 `v*` tag runs only `release.yml`. The test workflows never run on a tag.
- 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` is on `main` only for its Monday schedule, which GitHub runs from the default branch. Until the Linux app's directory lands on `main`, every build job skips and `weekly-dispatch` starts the workflow on the branch the `LINUX_BRANCH` variable names (the linux branch when unset), whose testing guide describes the jobs.
- `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.

## Caches and runner budget

- Swift package checkouts live in `~/.spm-cache`, keyed `spm-<os>-<hash of the app's Package.resolved>` with prefix restore. macOS Tests saves it only on a push and only on a miss: a cache saved on a PR ref is visible to that PR alone.
- `release.yml` restores that cache and never saves it, since a save in tag scope is unreadable from other refs.
- `release-build.yml` restores it and leaves the save to macOS Tests.
- There is no other cache: no libs or DerivedData cache, and none in the iOS, plugin or Ubuntu workflows.
- There is no other Apple cache: no libs or DerivedData cache, and none in the iOS or plugin workflows. The Linux jobs cache Cargo builds through `Swatinem/rust-cache` and the Flatpak build.
- CI fits the GitHub Free and Pro plans: no paid larger runner, no self-hosted Mac. At most 5 macOS jobs run at once, and main runs queue behind PR runs for those slots.

## Writing workflow and lint code
Expand Down
6 changes: 2 additions & 4 deletions .claude/skills/fix-issue/references/platform-linux.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,10 @@ The native Linux app. Every profile has these seven sections in this order, so a

A report from Linux. The code is `linux/`: a Cargo workspace of crates under `linux/crates/` (the GTK app, the UI-free core, storage, sessions, networking, SSH and one crate per driver), built by Meson. Drivers are linked in at compile time; there are no plugins. A fix lands in one or more crates; name them.

`linux/` exists only on the `linux` branch until the first Linux release, so a checkout of `main` cannot fix it.

## Base

- Worktree base: `origin/linux`.
- Pull request base: `linux`.
- Worktree base: `origin/main`.
- Pull request base: `main`.

## Done

Expand Down
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 @@ -41,7 +41,7 @@ load_rust_environment() {
require_linux_tree() {
[ -f "$LINUX_DIR/meson.build" ] && return 0
STATUS=INCONCLUSIVE
note "cause: $REPO_ROOT has no linux/. The Linux app is on the linux branch: make the tree from origin/linux."
note "cause: $REPO_ROOT has no linux/. Make the tree from origin/main."
emit "" 2
}

Expand Down
108 changes: 46 additions & 62 deletions .github/workflows/build-linux.yml
Original file line number Diff line number Diff line change
@@ -1,19 +1,9 @@
name: Build Linux

on:
push:
branches: [main, linux, "linux/**", "feat/linux-**"]
paths:
- "linux/**"
- ".github/workflows/build-linux.yml"
- ".github/workflows/linux-packages.yml"
- ".github/workflows/release-linux.yml"
pull_request:
paths:
- "linux/**"
- ".github/workflows/build-linux.yml"
- ".github/workflows/linux-packages.yml"
- ".github/workflows/release-linux.yml"
push:
branches: [main, "release/**"]
schedule:
- cron: "13 14 * * 1"
workflow_dispatch:
Expand All @@ -35,51 +25,27 @@
LYCHEE_SHA256: 1f4e0ef7f6554a6ed33dd7ac144fb2e1bbed98598e7af973042fc5cd43951c9a

jobs:
# The same file runs on main, where linux/ does not exist until the
# first Linux release. Every build job waits for this one and is skipped
# there.
detect:
name: Linux tree
changes:
name: Changes (Linux)
runs-on: ubuntu-24.04
timeout-minutes: 5
outputs:
linux: ${{ steps.tree.outputs.linux }}
run: ${{ steps.decide.outputs.run }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
persist-credentials: false
sparse-checkout: linux/meson.build
sparse-checkout-cone-mode: false
- name: Look for linux/meson.build
id: tree
run: |
if [ -f linux/meson.build ]; then
echo "linux=true" >> "$GITHUB_OUTPUT"
else
echo "linux=false" >> "$GITHUB_OUTPUT"
fi

# GitHub runs a schedule only from the default branch. Until linux/ is
# on main, the weekly run starts this workflow on the Linux branch.
weekly-dispatch:
name: Start the weekly run on the Linux branch
needs: detect
if: github.event_name == 'schedule' && needs.detect.outputs.linux == 'false'
runs-on: ubuntu-24.04
timeout-minutes: 5
permissions:
actions: write
steps:
- name: Dispatch this workflow
- name: Decide whether the Linux jobs run
id: decide
env:
GH_TOKEN: ${{ github.token }}
LINUX_BRANCH: ${{ vars.LINUX_BRANCH || 'linux' }}
run: gh workflow run build-linux.yml --repo "$GITHUB_REPOSITORY" --ref "$LINUX_BRANCH"
BASE: ${{ github.event.pull_request.base.sha || github.event.before }}
run: echo "run=$(scripts/ci/detect-changed-paths.sh --base "$BASE" linux)" >> "$GITHUB_OUTPUT"

fast:
name: Fast checks
needs: detect
if: needs.detect.outputs.linux == 'true'
needs: changes
if: ${{ !cancelled() && needs.changes.outputs.run != 'false' }}
runs-on: ubuntu-24.04
timeout-minutes: 150
# The host runner ships GLib 2.80, below the GNOME 50 floor.
Expand Down Expand Up @@ -145,7 +111,7 @@
components: rustfmt, clippy
# Meson points Cargo at _build/cargo-home and _build/cargo-target,
# so those are what the cache keeps.
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2

Check failure on line 114 in .github/workflows/build-linux.yml

View workflow job for this annotation

GitHub Actions / Supply chain

cache-poisoning

build-linux.yml:114: runtime artifacts potentially vulnerable to a cache poisoning attack: omitting `lookup-only` enables caching
with:
workspaces: linux -> _build/cargo-target
cache-directories: linux/_build/cargo-home
Expand Down Expand Up @@ -260,8 +226,8 @@
# compiler and not with rustup's.
msrv:
name: Minimum Rust version
needs: detect
if: needs.detect.outputs.linux == 'true'
needs: changes
if: ${{ !cancelled() && needs.changes.outputs.run != 'false' }}
runs-on: ubuntu-24.04
timeout-minutes: 60
container:
Expand Down Expand Up @@ -295,8 +261,8 @@

docs:
name: Documentation links
needs: detect
if: needs.detect.outputs.linux == 'true'
needs: changes
if: ${{ !cancelled() && needs.changes.outputs.run != 'false' }}
runs-on: ubuntu-24.04
timeout-minutes: 10
steps:
Expand All @@ -319,8 +285,8 @@

supply-chain:
name: Supply chain
needs: detect
if: needs.detect.outputs.linux == 'true'
needs: changes
if: ${{ !cancelled() && needs.changes.outputs.run != 'false' }}
runs-on: ubuntu-24.04
timeout-minutes: 30
defaults:
Expand Down Expand Up @@ -365,8 +331,8 @@

i18n:
name: Translations
needs: detect
if: needs.detect.outputs.linux == 'true'
needs: changes
if: ${{ !cancelled() && needs.changes.outputs.run != 'false' }}
runs-on: ubuntu-24.04
timeout-minutes: 30
# ubuntu:26.10 ships gettext 1.0, the first release whose xgettext
Expand Down Expand Up @@ -428,8 +394,8 @@

flatpak-devel:
name: Flatpak Devel (${{ matrix.arch }})
needs: detect
if: needs.detect.outputs.linux == 'true'
needs: changes
if: ${{ !cancelled() && needs.changes.outputs.run != 'false' }}
strategy:
fail-fast: false
matrix:
Expand Down Expand Up @@ -467,8 +433,8 @@

integration:
name: Docker tests (${{ matrix.package }})
needs: [detect, fast]
if: needs.detect.outputs.linux == 'true'
needs: [changes, fast]
if: ${{ !cancelled() && needs.changes.outputs.run != 'false' && needs.fast.result == 'success' }}
runs-on: ubuntu-24.04
timeout-minutes: 90
strategy:
Expand Down Expand Up @@ -503,7 +469,7 @@
- uses: dtolnay/rust-toolchain@683b588c487a69d01f6c6ddf40f036ff682cc2c8 # master
with:
toolchain: "1.98"
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2

Check failure on line 472 in .github/workflows/build-linux.yml

View workflow job for this annotation

GitHub Actions / Supply chain

cache-poisoning

build-linux.yml:472: runtime artifacts potentially vulnerable to a cache poisoning attack: omitting `lookup-only` enables caching
with:
workspaces: linux
key: ${{ matrix.package }}
Expand All @@ -530,8 +496,8 @@
# is mounted; testcontainers sees /.dockerenv and reaches the
# containers it starts through the bridge gateway.
name: Docker tests (tablepro-session)
needs: [detect, fast]
if: needs.detect.outputs.linux == 'true'
needs: [changes, fast]
if: ${{ !cancelled() && needs.changes.outputs.run != 'false' && needs.fast.result == 'success' }}
runs-on: ubuntu-24.04
timeout-minutes: 90
container:
Expand Down Expand Up @@ -568,7 +534,7 @@
- uses: dtolnay/rust-toolchain@683b588c487a69d01f6c6ddf40f036ff682cc2c8 # master
with:
toolchain: "1.98"
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2

Check failure on line 537 in .github/workflows/build-linux.yml

View workflow job for this annotation

GitHub Actions / Supply chain

cache-poisoning

build-linux.yml:537: runtime artifacts potentially vulnerable to a cache poisoning attack: omitting `lookup-only` enables caching
with:
workspaces: linux
key: tablepro-session
Expand All @@ -593,8 +559,8 @@
# session job. Only the named test targets run: the app's startup
# test needs Meson and the fast job already covers it.
name: Docker tests (tablepro)
needs: [detect, fast]
if: needs.detect.outputs.linux == 'true'
needs: [changes, fast]
if: ${{ !cancelled() && needs.changes.outputs.run != 'false' && needs.fast.result == 'success' }}
runs-on: ubuntu-24.04
timeout-minutes: 90
container:
Expand Down Expand Up @@ -632,7 +598,7 @@
- uses: dtolnay/rust-toolchain@683b588c487a69d01f6c6ddf40f036ff682cc2c8 # master
with:
toolchain: "1.98"
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2

Check failure on line 601 in .github/workflows/build-linux.yml

View workflow job for this annotation

GitHub Actions / Supply chain

cache-poisoning

build-linux.yml:601: runtime artifacts potentially vulnerable to a cache poisoning attack: omitting `lookup-only` enables caching
with:
workspaces: linux
key: tablepro-app
Expand All @@ -650,3 +616,21 @@
with:
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.
linux:
name: Linux
needs: [changes, fast, msrv, docs, supply-chain, i18n, flatpak-devel, integration, integration-session, integration-app]
if: always()
runs-on: ubuntu-24.04
timeout-minutes: 5
steps:
- name: Every Linux job passed or was skipped
env:
RESULTS: ${{ join(needs.*.result, ' ') }}
run: |
echo "$RESULTS"
case " $RESULTS " in
*" failure "* | *" cancelled "*) exit 1 ;;
esac
2 changes: 1 addition & 1 deletion .github/workflows/linux-packages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ on:
# 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]
branches: [main]
paths:
- "linux/**"
- ".github/workflows/linux-packages.yml"
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/release-linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ name: Release Linux
# LINUX_COPR_WEBHOOK: the custom webhook URL from the COPR project's
# Integrations page, ending in the package name: .../<uuid>/tablepro/.
# Variable LINUX_PPA: the PPA, as owner/name.
# Variable LINUX_BRANCH: the branch a release tag must be on, linux when unset.
# Variable LINUX_BRANCH: the branch a release tag must be on, main when unset.

on:
push:
Expand Down Expand Up @@ -68,7 +68,7 @@ jobs:
working-directory: linux
env:
TAG: ${{ github.ref_name }}
LINUX_BRANCH: ${{ vars.LINUX_BRANCH || 'linux' }}
LINUX_BRANCH: ${{ vars.LINUX_BRANCH || 'main' }}
run: |
git config --global --add safe.directory "$GITHUB_WORKSPACE"
if ! git merge-base --is-ancestor HEAD "origin/$LINUX_BRANCH"; then
Expand Down
3 changes: 2 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

A native macOS database client (SwiftUI and AppKit), a lightweight alternative to TablePlus. macOS 13.0+, Swift 6 language mode for the app targets (`Configs/Base.xcconfig`), Universal Binary. `TableProMobile/` is the iOS app.

This guide covers the macOS and iOS apps; another platform's app keeps its own guide beside its code. Claude Code loads it through `CLAUDE.md`, Codex directly.
This guide covers the macOS and iOS apps; `linux/AGENTS.md` covers the Linux app. Claude Code loads it through `CLAUDE.md`, Codex directly.

## Principles

Expand Down Expand Up @@ -36,6 +36,7 @@ Every capability is core, a module or a plugin. The first rule that fits decides
- Tests: `TableProTests/`, `TableProUITests/`, `Plugins/*Tests/`, `Packages/*/Tests/`, `TableProMobile/TableProMobileTests/`; plans in `TestPlans/`. Which holds what: `.claude/rules/tests.md`.
- `Native/`: first-party Rust and Go bridges (`.claude/rules/native-bridges.md`).
- `Libs/`: static libraries and iOS xcframeworks from `scripts/download-libs.sh`, not in git.
- `linux/`: the Linux app, a GTK 4 app built with Meson and Cargo.
- Docs site: TableProApp/docs, cloned beside this repo as `../tablepro-docs`, under its own `AGENTS.md` and `STYLE.md`.
- `TablePro.xcodeproj` and `TableProMobile/TableProMobile.xcodeproj` are generated by XcodeGen from their `project.yml`, gitignored except their `Package.resolved`; never hand-edit them. Signing overrides go in `Configs/Secrets.xcconfig`.

Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Contributing to TablePro

[`AGENTS.md`](AGENTS.md) holds the invariants and the rules every change meets; where this file and it disagree, it wins. The docs site lives in [TableProApp/docs](https://github.com/TableProApp/docs), and releasing is in [`RELEASING.md`](RELEASING.md).
[`AGENTS.md`](AGENTS.md) holds the invariants and the rules every change meets; where this file and it disagree, it wins. The docs site lives in [TableProApp/docs](https://github.com/TableProApp/docs), and releasing is in [`RELEASING.md`](RELEASING.md). The Linux app has its own setup and rules in [`linux/CONTRIBUTING.md`](linux/CONTRIBUTING.md).

## Setup

Expand Down
4 changes: 3 additions & 1 deletion README.ko.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ TablePro는 네 번째 선택지입니다. 네이티브이고 여러 데이터
|----------|--------|
| macOS 13+ | 안정 버전 |
| iOS / iPadOS 18+ | 안정 버전 |
| Linux | 프로토타입, 아직 설치할 수 없음 |
| Linux (GNOME 50+) | [`linux/`](linux/README.md)에서 개발 중, 아직 출시 전 |
| Windows | 지원하지 않음 |

## 지원 데이터베이스
Expand Down Expand Up @@ -104,6 +104,8 @@ brew install --cask tablepro

## 빌드 방법

아래 단계는 macOS와 iOS 앱을 빌드합니다. Linux 앱은 Meson과 Cargo로 빌드합니다([`linux/README.md`](linux/README.md)).

TablePro를 빌드하려면 Xcode 26 이상과 [XcodeGen](https://github.com/yonaskolb/XcodeGen)이 필요합니다.

저장소 루트에서 최초 설정을 실행하세요.
Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ TablePro is the missing fourth: native, multi-database, and open source.
|----------|--------|
| macOS 13+ | Stable |
| iOS / iPadOS 18+ | Stable |
| Linux | Prototype, nothing to install yet |
| Linux (GNOME 50+) | In development in [`linux/`](linux/README.md), not released yet |
| Windows | No |

## Supported Databases
Expand Down Expand Up @@ -104,6 +104,8 @@ Or download from [GitHub Releases](https://github.com/TableProApp/TablePro/relea

## How to Build

These steps build the macOS and iOS apps; the Linux app builds with Meson and Cargo ([`linux/README.md`](linux/README.md)).

Building TablePro requires Xcode 26 or later and
[XcodeGen](https://github.com/yonaskolb/XcodeGen).

Expand Down
2 changes: 1 addition & 1 deletion README.vi.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ TablePro là mảnh thứ tư còn thiếu: native, đa database, và mã nguồ
|----------|-----------|
| macOS 13+ | Ổn định |
| iOS / iPadOS 18+ | Ổn định |
| Linux | Bản thử nghiệm, chưa có gì để cài |
| Linux (GNOME 50+) | Đang phát triển trong [`linux/`](linux/README.md), chưa phát hành |
| Windows | Không |

## Database hỗ trợ
Expand Down
2 changes: 1 addition & 1 deletion README.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ TablePro 补上缺失的第四类:原生、多数据库、开源。
|------|------|
| macOS 13+ | 稳定版 |
| iOS / iPadOS 18+ | 稳定版 |
| Linux | 原型阶段,暂无可安装版本 |
| Linux (GNOME 50+) | 正在 [`linux/`](linux/README.md) 中开发,尚未发布 |
| Windows | 无 |

## 支持的数据库
Expand Down
2 changes: 1 addition & 1 deletion RELEASING.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Releasing

This runbook covers the macOS app, its registry plugins and the static libraries; the iOS app ships by hand. The `release` skill (`.claude/skills/release/SKILL.md`) decides what ships and points at the sections here. Every command below is the maintainer's to run.
This runbook covers the macOS app, its registry plugins and the static libraries; the iOS app ships by hand, and the Linux app's runbook is `linux/docs/releasing.md`. The `release` skill (`.claude/skills/release/SKILL.md`) decides what ships and points at the sections here. Every command below is the maintainer's to run.

## Channels

Expand Down
4 changes: 0 additions & 4 deletions linux/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,6 @@ The native Linux app: Rust, GTK 4 and libadwaita through gtk-rs, built with Meso

This is the guide for work under `linux/`. The root `AGENTS.md` describes the macOS and iOS apps; of it, only the principles named below, "Writing style" and the commit rules apply here. Claude Code loads this file through `linux/CLAUDE.md` once a file under `linux/` is read; Codex reads it when started in `linux/`, so otherwise read it yourself.

## Branch

`linux/` lives on the `linux` branch until the first Linux release, and `main` does not have it. Start work from `origin/linux` and open the pull request against `linux`.

## Principles

1. **Security first, fix the cause, tests define behavior**, as in the root `AGENTS.md`.
Expand Down
2 changes: 1 addition & 1 deletion linux/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ A user-visible change gets one entry under `[Unreleased]` in [CHANGELOG.md](CHAN

## Pull requests

1. Branch from `linux`, where this subproject lives until the first Linux release, and open the PR against `linux`. Branch name format: `feat/short-slug`, `fix/short-slug`, `refactor/short-slug`.
1. Branch off `main` and open the PR against `main`, as the root [CONTRIBUTING.md](../CONTRIBUTING.md#branch-naming) says.
2. PR title is the conventional commit message you intend to land.
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.
Expand Down
Loading
Loading