Skip to content

ci: fix pre-existing coverage and nextest failures#66

Merged
EffortlessSteven merged 1 commit into
mainfrom
feature/ci-health-fix
Apr 15, 2026
Merged

ci: fix pre-existing coverage and nextest failures#66
EffortlessSteven merged 1 commit into
mainfrom
feature/ci-health-fix

Conversation

@EffortlessSteven

Copy link
Copy Markdown
Member

Summary

Fix two distinct pre-existing failures that keep CI red on main and block every open decrating PR (#48-#58). These failures exist independently of any code change — they fail on doc-only PRs too.

Root cause analysis

1. Coverage (llvm-cov) — snapshot mismatch on Linux

Failing test: publish_invalid_manifest_content_snapshot in crates/shipper-cli/tests/e2e_expanded.rs.

On GitHub's Ubuntu runners, cargo metadata reports the invalid manifest using a path relative to the workspace CWD (/home/runner/work/shipper/shipper/), producing something like:

--> ../../../tmp/.tmpXXX/Cargo.toml:1:6

The test's tempdir replacement (/tmp/.tmpXXX<TMPDIR>) consumed the leading /, leaving ../../..<TMPDIR>/.... The old single-pass .replace("../", "") could only strip the first two ../, leaving behind ..<TMPDIR>/Cargo.toml — which did not match the snapshot (<TMPDIR>\Cargo.toml, committed from a Windows-side run).

2. Tests (nextest) on ubuntu-latest / macos-latest — concurrency race

Failing test: tests::concurrent_reads_and_writes_same_file in crates/shipper-storage/src/lib.rs (already #[cfg_attr(target_os = "windows", ignore)]).

Error:

write: failed to rename file to: /tmp/.tmpXXX/shared.txt
Caused by: No such file or directory (os error 2)

FileStorage::write built its scratch tmp path with full_path.with_extension("tmp"), so every concurrent write to shared.txt reused the single path shared.tmp. The interleaving

T1: write(shared.tmp)
T2: write(shared.tmp)   // overwrites
T1: rename(shared.tmp → shared.txt)  // removes tmp
T2: rename(shared.tmp → shared.txt)  // ENOENT

is a legitimate bug, not a test artifact — concurrent callers of the public write API can race on macOS/Linux (Windows's rename-into-existing semantics masked it).

Fixes

crates/shipper-cli/tests/e2e_expanded.rs

normalize_stderr now:

  • converts backslashes to forward slashes so path separators are platform-independent in snapshots; and
  • iteratively strips ../ prefixes and residual ..<TMPDIR> fragments until stable.

crates/shipper-cli/tests/snapshots/e2e_expanded__publish_invalid_manifest_content.snap

Committed snapshot updated to the canonical forward-slash form (<TMPDIR>/Cargo.toml).

crates/shipper-storage/src/lib.rs

Each write call builds a unique scratch filename from pid + thread id + subsecond nanos, so concurrent writes never collide on the tmp path. Two local tests that asserted the old fixed .tmp name were adapted to the more general invariant ("no .tmp extension files remain after a successful write"); atomic_write_overwrites_stale_tmp_from_prior_crash now only asserts the new write succeeds (the new impl intentionally does not clobber unrelated stale .tmp files).

Validation

Locally on Windows:

  • cargo test -p shipper-storage -- --include-ignored93 lib + 33 integration + 1 doctest pass, including concurrent_reads_and_writes_same_file which can now be un-#[cfg_attr(..., ignore)]'d in a follow-up (kept ignored on Windows for this minimal PR).
  • cargo test -p shipper-cli --test e2e_expanded126/126 pass.
  • cargo fmt --all -- --check clean.
  • cargo clippy -p shipper-storage -p shipper-cli --all-targets --all-features -- -D warnings clean.

Coverage-job-specific validation (cargo llvm-cov) is deferred to CI since it requires the coverage tool.

Impact

Unblocks green CI for the decrating PR queue (#48-#58) and restores main to a publishable state.

Test plan

  • Coverage (llvm-cov) job turns green
  • Tests (nextest) (ubuntu-latest) turns green
  • Tests (nextest) (macos-latest) turns green
  • Tests (nextest) (windows-latest) remains green
  • All other CI jobs remain green

Fixes two distinct failures that have kept CI red on `main` for weeks
and block every open PR:

1. Snapshot mismatch in `publish_invalid_manifest_content_snapshot`
   (`crates/shipper-cli/tests/e2e_expanded.rs`). On Linux CI runners,
   `cargo metadata` reports the invalid manifest using a path relative
   to the workspace CWD (e.g. `../../../tmp/.tmpXXX/Cargo.toml`). The
   tempdir replacement consumed the leading `/`, leaving `..` fragments
   that the previous single-pass `.replace("../", "")` could not clean
   up (it left `..<TMPDIR>/...`).

   `normalize_stderr` now:
   - converts backslashes to forward slashes so path separators are
     platform-independent in snapshots; and
   - iteratively strips `../` and any residual `..<TMPDIR>` fragments
     until stable.

   Updated the committed snapshot to the canonical forward-slash form.

2. Race in `FileStorage::write` causing
   `tests::concurrent_reads_and_writes_same_file` to panic with
   "No such file or directory (os error 2)" on macOS and Linux. All
   threads writing to the same destination shared a single
   `<basename>.tmp` scratch file, so one thread's `rename` could
   remove the file while another thread's `rename` was still pending.

   Each write now uses a unique temp filename built from pid, thread
   id, and subsecond timestamp. Adjusted two local tests that inspected
   the old fixed `.tmp` name to observe the more general invariant
   ("no `.tmp` extension files remain after a successful write") and
   relaxed `atomic_write_overwrites_stale_tmp_from_prior_crash` to only
   assert the new write succeeds (the new impl intentionally does not
   clobber unrelated stale `.tmp` files).

Validated locally on Windows:
- `cargo test -p shipper-storage -- --include-ignored` → 93 lib + 33
  integration + 1 doctest pass (including
  `concurrent_reads_and_writes_same_file`, previously `#[cfg_attr]`
  ignored on Windows; now runs cleanly).
- `cargo test -p shipper-cli --test e2e_expanded` → 126/126 pass.
- `cargo fmt --all -- --check` and
  `cargo clippy -p shipper-storage -p shipper-cli --all-targets
  --all-features -- -D warnings` both clean.

Coverage-job-only validation (`cargo llvm-cov`) is deferred to CI.

Unblocks green CI for the decrating PR queue (#48-#58).
@gemini-code-assist

Copy link
Copy Markdown

Warning

You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again!

@coderabbitai

coderabbitai Bot commented Apr 15, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@EffortlessSteven has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 7 minutes and 15 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 7 minutes and 15 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: fbbde38d-8f48-4344-9f76-e296ed2ba5a1

📥 Commits

Reviewing files that changed from the base of the PR and between 30a0c0c and 30fa188.

⛔ Files ignored due to path filters (1)
  • crates/shipper-cli/tests/snapshots/e2e_expanded__publish_invalid_manifest_content.snap is excluded by !**/*.snap
📒 Files selected for processing (2)
  • crates/shipper-cli/tests/e2e_expanded.rs
  • crates/shipper-storage/src/lib.rs
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/ci-health-fix

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@EffortlessSteven EffortlessSteven merged commit 2c31477 into main Apr 15, 2026
2 checks passed

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 30fa18801e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +307 to +311
let tmp_name = format!(
"{}.{pid}.{tid:?}.{nanos}.tmp",
full_path
.file_name()
.and_then(|n| n.to_str())

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Bound temp filename length before formatting suffix

Constructing the temp name from the full original filename plus .{pid}.{tid}.{nanos}.tmp can exceed per-component filename limits (commonly 255 bytes on ext4/APFS/NTFS), so writes that target long-but-valid filenames will now fail at std::fs::write with File name too long. This is a regression introduced by the new uniqueness scheme; the temp basename should be length-bounded (for example, by truncating or hashing the original name) before appending uniqueness metadata.

Useful? React with 👍 / 👎.

EffortlessSteven added a commit that referenced this pull request Apr 15, 2026
After cherry-picking PR #68's storage SPLIT commit onto main, `lib.rs`
ended up with two `pub(crate) mod ops;` declarations: the original from
main's ops/ layer introduction, and a new one added by PR #68 to house
ops::storage. Removed the duplicate.

Also preserved PR #66's per-call unique tmp-filename race fix in the new
FileStorage::write location at `crates/shipper/src/ops/storage/mod.rs`.
The pattern uses pid + thread-id + nanos suffix to prevent concurrent
writes to the same destination from racing on rename.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant