mirror of
https://github.com/GothenburgBitFactory/taskwarrior.git
synced 2025-07-07 20:06:36 +02:00
Make the sync server client an optional feature (#3216)
* Make the sync server client an optional feature * fix comment, remove unnecessary allow(dead_code)
This commit is contained in:
parent
e95f95eb08
commit
b52248f146
11 changed files with 85 additions and 23 deletions
38
.github/workflows/rust-tests.yml
vendored
38
.github/workflows/rust-tests.yml
vendored
|
@ -1,5 +1,3 @@
|
||||||
## Run the TaskChampion tests, using both the minimum supported rust version
|
|
||||||
## and the latest stable Rust.
|
|
||||||
|
|
||||||
name: tests - rust
|
name: tests - rust
|
||||||
|
|
||||||
|
@ -11,6 +9,42 @@ on:
|
||||||
types: [opened, reopened, synchronize]
|
types: [opened, reopened, synchronize]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
## Run the `taskchampion` crate's tests with various combinations of features.
|
||||||
|
features:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
features:
|
||||||
|
- ""
|
||||||
|
- "server-sync"
|
||||||
|
|
||||||
|
name: "taskchampion ${{ matrix.features == '' && 'with no features' || format('with features {0}', matrix.features) }}"
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Cache cargo registry
|
||||||
|
uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: ~/.cargo/registry
|
||||||
|
key: ubuntu-latest-stable-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
|
||||||
|
|
||||||
|
- name: Cache cargo build
|
||||||
|
uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: target
|
||||||
|
key: ubuntu-latest-stable-cargo-build-target-${{ hashFiles('**/Cargo.lock') }}
|
||||||
|
|
||||||
|
- uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: stable
|
||||||
|
override: true
|
||||||
|
|
||||||
|
- name: test
|
||||||
|
run: cargo test -p taskchampion --no-default-features --features "${{ matrix.features }}"
|
||||||
|
|
||||||
|
## Run all TaskChampion crate tests, using both the minimum supported rust version
|
||||||
|
## and the latest stable Rust.
|
||||||
test:
|
test:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
|
|
|
@ -7,7 +7,7 @@ publish = false
|
||||||
build = "build.rs"
|
build = "build.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
taskchampion = { path = "../taskchampion" }
|
taskchampion = { path = "../taskchampion", features = ["server-sync"] }
|
||||||
taskchampion-lib = { path = "../lib" }
|
taskchampion-lib = { path = "../lib" }
|
||||||
taskchampion-sync-server = { path = "../sync-server" }
|
taskchampion-sync-server = { path = "../sync-server" }
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,14 @@ readme = "../README.md"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["server-sync" ]
|
||||||
|
server-sync = ["crypto", "dep:ureq"]
|
||||||
|
crypto = ["dep:ring"]
|
||||||
|
|
||||||
|
[package.metadata.docs.rs]
|
||||||
|
all-features = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
uuid.workspace = true
|
uuid.workspace = true
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
|
@ -26,6 +34,9 @@ flate2.workspace = true
|
||||||
byteorder.workspace = true
|
byteorder.workspace = true
|
||||||
ring.workspace = true
|
ring.workspace = true
|
||||||
|
|
||||||
|
ureq.optional = true
|
||||||
|
ring.optional = true
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
proptest.workspace = true
|
proptest.workspace = true
|
||||||
tempfile.workspace = true
|
tempfile.workspace = true
|
||||||
|
|
|
@ -34,6 +34,7 @@ macro_rules! other_error {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
#[cfg(feature = "server-sync")]
|
||||||
other_error!(ureq::Error);
|
other_error!(ureq::Error);
|
||||||
other_error!(io::Error);
|
other_error!(io::Error);
|
||||||
other_error!(serde_json::Error);
|
other_error!(serde_json::Error);
|
||||||
|
|
|
@ -34,6 +34,14 @@ Create a server with [`ServerConfig`](crate::ServerConfig).
|
||||||
The [`server`](crate::server) module defines the interface a server must meet.
|
The [`server`](crate::server) module defines the interface a server must meet.
|
||||||
Users can define their own server impelementations.
|
Users can define their own server impelementations.
|
||||||
|
|
||||||
|
# Feature Flags
|
||||||
|
|
||||||
|
Support for some optional functionality is controlled by feature flags.
|
||||||
|
|
||||||
|
Sync server client support:
|
||||||
|
|
||||||
|
* `server-sync` - sync to the taskchampion-sync-server
|
||||||
|
|
||||||
# See Also
|
# See Also
|
||||||
|
|
||||||
See the [TaskChampion Book](http://taskchampion.github.com/taskchampion)
|
See the [TaskChampion Book](http://taskchampion.github.com/taskchampion)
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
use super::types::Server;
|
use super::types::Server;
|
||||||
use super::{LocalServer, RemoteServer};
|
|
||||||
use crate::errors::Result;
|
use crate::errors::Result;
|
||||||
|
use crate::server::local::LocalServer;
|
||||||
|
#[cfg(feature = "server-sync")]
|
||||||
|
use crate::server::sync::SyncServer;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
#[cfg(feature = "server-sync")]
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
/// The configuration for a replica's access to a sync server.
|
/// The configuration for a replica's access to a sync server.
|
||||||
|
@ -12,6 +15,7 @@ pub enum ServerConfig {
|
||||||
server_dir: PathBuf,
|
server_dir: PathBuf,
|
||||||
},
|
},
|
||||||
/// A remote taskchampion-sync-server instance
|
/// A remote taskchampion-sync-server instance
|
||||||
|
#[cfg(feature = "server-sync")]
|
||||||
Remote {
|
Remote {
|
||||||
/// Sync server "origin"; a URL with schema and hostname but no path or trailing `/`
|
/// Sync server "origin"; a URL with schema and hostname but no path or trailing `/`
|
||||||
origin: String,
|
origin: String,
|
||||||
|
@ -30,11 +34,12 @@ impl ServerConfig {
|
||||||
pub fn into_server(self) -> Result<Box<dyn Server>> {
|
pub fn into_server(self) -> Result<Box<dyn Server>> {
|
||||||
Ok(match self {
|
Ok(match self {
|
||||||
ServerConfig::Local { server_dir } => Box::new(LocalServer::new(server_dir)?),
|
ServerConfig::Local { server_dir } => Box::new(LocalServer::new(server_dir)?),
|
||||||
|
#[cfg(feature = "server-sync")]
|
||||||
ServerConfig::Remote {
|
ServerConfig::Remote {
|
||||||
origin,
|
origin,
|
||||||
client_id,
|
client_id,
|
||||||
encryption_secret,
|
encryption_secret,
|
||||||
} => Box::new(RemoteServer::new(origin, client_id, encryption_secret)?),
|
} => Box::new(SyncServer::new(origin, client_id, encryption_secret)?),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
/// document.
|
/// document.
|
||||||
use crate::errors::{Error, Result};
|
use crate::errors::{Error, Result};
|
||||||
use ring::{aead, digest, pbkdf2, rand, rand::SecureRandom};
|
use ring::{aead, digest, pbkdf2, rand, rand::SecureRandom};
|
||||||
use std::io::Read;
|
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
const PBKDF2_ITERATIONS: u32 = 100000;
|
const PBKDF2_ITERATIONS: u32 = 100000;
|
||||||
|
@ -177,11 +176,13 @@ pub(super) struct Sealed {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Sealed {
|
impl Sealed {
|
||||||
|
#[cfg(feature = "server-sync")]
|
||||||
pub(super) fn from_resp(
|
pub(super) fn from_resp(
|
||||||
resp: ureq::Response,
|
resp: ureq::Response,
|
||||||
version_id: Uuid,
|
version_id: Uuid,
|
||||||
content_type: &str,
|
content_type: &str,
|
||||||
) -> Result<Sealed> {
|
) -> Result<Sealed> {
|
||||||
|
use std::io::Read;
|
||||||
if resp.header("Content-Type") == Some(content_type) {
|
if resp.header("Content-Type") == Some(content_type) {
|
||||||
let mut reader = resp.into_reader();
|
let mut reader = resp.into_reader();
|
||||||
let mut payload = vec![];
|
let mut payload = vec![];
|
||||||
|
|
|
@ -12,15 +12,17 @@ However, users who wish to implement their own server interfaces can implement t
|
||||||
pub(crate) mod test;
|
pub(crate) mod test;
|
||||||
|
|
||||||
mod config;
|
mod config;
|
||||||
mod crypto;
|
|
||||||
mod local;
|
mod local;
|
||||||
mod op;
|
mod op;
|
||||||
mod remote;
|
|
||||||
mod types;
|
mod types;
|
||||||
|
|
||||||
|
#[cfg(feature = "crypto")]
|
||||||
|
mod crypto;
|
||||||
|
|
||||||
|
#[cfg(feature = "server-sync")]
|
||||||
|
mod sync;
|
||||||
|
|
||||||
pub use config::ServerConfig;
|
pub use config::ServerConfig;
|
||||||
pub use local::LocalServer;
|
|
||||||
pub use remote::RemoteServer;
|
|
||||||
pub use types::*;
|
pub use types::*;
|
||||||
|
|
||||||
pub(crate) use op::SyncOp;
|
pub(crate) use op::SyncOp;
|
||||||
|
|
|
@ -8,7 +8,7 @@ use uuid::Uuid;
|
||||||
|
|
||||||
use super::crypto::{Cryptor, Sealed, Secret, Unsealed};
|
use super::crypto::{Cryptor, Sealed, Secret, Unsealed};
|
||||||
|
|
||||||
pub struct RemoteServer {
|
pub struct SyncServer {
|
||||||
origin: String,
|
origin: String,
|
||||||
client_id: Uuid,
|
client_id: Uuid,
|
||||||
cryptor: Cryptor,
|
cryptor: Cryptor,
|
||||||
|
@ -21,19 +21,14 @@ const HISTORY_SEGMENT_CONTENT_TYPE: &str = "application/vnd.taskchampion.history
|
||||||
/// The content-type for snapshots (opaque blobs of bytes)
|
/// The content-type for snapshots (opaque blobs of bytes)
|
||||||
const SNAPSHOT_CONTENT_TYPE: &str = "application/vnd.taskchampion.snapshot";
|
const SNAPSHOT_CONTENT_TYPE: &str = "application/vnd.taskchampion.snapshot";
|
||||||
|
|
||||||
/// A RemoeServer communicates with a remote server over HTTP (such as with
|
/// A SyncServer communicates with a sync server over HTTP.
|
||||||
/// taskchampion-sync-server).
|
impl SyncServer {
|
||||||
impl RemoteServer {
|
/// Construct a new SyncServer. The `origin` is the sync server's protocol and hostname
|
||||||
/// Construct a new RemoteServer. The `origin` is the sync server's protocol and hostname
|
|
||||||
/// without a trailing slash, such as `https://tcsync.example.com`. Pass a client_id to
|
/// without a trailing slash, such as `https://tcsync.example.com`. Pass a client_id to
|
||||||
/// identify this client to the server. Multiple replicas synchronizing the same task history
|
/// identify this client to the server. Multiple replicas synchronizing the same task history
|
||||||
/// should use the same client_id.
|
/// should use the same client_id.
|
||||||
pub fn new(
|
pub fn new(origin: String, client_id: Uuid, encryption_secret: Vec<u8>) -> Result<SyncServer> {
|
||||||
origin: String,
|
Ok(SyncServer {
|
||||||
client_id: Uuid,
|
|
||||||
encryption_secret: Vec<u8>,
|
|
||||||
) -> Result<RemoteServer> {
|
|
||||||
Ok(RemoteServer {
|
|
||||||
origin,
|
origin,
|
||||||
client_id,
|
client_id,
|
||||||
cryptor: Cryptor::new(client_id, &Secret(encryption_secret.to_vec()))?,
|
cryptor: Cryptor::new(client_id, &Secret(encryption_secret.to_vec()))?,
|
||||||
|
@ -67,7 +62,7 @@ fn get_snapshot_urgency(resp: &ureq::Response) -> SnapshotUrgency {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Server for RemoteServer {
|
impl Server for SyncServer {
|
||||||
fn add_version(
|
fn add_version(
|
||||||
&mut self,
|
&mut self,
|
||||||
parent_version_id: VersionId,
|
parent_version_id: VersionId,
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::errors::Result;
|
use crate::errors::Result;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
/// Versions are referred to with sha2 hashes.
|
/// Versions are referred to with UUIDs.
|
||||||
pub type VersionId = Uuid;
|
pub type VersionId = Uuid;
|
||||||
|
|
||||||
/// The distinguished value for "no version"
|
/// The distinguished value for "no version"
|
||||||
|
@ -52,6 +52,11 @@ pub enum GetVersionResult {
|
||||||
/// A value implementing this trait can act as a server against which a replica can sync.
|
/// A value implementing this trait can act as a server against which a replica can sync.
|
||||||
pub trait Server {
|
pub trait Server {
|
||||||
/// Add a new version.
|
/// Add a new version.
|
||||||
|
///
|
||||||
|
/// This must ensure that the new version is the only version with the given
|
||||||
|
/// `parent_version_id`, and that all versions form a single parent-child chain. Inductively,
|
||||||
|
/// this means that if there are any versions on the server, then `parent_version_id` must be
|
||||||
|
/// the only version that does not already have a child.
|
||||||
fn add_version(
|
fn add_version(
|
||||||
&mut self,
|
&mut self,
|
||||||
parent_version_id: VersionId,
|
parent_version_id: VersionId,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue