From 6877d6535fd1bb8a23f18a476185ea86288f7488 Mon Sep 17 00:00:00 2001 From: aiongg <67543387+aiongg@users.noreply.github.com> Date: Fri, 23 Oct 2026 17:51:28 -0400 Subject: [PATCH] Links: resolve stored paths with craft-docpath's resolve_absolute (#3267) * Links: use resolve_absolute for stored paths * CI: release only with git dependencies from storytold * Release: check git sources from the parsed Cargo.lock and cargo config The grep let any character stand for the dot in github.com, passed a missing or stale Cargo.lock, and missed source replacement in .cargo/config. --- .github/workflows/release.yml | 40 +++++++++++++++++++++++++++++ crates/engine/src/cmd/fileio/mod.rs | 5 +--- crates/svg/src/import/files.rs | 2 +- 3 files changed, 42 insertions(+), 5 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 54a8e53fb..0cb9e9193 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -20,7 +20,47 @@ concurrency: cancel-in-progress: false jobs: + git-sources: + name: Git dependencies come from storytold + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - name: Git dependencies come from storytold + run: | + set -euo pipefail + test -f Cargo.lock || { echo "Cargo.lock is missing"; exit 1; } + # Source replacement, patches and path overrides in cargo config never show up + # in Cargo.lock. Parsed rather than grepped: TOML has many spellings of one key. + # Cargo still reads the legacy .cargo/config. + python3 - <<'PY' + import os, sys, tomllib + for path in (".cargo/config", ".cargo/config.toml"): + if not os.path.exists(path): + continue + config = tomllib.load(open(path, "rb")) + replaced = [name for name, source in config.get("source", {}).items() + if isinstance(source, dict) and "replace-with" in source] + if replaced or "patch" in config or "paths" in config: + print(f"{path} replaces, patches or overrides a source") + sys.exit(1) + PY + # Fails when Cargo.lock is out of date with the manifests. + cargo metadata --locked --format-version 1 > /dev/null + python3 - <<'PY' + import sys, tomllib + lock = tomllib.load(open("Cargo.lock", "rb")) + bad = sorted({p["source"] for p in lock.get("package", []) + if p.get("source", "").startswith("git+") + and not p["source"].startswith("git+https://github.com/storytold/")}) + if bad: + print("Git dependencies outside github.com/storytold:", *bad, sep="\n") + sys.exit(1) + PY + release: + needs: git-sources # Intentionally tracked at @main (not SHA-pinned) so org-wide CI improvements, security fixes, and build optimizations propagate without PR churn across all craftapp repos. uses: storytold/craft-common-actions/.github/workflows/release.yml@main with: diff --git a/crates/engine/src/cmd/fileio/mod.rs b/crates/engine/src/cmd/fileio/mod.rs index 8c224aa12..dc684f2c5 100644 --- a/crates/engine/src/cmd/fileio/mod.rs +++ b/crates/engine/src/cmd/fileio/mod.rs @@ -1184,7 +1184,7 @@ pub(crate) fn document_path_with( ) -> std::result::Result { use craft_docpath::Refused; let document = document.map(absolute_path); - let resolve = |document: Option<&str>| craft_docpath::resolve(stored, document.map(std::path::Path::new)); + let resolve = |document: Option<&str>| craft_docpath::resolve_absolute(stored, document.map(std::path::Path::new)); let path = match resolve(document.as_deref()) { Err(Refused::Network) => { let share = document.as_deref().and_then(share).ok_or(Refused::Network)?; @@ -1192,9 +1192,6 @@ pub(crate) fn document_path_with( } path => path?, }; - if !path.is_absolute() { - return Err(Refused::Invalid); - } path.into_os_string().into_string().map_err(|_| Refused::Invalid) } diff --git a/crates/svg/src/import/files.rs b/crates/svg/src/import/files.rs index f3ecbc862..e4732f8cd 100644 --- a/crates/svg/src/import/files.rs +++ b/crates/svg/src/import/files.rs @@ -116,7 +116,7 @@ fn target(h: &str, folder: Option<&str>) -> (String, Option) { }); match craft_docpath::resolve(&stored, document.as_deref()) { Ok(path) => { - let file = path.is_absolute().then(|| path.to_string_lossy().into_owned()); + let file = craft_docpath::resolve_absolute(&stored, document.as_deref()).ok().map(|p| p.to_string_lossy().into_owned()); let as_written = !file_url && craft_docpath::resolve(&stored, None).as_ref() == Ok(&path); let link = if as_written { stored } else { path.to_string_lossy().into_owned() }; (link, file)