omnydrive 1.11.0
omnydrive: ^1.11.0 copied to clipboard
Distributed file & git drive synchronization in pure Dart. Publish, mount, clone and sync directory and git drives across endpoints coordinated by a hub, with explicit conflict detection and an offici [...]
1.11.0 #
- Pluggable git push target via
GitPushPolicy. A push now consults an injectable policy to decide whether the checked-out branch is published to itself or to a fresh feature branch (BranchNamingStrategy). The defaultDefaultGitPushPolicyprotectsmain/master(feature branch) and pushes every other branch directly; embedders pass extraprotectedBranches(e.g. a mounted branch) or their ownGitPushPolicyviaGitProvider(pushPolicy: …). A protected branch is never moved by a push; a branch absent from the origin is created by the direct push. Diverged origins still surface aConflictDetectedException(a push never force-writes).
1.10.1 #
- Fix git-drive pull failing with
git merge --ff-only origin/<branch> … not something we can merge. This happened when the checked-out branch does not exist on the origin — e.g. the endpoint is on a branch it never pushed. Pull now no-ops when the branch is absent from the origin (nothing to pull), and otherwise fetches the branch by name (git fetch origin <branch>) and fast-forwards toFETCH_HEAD— which also fixes pulls on shallow/single-branch clones whoseorigin/<branch>tracking ref is absent.GitCli.fetchgains an optionalbranchargument and there's a newGitCli.remoteHasBranch.
1.10.0 #
GitCredentialStoreis now a public, exported class (moved out of the privatesrc/clitree intosrc/infrastructure/persistence), so embedding apps can reuse the same host-scoped git-credential store — either viaload/save, or by composing its in-memory API (fromJson/toJson/resolve/get/put/remove/hosts) into their own persistence.load/savetake an optionalfileName(defaulting todefaultFileName) to host the store elsewhere.- The credential file is now
git-credentials.json(wascredentials.json), agit-prefix that keeps git credentials distinct from any othercredentials.jsonin the state directory. The name is exposed asGitCredentialStore.defaultFileName. Theomnydrive credentialCLI reads/writes the new file; an existingcredentials.jsonis not migrated automatically — re-add credentials or rename the file.
1.9.0 #
- Git drives can now authenticate to private remotes with explicit credentials.
A new
GitCredentialvalue object models three variants — an HTTPS personal access token (GitPat), an HTTPS username+password (GitUserPass), and an SSH private key (GitSshKey) — each injecting itself at the singleGitCliprocess chokepoint (HTTPS via-c http.extraHeaderbasic auth, SSH viaGIT_SSH_COMMAND).GIT_TERMINAL_PROMPT=0is always set so a missing or wrong credential fails fast instead of hanging. - Credentials are stored host-scoped in a local
credentials.json(keyed by git host, tightened to0600) and resolved by the origin's host at invocation time viaGitCredentialResolver. They are never placed on theDriveentity, so they are never serialized todrives.json, registered with the hub, or transmitted to peers. Manage them with a new CLI command:omnydrive credential add <host> (--pat | --username/--password | --ssh-key),omnydrive credential list, andomnydrive credential remove <host>.publish/clone/syncpick up the matching credential automatically by host, so no per-command flags are needed. Passphrase-protected SSH keys still require an ssh-agent.
1.8.0 #
- Directory sync now preserves the executable (
+x) bit.FileManifestEntryrecords whether a file carries a POSIX execute bit (serialized asexecutable, kept out of the content-addressed manifest hash), and the destination applieschmod +xon write/copy. The manifest differ compares the bit explicitly, so a chmod-only change (identical content) still syncs. No-op on platforms without execute bits; existing directory references are unaffected.
1.7.0 #
PathFilternow matches slash-less patterns at any depth, matching gitignore semantics. Previously*.dill(or any wildcard pattern without a/) only matched files at the drive root, so nested files likebin/server.dillslipped past.omnyignore/--exclude. Patterns with a leading or internal/(e.g./build,a/b) remain anchored to the root.- Added
PathFilter.scope(prefix, patterns), which rewrites raw patterns (e.g. from a nested.omnyignore) so they apply within a subtree — anchored patterns bind to<prefix>/…, slash-less patterns to<prefix>/**/…, mirroring how git applies a nested.gitignore.
1.6.0 #
- Default ignore file: a directory can carry a gitignore-style
.omnyignorefile at its root listing glob patterns to exclude from the published drive.omnydrive publish <dir>consults it only when neither--includenor--excludeis given, turning its patterns into the drive's defaultexcludeset. Explicit filter flags override the file entirely.- The patterns are baked into
Drive.filterat publish time, so every cloner and subsequentsyncautomatically skips the ignored sub-paths — no client-side configuration. - The file name is customizable with
--ignore-file <name>(defaults to.omnyignore). - New
parseOmnyIgnore/loadOmnyIgnoreutilities and theomnyIgnoreFileNameconstant are exported fromomnydrive.dart. - Lines are trimmed; blank lines and
#comments are skipped. Negation (!pattern) is not supported (PathFilter has no rule ordering to re-include an excluded path) and such lines are ignored.
1.5.0 #
- Live sync progress: directory-drive sync now reports per-file upload progress
as bytes stream, so a TUI/CLI can draw a live progress bar for each of the
concurrent uploads instead of only learning a file is done after it settles.
ProgressEventgains per-item fields:path,itemKind(transferred/copied/removed),itemState(started/progress/completed),itemBytesanditemTotalBytes(the wire/compressed size), plusitemSize(the original uncompressed file size, so the real size can be shown alongside compressed progress).ContentSource.writeBytesgains an optionalonProgress(sent, total)callback.HttpContentSourceuploads through a streamed request that reports bytes at the socket's own pace;LocalContentSourcestreams viaopenWrite.- Each completed path is tagged transferred vs copied (deduplicated), so the distinction is visible during and after the sync.
- Final sync report:
SyncMetricsnow carriestransferredPaths,copiedPathsandremovedPaths, plusbytesOnWire(bytes after transport compression) alongside the existing rawbytesTransferred. DriveEndpoint.syncMountaccepts an optionalprogressreporter.- CLI:
omnydrive syncrenders a live multi-bar transfer view and prints a final report (file counts and raw vs on-wire bytes). Pass-v/--verboseto list every transferred/copied/removed path. - The
omnyDriveVersionconstant (surfaced byGET /version) is realigned withpubspec.yamlafter drifting in prior releases.
1.4.0 #
- Performance: directory-drive sync now deduplicates identical content instead
of transferring it repeatedly. When a write's content hash is already present
at the destination — either in an existing file or in another file sent the
same run — the bytes cross the wire once and the destination copies them into
place. Duplicate build artifacts, vendored files and the like sync for the
cost of a single payload.
- New
POST /drives/<endpoint>/<name>/copycontent-server route performs a verified in-place copy: it re-hashes the source and returns409if it drifted or vanished, so the client transparently falls back to a full byte transfer (guarding the time-of-check/time-of-use gap). - The content server advertises a
server-side-copycapability in itsGET /versionresponse. Clients probe for it once per transfer; servers that don't advertise it (older versions) fall back to full byte transfers, so peers interoperate transparently. - API:
ContentSourcegainssupportsCopy()andcopy(from, to, expectedHash); implemented byLocalContentSourceandHttpContentSource.
- New
1.3.0 #
- Performance: directory-drive content now transfers gzip-compressed over HTTP
by default. File pulls, file pushes and the JSON manifest are gzipped at level
4 (near-identical ratio to the default level 6, markedly faster) using
dart:io'sGZipCodec— no new dependency. Negotiation rides the standardAccept-Encoding/Content-Encodingheaders, so peers interoperate transparently.- Already-compressed file types (jpeg, png, mp4, zip, pdf, …) and payloads below the threshold (1 KiB by default) are sent verbatim, since re-gzipping them costs CPU for no real gain.
- The content-source HTTP client disables transparent auto-uncompress so gzip
handling stays explicit and deterministic;
HttpContentSourceandnetworkedProviderRegistryshare a single client factory.
- API: compression is now configurable and integration-friendly.
ContentCompressionis an injectable policy (enabled,level,minBytes,skipExtensions) withContentCompression.standard/.disabledpresets, accepted byContentServer,HttpContentSource,networkedProviderRegistryandOmnyClient. Defaults are unchanged.- Fix: reading a compressed file (> 1 KiB) through
OmnyClient— or any integrator-suppliedhttp.Clientwith transparent gzip — no longer throwsFormatException: Filter error, bad data. Decoding now also checks the gzip magic bytes (ContentCompression.looksGzipped), so it never double-decodes a body an auto-uncompress client already inflated.OmnyClientdefaults to the non-auto-uncompress content client. ContentCompressionis transport-agnostic (no HTTP dependency) and exported fromomnydrive_client.dart, so customContentSourcetransports can reuseencode/decode/shouldCompress/looksGzippeddirectly.
1.2.0 #
- Feature: publish only part of a local directory with sub-path filters. A new
PathFiltervalue object (gitignore-style globs:*,**,?, trailing-slash / bare-directory subtree matching) carriesinclude/excludepatterns with exclude-wins, include-as-whitelist semantics.- CLI:
omnydrive publishgains repeatable--include/--excludeoptions (directory drives only; combining them with--gitis rejected). - API:
DriveEndpoint.publishDirectoryaccepts an optionalfilter:; the filter is stored onDrive(serialized, registered with the hub) and applied during the manifest walk inManifestBuilder, so excluded files are never hashed. - The filter is enforced at the serving boundary (
ContentServer), so every cloner automatically receives only the surviving sub-paths with no client-side configuration.currentRef/describe(provider contract) and theContentSourceResolvernow thread the filter through;syncMountapplies it on both sides so a filtered drive syncs cleanly.
- CLI:
1.1.4 #
- Performance: building a directory manifest (the
currentRefcomputed twice on everysyncMount) no longer reads and SHA-256-hashes every file. A persisted stat-cache letsManifestBuilderreuse a file's recorded hash when its(size, mtime)are unchanged, so an unchanged mount with thousands of files costs onestat()per file instead of a full read + hash. The producedFileManifest— and therefore the directory's content-addressedSyncRef— is byte-identical to a full rebuild.- The cache lives at
<root>/.omnydrive/manifest-cache.json(an already-ignored directory) and is purely advisory: a missing, malformed, or version-mismatched cache, or a write failure on a read-only filesystem, falls back to a full rebuild and never errors. - Correctness: a file is trusted only when its mtime is strictly older than the cache's build timestamp, re-hashing anything modified within the filesystem's mtime resolution of the last build (git's "racy clean" rule).
- Added
ManifestCache/CachedEntry(lib/src/infrastructure/providers/directory/manifest_cache.dart) and auseCacheflag onManifestBuilder(default on; off disables the cache).
- The cache lives at
1.1.3 #
- Performance: directory drive sync now transfers changed files concurrently
instead of one at a time.
DirectorySynchronizerruns up totransferConcurrency(default 8) reads/writes in flight over the existing per-file content routes, so syncs dominated by per-file HTTP round-trips are substantially faster. Conflict detection, baseline checks, and progress reporting are unchanged. The limit is configurable via theDirectorySynchronizerconstructor.- Added
forEachConcurrentbounded worker-pool helper (lib/src/shared/utils/concurrent.dart).
- Added
1.1.2 #
- Fixed: syncing a mount could silently delete local-only changes. A pull
overwrites/deletes local files to match the origin, and
syncMountpreviously pulled whenever it was not pushing — so a read-only mount (or any mount whose local copy had diverged) would discard a newly created or edited file instead of preserving it.syncMountnow only pushes when the mount is read-write and only the local side changed; any other divergent case raisesConflictDetectedException(via the newConflictDetector.detectForPull) rather than destroying local work.- Added
ConflictKind.localDivergencefor a local copy that diverged from the baseline but cannot be published.
- Added
1.1.1 #
-
ManifestBuilder:- Updated default
ignoredDirsto include.dart_tooldirectory. - Documentation updated to reflect the addition of
.dart_toolto ignored directories.
- Updated default
-
Dependency updates:
args: ^2.5.0 → ^2.7.0crypto: ^3.0.3 → ^3.0.7http: ^1.2.0 → ^1.6.0path: ^1.9.0 → ^1.9.1shelf: ^1.4.1 → ^1.4.2lints: ^6.0.0 → ^6.1.0test: ^1.25.6 → ^1.31.1
1.1.0 #
- Added: per-file progress events during directory sync.
DirectorySynchronizer.applynow emits atransferringProgressEventafter each file is written or deleted (in both push and pull directions), carrying the path (message), cumulativebytes, andcompleted/totalcounts — enabling consumers to render live progress. Backward compatible: the start anddoneevents are unchanged andprogressstays optional.
1.0.0 #
First stable release. Promotes the complete vertical slice — directory and git
providers, the hub + content HTTP servers, the OmnyClient SDK and the
omnydrive CLI — to a stable API, with content-addressed conflict detection
throughout.
- Domain model: drives, mounts, endpoints, sync refs, conflicts, capabilities.
- Providers: directory (HTTP-mirrored) and git (via the
gitCLI). - Application layer:
LocalDriveHubandLocalDriveEndpoint(publish, clone, sync) with content-addressed conflict detection. - HTTP transport: hub server, endpoint content server, and matching clients
(
HttpDriveHub,HttpContentSource). - Client SDK:
OmnyClientand thepackage:omnydrive/omnydrive_client.dartsurface. omnydriveCLI:serve,serve-content,login,publish,clone,sync,mounts,drives, with file-backed per-endpoint state.- In-memory and file-backed persistence implementations.
- Runnable examples: core round-trip, conflict detection & resolution, read-only mirror, client SDK, and a git drive walkthrough.
0.1.0 #
Initial release.
- Domain model: drives, mounts, endpoints, sync refs, conflicts, capabilities.
- Providers: directory (HTTP-mirrored) and git (via the
gitCLI). - Application layer:
LocalDriveHubandLocalDriveEndpoint(publish, clone, sync) with content-addressed conflict detection. - HTTP transport: hub server, endpoint content server, and matching clients
(
HttpDriveHub,HttpContentSource). - Client SDK:
OmnyClientand thepackage:omnydrive/omnydrive_client.dartsurface. omnydriveCLI:serve,serve-content,login,publish,clone,sync,mounts,drives, with file-backed per-endpoint state.- In-memory and file-backed persistence implementations.
- Runnable examples: core round-trip, conflict detection & resolution, read-only mirror, client SDK, and a git drive walkthrough.