Skip to content

Commit f1c3928

Browse files
committed
fix(release): bundle macos-bundle-updater.sh inside freenet crate
## Problem `cargo install freenet --version 0.2.61` (and every subsequent published version) fails to build from crates.io with an `include_str!` "file not found" error on `crates/core/src/bin/commands/update.rs`. The line `include_str!("../../../../../scripts/macos-bundle-updater.sh")` walks five levels up — out of the freenet crate and into the workspace-root `scripts/` directory. That path is valid in a workspace checkout, but `cargo publish` only bundles files inside the crate directory, so the script is absent from the registry tarball and `cargo build` aborts before linking. This blocks anyone installing freenet from the registry rather than from source, including River's `cargo make sign-webapp` flow, which shells out to `cargo install freenet` to obtain `fdev`. ## Approach Move `scripts/macos-bundle-updater.sh` into the freenet crate at `crates/core/scripts/macos-bundle-updater.sh` so `cargo publish` ships it. Shorten the `include_str!` path accordingly. Why this over alternatives — the script is the only file the freenet binary embeds via `include_str!`; the rest of the workspace-root `scripts/` directory is build/CI tooling that shouldn't ride along in the published crate. A `[package].include` allowlist or a `build.rs` that copies the file would add configuration to maintain but would not surface the rule at the filesystem level. Co-locating the embedded resource with its consumer crate is the convention `cargo` was designed around. ## Testing A bug in this class is the published `.crate` tarball missing an embedded resource — that can't be caught by a runtime `#[test]` alone, so this PR closes the gap with TWO tests: 1. **Unit test** — a new `#[test]` in `update.rs::tests` re-includes the script via `include_str!` and asserts its shape (shebang + header). Locks the path: any future refactor that walks back outside the crate breaks compilation right here. 2. **CI gap test** — a new step in the `fmt_check` job runs `cargo package --list -p freenet` and fails the build if `scripts/macos-bundle-updater.sh` is missing from the would-be tarball. This is the assertion that would actually have caught the 0.2.61 regression before publish, and it generalizes to any future embedded resource that gets added to the include list. The workspace e2e harness (`scripts/macos-bundle-swap-e2e.sh` and its `.c` stub comment) was updated to point at the new path. Local validation: - `cargo fmt -- --check` clean. - `cargo clippy -p freenet --bin freenet --tests -- -D warnings` clean. - New test passes: `cargo test embedded_updater_script_resolves_inside_freenet_crate`. - `cargo package --list -p freenet | grep -Fx scripts/macos-bundle-updater.sh` resolves — the script is in the package contents. The `#[cfg(target_os = "macos")]` gate on `write_updater_script` is unchanged, so Linux builds remain unaffected. ## Why didn't CI catch this? CI ran `cargo build` / `cargo test` against the workspace checkout, where the workspace-root `scripts/` directory is on disk and `include_str!` resolves. CI never built the crate from its published `.crate` tarball, so the packaging gap was invisible. The new `fmt_check` step closes that gap and would have failed the 0.2.61 release PR before it merged. ## Fixes Unblocks `cargo install freenet` from crates.io and downstream tooling that depends on it (River's `cargo make sign-webapp`). Recommend cutting a `0.2.63` patch release after merge so downstreams can stop vendoring around this. [AI-assisted - Claude] Entire-Checkpoint: 046245fb4905
1 parent ac2bc76 commit f1c3928

5 files changed

Lines changed: 45 additions & 4 deletions

File tree

