Skip to content

Commit 47a3886

Browse files
committed
Create a new RefSpec alternative to EnvSpec
The `EnvSpec` type has become overloaded and used is contexts where it can represent things that don't make sense. For example, `"-"` is a legal (empty) `EnvSpec` which can be used to create an empty environment, but it doesn't make sense to `spfs push` an empty environment. A `RefSpec` is a similar type but instead of representing items that can be used to create an environment, it represents items that can be copied between repositories. The `EnvSpec` scope has narrowed to restrict that any digests within it are treated as object digests (not payloads), because it is non-sensical to create an environment from a payload digest. The `RefSpec` allows digests to be payload digests, but does not support live layer files. It is also required to be non-empty, since it doesn't make sense to copy "nothing" between repositories. Spec files are still supported although the files themselves cannot be copied, however it should be possible to provide the list of refs to sync to `spfs push` via a spec file. # Design Notes There are still several places that accept an `EnvSpec` that is intended to be used to create an environment, but it is first used to sync items locally, so it needs to be converted to a `RefSpec` first (and the sync result converted back to an `EnvSpec`). These conversions are either lossy or fallible, so there are things likely broken in this PR that just aren't covered by tests yet. In this design, it makes sense for the conversion from `EnvSpec` to `RefSpec` to be lossy (instead of fallible) in the context of syncing content locally. These changes were created to build on the ideas in PR #1289 and further leverage `PartialDigestType` introduced there. Signed-off-by: J Robert Ray <[email protected]>
1 parent bd31c3f commit 47a3886

File tree

16 files changed

+650
-100
lines changed

16 files changed

+650
-100
lines changed

crates/spfs-cli/cmd-render/src/cmd_render.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,10 @@ impl CmdRender {
6464
env_spec = self
6565
.sync
6666
.get_syncer(&origin, &handle)
67-
.sync_env(env_spec)
67+
.sync_ref_spec(env_spec.try_into()?)
6868
.await?
69-
.env;
69+
.ref_spec
70+
.into();
7071
}
7172

7273
// Use PayloadFallback to repair any missing payloads found in the

crates/spfs-cli/main/src/cmd_pull.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use clap::Args;
66
use miette::Result;
77
use spfs::sync::reporter::Summary;
8+
use spfs::tracking::RefSpec;
89
use spfs_cli_common as cli;
910

1011
/// Pull one or more objects to the local repository
@@ -27,7 +28,7 @@ pub struct CmdPull {
2728
/// These can be individual tags or digests, or they may also
2829
/// be a collection of items joined by a '+'
2930
#[clap(value_name = "REF", required = true)]
30-
refs: Vec<spfs::tracking::EnvSpec>,
31+
refs: Vec<spfs::tracking::RefSpec>,
3132
}
3233

