Skip to content

Development

github-actions[bot] edited this page Oct 17, 2026 · 1 revision

How to build, run, debug and release the client from source. Read CONTRIBUTING.md for the review rules and ARCHITECTURE.md for how the pieces fit together.

Setup

You need Rust 1.96 or newer and the native development packages listed under Build dependencies for your distribution. Then:

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:

systemctl --user stop proton-drive.service
RUST_LOG=pdfs_fuse=debug,info cargo run -p pdfs-cli -- daemon

In another terminal:

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.

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:

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, which CI runs only after a push to main, and the acceptance suite on a test account.

For changes to GUI strings, check that every catalog is complete:

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:

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 Topics 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. 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.

A bug fix. Add or update the entry in BUGS.md, 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. 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. 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 (secret COPR_CONFIG);
  7. calls .github/workflows/wiki.yml, which publishes docs/ at the tag to the wiki.

To prepare a release:

# 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. Open release-assurance items are listed in ROADMAP.md.

Clone this wiki locally