.github/workflows/ci.yml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,33 @@ jobs:
148148
exit 1
149149
fi
150150
151+
# Regression gate for #4240: cargo publish only bundles files
152+
# inside the crate, so an include_str! path that walks outside
153+
# the crate root ships a broken tarball that fails to build for
154+
# crates.io users.
155+
- name: Verify freenet crate package includes embedded resources
156+
run: |
157+
set -eu
158+
required=(
159+
scripts/macos-bundle-updater.sh
160+
)
161+
package_list=$(cargo package --list -p freenet)
162+
fail=0
163+
for f in "${required[@]}"; do
164+
if ! echo "$package_list" | grep -qFx "$f"; then
165+
echo "::error::Missing from published freenet crate: $f"
166+
fail=1
167+
fi
168+
done
169+
if [ "$fail" -ne 0 ]; then
170+
echo "Files referenced by include_str!/include_bytes! must live"
171+
echo "inside crates/core/ so cargo publish bundles them."
172+
echo
173+
echo "Package contents:"
174+
echo "$package_list"
175+
exit 1
176+
fi
177+
151178
# Grep-based lint for banned patterns in crates/core/ (DST-breaking APIs).
152179
# Only checks added lines in the diff — pre-existing violations are grandfathered.
153180
# Fast (~2s on GitHub-hosted runner), no Rust toolchain needed.
File renamed without changes.

crates/core/src/bin/commands/update.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1552,7 +1552,7 @@ fn hdiutil_detach(mount_point: &Path) -> Result<()> {
15521552
/// even when installed from crates.io without the `scripts/` directory.
15531553
#[cfg(target_os = "macos")]
15541554
fn write_updater_script() -> Result<PathBuf> {
1555-
const SCRIPT: &str = include_str!("../../../../../scripts/macos-bundle-updater.sh");
1555+
const SCRIPT: &str = include_str!("../../../scripts/macos-bundle-updater.sh");
15561556
let dir = updater_runtime_dir()?;
15571557
std::fs::create_dir_all(&dir)?;
15581558
let path = dir.join("macos-bundle-updater.sh");
@@ -1748,4 +1748,18 @@ mod tests {
17481748
// silently eat both leading chars): verify we strip just one.
17491749
assert_eq!(macos_dmg_asset_name("vv0.2.49"), "Freenet-v0.2.49.dmg");
17501750
}
1751+
1752+
#[test]
1753+
fn embedded_updater_script_resolves_inside_freenet_crate() {
1754+
// Regression for #4240 — the include_str! path used to walk
1755+
// outside the crate, so cargo publish dropped the script and
1756+
// `cargo install freenet` failed at build time. Re-included
1757+
// here so non-macOS CI also exercises the path. The packaging
1758+
// half is asserted in .github/workflows/ci.yml.
1759+
const SCRIPT: &str = include_str!("../../../scripts/macos-bundle-updater.sh");
1760+
assert!(
1761+
SCRIPT.contains("macOS DMG-swap updater"),
1762+
"include_str! resolved to the wrong file"
1763+
);
1764+
}
17511765
}

scripts/macos-bundle-swap-e2e-stub.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* via clang and dropped into each test .app bundle as Contents/MacOS/
44
* freenet-bin.
55
*
6-
* Rationale: the updater script (scripts/macos-bundle-updater.sh) uses
6+
* Rationale: the updater script (crates/core/scripts/macos-bundle-updater.sh) uses
77
* `pgrep -f "^${bundle}/Contents/MacOS/"` to detect the running Freenet
88
* process. That pattern assumes the kernel invoked a real Mach-O at the
99
* bundle path, so the process command line begins with that path. A

scripts/macos-bundle-swap-e2e.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#!/bin/bash
2-
# End-to-end test for scripts/macos-bundle-updater.sh.
2+
# End-to-end test for crates/core/scripts/macos-bundle-updater.sh.
33
#
44
# Builds two minimal unsigned .app bundles ("v1" and "v2"), launches v1 via
55
# /usr/bin/open, invokes the updater script with v2 staged as a sibling,
@@ -26,7 +26,7 @@
2626
set -euo pipefail
2727

2828
REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
29-
UPDATER="$REPO_ROOT/scripts/macos-bundle-updater.sh"
29+
UPDATER="$REPO_ROOT/crates/core/scripts/macos-bundle-updater.sh"
3030
STUB_SRC="$REPO_ROOT/scripts/macos-bundle-swap-e2e-stub.c"
3131

3232
if [[ ! -x "$UPDATER" ]]; then

0 commit comments

Comments
 (0)