Skip to content

Commit 8c15693

Browse files
authored
Merge pull request #7 from alexcrichton/prepublish
Update prepublish RFC with [augment]
2 parents 86b811a + 95cf2b4 commit 8c15693

File tree

1 file changed

+190
-52
lines changed

1 file changed

+190
-52
lines changed

text/0000-cargo-prepublish.md

Lines changed: 190 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,17 @@
66
# Summary
77
[summary]: #summary
88

9-
This RFC proposes the concept of *prepublication dependencies* for Cargo. These
10-
dependencies augment a crate index (like crates.io) with new versions of crates
11-
that have not yet been published to the index. Dependency resolution then works
12-
*as if* those prepublished versions actually existed in the
13-
index. Prepublication dependencies thus act as a kind of "staging index".
14-
15-
Prepublication makes it possible to perform integration testing within a large
16-
crate graph before publishing anything to crates.io, and without requiring
17-
dependencies to be switched from the crates.io index to git branches. It can, to
18-
a degree, simulate an "atomic" change across a large number of crates and
19-
repositories, which can then actually be landed in a piecemeal, non-atomic
9+
This RFC proposes the concept of *augmenting sources* for Cargo. Sources can be
10+
enhanced with new versions of a crate that did not exist or have existing
11+
versions replaced. Dependency resolution will work *as if* these additional
12+
crates actually existed in the original source.
13+
14+
One primary feature enabled by this is the ability to "prepublish" a crate to
15+
crates.io. Prepublication makes it possible to perform integration testing
16+
within a large crate graph before publishing anything to crates.io, and without
17+
requiring dependencies to be switched from the crates.io index to git branches.
18+
It can, to a degree, simulate an "atomic" change across a large number of crates
19+
and repositories, which can then actually be landed in a piecemeal, non-atomic
2020
fashion.
2121

2222
# Motivation
@@ -74,26 +74,34 @@ publication to crates.io.
7474
[design]: #detailed-design
7575

7676
The design itself is relatively straightforward. The Cargo.toml file will
77-
support a new section for prepublication:
77+
support a new section for augmenting a source of crates:
7878

7979
```toml
80-
[prepublish]
80+
[augment.crates-io]
8181
xml-rs = { path = "path/to/fork" }
8282
```
8383

84-
The listed dependencies must be path or git dependencies (though see
85-
[Unresolved Questions][unresolved] for the multi-index case). Cargo will load
86-
the crates and extract version information, supplementing the ambient index with
87-
the version it finds. If the same version *already* exists in the ambient index,
88-
the prepublication will act just like `[replace]`, replacing its source with the
89-
one specified in the `[prepublish]` section. However, unlike `[replace]`,
90-
Cargo will issue a warning in this case, since this situation is an indication
91-
that the prepublication is ready to be removed.
92-
93-
Like `[replace]`, the `[prepublish]` section is only taken into account for the
94-
root crate; allowing it to accumulate anywhere in the crate dependency graph
95-
creates intractable problems for dependency resolution. Cargo will also refuse
96-
to publish crates containing a `[prepublish]` section to crates.io
84+
The listed dependencies have the same syntax as the normal `[dependencies]`
85+
section, but they must all come form a different source than the source being
86+
augmented. For example you can't augment crates.io with other crates from
87+
crates.io! Cargo will load the crates and extract the version information for
88+
each dependency's name, supplementing the source specified with the version it
89+
finds. If the same name/version pair *already* exists in the source being
90+
augmented, then this will act just like `[replace]`, replacing its source with
91+
the one specified in the `[augment]` section.
92+
93+
Like `[replace]`, the `[augment]` section is only taken into account for the
94+
root crate (or workspace root); allowing it to accumulate anywhere in the crate
95+
dependency graph creates intractable problems for dependency resolution.
96+
97+
The sub-table of `[augment]` (where `crates-io` is used above) is used to
98+
specify the source that's being augmented. Cargo will know ahead of time one
99+
identifier, literally `crates-io`, but otherwise this field will currently be
100+
interpreted as a URL of a source. The name `crates-io` will correspond to the
101+
crates.io index, and other urls, such as git repositories, may also be specified
102+
for augmentation. Eventually it's intended we'll grow support for multiple
103+
registries here with their own identifiers, but for now just literally
104+
`crates-io` and other URLs are allowed.
97105

98106
## Examples
99107

@@ -112,6 +120,30 @@ With this setup, the dependency graph for Servo will contain *two* versions of
112120
`0.9.1` is considered a minor release against `0.9.0`, while `0.9.0` and `0.8.0`
113121
are incompatible.
114122

