Skip to content
Open
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
60 changes: 60 additions & 0 deletions doc/book/src/reference/config.md

@weihanglo weihanglo Aug 7, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Before diving into other aspects of the stabilization, have we got any feedback for this using in CI / production?

View changes since the review

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Not that I know of.

Comment thread
epage marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -152,11 +152,13 @@ rpath = false # Sets the rpath linking option.
[resolver]
lockfile-path = "…" # Overrides the path used for
incompatible-rust-versions = "allow" # Specifies how resolver reacts to these
incompatible-publish-age = "deny" # Specifies how resolver treats recently published versions

[registries.<name>] # registries other than crates.io
index = "…" # URL of the registry index
token = "…" # authentication token for the registry
credential-provider = "cargo:token" # The credential provider for this registry.
min-publish-age = "7 days" # Override `registry.global-min-publish-age` for this registry

[registries.crates-io]
protocol = "sparse" # The protocol to use to access crates.io.
Expand All @@ -166,6 +168,7 @@ default = "…" # name of the default registry
token = "…" # authentication token for crates.io
credential-provider = "cargo:token" # The credential provider for crates.io.
global-credential-providers = ["cargo:token"] # The credential providers to use by default.
global-min-publish-age = "7 days" # The time span allowed for registry packages to use by default.

