Compare commits

..
Author SHA1 Message Date
nvrl 2930b457d4 fix(docs): exclude SDK review from wiki 2026-11-05 19:18:18 +01:00
nvrl d9da0e2263 build(sdk): bump to 0.11.0 2026-11-05 19:12:21 +01:00
Nils Pukropp 800705ccb0 chore(aur): update packages to 3.7.0 2026-11-02 15:16:31 +00:00
nvrl 7ab14bb8f7 Merge pull request #76 from narrrl/chore/release-3.7.0
chore(release): 3.7.0
2026-11-02 15:34:08 +01:00
18 changed files with 156 additions and 36 deletions

No files matched your search

Generated
+6 -4
View File
@@ -3540,6 +3540,7 @@ version = "3.7.0"
dependencies = [
"async-trait",
"base64 0.23.1",
"chrono",
"dbus-secret-service-keyring-store",
"directories",
"ignore",
@@ -3825,13 +3826,14 @@ dependencies = [
[[package]]
name = "proton-drive-rs"
version = "0.10.1"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8fa2a88130883010b32035e208aaa36fb275f6d7d78ed44a1069b1d33d24481"
checksum = "14a22e621abbc1c61b4640728a7de969c16b66d5d983b59c638d501fda25a7b9"
dependencies = [
"async-trait",
"base64 0.23.1",
"bytes",
"chrono",
"futures",
"getrandom 0.4.3",
"hex",
@@ -3852,9 +3854,9 @@ dependencies = [
[[package]]
name = "proton-sdk"
version = "0.10.1"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af0bf8d50fa829d1a65b09c6855b606578d02734d4089874c971d1063c23c353"
checksum = "bd55a5e8b6600f3aa282ebbdf13ff366431ff9aae84d4eb20b0ef06d598919fe"
dependencies = [
"aes-gcm 0.11.1",
"async-trait",
+3 -2
View File
@@ -15,8 +15,9 @@ keywords = ["proton", "drive", "fuse", "sync", "linux"]
categories = ["filesystem", "command-line-utilities"]
[workspace.dependencies]
proton-sdk = { version = "0.10.1" }
proton-drive-rs = { version = "0.10.1" }
proton-sdk = { version = "0.11.0" }
proton-drive-rs = { version = "0.11.0" }
chrono = { version = "0.4", default-features = false, features = ["std"] }
# Internal crates.
pdfs-core = { path = "crates/pdfs-core" }
+1
View File
@@ -12,6 +12,7 @@ description = "Core auth/session/config for the Proton Drive Linux client"
[dependencies]
proton-sdk.workspace = true
proton-drive-rs.workspace = true
chrono.workspace = true
tokio.workspace = true
serde.workspace = true
serde_json.workspace = true
+1
View File
@@ -29,6 +29,7 @@ pub mod similar;
pub mod syncignore;
pub mod syncroot;
pub mod takeout;
pub mod timestamp;
pub mod tray;
pub mod update;
+33
View File
@@ -0,0 +1,33 @@
//! Convert the client's stored epoch seconds at the SDK boundary.
use proton_drive_rs::ModificationTime;
/// A UTC modification time, or `None` when outside the SDK's supported range.
pub fn modification_time(seconds: i64) -> Option<ModificationTime> {
chrono::DateTime::from_timestamp(seconds, 0).map(|time| time.fixed_offset())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn epoch_seconds_preserve_the_instant_including_before_1970() {
for seconds in [-1, 0, 1_709_208_000, 1_782_482_460] {
let time = modification_time(seconds).unwrap();
assert_eq!(time.timestamp(), seconds);
assert_eq!(time.offset().local_minus_utc(), 0);
assert_eq!(time.timestamp_subsec_nanos(), 0);
}
assert_eq!(
modification_time(-1).unwrap().to_rfc3339(),
"1969-12-31T23:59:59+00:00"
);
}
#[test]
fn out_of_range_seconds_omit_the_timestamp() {
assert!(modification_time(i64::MIN).is_none());
assert!(modification_time(i64::MAX).is_none());
}
}
+2
View File
@@ -938,6 +938,8 @@ fn handle_control_conn(core: &Core, username: &str, mountpoint: &Path, stream: U
});
let metadata = proton_drive_rs::PhotoUploadMetadata {
capture_time,
last_modification_time: capture_time
.and_then(pdfs_core::timestamp::modification_time),
..Default::default()
};
let thumbnails = core.upload_thumbnails(Path::new(&source_path), &name);
+10 -4
View File
@@ -346,7 +346,13 @@ impl DriveApi for ProtonDriveClient {
name: &str,
last_modification_time: Option<i64>,
) -> Result<NodeUid> {
ProtonDriveClient::create_folder(self, parent_uid, name, last_modification_time).await
ProtonDriveClient::create_folder(
self,
parent_uid,
name,
last_modification_time.and_then(pdfs_core::timestamp::modification_time),
)
.await
}
async fn upload_file(
@@ -378,7 +384,7 @@ impl DriveApi for ProtonDriveClient {
reader,
intended_size,
thumbnails,
last_modification_time,
last_modification_time.and_then(pdfs_core::timestamp::modification_time),
aead,
)
.await
@@ -403,7 +409,7 @@ impl DriveApi for ProtonDriveClient {
reader,
intended_size,
thumbnails,
last_modification_time,
last_modification_time.and_then(pdfs_core::timestamp::modification_time),
aead,
)
.await
@@ -423,7 +429,7 @@ impl DriveApi for ProtonDriveClient {
reader,
intended_size,
thumbnails,
last_modification_time,
last_modification_time.and_then(pdfs_core::timestamp::modification_time),
)
.await
}
+1
View File
@@ -200,6 +200,7 @@ impl Core {
let reader = CountingReader::new(std::io::BufReader::new(file), &guard);
let metadata = PhotoUploadMetadata {
capture_time: Some(candidate.wanted),
last_modification_time: pdfs_core::timestamp::modification_time(candidate.wanted),
..Default::default()
};
let uploaded = self.rt.block_on(photos.upload_photo_from(
+9 -3
View File
@@ -344,6 +344,9 @@ impl Core {
);
let metadata = PhotoUploadMetadata {
capture_time: photo.capture_time,
last_modification_time: photo
.capture_time
.and_then(pdfs_core::timestamp::modification_time),
..Default::default()
};
// A thumbnail is made from a file, so a photo that can have one
@@ -365,10 +368,13 @@ impl Core {
let thumbnails = self.upload_thumbnails(path, &photo.name);
// No sidecar and no date in the name: the photo's own
// EXIF, before Drive files it under today.
let capture_time = metadata
.capture_time
.or_else(|| pdfs_core::exiftime::capture_time_of(path));
let metadata = PhotoUploadMetadata {
capture_time: metadata
.capture_time
.or_else(|| pdfs_core::exiftime::capture_time_of(path)),
capture_time,
last_modification_time: capture_time
.and_then(pdfs_core::timestamp::modification_time),
..metadata
};
std::fs::File::open(path)
+7
View File
@@ -11,6 +11,13 @@ scratch (user data in `staging/` and `recovery/` is never touched by this).
## [Unreleased]
### Changed
- Update `proton-sdk` and `proton-drive-rs` to 0.11.0.
Convert epoch timestamps at the SDK boundary and explicitly preserve photo modification
times now that they are independent of capture times. See [the SDK review](SDK-0.11.0.md)
for features and breaking changes.
## [3.7.0] — 2026-11-02
Schema: **44**, unchanged.
+60
View File
@@ -0,0 +1,60 @@
# SDK 0.11.0 review
Reviewed the sibling `../proton-sdk-rs` checkout at `84039a1` against 0.10.1
(`48dd410`), including its changelog and source diff.
## Features
- Folder creation, streaming uploads, draft replacement, and revision uploads
preserve the supplied timestamp's UTC offset and fractional seconds.
- `ModificationTime` is exported as an alias for
`chrono::DateTime<chrono::FixedOffset>`.
- Photo modification time can be set independently of capture time. Downloaded
claimed modification timestamps remain verbatim strings.
- Neither SDK crate defines Cargo feature flags. No feature opt-in is needed.
The Drive crate now depends directly on `chrono` 0.4; the SDK already used it.
The minimum Rust version remains 1.96, matching this workspace.
## Breaking changes and adaptation
The modification-time argument changes from `Option<i64>` to
`Option<ModificationTime>` on `create_folder`, `upload_file_from`,
`upload_file_replacing_draft_from`, `upload_new_revision`, and
`upload_new_revision_from`. `None` remains valid.
The real `DriveApi` adapter converts the client's stored epoch seconds to UTC
through `pdfs_core::timestamp::modification_time`. Internal storage, the trait,
and the simulated Drive continue using epoch seconds. Out-of-range timestamps
are omitted rather than panicking. Existing upload paths still supply whole
seconds; this bump alone does not add subsecond storage or offset tracking.
`PhotoUploadMetadata` gains `last_modification_time`, and the SDK no longer
copies `capture_time` into it. Our literals already use `Default`, so this would
compile while silently changing uploaded metadata. Control uploads, Takeout
imports (including the EXIF fallback), and redating now set both explicitly to
preserve the previous behavior when a capture time is known. Without a known
capture time, modification time stays omitted as before.
No other public API changes were found in the reviewed diff. No database or
recovery migration is required by this SDK bump.
## Dependencies
The workspace requires the published 0.11.0 versions of both crates from
crates.io. No local patches or sibling checkout are needed to build it.
## Verification
The published crates' source files match the reviewed sibling checkout at
`84039a1`. On the final branch based on `main`, these checks passed:
```sh
cargo test --workspace --locked
cargo fmt --all -- --check
cargo clippy --workspace --all-targets --locked -- -D warnings
```
The test suite reported 1,135 passed and 67 ignored; ignored tests were not run.
Tests ran outside the sandbox because control-socket tests require socket
permissions. No live account or mounted-filesystem acceptance testing was
performed.
@@ -1,6 +1,6 @@
pkgbase = proton-drive-for-linux-bin
pkgdesc = Unofficial Proton Drive client: FUSE files-on-demand mount, CLI, GTK4 app, tray (binaries)
pkgver = 3.6.0
pkgver = 3.7.0
pkgrel = 1
url = https://proton-drive.narl.io
install = proton-drive.install
@@ -22,21 +22,21 @@ pkgbase = proton-drive-for-linux-bin
optdepends = xdg-utils: "Open folder" action in the tray menu
optdepends = fzf: search-as-you-type launcher (pdfs-prompt --fzf)
optdepends = foot: terminal that hosts pdfs-prompt --fzf when started from a keybinding
provides = proton-drive-for-linux=3.6.0
provides = proton-drive-linux=3.6.0
provides = proton-drive-for-linux=3.7.0
provides = proton-drive-linux=3.7.0
provides = pdfs
conflicts = proton-drive-linux
conflicts = proton-drive-for-linux
conflicts = proton-drive-for-linux-git
options = !strip
options = !debug
source = proton-drive-linux-3.6.0-x86_64.tar.gz::https://github.com/narrrl/proton-drive-linux/releases/download/v3.6.0/proton-drive-linux-3.6.0-x86_64.tar.gz
source = io.narl.proton-drive-linux-3.6.0.desktop::https://raw.githubusercontent.com/narrrl/proton-drive-linux/v3.6.0/packaging/io.narl.proton-drive-linux.desktop
source = io.narl.proton-drive-linux-tray-3.6.0.desktop::https://raw.githubusercontent.com/narrrl/proton-drive-linux/v3.6.0/packaging/io.narl.proton-drive-linux-tray.desktop
source = io.narl.proton-drive-linux-3.6.0.svg::https://raw.githubusercontent.com/narrrl/proton-drive-linux/v3.6.0/packaging/io.narl.proton-drive-linux.svg
source = proton-drive-3.6.0.service::https://raw.githubusercontent.com/narrrl/proton-drive-linux/v3.6.0/packaging/proton-drive.service
source = LICENSE-3.6.0::https://raw.githubusercontent.com/narrrl/proton-drive-linux/v3.6.0/LICENSE
sha256sums = 8e9b76b0df238af1c8978c33b4c27c0c548e1faa81b368d0d271ed564d0a76c7
source = proton-drive-linux-3.7.0-x86_64.tar.gz::https://github.com/narrrl/proton-drive-linux/releases/download/v3.7.0/proton-drive-linux-3.7.0-x86_64.tar.gz
source = io.narl.proton-drive-linux-3.7.0.desktop::https://raw.githubusercontent.com/narrrl/proton-drive-linux/v3.7.0/packaging/io.narl.proton-drive-linux.desktop
source = io.narl.proton-drive-linux-tray-3.7.0.desktop::https://raw.githubusercontent.com/narrrl/proton-drive-linux/v3.7.0/packaging/io.narl.proton-drive-linux-tray.desktop
source = io.narl.proton-drive-linux-3.7.0.svg::https://raw.githubusercontent.com/narrrl/proton-drive-linux/v3.7.0/packaging/io.narl.proton-drive-linux.svg
source = proton-drive-3.7.0.service::https://raw.githubusercontent.com/narrrl/proton-drive-linux/v3.7.0/packaging/proton-drive.service
source = LICENSE-3.7.0::https://raw.githubusercontent.com/narrrl/proton-drive-linux/v3.7.0/LICENSE
sha256sums = 3f0d16c19b10b30e3f181e3c7fb439f0a8c38980e8ab7a4a96ae6596b349787c
sha256sums = d190d6771fff0b975271fb62fd3fb0fa8bbf17d7278c10853c18557855b0c123
sha256sums = 1d95e250370220e3bb6aead6ce34d1d6cc759f42c4803b75f1667e78ff47c6c0
sha256sums = e598a93c2715c3a9fdc7fb937902083d4a1135e8fbdfe6fe7e37f0383ebbd828
@@ -1,7 +1,7 @@
# Maintainer: Nils Pukropp <contact@narl.io>
pkgname=proton-drive-for-linux-bin
pkgver=3.6.0
pkgver=3.7.0
pkgrel=1
pkgdesc="Unofficial Proton Drive client: FUSE files-on-demand mount, CLI, GTK4 app, tray (binaries)"
arch=('x86_64')
@@ -36,7 +36,7 @@ source=("proton-drive-linux-$pkgver-x86_64.tar.gz::$_repo/releases/download/$_ta
"io.narl.proton-drive-linux-$pkgver.svg::$_raw/packaging/io.narl.proton-drive-linux.svg"
"proton-drive-$pkgver.service::$_raw/packaging/proton-drive.service"
"LICENSE-$pkgver::$_raw/LICENSE")
sha256sums=('8e9b76b0df238af1c8978c33b4c27c0c548e1faa81b368d0d271ed564d0a76c7'
sha256sums=('3f0d16c19b10b30e3f181e3c7fb439f0a8c38980e8ab7a4a96ae6596b349787c'
'd190d6771fff0b975271fb62fd3fb0fa8bbf17d7278c10853c18557855b0c123'
'1d95e250370220e3bb6aead6ce34d1d6cc759f42c4803b75f1667e78ff47c6c0'
'e598a93c2715c3a9fdc7fb937902083d4a1135e8fbdfe6fe7e37f0383ebbd828'
@@ -1,6 +1,6 @@
pkgbase = proton-drive-for-linux-git
pkgdesc = Unofficial Proton Drive client: FUSE files-on-demand mount, CLI, GTK4 app, tray (git)
pkgver = 3.6.0.r0.g2720191
pkgver = 3.7.0.r0.g7ab14bb
pkgrel = 1
url = https://proton-drive.narl.io
install = proton-drive.install
@@ -25,8 +25,8 @@ pkgbase = proton-drive-for-linux-git
optdepends = xdg-utils: "Open folder" action in the tray menu
optdepends = fzf: search-as-you-type launcher (pdfs-prompt --fzf)
optdepends = foot: terminal that hosts pdfs-prompt --fzf when started from a keybinding
provides = proton-drive-for-linux=3.6.0.r0.g2720191
provides = proton-drive-linux=3.6.0.r0.g2720191
provides = proton-drive-for-linux=3.7.0.r0.g7ab14bb
provides = proton-drive-linux=3.7.0.r0.g7ab14bb
provides = pdfs
conflicts = proton-drive-linux
conflicts = proton-drive-for-linux
@@ -1,7 +1,7 @@
# Maintainer: Nils Pukropp <contact@narl.io>
pkgname=proton-drive-for-linux-git
pkgver=3.6.0.r0.g2720191
pkgver=3.7.0.r0.g7ab14bb
pkgrel=1
pkgdesc="Unofficial Proton Drive client: FUSE files-on-demand mount, CLI, GTK4 app, tray (git)"
arch=('x86_64')
@@ -1,6 +1,6 @@
pkgbase = proton-drive-for-linux
pkgdesc = Unofficial Proton Drive client: files-on-demand FUSE mount, CLI, GTK4 app and tray
pkgver = 3.6.0
pkgver = 3.7.0
pkgrel = 1
url = https://proton-drive.narl.io
install = proton-drive.install
@@ -24,13 +24,13 @@ pkgbase = proton-drive-for-linux
optdepends = xdg-utils: "Open folder" action in the tray menu
optdepends = fzf: search-as-you-type launcher (pdfs-prompt --fzf)
optdepends = foot: terminal that hosts pdfs-prompt --fzf when started from a keybinding
provides = proton-drive-linux=3.6.0
provides = proton-drive-linux=3.7.0
provides = pdfs
conflicts = proton-drive-linux
conflicts = proton-drive-for-linux-bin
conflicts = proton-drive-for-linux-git
options = !lto
source = proton-drive-for-linux-3.6.0.tar.gz::https://github.com/narrrl/proton-drive-linux/archive/refs/tags/v3.6.0.tar.gz
sha256sums = d9b16b1027816a379d6a11c6396d779f751a6f94becaa16b17439420f561d13d
source = proton-drive-for-linux-3.7.0.tar.gz::https://github.com/narrrl/proton-drive-linux/archive/refs/tags/v3.7.0.tar.gz
sha256sums = 542c534976e335cde6658de127e1eefffade9057d5332fd33457bc371c7a2737
pkgname = proton-drive-for-linux
@@ -1,7 +1,7 @@
# Maintainer: Nils Pukropp <contact@narl.io>
pkgname=proton-drive-for-linux
pkgver=3.6.0
pkgver=3.7.0
pkgrel=1
pkgdesc="Unofficial Proton Drive client: files-on-demand FUSE mount, CLI, GTK4 app and tray"
arch=('x86_64')
@@ -26,7 +26,7 @@ options=('!lto')
# Stops the running daemon before removal, so the mount stops mirroring deletes.
install=proton-drive.install
source=("$pkgname-$pkgver.tar.gz::$_repo/archive/refs/tags/v$pkgver.tar.gz")
sha256sums=('d9b16b1027816a379d6a11c6396d779f751a6f94becaa16b17439420f561d13d')
sha256sums=('542c534976e335cde6658de127e1eefffade9057d5332fd33457bc371c7a2737')
_srcname="proton-drive-linux-$pkgver"
+1 -1
View File
@@ -91,7 +91,7 @@ SECTIONS = [
]
# Too long or too internal for the wiki; links to them go to GitHub.
EXCLUDED = {"BUGS.md", "CHANGELOG.md"}
EXCLUDED = {"BUGS.md", "CHANGELOG.md", "SDK-0.11.0.md"}
# Files outside docs/ whose links are checked but not published.
CHECKED = ["README.md", "CONTRIBUTING.md", "SECURITY.md"]