Skip to content

Commit 8903850

Browse files
authored
Merge pull request #233 from Oxen-AI/feat/add-merkle-trees-clean
Merkle Trees Part II: The APIs
2 parents a03586b + afef418 commit 8903850

File tree

108 files changed

+7874
-1509
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

108 files changed

+7874
-1509
lines changed

Cargo.lock

+4-4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "Oxen"
3-
version = "0.9.19"
3+
version = "0.10.4"
44
edition = "2021"
55
license-file = "LICENSE"
66
description = "Oxen is a fast, unstructured data version control, to help version large machine learning datasets written in Rust."

src/cli/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "oxen-cli"
3-
version = "0.9.19"
3+
version = "0.10.4"
44
edition = "2021"
55

66
[dependencies]

src/cli/src/cmd_setup.rs

+40-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use clap::{arg, Arg, Command};
22
use liboxen::command::migrate::{
3-
CacheDataFrameSizeMigration, Migrate, PropagateSchemasMigration, UpdateVersionFilesMigration,
3+
CacheDataFrameSizeMigration, CreateMerkleTreesMigration, Migrate, PropagateSchemasMigration,
4+
UpdateVersionFilesMigration,
45
};
56
use liboxen::constants::{DEFAULT_BRANCH_NAME, DEFAULT_REMOTE_NAME};
67

@@ -918,7 +919,25 @@ pub fn migrate() -> Command {
918919
)
919920
.action(clap::ArgAction::SetTrue),
920921
),
921-
),
922+
)
923+
.subcommand(
924+
Command::new(CreateMerkleTreesMigration.name())
925+
.about("Reformats the underlying data model into merkle trees for storage and lookup efficiency")
926+
.arg(
927+
Arg::new("PATH")
928+
.help("Directory in which to apply the migration")
929+
.required(true),
930+
)
931+
.arg(
932+
Arg::new("all")
933+
.long("all")
934+
.short('a')
935+
.help(
936+
"Run the migration for all oxen repositories in this directory",
937+
)
938+
.action(clap::ArgAction::SetTrue),
939+
),
940+
)
922941
)
923942
.subcommand(
924943
Command::new("down")
@@ -977,7 +996,25 @@ pub fn migrate() -> Command {
977996
)
978997
.action(clap::ArgAction::SetTrue),
979998
),
980-
),
999+
)
1000+
.subcommand(
1001+
Command::new(CreateMerkleTreesMigration.name())
1002+
.about("Reformats the underlying data model into merkle trees for storage and lookup efficiency")
1003+
.arg(
1004+
Arg::new("PATH")
1005+
.help("Directory in which to apply the migration")
1006+
.required(true),
1007+
)
1008+
.arg(
1009+
Arg::new("all")
1010+
.long("all")
1011+
.short('a')
1012+
.help(
1013+
"Run the migration for all oxen repositories in this directory",
1014+
)
1015+
.action(clap::ArgAction::SetTrue),
1016+
),
1017+
)
9811018
)
9821019
}
9831020

src/cli/src/dispatch.rs

+74-40
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
use jwalk::WalkDir;
21
use liboxen::api;
32
use liboxen::command;
3+
use liboxen::command::migrate::CreateMerkleTreesMigration;
4+
use liboxen::command::migrate::Migrate;
5+
use liboxen::command::migrate::UpdateVersionFilesMigration;
46
use liboxen::config::{AuthConfig, UserConfig};
57
use liboxen::constants;
68
use liboxen::error;
@@ -22,6 +24,7 @@ use liboxen::opts::PaginateOpts;
2224
use liboxen::opts::RestoreOpts;
2325
use liboxen::opts::RmOpts;
2426
use liboxen::util;
27+
use liboxen::util::oxen_version::OxenVersion;
2528

2629
use colored::Colorize;
2730
use liboxen::view::PaginatedDirEntries;
@@ -66,51 +69,62 @@ pub async fn check_remote_version(host: impl AsRef<str>) -> Result<(), OxenError
6669
eprintln!("Err checking remote version: {err}")
6770
}
6871
}
69-
7072
Ok(())
7173
}
7274