123+
### Scenario: augmenting with a bugfix
124+
125+
Let's say that while developing `foo` we've got a lock file pointing to `xml-rs`
126+
`0.8.0`, and there's an `0.8.0` branch of `xml-rs` that hasn't been touched
127+
since it was published. We then find a bug in the 0.8.0 publication of `xml-rs`
128+
which we'd like to fix.
129+
130+
First we'll check out `foo` locally and implement what we believe is a fix for
131+
this bug, and next, we change `Cargo.toml` for `foo`:
132+
133+
```toml
134+
[augment.crates-io]
135+
xml-rs = { path = "../xml-rs" }
136+
```
137+
138+
When compiling `foo`, Cargo will resolve the `xml-rs` dependency to `0.8.0`,
139+
as it did before, but that version's been replaced with our local copy. The
140+
local path dependency, which has version 0.8.0, takes precedence over the
141+
version found in the registry.
142+
143+
Once we've confirmed a fix bug we then continue to run tests in `xml-rs` itself,
144+
and then we'll send a PR to the main `xml-rs` repo. This then leads us to the
145+
next section where a new version of `xml-rs` comes into play!
146+
115147
### Scenario: prepublishing a new minor version
116148

117149
Now, suppose that `foo` needs some changes to `xml-rs`, but we want to check
@@ -120,7 +152,7 @@ that all of Servo compiles before pushing the changes through.
120152
First, we change `Cargo.toml` for `foo`:
121153

122154
```toml
123-
[prepublish]
155+
[augment.crates-io]
124156
xml-rs = { git = "https://github.com/aturon/xml-rs", branch = "0.9.2" }
125157

126158
[dependencies]
@@ -132,7 +164,7 @@ or introduce any `xml-rs` dependencies; it's enough to be using the fork of
132164
`foo`, which we would be anyway:
133165

134166
```toml
135-
[prepublish]
167+
[augment.crates-io]
136168
xml-rs = { git = "https://github.com/aturon/xml-rs", branch = "0.9.2" }
137169

138170
[dependencies]
@@ -154,15 +186,15 @@ want to do integration testing for (`servo`); no sibling crates needed to be
154186
changed.
155187

156188
Once `xml-rs` version `0.9.2` is actually published, we can remove the
157-
`[prepublish]` sections, and Cargo will warn us that this needs to be done.
189+
`[augment]` sections, and Cargo will warn us that this needs to be done.
158190

159191
### Scenario: prepublishing a new major version
160192

161193
What happens if `foo` instead needs to make a breaking change to `xml-rs`? The
162194
workflow is identical. For `foo`:
163195

164196
```toml
165-
[prepublish]
197+
[augment.crates-io]
166198
xml-rs = { git = "https://github.com/aturon/xml-rs", branch = "0.10.0" }
167199

168200
[dependencies]
@@ -172,7 +204,7 @@ xml-rs = "0.10.0"
172204
For `servo`:
173205

174206
```toml
175-
[prepublish]
207+
[augment.crates-io]
176208
xml-rs = { git = "https://github.com/aturon/xml-rs", branch = "0.10.0" }
177209

178210
[dependencies]
@@ -191,31 +223,144 @@ prepublication version of `xml-rs`.
191223
would help catch this issue at the Cargo level and give a maximally informative
192224
error message).
193225

226+
## Impact on `Cargo.lock`
227+
228+
Usage of `[augment]` will perform backwards-incompatible modifications to
229+
`Cargo.lock`, meaning that usage of `[augment]` will prevent previous versions
230+
of Cargo from interpreting the lock file. Cargo will unconditionally resolve all
231+
entries in the `[augment]` section to precise dependencies, encoding them all in
232+
the lock file whether they're used or not.
233+
234+
Dependencies formed on crates listed in `[augment]` will then be listed directly
235+
in Cargo.lock, eschewing the actual dependency entirely. For example let's say
236+
we depend on `env_logger` but we're using `[augment]` to depend on a git version
237+
of the `log` crate, a dependency of `env_logger`. First we'll have our
238+
`Cargo.toml` including:
239+
240+
```toml
241+
# Cargo.toml
242+
[dependencies]
243+
env_logger = "0.4"
244+
```
245+
246+
With that we'll find this in `Cargo.lock`, notably everything comes from
247+
crates.io
248+
249+
```toml
250+
# Cargo.lock
251+
[[package]]
252+
name = "env_logger"
253+
version = "0.4.2"
254+
source = "registry+https://github.com/rust-lang/crates.io-index"
255+
dependencies = [
256+
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
257+
]
258+
259+
[[package]]
260+
name = "log"
261+
version = "0.3.7"
262+
source = "registry+https://github.com/rust-lang/crates.io-index"
263+
```
264+
265+
Next up we'll add our `[augment]` section to crates.io:
266+
267+
```toml
268+
# Cargo.toml
269+
[augment.crates-io]
270+
log = { git = 'https://github.com/rust-lang-nursery/log' }
271+
```
272+
273+
and that will generate a lock file that looks (roughly) like:
274+
275+
```toml
276+
# Cargo.lock
277+
[[package]]
278+
name = "env_logger"
279+
version = "0.4.2"
280+
source = "registry+https://github.com/rust-lang/crates.io-index"
281+
dependencies = [
282+
"log 0.3.7 (git+https://github.com/rust-lang-nursery/log)",
283+
]
284+
285+
[[package]]
286+
name = "log"
287+
version = "0.3.7"
288+
source = "git+https://github.com/rust-lang-nursery/log#cb9fa28812ac27c9cadc4e7b18c221b561277289"
289+
```
290+
291+
Notably `log` from crates.io *is not mentioned at all here*, and crucially so!
292+
Additionally Cargo has the fully resolved version of the `log` augmentation
293+
available to it, down to the sha of what to check out.
294+
295+
When Cargo rebuilds from this `Cargo.lock` it will not query the registry for
296+
versions of `log`, instead seeing that there's an exact dependency on the git
297+
repository (from the `Cargo.lock`) and the repository is listed as an
298+
augmentation, so it'll follow that pointer.
299+
300+
## Impact on `[replace]`
301+
302+
The `[augment]` section in the manifest can in many ways be seen as a "replace
303+
2.0". It is, in fact, strictly more expressive than the current `[replace]`
304+
section! For example these two sections are equivalent:
305+
306+
```toml
307+
[replace]
308+
'log:0.3.7' = { git = 'https://github.com/rust-lang-nursery/log' }
309+
310+
# is the same as...
311+
312+
[augment.crates-io]
313+
log = { git = 'https://github.com/rust-lang-nursery/log' }
314+
```
315+
316+
This is not accidental! The intial development of the `[augment]` feature was
317+
actually focused on prepublishing dependencies and was called `[prepublish]`,
318+
but while discussing it a conclusion was reached that `[prepublish]` already
319+
allowed replacing existing versions in a registry, but issued a warning when
320+
doing so. It turned out that without a warning we ended up having a full-on
321+
`[replace]` replacement!
322+
323+
At this time, though, it is not planned to deprecate the `[replace]` section,
324+
nor remove it. After the `[augment]` section is implemented, if it ends up
325+
working out this may change. If after a few cycles on stable the `[augment]`
326+
section seems to be working well we can issue an official deprecation for
327+
`[replace]`, printing a warning if it's still used.
328+
329+
Documentation, however, will immediately being to recommend `[augment]` over
330+
`[replace]`.
331+
194332
# How We Teach This
195333
[how-we-teach-this]: #how-we-teach-this
196334

