Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add uv sync --no-install-workspace to skip installation of all workspace members #6539

Merged
merged 1 commit into from
Aug 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions crates/uv-cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2282,6 +2282,16 @@ pub struct SyncArgs {
#[arg(long)]
pub no_install_project: bool,

/// Do not install any workspace members, including the root project.
///
/// By default, all of the workspace members and their dependencies are installed into the
/// environment. The `--no-install-workspace` option allows exclusion of all the workspace
/// members while retaining their dependencies. This is particularly useful in situations like
/// building Docker images where installing the workspace separately from its dependencies
/// allows optimal layer caching.
#[arg(long)]
pub no_install_workspace: bool,

/// Assert that the `uv.lock` will remain unchanged.
///
/// Requires that the lockfile is up-to-date. If the lockfile is missing or
Expand Down
1 change: 1 addition & 0 deletions crates/uv/src/commands/project/add.rs
Original file line number Diff line number Diff line change
Expand Up @@ -604,6 +604,7 @@ pub(crate) async fn add(
&extras,
dev,
false,
false,
Modifications::Sufficient,
settings.as_ref().into(),
&state,
Expand Down
2 changes: 2 additions & 0 deletions crates/uv/src/commands/project/remove.rs
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ pub(crate) async fn remove(
let extras = ExtrasSpecification::All;
let dev = true;
let no_install_project = false;
let no_install_workspace = false;

// Initialize any shared state.
let state = SharedState::default();
Expand All @@ -202,6 +203,7 @@ pub(crate) async fn remove(
&extras,
dev,
no_install_project,
no_install_workspace,
Modifications::Exact,
settings.as_ref().into(),
&state,
Expand Down
1 change: 1 addition & 0 deletions crates/uv/src/commands/project/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,7 @@ pub(crate) async fn run(
&extras,
dev,
false,
false,
Modifications::Sufficient,
settings.as_ref().into(),
&state,
Expand Down
22 changes: 22 additions & 0 deletions crates/uv/src/commands/project/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ pub(crate) async fn sync(
extras: ExtrasSpecification,
dev: bool,
no_install_project: bool,
no_install_workspace: bool,
modifications: Modifications,
python: Option<String>,
python_preference: PythonPreference,
Expand Down Expand Up @@ -106,6 +107,7 @@ pub(crate) async fn sync(
&extras,
dev,
no_install_project,
no_install_workspace,
modifications,
settings.as_ref().into(),
&state,
Expand All @@ -122,13 +124,15 @@ pub(crate) async fn sync(
}

/// Sync a lockfile with an environment.
#[allow(clippy::fn_params_excessive_bools)]
pub(super) async fn do_sync(
project: &VirtualProject,
venv: &PythonEnvironment,
lock: &Lock,
extras: &ExtrasSpecification,
dev: bool,
no_install_project: bool,
no_install_workspace: bool,
modifications: Modifications,
settings: InstallerSettingsRef<'_>,
state: &SharedState,
Expand Down Expand Up @@ -195,6 +199,9 @@ pub(super) async fn do_sync(
// If `--no-install-project` is set, remove the project itself.
let resolution = apply_no_install_project(no_install_project, resolution, project);

// If `--no-install-workspace` is set, remove the project and any workspace members.
let resolution = apply_no_install_workspace(no_install_workspace, resolution, project);

// Add all authenticated sources to the cache.
for url in index_locations.urls() {
store_credentials_from_url(url);
Expand Down Expand Up @@ -299,3 +306,18 @@ fn apply_no_install_project(

resolution.filter(|dist| dist.name() != project_name)
}

fn apply_no_install_workspace(
no_install_workspace: bool,
resolution: distribution_types::Resolution,
project: &VirtualProject,
) -> distribution_types::Resolution {
if !no_install_workspace {
return resolution;
}

let workspace_packages = project.workspace().packages();
resolution.filter(|dist| {
!workspace_packages.contains_key(dist.name()) && Some(dist.name()) != project.project_name()
})
}
1 change: 1 addition & 0 deletions crates/uv/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1103,6 +1103,7 @@ async fn run_project(
args.extras,
args.dev,
args.no_install_project,
args.no_install_workspace,
args.modifications,
args.python,
globals.python_preference,
Expand Down
3 changes: 3 additions & 0 deletions crates/uv/src/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -618,6 +618,7 @@ pub(crate) struct SyncSettings {
pub(crate) extras: ExtrasSpecification,
pub(crate) dev: bool,
pub(crate) no_install_project: bool,
pub(crate) no_install_workspace: bool,
pub(crate) modifications: Modifications,
pub(crate) package: Option<PackageName>,
pub(crate) python: Option<String>,
Expand All @@ -638,6 +639,7 @@ impl SyncSettings {
inexact,
exact,
no_install_project,
no_install_workspace,
locked,
frozen,
installer,
Expand Down Expand Up @@ -671,6 +673,7 @@ impl SyncSettings {
),
dev: flag(dev, no_dev).unwrap_or(true),
no_install_project,
no_install_workspace,
modifications,
package,
python,
Expand Down
67 changes: 67 additions & 0 deletions crates/uv/tests/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -863,3 +863,70 @@ fn no_install_project() -> Result<()> {

Ok(())
}

/// Avoid syncing local dependencies for workspace dependencies when `--no-install-project` is provided, but
/// include the workspace dependency's dependencies.
#[test]
fn no_install_workspace() -> Result<()> {
let context = TestContext::new("3.12");

let pyproject_toml = context.temp_dir.child("pyproject.toml");
pyproject_toml.write_str(
r#"
[project]
name = "project"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = ["anyio==3.7.0", "child"]

[tool.uv.workspace]
members = ["child"]

[tool.uv.sources]
child = { workspace = true }
"#,
)?;

// Add a workspace member.
let child = context.temp_dir.child("child");
child.child("pyproject.toml").write_str(
r#"
[project]
name = "child"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = ["iniconfig>1"]

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
"#,
)?;
child
.child("src")
.child("child")
.child("__init__.py")
.touch()?;

// Generate a lockfile.
context.lock().assert().success();

// Running with `--no-install-workspace` should install `anyio` and `iniconfig`, but not
// `project` or `child`.
uv_snapshot!(context.filters(), context.sync().arg("--no-install-workspace"), @r###"
success: true
exit_code: 0
----- stdout -----

----- stderr -----
Resolved 6 packages in [TIME]
Prepared 4 packages in [TIME]
Installed 4 packages in [TIME]
+ anyio==3.7.0
+ idna==3.6
+ iniconfig==2.0.0
+ sniffio==1.3.1
"###);

Ok(())
}
7 changes: 6 additions & 1 deletion docs/guides/integration/docker.md
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ If not mounting the cache, image size can be reduced with `--no-cache` flag.
### Intermediate layers

If you're using uv to manage your project, you can improve build times by moving your transitive
dependency installation into its own layer via `uv sync --no-install-project`.
dependency installation into its own layer via the `--no-install` options.

`uv sync --no-install-project` will install the dependencies of the project but not the project
itself. Since the project changes frequently, but its dependencies are generally static, this can be
Expand All @@ -216,3 +216,8 @@ WORKDIR /app
# Sync the project
RUN uv sync --frozen
```

!!! tip

If you're using a [workspace](../../concepts/workspaces.md), then consider the
`--no-install-workspace` flag which excludes the project _and_ any workspace members.
4 changes: 4 additions & 0 deletions docs/reference/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -1174,6 +1174,10 @@ uv sync [OPTIONS]

<p>By default, the current project is installed into the environment with all of its dependencies. The <code>--no-install-project</code> option allows the project to be excluded, but all of its dependencies are still installed. This is particularly useful in situations like building Docker images where installing the project separately from its dependencies allows optimal layer caching.</p>

</dd><dt><code>--no-install-workspace</code></dt><dd><p>Do not install any workspace members, including the root project.</p>

<p>By default, all of the workspace members and their dependencies are installed into the environment. The <code>--no-install-workspace</code> option allows exclusion of all the workspace members while retaining their dependencies. This is particularly useful in situations like building Docker images where installing the workspace separately from its dependencies allows optimal layer caching.</p>

</dd><dt><code>--no-progress</code></dt><dd><p>Hide all progress outputs.</p>

<p>For example, spinners or progress bars.</p>
Expand Down
Loading