[source.<name>] # source definition and replacement
replace-with = "…" # replace this source with the given named source
Expand Down Expand Up @@ -1154,6 +1157,23 @@ See the [resolver](resolver.md#rust-version) chapter for more details.
> - `allow` is supported on any version
> - `fallback` is respected as of 1.84

#### `resolver.incompatible-publish-age`
* Type: string
* Default: `"deny"`
* Environment: `CARGO_RESOLVER_INCOMPATIBLE_PUBLISH_AGE`

When resolving the version of a dependency,
specify the behavior for versions with a `pubtime` (if present)
that is incompatible with the configured `min-publish-age`.
Values include:

- `allow`: treat pubtime-incompatible versions like any other version
- `deny`: ignore pubtime-incompatible versions unless they already exist in the lock file

See the [resolver](resolver.md#publish-age) chapter for more details.

> **MSRV:** Respected as of 1.100+

### `[registries]`

The `[registries]` table is used for specifying additional [registries]. It
Expand Down Expand Up @@ -1193,6 +1213,26 @@ If the value exists in the [`[credential-alias]`](#credential-alias) table, the

See [Registry Authentication](registry-authentication.md) for more information.

#### `registries.<name>.min-publish-age`
* Type: string
* Default: [`registry.global-min-publish-age`](#registryglobal-min-publish-age)
* Environment: `CARGO_REGISTRIES_<name>_MIN_PUBLISH_AGE`

Specifies the minimum timespan since a version's `pubtime` that may be
considered for [`resolver.incompatible-publish-age`] for packages from this
registry. If not set, [`registry.global-min-publish-age`](#registryglobal-min-publish-age) will be used.

Will be ignored if the registry does not support this.

It supports the following values:

- An integer followed by "seconds", "minutes", "hours", "days", "weeks", or "months"
- `"0"` to allow all packages

Generally, `"0"`, `"N days"`, and `"N weeks"` will be used.

> **MSRV:** Respected as of 1.100+

#### `registries.crates-io.protocol`
* Type: string
* Default: `"sparse"`
Expand Down Expand Up @@ -1268,6 +1308,25 @@ referenced here by its alias.

See [Registry Authentication](registry-authentication.md) for more information.

#### `registry.global-min-publish-age`
* Type: string
* Default: `"0"`
* Environment: `CARGO_REGISTRY_GLOBAL_MIN_PUBLISH_AGE`

Specifies the global minimum timespan since a version's `pubtime` that it may
be considered for [`resolver.incompatible-publish-age`] for packages.
If `min-publish-age` is not set for a specific registry using
`registries.<name>.min-publish-age`, Cargo will use this minimum publish age.

It supports the following values:

- An integer followed by "seconds", "minutes", "hours", "days", "weeks", or "months"
- `"0"` to allow all packages

Generally, `"0"`, `"N days"`, and `"N weeks"` will be used.

> **MSRV:** Respected as of 1.100+

### `[source]`

The `[source]` table defines the registry sources available. See [Source
Expand Down Expand Up @@ -1555,3 +1614,4 @@ Report progress to the terminal emulator for display in places like the task bar
[crates.io]: https://crates.io/
[target triple]: ../appendix/glossary.md#target '"target" (glossary)'
[`<triple>`]: ../appendix/glossary.md#target '"target" (glossary)'
[`resolver.incompatible-publish-age`]: config.md#resolverincompatible-publish-age
7 changes: 7 additions & 0 deletions doc/book/src/reference/environment-variables.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,12 +133,15 @@ In summary, the supported environment variables are:
* `CARGO_PROFILE_<name>_STRIP` --- Controls stripping of symbols and/or debuginfos, see [`profile.<name>.strip`].
* `CARGO_REGISTRIES_<name>_CREDENTIAL_PROVIDER` --- Credential provider for a registry, see [`registries.<name>.credential-provider`].
* `CARGO_REGISTRIES_<name>_INDEX` --- URL of a registry index, see [`registries.<name>.index`].
* `CARGO_REGISTRIES_<name>_MIN_PUBLISH_AGE` --- Minimum publish age for packages from a registry, see [`registries.<name>.min-publish-age`].
* `CARGO_REGISTRIES_<name>_TOKEN` --- Authentication token of a registry, see [`registries.<name>.token`].
* `CARGO_REGISTRIES_CRATES_IO_PROTOCOL` --- The protocol used to access [crates.io], see [`registries.crates-io.protocol`].
* `CARGO_REGISTRY_CREDENTIAL_PROVIDER` --- Credential provider for [crates.io], see [`registry.credential-provider`].
* `CARGO_REGISTRY_DEFAULT` --- Default registry for the `--registry` flag, see [`registry.default`].
* `CARGO_REGISTRY_GLOBAL_CREDENTIAL_PROVIDERS` --- Credential providers for registries that do not have a specific provider defined. See [`registry.global-credential-providers`].
* `CARGO_REGISTRY_GLOBAL_MIN_PUBLISH_AGE` --- Default minimum publish age for packages from registries, see [`registry.global-min-publish-age`].
* `CARGO_REGISTRY_TOKEN` --- Authentication token for [crates.io], see [`registry.token`].
* `CARGO_RESOLVER_INCOMPATIBLE_PUBLISH_AGE` --- How recently published versions are treated during dependency resolution, see [`resolver.incompatible-publish-age`].
* `CARGO_RESOLVER_INCOMPATIBLE_RUST_VERSIONS` --- How incompatible Rust versions are treated during dependency resolution, see [`resolver.incompatible-rust-versions`].
* `CARGO_RESOLVER_LOCKFILE_PATH` --- The path to the lockfile, see [`resolver.lockfile-path`].
* `CARGO_TARGET_<triple>_LINKER` --- The linker to use, see [`target.<triple>.linker`]. The triple must be [converted to uppercase and underscores](config.md#environment-variables).
Expand Down Expand Up @@ -211,13 +214,17 @@ In summary, the supported environment variables are:
[`profile.<name>.strip`]: config.md#profilenamestrip
[`resolver.lockfile-path`]: config.md#resolverlockfile-path
[`resolver.incompatible-rust-versions`]: config.md#resolverincompatible-rust-versions
[`resolver.incompatible-publish-age`]: config.md#resolverincompatible-publish-age
[`registries.<name>.credential-provider`]: config.md#registriesnamecredential-provider
[`registries.<name>.index`]: config.md#registriesnameindex
[`registries.<name>.min-publish-age`]: config.md#registriesnamemin-publish-age
[`registries.<name>.token`]: config.md#registriesnametoken
[`registries.crates-io.protocol`]: config.md#registriescrates-ioprotocol
[`registry.credential-provider`]: config.md#registrycredential-provider
[`registry.default`]: config.md#registrydefault
[`registry.global-credential-providers`]: config.md#registryglobal-credential-providers
[`registry.global-min-publish-age`]: config.md#registryglobal-min-publish-age
[`registry.min-publish-age`]: config.md#registrymin-publish-age
[`registry.token`]: config.md#registrytoken
[`target.<triple>.linker`]: config.md#targettriplelinker
[`target.<triple>.runner`]: config.md#targettriplerunner
Expand Down
14 changes: 14 additions & 0 deletions doc/book/src/reference/resolver.md
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,20 @@ the resolver will need to pick one version that works in both cases and that wou
[Rust version]: rust-version.md
[`resolver.incompatible-rust-versions`]: config.md#resolverincompatible-rust-versions

### Publish age

Versions with a publish time newer than the configured [`min-publish-age`][`registry.global-min-publish-age`]
are considered pubtime-incompatible.
When [`resolver.incompatible-publish-age`] is set to `deny`,
the resolver will ignore these versions
unless they already exist in the `Cargo.lock` file.
Setting the config to `allow` would disable the check,
which if combined with `cargo update --precise`,
cargo would pull in a specific version and its transitive dependencies.

[`registry.global-min-publish-age`]: config.md#registryglobal-min-publish-age
[`resolver.incompatible-publish-age`]: config.md#resolverincompatible-publish-age

### Features

For the purpose of generating `Cargo.lock`, the resolver builds the dependency
Expand Down
104 changes: 6 additions & 98 deletions doc/book/src/reference/unstable.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,6 @@ Each new feature described below should explain how to use it.
* [sbom](#sbom) --- Generates SBOM pre-cursor files for compiled artifacts
* [feature-unification](#feature-unification) --- Enable new feature unification modes in workspaces
* [lockfile-publish-time](#lockfile-publish-time) --- Limit resolver to packages older than the specified time
* [min-publish-age](#min-publish-age) --- Filters out dependency versions published more recently than a configured minimum age.
* Output behavior
* [artifact-dir](#artifact-dir) --- Adds a directory where artifacts are copied to.
* [build-dir-new-layout](#build-dir-new-layout) --- Enables the new build-dir filesystem layout
Expand Down Expand Up @@ -2069,103 +2068,6 @@ option:
hint-msrv = true
```

## min-publish-age

* Tracking Issue: [#17009](https://github.com/rust-lang/cargo/issues/17009)
* RFC: [#3923](https://github.com/rust-lang/rfcs/pull/3923)

The `-Zmin-publish-age` feature allows users to specify a minimum age for
dependency versions. When specified, Cargo won't use a version of a registry
crate that is newer than the minimum age, with a way to override for exceptions
like urgent security fixes.

For example, in your `<repo>/.cargo/config.toml`:

```toml
[registry]
global-min-publish-age = "14 days"
```

### Added to Configuration

The following will be added to Cargo's configuration format:

```toml
[resolver]
incompatible-publish-age = "deny" # Specifies how resolver reacts to these

[registries.<name>]
min-publish-age = "..." # Override `registry.global-min-publish-age` for this registry

[registry]
global-min-publish-age = "0" # Minimum time span allowed for registry packages by default
```

#### `resolver.incompatible-publish-age`

* Type: String
* Default: `"deny"`
* Environment: `CARGO_RESOLVER_INCOMPATIBLE_PUBLISH_AGE`

When resolving the version of a dependency,
specify the behavior for versions with a `pubtime` (if present)
that is incompatible with the configured `min-publish-age`.
Values include:

- `allow`: treat pubtime-incompatible versions like any other version
- `deny`: ignore pubtime-incompatible versions unless they already exist in the lock file

#### `registries.<name>.min-publish-age`

* Type: String
* Default: none
* Environment: `CARGO_REGISTRIES_<name>_MIN_PUBLISH_AGE`

Specifies the minimum timespan since a version's `pubtime` that it may be
considered for `resolver.incompatible-publish-age` for packages from this
registry. If not set, `registry.global-min-publish-age` will be used.

Will be ignored if the registry does not support this.

It supports the following values:

- An integer followed by "seconds", "minutes", "hours", "days", "weeks", or "months"
- `"0"` to allow all packages

#### `registry.global-min-publish-age`

* Type: String
* Default: `"0"`
* Environment: `CARGO_REGISTRY_GLOBAL_MIN_PUBLISH_AGE`

Specifies the global minimum timespan since a version's `pubtime` that it may
be considered for `resolver.incompatible-publish-age` for packages.
If `min-publish-age` is not set for a specific registry using
`registries.<name>.min-publish-age`, Cargo will use this minimum publish age.

It supports the following values:

- An integer followed by "seconds", "minutes", "hours", "days", "weeks", or "months"
- `"0"` to allow all packages

### Added to Resolver

The following will be added to the [resolver chapter] as a sibling section to
"Yanked versions":

> "Pubtime-incompatible versions"
>
> Versions with a publish time newer than the configured `min-publish-age`
> are considered pubtime-incompatible.
> When `resolver.incompatible-publish-age` is set to `deny`,
> the resolver will ignore these versions
> unless they already exist in the `Cargo.lock` file.
> Setting the config to `allow` would disable the check,
> which if combined with `cargo update --precise`,
> cargo would pull in a specific version and its transitive dependencies.

[resolver chapter]: ../reference/resolver.md

# Stabilized and removed features

## Compile progress
Expand Down Expand Up @@ -2465,3 +2367,9 @@ The `build.warnings` config field has been stabilized in Rust 1.97.

The `cargo update -Zunstable-options --breaking` flag has been removed in 1.99-nightly.
See <https://github.com/rust-lang/cargo/pull/17333> fopr the reason for its removal.

## min-publish-age

Minimum publish-age configuration for dependency resolution was stabilized in Rust 1.100.
See the [minimum publish-age configuration](config.md#registryglobal-min-publish-age)
and [resolver behavior](resolver.md#publish-age) for more information.
5 changes: 0 additions & 5 deletions src/resolver/version_prefs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,6 @@ impl PublishAgePolicy {
///
/// Returns `None` when either meets
///
/// * the `-Zmin-publish-age` gate is off
/// * the resolver is configured to allow pubtime-incompatible versions
/// * no threshold is configured at all
pub fn new(now: Option<jiff::Timestamp>, gctx: &GlobalContext) -> CargoResult<Option<Self>> {
Expand All @@ -193,10 +192,6 @@ impl PublishAgePolicy {
now: Option<jiff::Timestamp>,
gctx: &GlobalContext,
) -> CargoResult<Option<Self>> {
if !gctx.cli_unstable().min_publish_age {
return Ok(None);
}

let parse = |key: &str, config: Option<String>| -> CargoResult<MinPublishAge> {
let Some(config) = config else {
return Ok(MinPublishAge::Unset);
Expand Down
6 changes: 4 additions & 2 deletions src/workspace/features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -907,7 +907,6 @@ unstable_cli_options!(
hint_msrv: bool = ("Enable passing `package.rust-version` to rustc for lints"),
host_config: bool = ("Enable the `[host]` section in the .cargo/config.toml file"),
json_target_spec: bool = ("Enable `.json` target spec files"),
min_publish_age: bool = ("Enable the `min-publish-age` configuration for dependency version age filtering"),
minimal_versions: bool = ("Resolve minimal dependency versions instead of maximum"),
msrv_policy: bool = ("Enable rust-version aware policy within cargo"),
mtime_on_use: bool = ("Configure Cargo to update the mtime of used files"),
Expand Down Expand Up @@ -1023,6 +1022,9 @@ const STABILIZED_LOCKFILE_PATH: &str = "The `lockfile-path` config key is now al

const STABILIZED_WARNINGS: &str = "The `build.warnings` config key is now always available";

const STABILIZED_MIN_PUBLISH_AGE: &str =
"The `min-publish-age` configuration is now always available.";

fn deserialize_comma_separated_list<'de, D>(
deserializer: D,
) -> Result<Option<Vec<String>>, D::Error>
Expand Down Expand Up @@ -1423,6 +1425,7 @@ impl CliUnstable {
"config-include" => stabilized_warn(k, "1.93", STABILIZED_CONFIG_INCLUDE),
"lockfile-path" => stabilized_warn(k, "1.97", STABILIZED_LOCKFILE_PATH),
"warnings" => stabilized_warn(k, "1.97", STABILIZED_WARNINGS),
"min-publish-age" => stabilized_warn(k, "1.100", STABILIZED_MIN_PUBLISH_AGE),

// Unstable features
// Sorted alphabetically:
Expand Down Expand Up @@ -1461,7 +1464,6 @@ impl CliUnstable {
}
"host-config" => self.host_config = parse_empty(k, v)?,
"json-target-spec" => self.json_target_spec = parse_empty(k, v)?,
"min-publish-age" => self.min_publish_age = parse_empty(k, v)?,
"hint-msrv" => self.hint_msrv = parse_empty(k, v)?,
"next-lockfile-bump" => self.next_lockfile_bump = parse_empty(k, v)?,
"minimal-versions" => self.minimal_versions = parse_empty(k, v)?,
Expand Down
33 changes: 0 additions & 33 deletions src/workspace/workspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -358,15 +358,6 @@ impl<'gctx> Workspace<'gctx> {
.warn("ignoring `resolver.feature-unification` without `-Zfeature-unification`")?;
};

if !self.gctx().cli_unstable().min_publish_age {
if config.incompatible_publish_age.is_some() {
self.gctx().shell().warn(
"ignoring `resolver.incompatible-publish-age` without `-Zmin-publish-age`",
)?;
}
warn_unused_min_publish_age(self.gctx())?;
}

if let Some(lockfile_path) = config.lockfile_path {
// Reserve the ability to add templates in the future.
let replacements: [(&str, &str); 0] = [];
Expand Down Expand Up @@ -2099,30 +2090,6 @@ impl WorkspaceRootConfig {
}
}

fn warn_unused_min_publish_age(gctx: &GlobalContext) -> CargoResult<()> {
if gctx
.get::<Option<String>>("registry.global-min-publish-age")?
.is_some()
{
gctx.shell()
.warn("ignoring `registry.global-min-publish-age` without `-Zmin-publish-age`")?;
}

if let Some(context::ConfigValue::Table(registries, _)) = gctx.values()?.get("registries") {
for (name, val) in registries {
if let context::ConfigValue::Table(val, _) = val {
if val.contains_key("min-publish-age") {
gctx.shell().warn(format!(
"ignoring `registries.{name}.min-publish-age` without `-Zmin-publish-age`"
))?;
}
}
}
}

Ok(())
}

pub fn resolve_relative_path(
label: &str,
old_root: &Path,
Expand Down
Loading