Files
proton-drive-linux/docs/DEVELOPMENT.md
T
nvrl 9c30ff8276 docs: publish docs/ to the GitHub wiki and reorganise the user guide
- scripts/publish-wiki.sh publishes every page listed in SECTIONS, copies
  images, writes a sectioned sidebar and footer, and removes stale pages.
  --check fails on links to missing files or headings and on docs pages
  that are neither published nor excluded.
- New wiki.yml runs the check on pull requests and pushes to main, and
  publishes when release.yml calls it or when started by hand.
- Split USER_GUIDE.md into topic pages (files, synced folders, photos,
  sharing, computers, launcher and tray); add Getting started and FAQ.
- Make the COPR repository the main Fedora path, with update and
  uninstall steps for every package type.
- Fix the stale CAPTCHA note in CLI.md, document pdfs unlock and lock,
  and drop the broken AGENTS.md link from CONTRIBUTING.md.
2026-10-17 13:57:55 +02:00

198 lines
9.4 KiB
Markdown

# Development guide
How to build, run, debug and release the client from source. Read
[CONTRIBUTING.md](../CONTRIBUTING.md) for the review rules and [ARCHITECTURE.md](ARCHITECTURE.md)
for how the pieces fit together.
- [Setup](#setup)
- [Repository layout](#repository-layout)
- [Running from source](#running-from-source)
- [Quality gates](#quality-gates)
- [Debugging](#debugging)
- [Common changes](#common-changes)
- [Releasing](#releasing)
## Setup
You need Rust 1.96 or newer and the native development packages listed under
[Build dependencies](INSTALL.md#build-dependencies) for your distribution. Then:
```bash
git clone https://github.com/narrrl/proton-drive-linux.git
cd proton-drive-linux
cargo build --workspace
```
The GUI builds against libadwaita 1.5, the version in Ubuntu 24.04, and uses newer widgets at run
time when the installed library has them (`crates/pdfs-gui/src/compat.rs`).
## Repository layout
| Path | Contents |
|---|---|
| `crates/pdfs-core` | Shared library: authentication and keyring (`auth`), configuration (`config`), SQLite schema and queries (`db/`), content cache (`cache`), control protocol (`control`), search scoring, sync ignore rules, Google Takeout parsing, machine profile |
| `crates/pdfs-fuse` | The daemon: FUSE handlers (`filesystem`), the tree (`state`), reads (`reads`), the queue and the drain (`queue`, `drain`, `upload`), link state (`link`), the sync engine (`sync`, `sync/`), moves between locations (`relocate`), remote events (`background`), push events (`events`), control handlers (`control`), photos, devices, the supervisor, and the simulation tests (`sim/`) |
| `crates/pdfs-cli` | The `pdfs` binary |
| `crates/pdfs-gui` | `pdfs-app`, `pdfs-tray` and `pdfs-prompt`. Pages in `src/app/pages/`, shared widgets in `src/app/widgets/`, CSS and icons in `resources/` |
| `po/` | Translation template, catalogs and the scripts that maintain them |
| `packaging/` | systemd unit, desktop files, icon, PKGBUILD, RPM spec, AUR recipes |
| `scripts/` | FUSE acceptance suite, map-data generators, the wiki publisher |
| `docs/` | This documentation |
The GUI talks to the daemon only through the control socket. Anything a front end needs that the
daemon does not offer yet is a new request in `pdfs_core::control`, not a database read.
## Running from source
The packaged service and a daemon run from source cannot share the same state directory. Stop the
service before you run your own build:
```bash
systemctl --user stop proton-drive.service
RUST_LOG=pdfs_fuse=debug,info cargo run -p pdfs-cli -- daemon
```
In another terminal:
```bash
cargo run -p pdfs-cli -- status
po/build.sh target/locale # compile translations once
PDFS_LOCALEDIR=target/locale cargo run -p pdfs-gui --bin pdfs-app
cargo run -p pdfs-gui --bin pdfs-tray
cargo run -p pdfs-gui --bin pdfs-prompt -- --fzf
```
To keep the systemd unit but point it at your build, see
[INSTALL.md](INSTALL.md#using-a-binary-outside-usrbin).
> [!CAUTION]
> Develop against a test account. The acceptance suite and a buggy daemon can delete or rewrite
> files in Proton Drive.
> [!TIP]
> A daemon stopped in a debugger or deadlocked freezes every process that touches the mount,
> including your shell if its prompt reads the current directory. Keep your shell outside
> `~/ProtonDrive`, prefix commands that touch it with `timeout`, and unmount a dead mount with
> `fusermount3 -uz ~/ProtonDrive`.
## Quality gates
CI (`.github/workflows/ci.yml`) runs these on every push to `main` and every pull request. Run them before you
push:
```bash
cargo fmt --all -- --check
cargo clippy --workspace --all-targets -- -D warnings
cargo test --workspace --locked
scripts/fuse-acceptance.sh --offline-only
```
For changes to the FUSE layer, the queue or the sync engine, also run the
[simulation runs](TESTING.md#simulation-runs), which CI runs only after a push to `main`, and the
[acceptance suite](TESTING.md#acceptance-suite) on a test account.
For changes to GUI strings, check that every catalog is complete:
```bash
for l in $(cat po/LINGUAS); do msgfmt --check --statistics -o /dev/null po/$l.po; done
```
## Debugging
| Tool | Use |
|---|---|
| `RUST_LOG` | Log filter for every binary, for example `RUST_LOG=pdfs_fuse::sync=trace,info` |
| `pdfs diagnose` | Static checks: directories, FUSE, keyring, socket, database integrity. Works without the daemon |
| `pdfs diagnostics` | Live state of a running daemon: each worker's current job and its age, queue depth, control requests in flight, memory |
| `pdfs cache inspect --deep` | Database size, row counts and SQLite `integrity_check` |
| `pdfs --json …` | Raw protocol payloads for query commands |
| `GTK_DEBUG=interactive` | The GTK inspector for `pdfs-app` |
| `PDFS_STAND_IN_WIDGETS=1` | Force the libadwaita 1.5 fallback widgets on a newer system |
| `G_DEBUG=fatal-criticals` | Turn GLib criticals into crashes to get a backtrace |
The databases are ordinary SQLite files. Open a copy, not the live file:
```bash
cp ~/.local/state/proton-drive-linux/cache.db /tmp/cache.db
sqlite3 /tmp/cache.db '.tables'
```
## Common changes
**A new control request.** Add the variant to `Request` (and a `Response` if it returns data) in
`crates/pdfs-core/src/control.rs`, handle it in `crates/pdfs-fuse/src/control.rs`, and list the
`Topic`s it changes in the same file so subscribed front ends refresh. A daemon from the previous
release may still be running when a new front end starts, so treat an error reply to a new
request as "not supported".
**A schema change.** Migrations are forward-only. Add a new step to
`crates/pdfs-core/src/db/migrations.rs` and bump `SCHEMA_VERSION`; never edit a shipped step. Add
a test in `crates/pdfs-core/src/db/tests.rs` that migrates from the previous version.
**A GUI string.** Wrap it in the helpers from `crates/pdfs-gui/src/i18n.rs`, add any new source
file to `po/POTFILES.in`, run `po/update.sh`, and translate the new entries in every catalog. See
[TRANSLATING.md](TRANSLATING.md). The CLI and daemon messages stay in English.
**A setting.** Add the field to `AppConfig` in `crates/pdfs-core/src/config.rs` with
`#[serde(default)]`, so older files still load, and document it in
[CONFIGURATION.md](CONFIGURATION.md).
**A bug fix.** Add or update the entry in [BUGS.md](BUGS.md#adding-an-entry), cover the fix with a
unit test, a simulation scenario or a `regression B<n>` acceptance case, and cite the entry in the
code as `docs/BUGS.md B<n>`. Do not cite planning notes that are not in the repository.
**A user-visible change.** Update the matching page (a user guide topic, the CLI reference,
configuration) and the `[Unreleased]` section of [CHANGELOG.md](CHANGELOG.md). Link between pages
with relative links such as `[Conflicts](SYNCED_FOLDERS.md#conflicts)`, and show screenshots from
`images/` with `../images/<name>.png`.
**The wiki.** `docs/` is the source of the [GitHub wiki](https://github.com/narrrl/proton-drive-linux/wiki).
`scripts/publish-wiki.sh` turns each page listed in its `SECTIONS` into a wiki page, rewrites the
links, copies the images and writes the sidebar. `docs/README.md` becomes the wiki's Home.
`BUGS.md` and `CHANGELOG.md` stay out of it; the wiki links to them on GitHub.
- `scripts/publish-wiki.sh --check` checks every link and heading anchor in `docs/`, `README.md`,
`CONTRIBUTING.md` and `SECURITY.md`. It fails on a page in `docs/` that is not in `SECTIONS`
or `EXCLUDED`. `.github/workflows/wiki.yml` runs it for every change to the documentation.
- Each release publishes the wiki from the tag. Run the **Wiki** workflow from the Actions tab to
publish `main` in between, for example after fixing a mistake.
- `scripts/publish-wiki.sh [WORKDIR]` builds and commits the wiki in a local clone without
pushing, so you can look at the result first. `--push` pushes it.
Never edit the wiki in the browser: the next publish replaces every page.
## Releasing
Releases are cut from `main` by pushing a `v*` tag. `.github/workflows/release.yml` then:
1. checks that the tag, the workspace version in `Cargo.toml`, `pkgver` in
`packaging/PKGBUILD` and the default version in `packaging/proton-drive-linux.spec` agree;
2. runs the quality gates;
3. builds the release binaries and packages a `.tar.gz`, a `.deb`, a GPG-signed `.rpm` and a
`.tar.xz` of the vendored crates, and builds the Nix flake at the tag, which is what a flake
input pinned to `vX.Y.Z` gets, and pushes it to the `proton-drive-linux` Cachix cache (secret
`CACHIX_AUTH_TOKEN`);
4. publishes them as a GitHub release, with the version's section of `docs/CHANGELOG.md` as the
notes;
5. calls `.github/workflows/aur.yml`, which updates and pushes the AUR recipes and commits them
back to `main`;
6. calls `.github/workflows/copr.yml`, which builds the release in the
[COPR repository](../packaging/copr/README.md) (secret `COPR_CONFIG`);
7. calls `.github/workflows/wiki.yml`, which publishes `docs/` at the tag to the wiki.
To prepare a release:
```bash
# 1. Bump the version in Cargo.toml, packaging/PKGBUILD, packaging/proton-drive-linux.spec,
# po/pdfs.pot and the pinned flake URL in docs/INSTALL.md, then refresh the lock file.
cargo update --workspace
# 2. Move the [Unreleased] notes in docs/CHANGELOG.md under the new version.
# 3. Commit, tag and push.
git commit -am "chore(release): X.Y.Z"
git tag vX.Y.Z
git push origin main vX.Y.Z
```
Before a release, work through [Before a release](TESTING.md#before-a-release). Open
release-assurance items are listed in [ROADMAP.md](ROADMAP.md#release-assurance).