197-
Prepublication is a feature intended for large-scale projects spanning many
198-
repos and crates, where you want to make something like an atomic change across
199-
the repos. As such, it should likely be explained in a dedicated section for
335+
Augmentation is a feature intended for large-scale projects spanning many repos
336+
and crates, where you want to make something like an atomic change across the
337+
repos. As such, it should likely be explained in a dedicated section for
200338
large-scale Cargo usage, which would also include build system integration and
201339
other related topics.
202340

203341
The mechanism itself is straightforward enough that a handful of examples (as in
204342
this RFC) is generally enough to explain it. In the docs, these examples should
205343
be spelled out in greater detail.
206344

345+
Most notably, however, the [overriding dependenices][over] section of Cargo's
346+
documentation will be rewritten to primarily mention `[augment]`, but
347+
`[replace]` will be mentioned still with a recommendation to use `[augment]`
348+
instead if possible.
349+
350+
[over]: http://doc.crates.io/specifying-dependencies.html#overriding-dependencies
351+
207352
# Drawbacks
208353
[drawbacks]: #drawbacks
209354

210355
This feature adds yet another knob around where, exactly, Cargo is getting its
211-
source and version information. In particular, its similarity to `[replace]`
212-
means the two features are likely to be confused. One saving grace is that
213-
`[replace]` emphatically does not allow version numbers to be changed; it's very
214-
tailored to surgical patches.
356+
source and version information. In particular, it's basically deprecating
357+
`[replace]` if it works out, and it's typically a shame to deprecate major
358+
stable features.
215359

216-
Fortunately, because both features are rarely used, are only used for very large
217-
projects, and cannot be published to crates.io, the knobs are largely invisible
218-
to the vast majority of Cargo users, who are unaffected by them.
360+
Fortunately, because these features are intended to be relatively rarely used,
361+
checked in even more rarely, are only used for very large projects, and cannot
362+
be published to crates.io, the knobs are largely invisible to the vast majority
363+
of Cargo users, who are unaffected by them.
219364

220365
# Alternatives
221366
[alternatives]: #alternatives
@@ -244,14 +389,7 @@ address the desired workflow, for a few reasons:
244389
# Unresolved questions
245390
[unresolved]: #unresolved-questions
246391

247-
There are two unresolved questions, both about possible future extensions.
248-
249-
First: it would be extremely helpful to provide a first-class workflow for
250-
forking a dependency and making the necessary changes to Cargo.toml for
251-
prepublication, and for fixing things up when publication actually occurs. That
252-
shouldn't be hard to do, but is out of scope for this RFC.
253-
254-
Second: we may eventually want to use multiple crate indexes within a Cargo.toml
255-
file, and we'll need some way to express *which* we're talking about with
256-
prepublication. However, this will also be the case for standard dependencies,
257-
so this RFC assumes that any solution will cover both cases.
392+
- It would be extremely helpful to provide a first-class workflow for forking a
393+
dependency and making the necessary changes to Cargo.toml for prepublication,
394+
and for fixing things up when publication actually occurs. That shouldn't be
395+
hard to do, but is out of scope for this RFC.

0 commit comments

Comments
 (0)