mirror of
https://github.com/narrrl/proton-drive-linux.git
synced 2026-11-04 16:56:59 +01:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0b81b76cb7 | ||
|
|
7470bd89f3 | ||
|
|
e6cb52339d | ||
|
|
912acc760a |
No files matched your search
Generated
+4
-4
@@ -3449,7 +3449,7 @@ checksum = "2ee67f1008b1ba2321834326597b8e186293b049a023cdef258527550b9935b4"
|
||||
|
||||
[[package]]
|
||||
name = "pdfs-cli"
|
||||
version = "2.2.1"
|
||||
version = "2.2.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
@@ -3464,7 +3464,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pdfs-core"
|
||||
version = "2.2.1"
|
||||
version = "2.2.2"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"dbus-secret-service-keyring-store",
|
||||
@@ -3488,7 +3488,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pdfs-fuse"
|
||||
version = "2.2.1"
|
||||
version = "2.2.2"
|
||||
dependencies = [
|
||||
"base64 0.23.1",
|
||||
"fuser",
|
||||
@@ -3508,7 +3508,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pdfs-gui"
|
||||
version = "2.2.1"
|
||||
version = "2.2.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-channel",
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ resolver = "2"
|
||||
members = ["crates/pdfs-core", "crates/pdfs-fuse", "crates/pdfs-cli", "crates/pdfs-gui"]
|
||||
|
||||
[workspace.package]
|
||||
version = "2.2.1"
|
||||
version = "2.2.2"
|
||||
edition = "2024"
|
||||
license = "MIT"
|
||||
authors = ["Nils Pukropp <contact@narl.io>"]
|
||||
|
||||
@@ -429,10 +429,12 @@ pub enum Request {
|
||||
/// Retry a backed-off queued op now instead of waiting out its backoff;
|
||||
/// every failed op when `id` is `None`. Replies with [`Response::Ok`].
|
||||
RetryPendingOp { id: Option<i64> },
|
||||
/// List the `(sync-conflict …)` copies under My Files. Replies with
|
||||
/// List the `(sync-conflict …)` copies under My Files and in synced
|
||||
/// folders. Replies with
|
||||
/// [`Response::Conflicts`].
|
||||
ListConflicts,
|
||||
/// Resolve one conflict copy (`path` mountpoint-relative or absolute).
|
||||
/// Resolve one conflict copy (`path` mountpoint-relative, or absolute for a
|
||||
/// copy in a synced folder).
|
||||
/// Everything removed goes to the Proton trash. Replies with
|
||||
/// [`Response::Ok`].
|
||||
ResolveConflict { path: String, keep: ConflictKeep },
|
||||
@@ -701,9 +703,10 @@ pub struct DeviceInfo {
|
||||
/// [`Response::Conflicts`]).
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct ConflictInfo {
|
||||
/// Mountpoint-relative path of the copy.
|
||||
/// Mountpoint-relative path of the copy, or its absolute local path when it
|
||||
/// sits in a synced folder.
|
||||
pub path: String,
|
||||
/// Mountpoint-relative path of the file it is a copy of.
|
||||
/// Path of the file it is a copy of, in the same form as `path`.
|
||||
pub original_path: String,
|
||||
/// False when the original is gone and only the copy is left.
|
||||
pub original_exists: bool,
|
||||
|
||||
@@ -29,6 +29,17 @@ impl Db {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Whether the kept log already holds an entry of `kind` for `target`. Lets
|
||||
/// the daemon report something once across restarts rather than once per
|
||||
/// run, without keeping a second table in step with the log.
|
||||
pub fn activity_has(&self, kind: ActivityKind, target: &str) -> Result<bool> {
|
||||
let kind = serde_json::to_string(&kind)?;
|
||||
let conn = self.read();
|
||||
let mut stmt =
|
||||
conn.prepare("SELECT 1 FROM activity WHERE kind = ?1 AND target = ?2 LIMIT 1")?;
|
||||
Ok(stmt.exists(params![kind, target])?)
|
||||
}
|
||||
|
||||
/// The most recent activity, newest first, capped at `limit` entries. Rows
|
||||
/// whose stored `kind` no longer parses (written by an older build) are
|
||||
/// skipped rather than failing the whole read.
|
||||
|
||||
@@ -1414,6 +1414,29 @@ fn activity_reads_back_newest_first() {
|
||||
assert_eq!(db.activity_list(1).unwrap().len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn activity_has_finds_only_matching_kind_and_target() {
|
||||
let db = Db::open_in_memory().unwrap();
|
||||
db.activity_add(&activity(
|
||||
"a (sync-conflict 1).txt",
|
||||
ActivityKind::Conflict,
|
||||
false,
|
||||
))
|
||||
.unwrap();
|
||||
db.activity_add(&activity("b.txt", ActivityKind::Upload, true))
|
||||
.unwrap();
|
||||
|
||||
assert!(
|
||||
db.activity_has(ActivityKind::Conflict, "a (sync-conflict 1).txt")
|
||||
.unwrap()
|
||||
);
|
||||
assert!(
|
||||
!db.activity_has(ActivityKind::Upload, "a (sync-conflict 1).txt")
|
||||
.unwrap()
|
||||
);
|
||||
assert!(!db.activity_has(ActivityKind::Conflict, "b.txt").unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn activity_prunes_to_the_keep_limit() {
|
||||
let db = Db::open_in_memory().unwrap();
|
||||
|
||||
@@ -14,15 +14,38 @@
|
||||
//! that is open, being written, or still owed an upload, for the same reason the
|
||||
//! sweep does: the node is about to change under the decision.
|
||||
//!
|
||||
//! Only copies under My Files are handled here. A copy inside a mirror folder is
|
||||
//! a local file first; the sync engine owns it.
|
||||
//! Copies under My Files are named by their mountpoint-relative path. Copies
|
||||
//! inside a synced folder are named by their absolute local path, so a resolve
|
||||
//! request routes to the on-demand mount that serves them. A copy inside a
|
||||
//! mirror folder is listed too, but it is a local file first: the sync engine
|
||||
//! owns it, so it is resolved by editing the folder, not through here.
|
||||
|
||||
use super::*;
|
||||
use pdfs_core::control::{ConflictInfo, ConflictKeep};
|
||||
use sweep::conflict_base_name;
|
||||
|
||||
impl Core {
|
||||
/// Every conflict copy under My Files, with the file it is a copy of.
|
||||
/// Where the user finds a conflict copy: its mountpoint-relative path under
|
||||
/// My Files, or its absolute local path inside a synced folder. `None` for a
|
||||
/// node no location of this machine shows, such as one in another
|
||||
/// computer's device tree.
|
||||
///
|
||||
/// The sweep only flags what this can name, and the listing uses it too. A
|
||||
/// flagged copy the listing cannot see reads as resolved in the activity
|
||||
/// feed while it still sits in the folder (`docs/BUGS.md` B99).
|
||||
pub(crate) fn conflict_path(&self, roots: &SearchRoots, uid: &NodeUid) -> Option<String> {
|
||||
let uid = uid.to_string();
|
||||
if let Ok(Some(path)) = self
|
||||
.db
|
||||
.path_relative_to(&self.primary_root_uid.to_string(), &uid)
|
||||
{
|
||||
return Some(path);
|
||||
}
|
||||
let drive_path = self.db.node_path(&uid).ok().flatten()?;
|
||||
roots.resolve(&drive_path)
|
||||
}
|
||||
|
||||
/// Every conflict copy this machine shows, with the file it is a copy of.
|
||||
pub(crate) fn list_conflicts(&self) -> CoreResult<Vec<ConflictInfo>> {
|
||||
let stored = self
|
||||
.db
|
||||
@@ -37,13 +60,13 @@ impl Core {
|
||||
for node in &nodes {
|
||||
by_parent_name.insert((node.parent_uid.as_ref(), node.name.as_str()), node);
|
||||
}
|
||||
let root = self.primary_root_uid.to_string();
|
||||
let roots = self.search_roots();
|
||||
let mut items = Vec::new();
|
||||
for node in &nodes {
|
||||
let Some(base) = conflict_base_name(&node.name) else {
|
||||
continue;
|
||||
};
|
||||
let Ok(Some(path)) = self.db.path_relative_to(&root, &node.uid.to_string()) else {
|
||||
let Some(path) = self.conflict_path(&roots, &node.uid) else {
|
||||
continue;
|
||||
};
|
||||
let original = by_parent_name
|
||||
|
||||
@@ -185,6 +185,38 @@ fn route_to_mount(core: &Core, mountpoint: &Path, path: &str) -> CoreResult<(Cor
|
||||
rel_to_mount(mountpoint, path).map(|rel| (core.clone(), rel))
|
||||
}
|
||||
|
||||
/// [`route_to_mount`] for a conflict copy. The listing names a copy inside a
|
||||
/// synced folder by its absolute local path (`docs/BUGS.md` B99), so the
|
||||
/// resolve has to run against the on-demand mount serving it. A mirror folder
|
||||
/// has no mount to run against: its copy is an ordinary local file.
|
||||
fn route_conflict(core: &Core, mountpoint: &Path, path: &str) -> CoreResult<(Core, PathBuf)> {
|
||||
let routed = route_to_mount(core, mountpoint, path).map_err(|e| {
|
||||
let p = Path::new(path);
|
||||
let mirrored = core
|
||||
.db
|
||||
.sync_folder_list()
|
||||
.unwrap_or_default()
|
||||
.iter()
|
||||
.any(|folder| folder.mode == "mirror" && p.starts_with(&folder.local_path));
|
||||
if mirrored {
|
||||
CoreError::invalid(format!(
|
||||
"{path} is in a mirrored folder; rename or delete the local file there"
|
||||
))
|
||||
} else {
|
||||
e
|
||||
}
|
||||
})?;
|
||||
if routed
|
||||
.1
|
||||
.components()
|
||||
.all(|component| matches!(component, Component::Normal(_) | Component::CurDir))
|
||||
{
|
||||
Ok(routed)
|
||||
} else {
|
||||
Err(CoreError::invalid(format!("{path} escapes the mountpoint")))
|
||||
}
|
||||
}
|
||||
|
||||
/// Turn a CLI-supplied path into a mountpoint-relative path. An absolute path
|
||||
/// must live under `mountpoint`; a relative path is taken as already relative to
|
||||
/// the mount root.
|
||||
@@ -1123,13 +1155,15 @@ fn handle_control_conn(core: &Core, username: &str, mountpoint: &Path, stream: U
|
||||
Ok(items) => CtlResponse::Conflicts { items },
|
||||
Err(e) => CtlResponse::error(e),
|
||||
},
|
||||
Ok(CtlRequest::ResolveConflict { path, keep }) => match rel_to_mount(mountpoint, &path) {
|
||||
Ok(rel) => match core.resolve_conflict(&rel, &keep) {
|
||||
Ok(message) => CtlResponse::Ok { message },
|
||||
Ok(CtlRequest::ResolveConflict { path, keep }) => {
|
||||
match route_conflict(core, mountpoint, &path) {
|
||||
Ok((core, rel)) => match core.resolve_conflict(&rel, &keep) {
|
||||
Ok(message) => CtlResponse::Ok { message },
|
||||
Err(e) => CtlResponse::error(e),
|
||||
},
|
||||
Err(e) => CtlResponse::error(e),
|
||||
},
|
||||
Err(e) => CtlResponse::error(e),
|
||||
},
|
||||
}
|
||||
}
|
||||
Ok(CtlRequest::ListPendingOps) => match core.pending_op_infos() {
|
||||
Ok(items) => CtlResponse::PendingOps { items },
|
||||
Err(e) => CtlResponse::error(e),
|
||||
|
||||
@@ -558,9 +558,10 @@ struct Core {
|
||||
/// instead of polling or blocking for the whole run. See
|
||||
/// [`Core::await_trash_refresh`].
|
||||
trash_progress: Arc<tokio::sync::Notify>,
|
||||
/// Conflict copies the sweep has already flagged as needing attention this
|
||||
/// run, so a divergent `(sync-conflict …)` file is logged once rather than
|
||||
/// on every sweep pass. See [`Core::run_conflict_sweep_loop`].
|
||||
/// Conflict copies the sweep has already checked against the activity log
|
||||
/// this run, so each pass does not ask the database again. The log itself
|
||||
/// is what keeps a copy from being flagged again after a restart. See
|
||||
/// [`Core::run_conflict_sweep_loop`] and `docs/BUGS.md` B99.
|
||||
conflict_notified: Arc<Mutex<HashSet<NodeUid>>>,
|
||||
/// Whether the conflict sweep may actually trash the duplicates it finds, or
|
||||
/// only report them. Resolved once at mount from config + environment
|
||||
|
||||
@@ -109,6 +109,7 @@ impl Core {
|
||||
by_parent_name.insert((parent, node.name.as_str()), node);
|
||||
}
|
||||
|
||||
let roots = self.search_roots();
|
||||
let online = self.online.load(std::sync::atomic::Ordering::Relaxed);
|
||||
for node in &nodes {
|
||||
if node.trashed || node.is_folder() {
|
||||
@@ -132,7 +133,16 @@ impl Core {
|
||||
self.remove_conflict_copy(node, &base);
|
||||
}
|
||||
}
|
||||
SweepAction::Flag(detail) => self.flag_conflict(&node.uid, &node.name, detail),
|
||||
// Only flag what the conflict listing can show. Anything else
|
||||
// would sit in the feed as "resolved" with no way to act on
|
||||
// it (`docs/BUGS.md` B99).
|
||||
SweepAction::Flag(detail) => {
|
||||
if self.conflict_path(&roots, &node.uid).is_some() {
|
||||
self.flag_conflict(&node.uid, &node.name, detail);
|
||||
} else {
|
||||
debug!(uid = %node.uid, name = %node.name, "conflict sweep: copy is outside every location; not flagged");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -273,12 +283,21 @@ impl Core {
|
||||
busy
|
||||
}
|
||||
|
||||
/// Surface a conflict copy that needs the user's attention, at most once per
|
||||
/// daemon run so the activity feed is not spammed each pass.
|
||||
/// Surface a conflict copy that needs the user's attention, once. The
|
||||
/// in-memory set spares the database each pass; the activity log itself is
|
||||
/// what keeps a restart from logging the same copy again (`docs/BUGS.md`
|
||||
/// B99). A copy's name carries its own timestamp, so the name identifies it.
|
||||
fn flag_conflict(&self, uid: &NodeUid, name: &str, detail: String) {
|
||||
if !self.conflict_notified.lock().insert(uid.clone()) {
|
||||
return;
|
||||
}
|
||||
match self.db.activity_has(ActivityKind::Conflict, name) {
|
||||
Ok(false) => {}
|
||||
Ok(true) => return,
|
||||
// Cannot tell: a repeat entry is harmless, a missing one hides a
|
||||
// conflict.
|
||||
Err(e) => warn!(%uid, name, error = ?e, "conflict sweep: activity check failed"),
|
||||
}
|
||||
debug!(%uid, name, detail, "conflict sweep: flagged divergent conflict copy");
|
||||
self.log_activity(ActivityKind::Conflict, name, detail, false);
|
||||
}
|
||||
|
||||
@@ -26,4 +26,5 @@ fn main() {
|
||||
println!("cargo:rerun-if-changed=resources/pdfs.gresource.xml");
|
||||
println!("cargo:rerun-if-changed=resources/icons");
|
||||
println!("cargo:rerun-if-changed=resources/style.css");
|
||||
println!("cargo:rerun-if-changed=../../packaging/io.narl.proton-drive-linux.svg");
|
||||
}
|
||||
@@ -9,6 +9,9 @@
|
||||
<file preprocess="xml-stripblanks">scalable/actions/pdfs-upload-symbolic.svg</file>
|
||||
<file preprocess="xml-stripblanks">scalable/actions/pdfs-offline-symbolic.svg</file>
|
||||
<file preprocess="xml-stripblanks">scalable/actions/pdfs-online-only-symbolic.svg</file>
|
||||
<!-- The app icon, from the one file packaging installs, so the in-app brand
|
||||
matches the launcher and resolves even when nothing is installed. -->
|
||||
<file alias="scalable/apps/io.narl.proton-drive-linux.svg" preprocess="xml-stripblanks">../../../packaging/io.narl.proton-drive-linux.svg</file>
|
||||
</gresource>
|
||||
<!-- The app stylesheet, loaded once at startup. Colours come from the
|
||||
libadwaita palette so the app follows the system accent, light/dark and
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
|
||||
/* Brand */
|
||||
.brand-title { font-size: 1.6rem; font-weight: 800; }
|
||||
.brand-icon { color: #6d4aff; }
|
||||
|
||||
/* Sidebar footer */
|
||||
.sidebar-footer { padding: 6px 12px 12px 12px; }
|
||||
|
||||
@@ -884,8 +884,8 @@ fn build_sidebar(footer: >k4::Box) -> (adw::NavigationPage, gtk4::ListBox) {
|
||||
});
|
||||
|
||||
let brand = gtk4::Box::new(gtk4::Orientation::Horizontal, 8);
|
||||
let icon = gtk4::Image::from_icon_name("folder-remote-symbolic");
|
||||
icon.add_css_class("brand-icon");
|
||||
let icon = gtk4::Image::from_icon_name(APP_ID);
|
||||
icon.set_pixel_size(24);
|
||||
brand.append(&icon);
|
||||
let name = gtk4::Label::new(Some("Proton Drive"));
|
||||
name.add_css_class("heading");
|
||||
|
||||
@@ -52,9 +52,8 @@ pub(crate) fn build_login_page() -> (gtk4::Widget, LoginState) {
|
||||
login_status.add_css_class("dim-label");
|
||||
|
||||
let header = gtk4::Box::new(gtk4::Orientation::Vertical, 4);
|
||||
let logo = gtk4::Image::from_icon_name("folder-remote-symbolic");
|
||||
logo.set_pixel_size(64);
|
||||
logo.add_css_class("brand-icon");
|
||||
let logo = gtk4::Image::from_icon_name(APP_ID);
|
||||
logo.set_pixel_size(96);
|
||||
let title = gtk4::Label::new(Some("Proton Drive"));
|
||||
title.add_css_class("brand-title");
|
||||
header.append(&logo);
|
||||
|
||||
@@ -12,6 +12,42 @@ Conventions:
|
||||
|
||||
---
|
||||
|
||||
## B99 — Conflict copies in synced folders are logged again on every start and shown as "Resolved"
|
||||
|
||||
**Status:** Fixed (unverified). No daemon carrying the change has been restarted against the
|
||||
affected copies yet.
|
||||
**Found:** 2026-09-27. The Activity page listed the same six conflict copies every day, e.g.
|
||||
`Conflict [EMBER] Oshi no Ko S2 - 12 (sync-conflict 1786815864).mkv` with `Differs from … · 24 times
|
||||
since Sep 20, 13:13 · Resolved`. `find` showed all six copies still on disk, in `~/Videos` and
|
||||
`~/Documents`. Both are on-demand sync folders. `pdfs conflicts` answered `No conflicts.`. The
|
||||
journal showed the daemon mounting at 17:29:33, and the entries were stamped 17:30. That is the
|
||||
sweep's 30-second warmup.
|
||||
|
||||
**Where:** `crates/pdfs-fuse/src/sweep.rs` (`flag_conflict`, `sweep_conflicts_once`),
|
||||
`crates/pdfs-fuse/src/conflicts.rs` (`list_conflicts`), `crates/pdfs-fuse/src/control.rs`
|
||||
(`ResolveConflict`).
|
||||
|
||||
**Cause.** There were two faults.
|
||||
|
||||
- The sweep kept its "already flagged" set only in memory. Every daemon start forgot it, so the
|
||||
first pass logged every open conflict again.
|
||||
- The sweep flags copies anywhere in the account's own tree. `list_conflicts` only reported copies
|
||||
below My Files, and resolving only accepted paths under the primary mount. A copy in a synced
|
||||
folder was logged but never listed, so the GUI read it as resolved. It also could not be
|
||||
resolved from the GUI or the CLI.
|
||||
|
||||
**Fix.** `Core::conflict_path` names a copy by its mountpoint-relative path under My Files, or
|
||||
by its absolute local path inside a synced folder. It reuses the roots that search already
|
||||
resolves hits through. The listing uses it, and the sweep flags only copies it can name. So the
|
||||
feed never holds a conflict the listing cannot answer for. `ResolveConflict` routes an absolute
|
||||
path to the on-demand mount that serves it. For a copy in a mirror folder, it says to edit the
|
||||
local file instead. Before logging, `flag_conflict` checks the activity log
|
||||
(`Db::activity_has`), so a restart does not log the same copy again. The copy's name carries its
|
||||
own timestamp, so the name identifies the copy. No schema change.
|
||||
Regression test: `activity_has_finds_only_matching_kind_and_target`.
|
||||
|
||||
---
|
||||
|
||||
## B98 — A superseded upload spins its drain thread forever and shows as "Uploading" at 0 bytes
|
||||
|
||||
**Status:** Fixed (unverified) — the daemon carrying the change has not been driven against a
|
||||
|
||||
@@ -11,6 +11,15 @@ scratch (user data in `staging/` and `recovery/` is never touched by this).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [2.2.2] — 2026-09-27
|
||||
|
||||
Schema: **32** (unchanged).
|
||||
|
||||
### Fixed
|
||||
- **Conflicts in synced folders.** A conflict copy inside a synced folder is now listed under
|
||||
Needs attention and can be resolved there or with `pdfs conflicts`. It used to be logged again
|
||||
after every restart and marked "Resolved" while it was still on disk (B99).
|
||||
|
||||
## [2.2.1] — 2026-09-24
|
||||
|
||||
Schema: **32** (unchanged).
|
||||
@@ -1038,6 +1047,7 @@ 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.
|
||||
|
||||
[2.2.2]: https://github.com/narrrl/proton-drive-linux/releases/tag/v2.2.2
|
||||
[2.2.1]: https://github.com/narrrl/proton-drive-linux/releases/tag/v2.2.1
|
||||
[2.1.0]: https://github.com/narrrl/proton-drive-linux/releases/tag/v2.1.0
|
||||
[2.0.0]: https://github.com/narrrl/proton-drive-linux/releases/tag/v2.0.0
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@
|
||||
# Build + install: cd packaging && makepkg -fi
|
||||
|
||||
pkgname=proton-drive-linux
|
||||
pkgver=2.2.1
|
||||
pkgver=2.2.2
|
||||
pkgrel=1
|
||||
pkgdesc="Unofficial Proton Drive client for Linux: files-on-demand FUSE mount, CLI, GTK4 app + tray"
|
||||
arch=('x86_64')
|
||||
|
||||
@@ -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 = 2.2.0
|
||||
pkgver = 2.2.1
|
||||
pkgrel = 1
|
||||
url = https://github.com/narrrl/proton-drive-linux
|
||||
arch = x86_64
|
||||
@@ -20,21 +20,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=2.2.0
|
||||
provides = proton-drive-linux=2.2.0
|
||||
provides = proton-drive-for-linux=2.2.1
|
||||
provides = proton-drive-linux=2.2.1
|
||||
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-2.2.0-x86_64.tar.gz::https://github.com/narrrl/proton-drive-linux/releases/download/v2.2.0/proton-drive-linux-2.2.0-x86_64.tar.gz
|
||||
source = io.narl.proton-drive-linux.desktop::https://raw.githubusercontent.com/narrrl/proton-drive-linux/v2.2.0/packaging/io.narl.proton-drive-linux.desktop
|
||||
source = io.narl.proton-drive-linux-tray.desktop::https://raw.githubusercontent.com/narrrl/proton-drive-linux/v2.2.0/packaging/io.narl.proton-drive-linux-tray.desktop
|
||||
source = io.narl.proton-drive-linux.svg::https://raw.githubusercontent.com/narrrl/proton-drive-linux/v2.2.0/packaging/io.narl.proton-drive-linux.svg
|
||||
source = proton-drive.service::https://raw.githubusercontent.com/narrrl/proton-drive-linux/v2.2.0/packaging/proton-drive.service
|
||||
source = LICENSE-2.2.0::https://raw.githubusercontent.com/narrrl/proton-drive-linux/v2.2.0/LICENSE
|
||||
sha256sums = 4d746d83abd5174acb324236b6146c083a43b8c25e8d30e31c2a6d5b594ba479
|
||||
source = proton-drive-linux-2.2.1-x86_64.tar.gz::https://github.com/narrrl/proton-drive-linux/releases/download/v2.2.1/proton-drive-linux-2.2.1-x86_64.tar.gz
|
||||
source = io.narl.proton-drive-linux.desktop::https://raw.githubusercontent.com/narrrl/proton-drive-linux/v2.2.1/packaging/io.narl.proton-drive-linux.desktop
|
||||
source = io.narl.proton-drive-linux-tray.desktop::https://raw.githubusercontent.com/narrrl/proton-drive-linux/v2.2.1/packaging/io.narl.proton-drive-linux-tray.desktop
|
||||
source = io.narl.proton-drive-linux.svg::https://raw.githubusercontent.com/narrrl/proton-drive-linux/v2.2.1/packaging/io.narl.proton-drive-linux.svg
|
||||
source = proton-drive.service::https://raw.githubusercontent.com/narrrl/proton-drive-linux/v2.2.1/packaging/proton-drive.service
|
||||
source = LICENSE-2.2.1::https://raw.githubusercontent.com/narrrl/proton-drive-linux/v2.2.1/LICENSE
|
||||
sha256sums = 69f0d6996feb95b18d81bb96e9373c61fddb64ab17b50c94cd83ef8d5e0a1425
|
||||
sha256sums = d190d6771fff0b975271fb62fd3fb0fa8bbf17d7278c10853c18557855b0c123
|
||||
sha256sums = 1d95e250370220e3bb6aead6ce34d1d6cc759f42c4803b75f1667e78ff47c6c0
|
||||
sha256sums = e598a93c2715c3a9fdc7fb937902083d4a1135e8fbdfe6fe7e37f0383ebbd828
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Maintainer: Nils Pukropp <contact@narl.io>
|
||||
|
||||
pkgname=proton-drive-for-linux-bin
|
||||
pkgver=2.2.0
|
||||
pkgver=2.2.1
|
||||
pkgrel=1
|
||||
pkgdesc="Unofficial Proton Drive client: FUSE files-on-demand mount, CLI, GTK4 app, tray (binaries)"
|
||||
arch=('x86_64')
|
||||
@@ -30,7 +30,7 @@ source=("proton-drive-linux-$pkgver-x86_64.tar.gz::$url/releases/download/$_tag/
|
||||
"io.narl.proton-drive-linux.svg::$_raw/packaging/io.narl.proton-drive-linux.svg"
|
||||
"proton-drive.service::$_raw/packaging/proton-drive.service"
|
||||
"LICENSE-$pkgver::$_raw/LICENSE")
|
||||
sha256sums=('4d746d83abd5174acb324236b6146c083a43b8c25e8d30e31c2a6d5b594ba479'
|
||||
sha256sums=('69f0d6996feb95b18d81bb96e9373c61fddb64ab17b50c94cd83ef8d5e0a1425'
|
||||
'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 = 2.2.0.r1.g82e9654
|
||||
pkgver = 2.2.1.r0.g5109427
|
||||
pkgrel = 1
|
||||
url = https://github.com/narrrl/proton-drive-linux
|
||||
arch = x86_64
|
||||
@@ -23,8 +23,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=2.2.0.r1.g82e9654
|
||||
provides = proton-drive-linux=2.2.0.r1.g82e9654
|
||||
provides = proton-drive-for-linux=2.2.1.r0.g5109427
|
||||
provides = proton-drive-linux=2.2.1.r0.g5109427
|
||||
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=2.2.0.r1.g82e9654
|
||||
pkgver=2.2.1.r0.g5109427
|
||||
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 = 2.2.0
|
||||
pkgver = 2.2.1
|
||||
pkgrel = 1
|
||||
url = https://github.com/narrrl/proton-drive-linux
|
||||
arch = x86_64
|
||||
@@ -22,13 +22,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=2.2.0
|
||||
provides = proton-drive-linux=2.2.1
|
||||
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-2.2.0.tar.gz::https://github.com/narrrl/proton-drive-linux/archive/refs/tags/v2.2.0.tar.gz
|
||||
sha256sums = 73f68211d008894c3254b5adca0acca7f9e1af90e89209c22ae0213aa75add4b
|
||||
source = proton-drive-for-linux-2.2.1.tar.gz::https://github.com/narrrl/proton-drive-linux/archive/refs/tags/v2.2.1.tar.gz
|
||||
sha256sums = 4635716908232896bfb0bf16d0f10300dc2eb59c27c7693607a21eabcac2edff
|
||||
|
||||
pkgname = proton-drive-for-linux
|
||||
@@ -1,7 +1,7 @@
|
||||
# Maintainer: Nils Pukropp <contact@narl.io>
|
||||
|
||||
pkgname=proton-drive-for-linux
|
||||
pkgver=2.2.0
|
||||
pkgver=2.2.1
|
||||
pkgrel=1
|
||||
pkgdesc="Unofficial Proton Drive client: files-on-demand FUSE mount, CLI, GTK4 app and tray"
|
||||
arch=('x86_64')
|
||||
@@ -22,7 +22,7 @@ conflicts=('proton-drive-linux' 'proton-drive-for-linux-bin' 'proton-drive-for-l
|
||||
# LTO has broken GTK/Rust links for this workspace; the release profile keeps it off.
|
||||
options=('!lto')
|
||||
source=("$pkgname-$pkgver.tar.gz::$url/archive/refs/tags/v$pkgver.tar.gz")
|
||||
sha256sums=('73f68211d008894c3254b5adca0acca7f9e1af90e89209c22ae0213aa75add4b')
|
||||
sha256sums=('4635716908232896bfb0bf16d0f10300dc2eb59c27c7693607a21eabcac2edff')
|
||||
|
||||
_srcname="proton-drive-linux-$pkgver"
|
||||
|
||||
|
||||
Reference in new issue
Block a user