fix(xtask): resolve local-registry install via sparse+file index (#856) - #970
fix(xtask): resolve local-registry install via sparse+file index (#856)#970EffortlessSteven wants to merge 4 commits into
Conversation
Adds a manual ubuntu-latest lane that runs verify-local-release-install and uploads the machine-readable receipt artifact. Registers the lane in policy/ci-lane-whitelist.toml as advisory deep product proof. Co-authored-by: Cursor <cursoragent@cursor.com>
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
WalkthroughChangesLocal release receipt flow
Possibly related issues
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
d23d58b to
6356c57
Compare
6356c57 to
43dd241
Compare
There was a problem hiding this comment.
Actionable comments posted: 6
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/local-release-receipt.yml:
- Line 29: Pin the third-party actions in the workflow—actions/checkout,
dtolnay/rust-toolchain, and actions/upload-artifact—to their resolved immutable
commit SHAs instead of the current version tags or master branch, preserving
each action’s existing version/reference.
- Line 29: Update the actions/checkout step to disable credential persistence by
setting persist-credentials to false; leave the existing checkout version and
workflow steps unchanged.
- Line 38: Update both run steps invoking verify-local-release-install to pass
inputs.version through an environment variable, then reference that variable in
the shell command instead of interpolating the GitHub expression inside the
script. Preserve the existing command arguments and dry-run behavior while
ensuring the untrusted value is not embedded in shell script text.
In `@xtask/src/local_registry.rs`:
- Around line 127-138: Extend manifest_missing_registry_for_workspace_deps to
inspect every target.<cfg>.dependencies table in addition to the top-level
dependency tables, matching the target-table handling in
inject_registry_into_manifest. Update the related regression test to include an
unpinned workspace dependency under a target-specific table and verify the
checker rejects it.
- Around line 589-665: The dependency checks in inject_registry_into_manifest
and the corresponding target-specific path must match published_set against each
dependency’s effective package name, using the package field when present and
the table key otherwise. Apply this lookup consistently before
registry_pinned_dependency_spec and in the related validation logic so renamed
dependencies such as parser/package = "adze-parser" are pinned correctly.
- Around line 701-760: The crate packaging flow should no longer depend on
external tar subprocesses. Replace the extract, manifest rewrite, and repack
logic in repack_crate_with_manifest, together with the related manifest-read
path in the same module, with in-process Rust tar/gzip handling using the
project’s tar/flate2 support; preserve archive contents, crate-root layout, and
existing error context.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 590bd3aa-4fb4-4473-b51b-8246ee0b43bc
📒 Files selected for processing (3)
.github/workflows/local-release-receipt.ymlpolicy/ci-lane-whitelist.tomlxtask/src/local_registry.rs
🔗 Linked repositories identified
CodeRabbit considers these linked repositories for cross-repo context during reviews:
EffortlessMetrics/adze(auto-detected)EffortlessMetrics/em-ci(auto-detected)EffortlessMetrics/ripr(auto-detected)EffortlessMetrics/unsafe-review(auto-detected)EffortlessMetrics/cargo-allow(auto-detected)
| runs-on: ubuntu-latest | ||
| timeout-minutes: 60 | ||
| steps: | ||
| - uses: actions/checkout@v6.0.2 |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Security Misconfiguration (CWE-1357)
Reachability: Internal
Pin third-party actions to a commit SHA.
actions/checkout@v6.0.2, dtolnay/rust-toolchain@master, and actions/upload-artifact@v7.0.0 are all referenced by mutable tag/branch rather than a pinned commit SHA; @master in particular can change at any time. This is flagged by policy as required (blanket SHA-pinning) and is a standard supply-chain hardening step for CI.
#!/bin/bash
# Resolve current commit SHAs for the referenced tags/branches to pin against.
gh api repos/actions/checkout/git/refs/tags/v6.0.2 --jq '.object.sha'
gh api repos/actions/upload-artifact/git/refs/tags/v7.0.0 --jq '.object.sha'
gh api repos/dtolnay/rust-toolchain/git/refs/heads/master --jq '.object.sha'Also applies to: 32-32, 46-46
🧰 Tools
🪛 zizmor (1.26.1)
[warning] 29-29: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false
(artipacked)
[error] 29-29: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/local-release-receipt.yml at line 29, Pin the third-party
actions in the workflow—actions/checkout, dtolnay/rust-toolchain, and
actions/upload-artifact—to their resolved immutable commit SHAs instead of the
current version tags or master branch, preserving each action’s existing
version/reference.
Source: Linters/SAST tools
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Sensitive Data Exposure (CWE-522): Insufficiently Protected Credentials
Reachability: Internal
Disable credential persistence on checkout.
actions/checkout defaults to persist-credentials: true, leaving the GITHUB_TOKEN in .git/config for the rest of the job — including the cargo run/cargo install steps that execute arbitrary workspace/dependency build code. This job never needs to push, so persistence should be disabled.
🔒️ Proposed fix
- uses: actions/checkout@v6.0.2
+ with:
+ persist-credentials: false📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| - uses: actions/checkout@v6.0.2 | |
| - uses: actions/checkout@v6.0.2 | |
| with: | |
| persist-credentials: false |
🧰 Tools
🪛 zizmor (1.26.1)
[warning] 29-29: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false
(artipacked)
[error] 29-29: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/local-release-receipt.yml at line 29, Update the
actions/checkout step to disable credential persistence by setting
persist-credentials to false; leave the existing checkout version and workflow
steps unchanged.
Source: Linters/SAST tools
|
|
||
| - name: Run local-registry receipt (dry-run) | ||
| if: inputs.dry_run | ||
| run: cargo run -q -p xtask -- verify-local-release-install --version "${{ inputs.version }}" --dry-run |
There was a problem hiding this comment.
🔒 Security & Privacy | 🔴 Critical | ⚡ Quick win
Injection (CWE-94): Improper Control of Generation of Code ('Code Injection')
Reachability: External
Shell/template injection via ${{ inputs.version }} in run:.
Both run: steps interpolate the untrusted workflow_dispatch input directly into the shell command text. A version string containing quote/shell metacharacters can break out of the quoted argument and execute arbitrary commands on the runner. Pass the input through an environment variable instead so the value is never substituted into the script text itself.
🛡️ Proposed fix
- name: Run local-registry receipt (dry-run)
if: inputs.dry_run
- run: cargo run -q -p xtask -- verify-local-release-install --version "${{ inputs.version }}" --dry-run
+ env:
+ RECEIPT_VERSION: ${{ inputs.version }}
+ run: cargo run -q -p xtask -- verify-local-release-install --version "$RECEIPT_VERSION" --dry-run
- name: Run local-registry receipt
if: ${{ !inputs.dry_run }}
- run: cargo run -q -p xtask -- verify-local-release-install --version "${{ inputs.version }}" --receipt-out target/local-release-receipt.json
+ env:
+ RECEIPT_VERSION: ${{ inputs.version }}
+ run: cargo run -q -p xtask -- verify-local-release-install --version "$RECEIPT_VERSION" --receipt-out target/local-release-receipt.jsonAlso applies to: 42-42
🧰 Tools
🪛 zizmor (1.26.1)
[error] 38-38: code injection via template expansion (template-injection): may expand into attacker-controllable code
(template-injection)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/local-release-receipt.yml at line 38, Update both run
steps invoking verify-local-release-install to pass inputs.version through an
environment variable, then reference that variable in the shell command instead
of interpolating the GitHub expression inside the script. Preserve the existing
command arguments and dry-run behavior while ensuring the untrusted value is not
embedded in shell script text.
Source: Linters/SAST tools
| for (name, spec) in deps { | ||
| if !published_set.contains(name) { | ||
| continue; | ||
| } | ||
| if !dependency_spec_has_registry(spec) { | ||
| return true; | ||
| } | ||
| } | ||
| } | ||
| false | ||
| } | ||
|
|
||
| fn dependency_spec_has_registry(spec: &toml::Value) -> bool { | ||
| match spec { | ||
| toml::Value::Table(table) => table | ||
| .get("registry") | ||
| .and_then(toml::Value::as_str) | ||
| .is_some_and(|registry| registry == REGISTRY_NAME), | ||
| _ => false, | ||
| } | ||
| } | ||
|
|
||
| fn inject_registry_deps_into_crate( | ||
| crate_path: &Path, | ||
| published_set: &BTreeSet<String>, | ||
| ) -> Result<()> { | ||
| let manifest_member = packaged_manifest_member(crate_path)?; | ||
| let manifest = packaged_manifest_text(crate_path)?; | ||
| let updated = inject_registry_into_manifest(&manifest, published_set)?; | ||
| if updated == manifest { | ||
| return Ok(()); | ||
| } | ||
| repack_crate_with_manifest(crate_path, &manifest_member, &updated) | ||
| } | ||
|
|
||
| fn inject_registry_into_manifest( | ||
| manifest: &str, | ||
| published_set: &BTreeSet<String>, | ||
| ) -> Result<String> { | ||
| let mut value: toml::Value = manifest | ||
| .parse() | ||
| .context("parsing packaged manifest for registry injection")?; | ||
| let root = value | ||
| .as_table_mut() | ||
| .context("packaged manifest root must be a table")?; | ||
|
|
||
| for table_name in ["dependencies", "dev-dependencies", "build-dependencies"] { | ||
| let Some(deps) = root.get_mut(table_name).and_then(toml::Value::as_table_mut) else { | ||
| continue; | ||
| }; | ||
| for (name, spec) in deps.iter_mut() { | ||
| if !published_set.contains(name) { | ||
| continue; | ||
| } | ||
| *spec = registry_pinned_dependency_spec(spec)?; | ||
| } | ||
| } | ||
|
|
||
| if let Some(target) = root.get_mut("target").and_then(toml::Value::as_table_mut) { | ||
| let target_keys = target.keys().cloned().collect::<Vec<_>>(); | ||
| for target_key in target_keys { | ||
| let Some(target_table) = target.get_mut(&target_key).and_then(toml::Value::as_table_mut) | ||
| else { | ||
| continue; | ||
| }; | ||
| for table_name in ["dependencies", "dev-dependencies", "build-dependencies"] { | ||
| let Some(deps) = target_table | ||
| .get_mut(table_name) | ||
| .and_then(toml::Value::as_table_mut) | ||
| else { | ||
| continue; | ||
| }; | ||
| for (name, spec) in deps.iter_mut() { | ||
| if !published_set.contains(name) { | ||
| continue; | ||
| } | ||
| *spec = registry_pinned_dependency_spec(spec)?; |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== relevant locations =="
rg -n "published_set|dependency_spec_has_registry|inject_registry_into_manifest|registry_pinned_dependency_spec|package =" xtask/src/local_registry.rs
echo
echo "== file outline =="
ast-grep outline xtask/src/local_registry.rs --view expanded || trueRepository: EffortlessMetrics/adze-swarm
Length of output: 5546
🏁 Script executed:
#!/bin/bash
set -euo pipefail
sed -n '599,720p' xtask/src/local_registry.rs
printf '\n====\n'
sed -n '888,1020p' xtask/src/local_registry.rs
printf '\n====\n'
sed -n '1025,1085p' xtask/src/local_registry.rsRepository: EffortlessMetrics/adze-swarm
Length of output: 10585
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
text = Path('xtask/src/local_registry.rs').read_text()
for needle in [
'package = "',
'dependency_spec_has_registry',
'manifest_missing_registry_for_workspace_deps',
'inject_registry_into_manifest',
'index_dependency(',
]:
print(f'== {needle} ==')
for i, line in enumerate(text.splitlines(), 1):
if needle in line:
start = max(1, i-5)
end = min(len(text.splitlines()), i+25)
for j in range(start, end+1):
print(f'{j}:{text.splitlines()[j-1]}')
print()
break
PYRepository: EffortlessMetrics/adze-swarm
Length of output: 4797
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n "package\\s*=\\s*\"|published_set.contains\\(|target.*dependencies|target_keys|CargoDependency|package_name" xtask/src/local_registry.rsRepository: EffortlessMetrics/adze-swarm
Length of output: 734
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
p = Path('xtask/src/local_registry.rs')
lines = p.read_text().splitlines()
for start in [599, 645, 888, 1025]:
print(f'## {start}')
for i in range(start, min(start+45, len(lines))+1):
print(f'{i}:{lines[i-1]}')
print()
PYRepository: EffortlessMetrics/adze-swarm
Length of output: 6822
🏁 Script executed:
#!/bin/bash
set -euo pipefail
sed -n '599,720p' xtask/src/local_registry.rs
printf '\n====\n'
sed -n '888,1020p' xtask/src/local_registry.rs
printf '\n====\n'
sed -n '1025,1095p' xtask/src/local_registry.rsRepository: EffortlessMetrics/adze-swarm
Length of output: 10915
🏁 Script executed:
#!/bin/bash
set -euo pipefail
sed -n '100,130p' xtask/src/local_registry.rs
printf '\n====\n'
sed -n '930,1018p' xtask/src/local_registry.rsRepository: EffortlessMetrics/adze-swarm
Length of output: 4597
🏁 Script executed:
#!/bin/bash
set -euo pipefail
sed -n '100,125p' xtask/src/local_registry.rs
printf '\n====\n'
sed -n '430,520p' xtask/src/local_registry.rsRepository: EffortlessMetrics/adze-swarm
Length of output: 4109
🏁 Script executed:
#!/bin/bash
set -euo pipefail
sed -n '520,620p' xtask/src/local_registry.rs
printf '\n====\n'
sed -n '930,1035p' xtask/src/local_registry.rsRepository: EffortlessMetrics/adze-swarm
Length of output: 7199
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n "published_set|ordered_crates|BTreeSet::from|collect::<BTreeSet|package_patch_config_args|published_crates|CargoDependency \{" xtask/src/local_registry.rsRepository: EffortlessMetrics/adze-swarm
Length of output: 1475
🏁 Script executed:
#!/bin/bash
set -euo pipefail
sed -n '60,120p' xtask/src/local_registry.rs
printf '\n====\n'
sed -n '120,220p' xtask/src/local_registry.rsRepository: EffortlessMetrics/adze-swarm
Length of output: 5796
Other (CWE-829): Inclusion of Functionality from Untrusted Control Sphere
Reachability: External
Use the effective package name when matching published deps xtask/src/local_registry.rs:599-686 — renamed deps like parser = { package = "adze-parser", ... } are skipped here because the check uses the table key instead of the dependency’s package name, so they can stay unpinned and fall back to the default registry. Apply the same lookup in the target-specific validation path too.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@xtask/src/local_registry.rs` around lines 589 - 665, The dependency checks in
inject_registry_into_manifest and the corresponding target-specific path must
match published_set against each dependency’s effective package name, using the
package field when present and the table key otherwise. Apply this lookup
consistently before registry_pinned_dependency_spec and in the related
validation logic so renamed dependencies such as parser/package = "adze-parser"
are pinned correctly.
| fn repack_crate_with_manifest( | ||
| crate_path: &Path, | ||
| manifest_member: &str, | ||
| manifest: &str, | ||
| ) -> Result<()> { | ||
| let extract_dir = tempfile::Builder::new() | ||
| .prefix("adze-local-crate-repack-") | ||
| .tempdir() | ||
| .context("creating crate repack extraction directory")?; | ||
| let status = Command::new("tar") | ||
| .args(["-xzf"]) | ||
| .arg(crate_path) | ||
| .arg("-C") | ||
| .arg(extract_dir.path()) | ||
| .status() | ||
| .with_context(|| format!("extracting {} for manifest rewrite", crate_path.display()))?; | ||
| if !status.success() { | ||
| bail!( | ||
| "failed to extract {} for manifest rewrite", | ||
| crate_path.display() | ||
| ); | ||
| } | ||
|
|
||
| let manifest_path = extract_dir.path().join(manifest_member); | ||
| fs::write(&manifest_path, manifest).with_context(|| { | ||
| format!( | ||
| "writing registry-pinned manifest to {}", | ||
| manifest_path.display() | ||
| ) | ||
| })?; | ||
|
|
||
| let crate_root = manifest_path | ||
| .parent() | ||
| .with_context(|| format!("missing crate root for {}", manifest_path.display()))?; | ||
| let crate_root_name = crate_root | ||
| .file_name() | ||
| .and_then(OsStr::to_str) | ||
| .with_context(|| format!("missing crate root name for {}", crate_root.display()))?; | ||
|
|
||
| let repacked_path = crate_path.with_extension("crate.repacked"); | ||
| let status = Command::new("tar") | ||
| .args(["-czf"]) | ||
| .arg(&repacked_path) | ||
| .arg("-C") | ||
| .arg(extract_dir.path()) | ||
| .arg(crate_root_name) | ||
| .status() | ||
| .with_context(|| format!("repacking {}", crate_path.display()))?; | ||
| if !status.success() { | ||
| bail!("failed to repack {}", crate_path.display()); | ||
| } | ||
| fs::rename(&repacked_path, crate_path).with_context(|| { | ||
| format!( | ||
| "replacing {} with registry-pinned package", | ||
| crate_path.display() | ||
| ) | ||
| })?; | ||
| Ok(()) | ||
| } | ||
|
|
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value
🧩 Analysis chain
🌐 Web query:
Are the tar and flate2 crates suitable for reading and rewriting a Cargo .crate archive's Cargo.toml in place?
💡 Result:
The tar and flate2 crates are not suitable for modifying a Cargo.crate archive in place because the underlying tar archive format and the Gzip compression format (used in.crate files) are stream-based and do not support random-access, in-place modification [1][2][3]. To update a Cargo.toml file within a.crate archive, you must follow a read-transform-write process: 1. Decompress the entire archive into a temporary directory [2][3]. 2. Locate and modify the Cargo.toml file using standard file operations or specialized manifest-parsing tools (such as cargo-manifest or those found within the Cargo source code) [4][5][6]. 3. Re-create the archive by streaming the files into a new.crate file (a.tar.gz archive) using tar::Builder and flate2::write::GzEncoder [1][5][7]. Because of the architectural limitations of these formats, any attempt to overwrite a file directly within the existing compressed stream without full reconstruction would result in a corrupted archive [7][8]. If your goal is to modify dependencies or experiment with external crates, it is standard practice to avoid modifying the.crate archive directly. Instead, you can use features like Cargo's [patch] section or tools designed for "hacking" or patching dependencies (e.g., cargo-rhack), which transparently redirect Cargo to use a local, mutable copy of the crate source code instead of the immutable, archived version [9][10][11].
Citations:
- 1: https://docs.rs/cargo/latest/src/cargo/ops/cargo_package/mod.rs.html
- 2: https://rust-lang-nursery.github.io/rust-cookbook/compression/tar.html
- 3: https://stackoverflow.com/questions/68395708/compressing-and-decompressing-a-tar-gz-file
- 4: https://doc.rust-lang.org/nightly/cargo/commands/cargo-package.html
- 5: https://github.com/rust-lang/cargo/blob/master/src/cargo/ops/cargo_package/mod.rs
- 6: https://doc.rust-lang.org/nightly/nightly-rustc/src/cargo/util/toml_mut/manifest.rs.html
- 7: https://docs.rs/tar/latest/tar/struct.Builder.html
- 8: Fix handling contents added after header creation composefs/tar-rs#283
- 9: https://doc.rust-lang.org/cargo/reference/overriding-dependencies.html
- 10: https://internals.rust-lang.org/t/add-support-for-editing-dependencies-directly/18902
- 11: https://crates.io/crates/cargo-rhack
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n## xtask/src/local_registry.rs (relevant section)\n'
sed -n '650,790p' xtask/src/local_registry.rs
printf '\n## search for tar / flate2 usage and platform hints\n'
rg -n '\bCommand::new\("tar"\)|\btar\b|flate2|ubuntu-latest|windows|macos|linux' xtask Cargo.toml .github -g '!target' || true
printf '\n## workspace/package metadata\n'
sed -n '1,220p' Cargo.tomlRepository: EffortlessMetrics/adze-swarm
Length of output: 11427
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n## GitHub Actions workflows\n'
find .github/workflows -maxdepth 1 -type f \( -name '*.yml' -o -name '*.yaml' \) -print | sort
printf '\n## runs-on / xtask references\n'
rg -n 'runs-on:|xtask|local_registry|adze-local-crate-repack|tar"\)' .github xtask Cargo.toml -g '!target' || trueRepository: EffortlessMetrics/adze-swarm
Length of output: 15569
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n## xtask/src/local_registry.rs around existing tar extraction helpers\n'
sed -n '500,590p' xtask/src/local_registry.rs
printf '\n## xtask/src/local_registry.rs around platform-specific code\n'
sed -n '800,890p' xtask/src/local_registry.rs
printf '\n## local_registry module declarations / tests\n'
sed -n '1,120p' xtask/src/local_registry.rsRepository: EffortlessMetrics/adze-swarm
Length of output: 9874
Consider moving the full crate extract/rewrap flow off tar subprocesses. This helper still depends on tar, and the manifest-read path in the same module does too, so switching only this step to tar/flate2 would not remove the external dependency. An in-process tar/gzip implementation would make the xtask more portable and keep error handling in Rust.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@xtask/src/local_registry.rs` around lines 701 - 760, The crate packaging flow
should no longer depend on external tar subprocesses. Replace the extract,
manifest rewrite, and repack logic in repack_crate_with_manifest, together with
the related manifest-read path in the same module, with in-process Rust tar/gzip
handling using the project’s tar/flate2 support; preserve archive contents,
crate-root layout, and existing error context.
7ce329a to
4e28f2a
Compare
Publish crates with git-index + legacy dl layout, inject registry pins into packaged manifests, and install via `cargo install --index file://… --token local` so Cargo resolves the named registry index. Default receipt workflow to workspace version 0.9.0 until #325. Co-authored-by: Cursor <cursoragent@cursor.com>
4e28f2a to
a96562e
Compare
Cargo rejects sparse+file:// in config.json dl; only the index URL uses
sparse+file. Keeps crate downloads on file:///{crate}/{version}/download.
EOF
Co-authored-by: Cursor <cursoragent@cursor.com>
sparse+file:// cannot fetch config.json on Linux (HTTP status 0). Use a committed git index at file:// with plain file:// dl URLs and install via --registry adze-local so manifest registry pins resolve. EOF Co-authored-by: Cursor <cursoragent@cursor.com>
Queue review for #856: recommend closing this in favour of #971I reproduced the install failure in an isolated two-crate registry that mirrors The short version: the failure is not the index protocol. Every case I ran already used That means this PR lands on the failing case: it repacks manifests with #971 makes both changes, and additionally fixes One thing worth salvaging from here rather than losing: Also note Generated by Claude Code |
Summary
verify-local-release-installLinux index resolution by usingsparse+file://for the isolated registry index andcargo install --registry adze-localinstead of anonymous--index.registry = "adze-local"into packaged workspace manifests so install-time dependency resolution stays on the local registry.0.9.0(0.10.0 remains blocked on release: authorize publish workflow and capture crates.io install receipt #325).Root cause
Publish succeeded but
cargo install --index file://…could not resolveadze-clionubuntu-latest. Cargo treats plainfile://as git-index protocol; the harness writes sparse index files. Switching tosparse+file://+ named registry matches Cargo's sparse local registry contract.Test plan
cargo test -p xtask local_registrycargo test -p xtask local_releasefix/856-sparse-registry-installwith version0.9.0after merge (or on branch pre-merge)Claim boundary
Proves pre-release local-registry package-first flow at current workspace version (
0.9.0). Does not prove crates.io install, 0.10.0 publish, or #325 release authorization.Parent: #856 / #853
Summary by cubic
Fixes Linux local-registry installs by switching to a git-backed local index at file:// and installing via
cargo install --registry adze-local. Packaged crates pin workspace deps toadze-local; crate downloads usefile://.Bug Fixes
file://and setconfig.jsondl tofile://; install via--registry adze-local.registry = "adze-local"for workspace deps (including undertarget.*); fail if any path deps remain or pins are missing.New Features
.github/workflows/local-release-receipt.yml(manual, Ubuntu) to runcargo xtask verify-local-release-installand uploadtarget/local-release-receipt.json(supportsdry_run).policy/ci-lane-whitelist.toml; default version is0.9.0until release: authorize publish workflow and capture crates.io install receipt #325 unlocks0.10.0.Written for commit d4570b6. Summary will update on new commits.