Compare commits

..
Author SHA1 Message Date
nvrl 170c3f01ff chore(release): 3.2.1 2026-10-11 19:19:36 +02:00
nvrl 976ad2aa04 docs(changelog): list the issue #35 fixes under Unreleased
The CPU spin while offline, the expired session shown as offline, the
lost refresh tokens and the keyring-refused rotation were fixed without
a changelog entry.
2026-10-11 18:56:55 +02:00
nvrl b73cd087aa test(core): time the B12 index write as the best of five runs
A single timed run lost the CPU to the rest of the suite running
alongside it and read as the full-scan regression (171 ms against
701 ms). The fastest of five runs still catches the 6x scaling of the
old path.
2026-10-11 18:56:55 +02:00
14 changed files with 38 additions and 340 deletions

No files matched your search

-52
View File
@@ -1,52 +0,0 @@
name: Nix
# Builds the flake (docs/INSTALL.md#nixos-flake) whenever its recipe, the lock
# files or the code change, so a NixOS user's `nix build` keeps working. Run it
# from the Actions tab for any branch to get a build to try.
on:
push:
paths:
- 'flake.nix'
- 'flake.lock'
- 'packaging/nix/**'
- 'Cargo.lock'
- 'Cargo.toml'
- 'crates/**'
- 'po/**'
- '.github/workflows/nix.yml'
pull_request:
paths:
- 'flake.nix'
- 'flake.lock'
- 'packaging/nix/**'
- 'Cargo.lock'
- '.github/workflows/nix.yml'
workflow_dispatch:
permissions:
contents: read
concurrency:
group: nix-${{ github.ref }}
cancel-in-progress: true
jobs:
build:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v7
- uses: cachix/install-nix-action@v31
- name: Check the flake
run: nix flake check --print-build-logs --no-build
- name: Build the package
run: nix build --print-build-logs .#proton-drive-linux
- name: Smoke test
run: |
./result/bin/pdfs --version
for bin in pdfs-app pdfs-tray pdfs-prompt; do test -x "result/bin/$bin"; done
grep -q "ExecStart=$(readlink -f result)/bin/pdfs" result/lib/systemd/user/proton-drive.service
ls result/share/locale/*/LC_MESSAGES/pdfs.mo
-2
View File
@@ -3,5 +3,3 @@
*.pdb
.env
/rpmbuild
/result
/result-*
Generated
+4 -4
View File
@@ -3474,7 +3474,7 @@ checksum = "2ee67f1008b1ba2321834326597b8e186293b049a023cdef258527550b9935b4"
[[package]]
name = "pdfs-cli"
version = "3.2.0"
version = "3.2.1"
dependencies = [
"anyhow",
"clap",
@@ -3490,7 +3490,7 @@ dependencies = [
[[package]]
name = "pdfs-core"
version = "3.2.0"
version = "3.2.1"
dependencies = [
"async-trait",
"base64 0.23.1",
@@ -3516,7 +3516,7 @@ dependencies = [
[[package]]
name = "pdfs-fuse"
version = "3.2.0"
version = "3.2.1"
dependencies = [
"async-trait",
"base64 0.23.1",
@@ -3538,7 +3538,7 @@ dependencies = [
[[package]]
name = "pdfs-gui"
version = "3.2.0"
version = "3.2.1"
dependencies = [
"anyhow",
"async-channel",
+1 -1
View File
@@ -3,7 +3,7 @@ resolver = "2"
members = ["crates/pdfs-core", "crates/pdfs-fuse", "crates/pdfs-cli", "crates/pdfs-gui"]
[workspace.package]
version = "3.2.0"
version = "3.2.1"
edition = "2024"
license = "MIT"
authors = ["Nils Pukropp <contact@narl.io>"]
-1
View File
@@ -76,7 +76,6 @@ scriptable CLI.
| Debian, Ubuntu 24.04 or newer | `.deb` from the [latest release](https://github.com/narrrl/proton-drive-linux/releases/latest) |
| Fedora 44 or newer | `.rpm` from the [latest release](https://github.com/narrrl/proton-drive-linux/releases/latest) |
| Arch Linux | [`proton-drive-for-linux`](https://aur.archlinux.org/packages/proton-drive-for-linux) (also `-bin` and `-git`) in the AUR |
| NixOS | The flake in this repository: see [Install → NixOS](docs/INSTALL.md#nixos-flake) |
| Anything else | [Build from source](docs/INSTALL.md#build-from-source) |
```bash
+10 -3
View File
@@ -1689,9 +1689,16 @@ fn writing_nodes_does_not_scale_with_the_size_of_the_index() {
.collect();
db.upsert_nodes(&batch).unwrap();
let t = Instant::now();
db.upsert_nodes(&batch).unwrap();
t.elapsed()
// The fastest of a few runs: the rest of the suite runs alongside, and
// one run that lost the CPU to it read as the regression.
(0..5)
.map(|_| {
let t = Instant::now();
db.upsert_nodes(&batch).unwrap();
t.elapsed()
})
.min()
.unwrap()
}
let small = cost_against_index_of(200);
+21 -5
View File
@@ -11,11 +11,9 @@ scratch (user data in `staging/` and `recovery/` is never touched by this).
## [Unreleased]
### Added
- **A Nix flake for NixOS.** `nix build github:narrrl/proton-drive-linux` builds the client, and
the flake's NixOS module installs it and runs the mount in every graphical session. See
[Install → NixOS](INSTALL.md#nixos-flake).
([#37](https://github.com/narrrl/proton-drive-linux/issues/37))
## [3.2.1] — 2026-10-11
Schema: **42**, unchanged.
### Fixed
- **Writing to a file again soon after a partial write could fail with an I/O error.** When the
@@ -26,6 +24,22 @@ scratch (user data in `staging/` and `recovery/` is never touched by this).
the app, as on secureblue, the app now offers it in your browser and continues the sign-in
once you are done. `pdfs login` now handles a CAPTCHA the same way instead of failing.
([#29](https://github.com/narrrl/proton-drive-linux/issues/29))
- **The daemon kept several CPU cores busy while offline with changes queued.** Its upload
workers checked the queue in a tight loop for as long as the outage lasted. They now sleep
until the connection comes back. ([#35](https://github.com/narrrl/proton-drive-linux/issues/35))
- **An expired sign-in was shown as "offline" for good.** When Proton refuses the session, the
tray now says "Session expired — sign in again" with a Sign In Again… item, the app shows the
same with a button, and `pdfs status` and `pdfs diagnose` tell you to run `pdfs login`. Cached
files stay readable and changes stay queued until you sign in.
([#35](https://github.com/narrrl/proton-drive-linux/issues/35))
- **The sign-in could be lost after `pdfs login`, `pdfs unlock` or a slow network.** The old
daemon wrote its spent tokens over a new sign-in when it stopped, a timeout could drop a token
refresh halfway, and a refresh was retried with a token Proton had already used. Each process
now writes only into its own session, a refresh always finishes, and it is never repeated.
([#35](https://github.com/narrrl/proton-drive-linux/issues/35))
- **A sign-in refreshed while the keyring was locked was lost on the next restart.** The client
now keeps retrying, holds the new tokens in the kernel keyring meanwhile, and the tray says
"Sign-in not saved — unlock your keyring" until it is written.
## [3.2.0] — 2026-10-08
@@ -1722,6 +1736,8 @@ First stable release: FUSE files-on-demand mount, sync daemon under `proton-driv
- The outstanding FUSE defects tracked in `docs/BUGS.md`, plus a truncate defect, validated
by a new POSIX compliance suite for the filesystem.
[3.2.1]: https://github.com/narrrl/proton-drive-linux/releases/tag/v3.2.1
[3.2.0]: https://github.com/narrrl/proton-drive-linux/releases/tag/v3.2.0
[3.1.2]: https://github.com/narrrl/proton-drive-linux/releases/tag/v3.1.2
-33
View File
@@ -82,39 +82,6 @@ paru -S proton-drive-for-linux # or yay, or any other AUR helper
The recipes live in [`packaging/aur/`](../packaging/aur/README.md).
### NixOS (flake)
The repository is a flake. Add it as an input and enable the module, which installs the package,
links the user service into every graphical session and turns on the `fusermount3` wrapper and
GNOME Keyring for you:
```nix
{
inputs.proton-drive-linux.url = "github:narrrl/proton-drive-linux";
outputs = { nixpkgs, proton-drive-linux, ... }: {
nixosConfigurations.<host> = nixpkgs.lib.nixosSystem {
modules = [
proton-drive-linux.nixosModules.default
{ services.proton-drive-linux.enable = true; }
];
};
};
}
```
To try it without changing your system, run the app straight from the flake:
```bash
nix run github:narrrl/proton-drive-linux#proton-drive-linux -- --help # the CLI
nix shell github:narrrl/proton-drive-linux -c pdfs-app # the desktop app
```
The mount itself needs the setuid `fusermount3` from NixOS (`programs.fuse.enable`, on by default
with the module) and a Secret Service such as GNOME Keyring or KWallet. To leave out ffmpeg and
with it video thumbnails, override the package with `withFfmpeg = false`. The recipe lives in
[`packaging/nix/`](../packaging/nix/).
### Other distributions
Unpack the `.tar.gz` into a directory on your `PATH`, then install the service unit as shown in
Generated
-27
View File
@@ -1,27 +0,0 @@
{
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1791456040,
"narHash": "sha256-H83yz/do+hjOO0uujHyH8RgtIQP2v7Mw+cLwcZK/xKc=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "e7439b6b14ad3cc35d05608ebca9bce01a25f5f8",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}
-41
View File
@@ -1,41 +0,0 @@
{
description = "Unofficial Proton Drive client for Linux: files-on-demand FUSE mount, CLI, GTK4 app and tray";
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
outputs =
{ self, nixpkgs }:
let
systems = [
"x86_64-linux"
"aarch64-linux"
];
forAllSystems = f: nixpkgs.lib.genAttrs systems (system: f nixpkgs.legacyPackages.${system});
in
{
packages = forAllSystems (pkgs: rec {
proton-drive-linux = pkgs.callPackage ./packaging/nix/package.nix { };
default = proton-drive-linux;
});
overlays.default = final: _: {
proton-drive-linux = final.callPackage ./packaging/nix/package.nix { };
};
nixosModules.default = import ./packaging/nix/module.nix self;
devShells = forAllSystems (pkgs: {
default = pkgs.mkShell {
inputsFrom = [ self.packages.${pkgs.stdenv.hostPlatform.system}.proton-drive-linux ];
packages = with pkgs; [
cargo
clippy
rustfmt
rust-analyzer
];
};
});
formatter = forAllSystems (pkgs: pkgs.nixfmt);
};
}
+1 -1
View File
@@ -9,7 +9,7 @@
# Build + install: cd packaging && makepkg -fi
pkgname=proton-drive-linux
pkgver=3.2.0
pkgver=3.2.1
pkgrel=1
pkgdesc="Unofficial Proton Drive client for Linux: files-on-demand FUSE mount, CLI, GTK4 app + tray"
arch=('x86_64')
-42
View File
@@ -1,42 +0,0 @@
# NixOS module: installs the package and runs the mount daemon in every
# graphical session, as the other packages' user service does.
#
# services.proton-drive-linux.enable = true;
self:
{
config,
lib,
pkgs,
...
}:
let
cfg = config.services.proton-drive-linux;
in
{
options.services.proton-drive-linux = {
enable = lib.mkEnableOption "the Proton Drive for Linux mount daemon, app and tray";
package = lib.mkOption {
type = lib.types.package;
default = self.packages.${pkgs.stdenv.hostPlatform.system}.proton-drive-linux;
defaultText = lib.literalExpression "proton-drive-linux.packages.\${system}.proton-drive-linux";
description = "The package to use.";
};
};
config = lib.mkIf cfg.enable {
environment.systemPackages = [ cfg.package ];
# The unit ships in the package; this links it and pulls it into the
# graphical session, which is what `systemctl --user enable` would do.
systemd.packages = [ cfg.package ];
systemd.user.services.proton-drive = {
wantedBy = [ "graphical-session.target" ];
# The mount runs fusermount3 from PATH, and it has to be the setuid
# wrapper; the PATH NixOS gives a unit does not include it.
path = [ "/run/wrappers" ];
};
programs.fuse.enable = lib.mkDefault true;
# Session tokens are kept in the Secret Service.
services.gnome.gnome-keyring.enable = lib.mkDefault true;
};
}
-127
View File
@@ -1,127 +0,0 @@
# Nix package for the workspace, built from the checkout this file lives in.
# The flake at the repository root exposes it; `nix build` builds it.
{
lib,
rustPlatform,
adwaita-icon-theme,
pkg-config,
gettext,
glib,
wrapGAppsHook4,
fuse3,
glib-networking,
gtk4,
libadwaita,
webkitgtk_6_0,
libsecret,
dbus,
exiftool,
ffmpeg-headless,
withFfmpeg ? true,
}:
let
cargoToml = lib.importTOML ../../Cargo.toml;
in
rustPlatform.buildRustPackage {
pname = "proton-drive-linux";
inherit (cargoToml.workspace.package) version;
# Only what the build reads, so editing docs or CI does not rebuild.
src = lib.fileset.toSource {
root = ../..;
fileset = lib.fileset.unions [
../../Cargo.toml
../../Cargo.lock
../../crates
../../po
../../LICENSE
../proton-drive.service
../io.narl.proton-drive-linux.desktop
../io.narl.proton-drive-linux-tray.desktop
../io.narl.proton-drive-linux.svg
];
};
cargoLock.lockFile = ../../Cargo.lock;
cargoBuildFlags = [
"--bin"
"pdfs"
"--bin"
"pdfs-tray"
"--bin"
"pdfs-app"
"--bin"
"pdfs-prompt"
];
nativeBuildInputs = [
pkg-config
gettext # msgfmt, for po/build.sh
glib # glib-compile-resources, for the GUI's build.rs
wrapGAppsHook4
];
buildInputs = [
fuse3
# TLS for WebKitGTK's GIO: without it the CAPTCHA page fails with "TLS
# support is not available". wrapGAppsHook4 adds it to GIO_EXTRA_MODULES.
glib-networking
# The stock icons the app asks for, such as the Photos and Computers
# sidebar entries. GTK ships only a few itself, and a session without
# Adwaita installed showed the rest as missing. wrapGAppsHook4 adds it to
# XDG_DATA_DIRS.
adwaita-icon-theme
gtk4
libadwaita
webkitgtk_6_0
libsecret
dbus
];
# The app looks for its catalogs here instead of /usr/share/locale.
env.PDFS_LOCALEDIR = "${placeholder "out"}/share/locale";
# The suite mounts FUSE, talks to D-Bus and the kernel keyring, and checks
# permissions a build sandbox does not have; CI runs it on every push.
doCheck = false;
postInstall = ''
install -Dm644 packaging/io.narl.proton-drive-linux.desktop \
$out/share/applications/io.narl.proton-drive-linux.desktop
install -Dm644 packaging/io.narl.proton-drive-linux-tray.desktop \
$out/etc/xdg/autostart/io.narl.proton-drive-linux-tray.desktop
install -Dm644 packaging/io.narl.proton-drive-linux.svg \
$out/share/icons/hicolor/scalable/apps/io.narl.proton-drive-linux.svg
substituteInPlace $out/share/applications/io.narl.proton-drive-linux.desktop \
--replace-fail "Exec=pdfs-app" "Exec=$out/bin/pdfs-app"
substituteInPlace $out/etc/xdg/autostart/io.narl.proton-drive-linux-tray.desktop \
--replace-fail "Exec=pdfs-tray" "Exec=$out/bin/pdfs-tray"
install -Dm644 packaging/proton-drive.service \
$out/lib/systemd/user/proton-drive.service
substituteInPlace $out/lib/systemd/user/proton-drive.service \
--replace-fail "ExecStart=/usr/bin/pdfs" "ExecStart=$out/bin/pdfs"
patchShebangs po/build.sh
po/build.sh $out/share/locale
'';
# Thumbnails shell out to exiftool, and to ffmpeg for video. Suffixed, so a
# user's own builds on PATH win. fusermount3 comes from the system: on NixOS
# it is the setuid wrapper in /run/wrappers/bin.
preFixup = ''
gappsWrapperArgs+=(--suffix PATH : ${
lib.makeBinPath ([ exiftool ] ++ lib.optional withFfmpeg ffmpeg-headless)
})
'';
meta = {
description = "Unofficial Proton Drive client for Linux: files-on-demand FUSE mount, CLI, GTK4 app and tray";
homepage = "https://proton-drive.narl.io";
license = lib.licenses.mit;
platforms = lib.platforms.linux;
mainProgram = "pdfs";
};
}
+1 -1
View File
@@ -6,7 +6,7 @@
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: proton-drive-linux 3.2.0\n"
"Project-Id-Version: proton-drive-linux 3.2.1\n"
"Report-Msgid-Bugs-To: https://github.com/narrrl/proton-drive-linux/issues\n"
"POT-Creation-Date: 2026-10-09 12:17+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"