73-
fn version_files_out_of_date(repo: &LocalRepository) -> Result<bool, OxenError> {
74-
let versions_dir = repo
75-
.path
76-
.join(constants::OXEN_HIDDEN_DIR)
77-
.join(constants::VERSIONS_DIR);
78-
if !versions_dir.exists() {
79-
return Ok(false);
80-
}
81-
for entry in WalkDir::new(&versions_dir) {
82-
let entry = entry?;
83-
if entry.file_type().is_file() {
84-
let path = entry.path();
85-
let filename = match path.file_name() {
86-
Some(filename) => filename.to_string_lossy().to_string(),
87-
None => continue,
88-
};
89-
90-
if filename.starts_with(constants::HASH_FILE) {
91-
continue;
92-
}
93-
94-
if filename.starts_with(constants::VERSION_FILE_NAME) {
95-
return Ok(false);
75+
pub async fn check_remote_version_blocking(host: impl AsRef<str>) -> Result<(), OxenError> {
76+
match api::remote::version::get_min_cli_version(host.as_ref()).await {
77+
Ok(remote_version) => {
78+
let local_version: &str = constants::OXEN_VERSION;
79+
let min_oxen_version = OxenVersion::from_str(&remote_version)?;
80+
let local_oxen_version = OxenVersion::from_str(local_version)?;
81+
82+
if local_oxen_version < min_oxen_version {
83+
return Err(OxenError::OxenUpdateRequired(format!(
84+
"Error: Oxen CLI out of date. Pushing to OxenHub requires version >= {:?}, found version {:?}.\n\nVisit https://docs.oxen.ai/getting-started/intro for update instructions.",
85+
min_oxen_version,
86+
local_oxen_version
87+
).into()));
9688
}
97-
return Ok(true);
89+
}
90+
Err(_) => {
91+
return Err(OxenError::basic_str(
92+
"Error: unable to verify remote version",
93+
));
9894
}
9995
}
100-
Ok(false)
96+
Ok(())
10197
}
10298

103-
pub fn check_versions_migration_needed(repo: &LocalRepository) -> Result<(), OxenError> {
104-
let migration_needed = version_files_out_of_date(repo)?;
99+
pub fn check_repo_migration_needed(repo: &LocalRepository) -> Result<(), OxenError> {
100+
let migrations: Vec<Box<dyn Migrate>> = vec![
101+
Box::new(UpdateVersionFilesMigration),
102+
Box::new(CreateMerkleTreesMigration),
103+
];
104+
105+
let mut migrations_needed: Vec<Box<dyn Migrate>> = Vec::new();
105106

106-
if migration_needed {
107-
let warning = "Warning: 🐂 This repo requires a quick migration to the latest Oxen version.\n\nPlease run `oxen migrate up update-version-files .` to migrate.\n".to_string().yellow();
108-
eprintln!("{warning}");
109-
return Err(OxenError::MigrationRequired(
110-
"Error: Migration required".to_string().into(),
111-
));
107+
for migration in migrations {
108+
if migration.is_needed(repo)? {
109+
migrations_needed.push(migration);
110+
}
112111
}
113-
Ok(())
112+
113+
if migrations_needed.is_empty() {
114+
return Ok(());
115+
}
116+
let warning = "\nWarning: 🐂 This repo requires a quick migration to the latest Oxen version. \n\nPlease run the following to update:".to_string().yellow();
117+
eprintln!("{warning}\n\n");
118+
for migration in migrations_needed {
119+
eprintln!(
120+
"{}",
121+
format!("oxen migrate up {} .\n", migration.name()).yellow()
122+
);
123+
}
124+
eprintln!("\n");
125+
Err(OxenError::MigrationRequired(
126+
"Error: Migration required".to_string().into(),
127+
))
114128
}
115129

116130
pub async fn init(path: &str) -> Result<(), OxenError> {
@@ -126,6 +140,7 @@ pub async fn init(path: &str) -> Result<(), OxenError> {
126140

127141
pub async fn clone(opts: &CloneOpts) -> Result<(), OxenError> {
128142
let host = api::remote::client::get_host_from_url(&opts.url)?;
143+
check_remote_version_blocking(host.clone()).await?;
129144
check_remote_version(host).await?;
130145

131146
command::clone(opts).await?;
@@ -304,6 +319,8 @@ pub async fn download(opts: DownloadOpts) -> Result<(), OxenError> {
304319
return Err(OxenError::basic_str("Must supply a path to download."));
305320
}
306321

322+
check_remote_version_blocking(opts.clone().host).await?;
323+
307324
// Check if the first path is a valid remote repo
308325
let name = paths[0].to_string_lossy();
309326
if let Some(remote_repo) =
@@ -329,6 +346,7 @@ pub async fn remote_download(opts: DownloadOpts) -> Result<(), OxenError> {
329346
return Err(OxenError::basic_str("Must supply a path to download."));
330347
}
331348

349+
check_remote_version_blocking(opts.clone().host).await?;
332350
// Check if the first path is a valid remote repo
333351
let name = paths[0].to_string_lossy();
334352
if let Some(remote_repo) =
@@ -414,6 +432,7 @@ pub async fn remote_metadata_list_image(path: impl AsRef<Path>) -> Result<(), Ox
414432
pub async fn add(opts: AddOpts) -> Result<(), OxenError> {
415433
let repo_dir = env::current_dir().unwrap();
416434
let repository = LocalRepository::from_dir(&repo_dir)?;
435+
check_repo_migration_needed(&repository)?;
417436

418437
for path in &opts.paths {
419438
if opts.is_remote {
@@ -429,6 +448,7 @@ pub async fn add(opts: AddOpts) -> Result<(), OxenError> {
429448
pub async fn rm(paths: Vec<PathBuf>, opts: &RmOpts) -> Result<(), OxenError> {
430449
let repo_dir = env::current_dir().unwrap();
431450
let repository = LocalRepository::from_dir(&repo_dir)?;
451+
check_repo_migration_needed(&repository)?;
432452

433453
for path in paths {
434454
let path_opts = RmOpts::from_path_opts(&path, opts);
@@ -442,7 +462,7 @@ pub async fn restore(opts: RestoreOpts) -> Result<(), OxenError> {
442462
let repo_dir = env::current_dir().unwrap();
443463
let repository = LocalRepository::from_dir(&repo_dir)?;
444464

445-
check_versions_migration_needed(&repository)?;
465+
check_repo_migration_needed(&repository)?;
446466
if opts.is_remote {
447467
command::remote::restore(&repository, opts).await?;
448468
} else {
@@ -455,9 +475,10 @@ pub async fn restore(opts: RestoreOpts) -> Result<(), OxenError> {
455475
pub async fn push(remote: &str, branch: &str) -> Result<(), OxenError> {
456476
let repo_dir = env::current_dir().unwrap();
457477
let repository = LocalRepository::from_dir(&repo_dir)?;
458-
459-
check_versions_migration_needed(&repository)?;
460478
let host = get_host_from_repo(&repository)?;
479+
480+
check_repo_migration_needed(&repository)?;
481+
check_remote_version_blocking(host.clone()).await?;
461482
check_remote_version(host).await?;
462483

463484
command::push_remote_branch(&repository, remote, branch).await?;
@@ -469,7 +490,8 @@ pub async fn pull(remote: &str, branch: &str, all: bool) -> Result<(), OxenError
469490
let repository = LocalRepository::from_dir(&repo_dir)?;
470491

471492
let host = get_host_from_repo(&repository)?;
472-
check_versions_migration_needed(&repository)?;
493+
check_repo_migration_needed(&repository)?;
494+
check_remote_version_blocking(host.clone()).await?;
473495
check_remote_version(host).await?;
474496

475497
command::pull_remote_branch(&repository, remote, branch, all).await?;
@@ -501,6 +523,7 @@ pub fn compare(
501523
) -> Result<(), OxenError> {
502524
let repo_dir = env::current_dir().unwrap();
503525
let repository = LocalRepository::from_dir(&repo_dir)?;
526+
check_repo_migration_needed(&repository)?;
504527

505528
let current_commit = api::local::commits::head_commit(&repository)?;
506529
// For revision_1 and revision_2, if none, set to current_commit
@@ -528,6 +551,7 @@ pub fn compare(
528551
pub fn merge(branch: &str) -> Result<(), OxenError> {
529552
let repo_dir = env::current_dir().unwrap();
530553
let repository = LocalRepository::from_dir(&repo_dir)?;
554+
check_repo_migration_needed(&repository)?;
531555

532556
command::merge(&repository, branch)?;
533557
Ok(())
@@ -536,6 +560,7 @@ pub fn merge(branch: &str) -> Result<(), OxenError> {
536560
pub async fn commit(message: &str, is_remote: bool) -> Result<(), OxenError> {
537561
let repo_dir = env::current_dir().unwrap();
538562
let repo = LocalRepository::from_dir(&repo_dir)?;
563+
check_repo_migration_needed(&repo)?;
539564

540565
if is_remote {
541566
println!("Committing to remote with message: {message}");
@@ -562,6 +587,10 @@ pub async fn fetch() -> Result<(), OxenError> {
562587
util::fs::get_repo_root(&current_dir).ok_or(OxenError::basic_str(error::NO_REPO_FOUND))?;
563588

564589
let repository = LocalRepository::from_dir(&repo_dir)?;
590+
let host = get_host_from_repo(&repository)?;
591+
592+
check_repo_migration_needed(&repository)?;
593+
check_remote_version_blocking(host.clone()).await?;
565594
command::fetch(&repository).await?;
566595
Ok(())
567596
}
@@ -615,6 +644,8 @@ pub async fn status(directory: Option<PathBuf>, opts: &StagedDataOpts) -> Result
615644

616645
let directory = directory.unwrap_or(current_dir);
617646
let repository = LocalRepository::from_dir(&repo_dir)?;
647+
check_repo_migration_needed(&repository)?;
648+
618649
let repo_status = command::status_from_dir(&repository, &directory)?;
619650

620651
if let Some(current_branch) = api::local::branches::current_branch(&repository)? {
@@ -681,6 +712,7 @@ async fn remote_status(directory: Option<PathBuf>, opts: &StagedDataOpts) -> Res
681712

682713
let repository = LocalRepository::from_dir(&repo_dir)?;
683714
let host = get_host_from_repo(&repository)?;
715+
check_remote_version_blocking(host.clone()).await?;
684716
check_remote_version(host).await?;
685717

686718
let directory = directory.unwrap_or(PathBuf::from("."));
@@ -746,6 +778,7 @@ pub async fn remote_ls(opts: &ListOpts) -> Result<(), OxenError> {
746778
let repository = LocalRepository::from_dir(&repo_dir)?;
747779

748780
let host = get_host_from_repo(&repository)?;
781+
check_remote_version_blocking(host.clone()).await?;
749782
check_remote_version(host).await?;
750783

751784
let directory = paths[0].clone();
@@ -1032,6 +1065,7 @@ pub async fn list_remote_branches(name: &str) -> Result<(), OxenError> {
10321065
let repo = LocalRepository::from_dir(&repo_dir)?;
10331066

10341067
let host = get_host_from_repo(&repo)?;
1068+
check_remote_version_blocking(host.clone()).await?;
10351069
check_remote_version(host).await?;
10361070

10371071
let remote = repo

src/cli/src/parse_and_run.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ use crate::cmd_setup::{ADD, COMMIT, DF, DIFF, DOWNLOAD, LOG, LS, METADATA, RESTO
88
use crate::dispatch;
99
use clap::ArgMatches;
1010
use liboxen::command::migrate::{
11-
CacheDataFrameSizeMigration, Migrate, PropagateSchemasMigration, UpdateVersionFilesMigration,
11+
CacheDataFrameSizeMigration, CreateMerkleTreesMigration, Migrate, PropagateSchemasMigration,
12+
UpdateVersionFilesMigration,
1213
};
1314
use liboxen::constants::{DEFAULT_BRANCH_NAME, DEFAULT_HOST, DEFAULT_REMOTE_NAME};
1415
use liboxen::error::OxenError;
@@ -1115,6 +1116,13 @@ pub async fn migrate(sub_matches: &ArgMatches) {
11151116
eprintln!("Error running migration: {}", err);
11161117
std::process::exit(1);
11171118
}
1119+
} else if migration == CreateMerkleTreesMigration.name() {
1120+
if let Err(err) =
1121+
run_migration(&CreateMerkleTreesMigration, direction, sub_matches)
1122+
{
1123+
eprintln!("Error running migration: {}", err);
1124+
std::process::exit(1);
1125+
}
11181126
} else {
11191127
eprintln!("Invalid migration: {}", migration);
11201128
}

0 commit comments

Comments
 (0)