3334
impl CmdPull {
@@ -37,11 +38,11 @@ impl CmdPull {
3738
spfs::config::open_repository_from_string(config, self.repos.remote.as_ref())
3839
)?;
3940

40-
let env_spec = self.refs.iter().cloned().collect();
41+
let ref_spec = RefSpec::combine(&self.refs)?;
4142
let summary = self
4243
.sync
4344
.get_syncer(&remote, &repo)
44-
.sync_env(env_spec)
45+
.sync_ref_spec(ref_spec)
4546
.await?
4647
.summary();
4748

crates/spfs-cli/main/src/cmd_push.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use clap::Args;
66
use miette::Result;
77
use spfs::sync::reporter::Summary;
8+
use spfs::tracking::RefSpec;
89
use spfs_cli_common as cli;
910

1011
/// Push one or more objects to a remote repository
@@ -24,7 +25,7 @@ pub struct CmdPush {
2425
/// These can be individual tags or digests, or they may also
2526
/// be a collection of items joined by a '+'
2627
#[clap(value_name = "REF", required = true)]
27-
refs: Vec<spfs::tracking::EnvSpec>,
28+
refs: Vec<spfs::tracking::RefSpec>,
2829
}
2930

3031
impl CmdPush {
@@ -40,13 +41,13 @@ impl CmdPush {
4041
spfs::config::open_repository_from_string(config, self.repos.remote.as_ref()),
4142
)?;
4243

43-
let env_spec = self.refs.iter().cloned().collect();
44+
let ref_spec = RefSpec::combine(&self.refs)?;
4445
// the latest tag is always synced when pushing
4546
self.sync.sync = true;
4647
let summary = self
4748
.sync
4849
.get_syncer(&repo, &remote)
49-
.sync_env(env_spec)
50+
.sync_ref_spec(ref_spec)
5051
.await?
5152
.summary();
5253
tracing::info!("{}", spfs::io::format_sync_summary(&summary));

crates/spfs-cli/main/src/cmd_reset.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,10 @@ impl CmdReset {
4747
env_spec = self
4848
.sync
4949
.get_syncer(&origin, &repo)
50-
.sync_env(env_spec)
50+
.sync_ref_spec(env_spec.try_into()?)
5151
.await?
52-
.env;
52+
.ref_spec
53+
.into();
5354
}
5455
for item in env_spec.iter() {
5556
let digest = item.resolve_digest(&repo).await?;

crates/spfs-cli/main/src/cmd_run.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ impl CmdRun {
212212
let _synced = self
213213
.sync
214214
.get_syncer(&origin, &repo)
215-
.sync_env(references_to_sync)
215+
.sync_ref_spec(references_to_sync.try_into()?)
216216
.await?;
217217
}
218218
tracing::debug!("synced and about to launch process with durable runtime");
@@ -300,9 +300,9 @@ impl CmdRun {
300300
let synced = self
301301
.sync
302302
.get_syncer(&origin, &repo)
303-
.sync_env(references_to_sync)
303+
.sync_ref_spec(references_to_sync.try_into()?)
304304
.await?;
305-
for item in synced.env.iter() {
305+
for item in synced.ref_spec.iter() {
306306
let digest = item.resolve_digest(&repo).await?;
307307
runtime.push_digest(digest);
308308
}

crates/spfs/src/sync.rs

Lines changed: 28 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@ use reporter::{
1111
SyncAnnotationResult,
1212
SyncBlobResult,
1313
SyncEntryResult,
14-
SyncEnvItemResult,
15-
SyncEnvResult,
1614
SyncLayerResult,
1715
SyncManifestResult,
1816
SyncObjectResult,
1917
SyncPayloadResult,
2018
SyncPlatformResult,
19+
SyncRefItemResult,
20+
SyncRefResult,
2121
SyncReporter,
2222
SyncReporters,
2323
SyncTagResult,
@@ -177,52 +177,57 @@ impl<'src, 'dst> Syncer<'src, 'dst> {
177177
/// Sync the object(s) referenced by the given string.
178178
///
179179
/// Any valid [`crate::tracking::EnvSpec`] is accepted as a reference.
180-
pub async fn sync_ref<R: AsRef<str>>(&self, reference: R) -> Result<SyncEnvResult> {
180+
pub async fn sync_ref<R: AsRef<str>>(&self, reference: R) -> Result<SyncRefResult> {
181181
let env_spec = reference.as_ref().parse()?;
182-
self.sync_env(env_spec).await
182+
self.sync_ref_spec(env_spec).await
183183
}
184184

185-
/// Sync all of the objects identified by the given env.
186-
pub async fn sync_env(&self, env: tracking::EnvSpec) -> Result<SyncEnvResult> {
187-
self.reporter.visit_env(&env);
185+
/// Sync all of the objects identified by the given ref spec.
186+
pub async fn sync_ref_spec(&self, ref_spec: tracking::RefSpec) -> Result<SyncRefResult> {
187+
self.reporter.visit_ref_spec(&ref_spec);
188188
let mut futures = FuturesUnordered::new();
189-
for item in env.iter().cloned() {
190-
futures.push(self.sync_env_item(item));
189+
for item in ref_spec.iter().cloned() {
190+
futures.push(self.sync_ref_item(item));
191191
}
192-
let mut results = Vec::with_capacity(env.len());
192+
let mut results = Vec::with_capacity(ref_spec.len());
193193
while let Some(result) = futures.try_next().await? {
194194
results.push(result);
195195
}
196-
let res = SyncEnvResult { env, results };
197-
self.reporter.synced_env(&res);
196+
let res = SyncRefResult { ref_spec, results };
197+
self.reporter.synced_ref_spec(&res);
198198
Ok(res)
199199
}
200200

201201
/// Sync one environment item and any associated data.
202-
pub async fn sync_env_item(&self, item: tracking::EnvSpecItem) -> Result<SyncEnvItemResult> {
202+
pub async fn sync_ref_item(&self, item: tracking::RefSpecItem) -> Result<SyncRefItemResult> {
203203
tracing::debug!(?item, "Syncing item");
204-
self.reporter.visit_env_item(&item);
204+
self.reporter.visit_ref_item(&item);
205205
let res = match item {
206-
tracking::EnvSpecItem::Digest(digest) => match self.sync_object_digest(digest).await {
207-
Ok(r) => SyncEnvItemResult::Object(r),
206+
tracking::RefSpecItem::Digest(digest) => match self.sync_object_digest(digest).await {
207+
Ok(r) => SyncRefItemResult::Object(r),
208208
Err(Error::UnknownObject(digest)) => self
209209
.sync_payload(digest)
210210
.await
211-
.map(SyncEnvItemResult::Payload)?,
211+
.map(SyncRefItemResult::Payload)?,
212212
Err(e) => return Err(e),
213213
},
214-
tracking::EnvSpecItem::PartialDigest(digest) => {
214+
tracking::RefSpecItem::PartialDigest(digest) => {
215215
self.sync_partial_digest(digest).await.map(Into::into)?
216216
}
217-
tracking::EnvSpecItem::TagSpec(tag_spec) => {
218-
self.sync_tag(tag_spec).await.map(SyncEnvItemResult::Tag)?
217+
tracking::RefSpecItem::TagSpec(tag_spec) => {
218+
self.sync_tag(tag_spec).await.map(SyncRefItemResult::Tag)?
219219
}
220220
// These are not objects in spfs, so they are not syncable
221-
tracking::EnvSpecItem::SpecFile(_) => {
222-
return Ok(SyncEnvItemResult::Object(SyncObjectResult::Ignorable));
221+
// XXX but it can be a spec file the contains syncable things? Would
222+
// those things become garbage instantly in the destination repo?
223+
// XXX shouldn't this be an error to inform the user that it was
224+
// not synced? Or should a RefSpecItem even be allowed to contain
225+
// SpecFiles?
226+
tracking::RefSpecItem::SpecFile(_) => {
227+
return Ok(SyncRefItemResult::Object(SyncObjectResult::Ignorable));
223228
}
224229
};
225-
self.reporter.synced_env_item(&res);
230+
self.reporter.synced_ref_item(&res);
226231
Ok(res)
227232
}
228233

crates/spfs/src/sync/reporter.rs

Lines changed: 39 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -43,17 +43,17 @@ impl SyncReporters {
4343
/// followed up by a call to the corresponding synced_*.
4444
#[enum_dispatch::enum_dispatch]
4545
pub trait SyncReporter: Send + Sync {
46-
/// Called when an environment has been identified to sync
47-
fn visit_env(&self, _env: &tracking::EnvSpec) {}
46+
/// Called when an ref spec has been identified to sync
47+
fn visit_ref_spec(&self, _ref_spec: &tracking::RefSpec) {}
4848

49-
/// Called when a environment has finished syncing
50-
fn synced_env(&self, _result: &SyncEnvResult) {}
49+
/// Called when a ref spec has finished syncing
50+
fn synced_ref_spec(&self, _result: &SyncRefResult) {}
5151

52-
/// Called when an environment item has been identified to sync
53-
fn visit_env_item(&self, _item: &tracking::EnvSpecItem) {}
52+
/// Called when an ref item has been identified to sync
53+
fn visit_ref_item(&self, _item: &tracking::RefSpecItem) {}
5454

55-
/// Called when a environment item has finished syncing
56-
fn synced_env_item(&self, _result: &SyncEnvItemResult) {}
55+
/// Called when a ref item has finished syncing
56+
fn synced_ref_item(&self, _result: &SyncRefItemResult) {}
5757

5858
/// Called when a tag has been identified to sync
5959
fn visit_tag(&self, _tag: &tracking::TagSpec) {}
@@ -114,17 +114,17 @@ impl<T> SyncReporter for Arc<T>
114114
where
115115
T: SyncReporter,
116116
{
117-
fn visit_env(&self, env: &tracking::EnvSpec) {
118-
(**self).visit_env(env)
117+
fn visit_ref_spec(&self, ref_spec: &tracking::RefSpec) {
118+
(**self).visit_ref_spec(ref_spec)
119119
}
120-
fn synced_env(&self, result: &SyncEnvResult) {
121-
(**self).synced_env(result)
120+
fn synced_ref_spec(&self, result: &SyncRefResult) {
121+
(**self).synced_ref_spec(result)
122122
}
123-
fn visit_env_item(&self, item: &tracking::EnvSpecItem) {
124-
(**self).visit_env_item(item)
123+
fn visit_ref_item(&self, item: &tracking::RefSpecItem) {
124+
(**self).visit_ref_item(item)
125125
}
126-
fn synced_env_item(&self, result: &SyncEnvItemResult) {
127-
(**self).synced_env_item(result)
126+
fn synced_ref_item(&self, result: &SyncRefItemResult) {
127+
(**self).synced_ref_item(result)
128128
}
129129
fn visit_tag(&self, tag: &tracking::TagSpec) {
130130
(**self).visit_tag(tag)
@@ -177,17 +177,17 @@ where
177177
}
178178

179179
impl SyncReporter for Box<dyn SyncReporter> {
180-
fn visit_env(&self, env: &tracking::EnvSpec) {
181-
(**self).visit_env(env)
180+
fn visit_ref_spec(&self, ref_spec: &tracking::RefSpec) {
181+
(**self).visit_ref_spec(ref_spec)
182182
}
183-
fn synced_env(&self, result: &SyncEnvResult) {
184-
(**self).synced_env(result)
183+
fn synced_ref_spec(&self, result: &SyncRefResult) {
184+
(**self).synced_ref_spec(result)
185185
}
186-
fn visit_env_item(&self, item: &tracking::EnvSpecItem) {
187-
(**self).visit_env_item(item)
186+
fn visit_ref_item(&self, item: &tracking::RefSpecItem) {
187+
(**self).visit_ref_item(item)
188188
}
189-
fn synced_env_item(&self, result: &SyncEnvItemResult) {
190-
(**self).synced_env_item(result)
189+
fn synced_ref_item(&self, result: &SyncRefItemResult) {
190+
(**self).synced_ref_item(result)
191191
}
192192
fn visit_tag(&self, tag: &tracking::TagSpec) {
193193
(**self).visit_tag(tag)
@@ -243,17 +243,17 @@ impl<T> SyncReporter for Box<Arc<T>>
243243
where
244244
T: SyncReporter,
245245
{
246-
fn visit_env(&self, env: &tracking::EnvSpec) {
247-
(***self).visit_env(env)
246+
fn visit_ref_spec(&self, ref_spec: &tracking::RefSpec) {
247+
(***self).visit_ref_spec(ref_spec)
248248
}
249-
fn synced_env(&self, result: &SyncEnvResult) {
250-
(***self).synced_env(result)
249+
fn synced_ref_spec(&self, result: &SyncRefResult) {
250+
(***self).synced_ref_spec(result)
251251
}
252-
fn visit_env_item(&self, item: &tracking::EnvSpecItem) {
253-
(***self).visit_env_item(item)
252+
fn visit_ref_item(&self, item: &tracking::RefSpecItem) {
253+
(***self).visit_ref_item(item)
254254
}
255-
fn synced_env_item(&self, result: &SyncEnvItemResult) {
256-
(***self).synced_env_item(result)
255+
fn synced_ref_item(&self, result: &SyncRefItemResult) {
256+
(***self).synced_ref_item(result)
257257
}
258258
fn visit_tag(&self, tag: &tracking::TagSpec) {
259259
(***self).visit_tag(tag)
@@ -342,7 +342,7 @@ impl SyncReporter for ConsoleSyncReporter {
342342
bars.bytes.inc(result.summary().synced_payload_bytes);
343343
}
344344

345-
fn synced_env(&self, _result: &SyncEnvResult) {
345+
fn synced_ref_spec(&self, _result: &SyncRefResult) {
346346
// Don't cause the bars to be initialized here if they haven't already
347347
// been, calling abandon will briefly display some zero-progress bars.
348348
if let Some(bars) = self.bars.get() {
@@ -453,26 +453,26 @@ where
453453
}
454454

455455
#[derive(Debug)]
456-
pub struct SyncEnvResult {
457-
pub env: tracking::EnvSpec,
458-
pub results: Vec<SyncEnvItemResult>,
456+
pub struct SyncRefResult {
457+
pub ref_spec: tracking::RefSpec,
458+
pub results: Vec<SyncRefItemResult>,
459459
}
460460

461-
impl Summary for SyncEnvResult {
461+
impl Summary for SyncRefResult {
462462
fn summary(&self) -> SyncSummary {
463463
self.results.iter().map(|r| r.summary()).sum()
464464
}
465465
}
466466

467467
#[derive(Debug)]
468468
#[enum_dispatch::enum_dispatch(Summary)]
469-
pub enum SyncEnvItemResult {
469+
pub enum SyncRefItemResult {
470470
Tag(SyncTagResult),
471471
Object(SyncObjectResult),
472472
Payload(SyncPayloadResult),
473473
}
474474

475-
impl From<SyncItemResult> for SyncEnvItemResult {
475+
impl From<SyncItemResult> for SyncRefItemResult {
476476
fn from(value: SyncItemResult) -> Self {
477477
match value {
478478
SyncItemResult::Object(obj) => Self::Object(obj),

crates/spfs/src/sync_test.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -283,11 +283,11 @@ async fn test_sync_missing_from_source(
283283
.await
284284
.expect("Should not fail when object is already in destination");
285285
syncer
286-
.sync_env(tag.into())
286+
.sync_ref_spec(tag.into())
287287
.await
288288
.expect("Should not fail when object is already in destination");
289289
syncer
290-
.sync_env(platform_digest.into())
290+
.sync_ref_spec(platform_digest.into())
291291
.await
292292
.expect("Should not fail when object is already in destination");
293293
}

0 commit comments

Comments
 (0)