|
@@ -123,6 +123,31 @@ async fn symlink_source(
|
|
|
);
|
|
);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // Build a shell `case` clause to skip excluded paths. Each entry is
|
|
|
|
|
+ // matched against the file's path relative to the snapshot root (e.g.
|
|
|
|
|
+ // `L5.safetensors`, or `subdir/file.bin`). Same character set as glob.
|
|
|
|
|
+ let exclude_clause = match &source.exclude {
|
|
|
|
|
+ None => String::new(),
|
|
|
|
|
+ Some(entries) if entries.is_empty() => String::new(),
|
|
|
|
|
+ Some(entries) => {
|
|
|
|
|
+ for e in entries {
|
|
|
|
|
+ if e.is_empty()
|
|
|
|
|
+ || !e
|
|
|
|
|
+ .chars()
|
|
|
|
|
+ .all(|c| c.is_ascii_alphanumeric() || "*?._-/".contains(c))
|
|
|
|
|
+ {
|
|
|
|
|
+ anyhow::bail!(
|
|
|
|
|
+ "composed_model source '{}': exclude entry '{}' is invalid \
|
|
|
|
|
+ (allowed: alphanumeric, * ? . _ - /)",
|
|
|
|
|
+ source.repo,
|
|
|
|
|
+ e
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ format!("case \"$rel\" in {}) continue ;; esac; ", entries.join("|"))
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
// Enumerate: no glob → recursive `find` (whole hierarchy, every file in
|
|
// Enumerate: no glob → recursive `find` (whole hierarchy, every file in
|
|
|
// every subdir); glob → shell glob match (one level, `/` allowed for a
|
|
// every subdir); glob → shell glob match (one level, `/` allowed for a
|
|
|
// specific subdir). Both feed the same per-file body below.
|
|
// specific subdir). Both feed the same per-file body below.
|
|
@@ -158,6 +183,7 @@ async fn symlink_source(
|
|
|
{enumerate} \
|
|
{enumerate} \
|
|
|
[ -e \"$f\" ] || [ -L \"$f\" ] || continue; \
|
|
[ -e \"$f\" ] || [ -L \"$f\" ] || continue; \
|
|
|
rel=\"${{f#\"$snap\"/}}\"; \
|
|
rel=\"${{f#\"$snap\"/}}\"; \
|
|
|
|
|
+ {exclude_clause}\
|
|
|
parent=\"$(dirname {host_q}/\"$rel\")\"; \
|
|
parent=\"$(dirname {host_q}/\"$rel\")\"; \
|
|
|
mkdir -p \"$parent\" && \
|
|
mkdir -p \"$parent\" && \
|
|
|
ln -snf \"$csnap/$rel\" {host_q}/\"$rel\"; \
|
|
ln -snf \"$csnap/$rel\" {host_q}/\"$rel\"; \
|
|
@@ -167,6 +193,7 @@ async fn symlink_source(
|
|
|
model_q = shq(&model_dir),
|
|
model_q = shq(&model_dir),
|
|
|
host_q = shq(host_path),
|
|
host_q = shq(host_path),
|
|
|
enumerate = enumerate,
|
|
enumerate = enumerate,
|
|
|
|
|
+ exclude_clause = exclude_clause,
|
|
|
);
|
|
);
|
|
|
|
|
|
|
|
run_ssh(config, node, cmd, false).await.with_context(|| {
|
|
run_ssh(config, node, cmd, false).await.with_context(|| {
|