feat(package+publish): package arguments

adds -p, --workspace, and --exclude to package command,
and -p to publish command, as well as tests for both.

closes #7345
This commit is contained in:
tcmal 2021-07-21 18:33:19 +01:00
parent 9535dc3dfd
commit 8f1f0e40fd
7 changed files with 245 additions and 31 deletions

View File

@ -28,6 +28,11 @@ pub fn cli() -> App {
.arg_target_triple("Build for the target triple")
.arg_target_dir()
.arg_features()
.arg_package_spec(
"Package(s) to assemble",
"Assemble all packages in the workspace",
"Don't assemble specified packages",
)
.arg_manifest_path()
.arg_jobs()
.after_help("Run `cargo help package` for more detailed information.\n")
@ -35,6 +40,8 @@ pub fn cli() -> App {
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
let ws = args.workspace(config)?;
let specs = args.packages_from_flags()?;
ops::package(
&ws,
&PackageOpts {
@ -43,10 +50,12 @@ pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
list: args.is_present("list"),
check_metadata: !args.is_present("no-metadata"),
allow_dirty: args.is_present("allow-dirty"),
to_package: specs,
targets: args.targets(),
jobs: args.jobs()?,
cli_features: args.cli_features()?,
},
)?;
Ok(())
}

View File

@ -18,6 +18,7 @@ pub fn cli() -> App {
))
.arg_target_triple("Build for the target triple")
.arg_target_dir()
.arg_package("Package to publish")
.arg_manifest_path()
.arg_features()
.arg_jobs()
@ -41,6 +42,7 @@ pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
index,
verify: !args.is_present("no-verify"),
allow_dirty: args.is_present("allow-dirty"),
to_publish: args.packages_from_flags()?,
targets: args.targets(),
jobs: args.jobs()?,
dry_run: args.is_present("dry-run"),

View File

@ -29,6 +29,7 @@ pub struct PackageOpts<'cfg> {
pub allow_dirty: bool,
pub verify: bool,
pub jobs: Option<u32>,
pub to_package: ops::Packages,
pub targets: Vec<String>,
pub cli_features: CliFeatures,
}
@ -61,16 +62,12 @@ enum GeneratedFile {
VcsInfo(String),
}
pub fn package(ws: &Workspace<'_>, opts: &PackageOpts<'_>) -> CargoResult<Option<FileLock>> {
if ws.root().join("Cargo.lock").exists() {
// Make sure the Cargo.lock is up-to-date and valid.
let _ = ops::resolve_ws(ws)?;
// If Cargo.lock does not exist, it will be generated by `build_lock`
// below, and will be validated during the verification step.
}
let pkg = ws.current()?;
pub fn package_one(
ws: &Workspace<'_>,
pkg: &Package,
opts: &PackageOpts<'_>,
) -> CargoResult<Option<FileLock>> {
let config = ws.config();
let mut src = PathSource::new(pkg.root(), pkg.package_id().source_id(), config);
src.update()?;
@ -96,12 +93,13 @@ pub fn package(ws: &Workspace<'_>, opts: &PackageOpts<'_>) -> CargoResult<Option
None
};
let ar_files = build_ar_list(ws, pkg, src_files, vcs_info)?;
let ar_files = build_ar_list(&ws, pkg, src_files, vcs_info)?;
if opts.list {
for ar_file in ar_files {
drop_println!(config, "{}", ar_file.rel_str);
}
return Ok(None);
}
@ -125,20 +123,65 @@ pub fn package(ws: &Workspace<'_>, opts: &PackageOpts<'_>) -> CargoResult<Option
.shell()
.status("Packaging", pkg.package_id().to_string())?;
dst.file().set_len(0)?;
tar(ws, ar_files, dst.file(), &filename)
tar(&ws, pkg, ar_files, dst.file(), &filename)
.with_context(|| "failed to prepare local package for uploading")?;
if opts.verify {
dst.seek(SeekFrom::Start(0))?;
run_verify(ws, &dst, opts).with_context(|| "failed to verify package tarball")?
run_verify(&ws, pkg, &dst, opts).with_context(|| "failed to verify package tarball")?
}
dst.seek(SeekFrom::Start(0))?;
{
let src_path = dst.path();
let dst_path = dst.parent().join(&filename);
fs::rename(&src_path, &dst_path)
.with_context(|| "failed to move temporary tarball into final location")?;
let src_path = dst.path();
let dst_path = dst.parent().join(&filename);
fs::rename(&src_path, &dst_path)
.with_context(|| "failed to move temporary tarball into final location")?;
return Ok(Some(dst));
}
pub fn package(ws: &Workspace<'_>, opts: &PackageOpts<'_>) -> CargoResult<Option<Vec<FileLock>>> {
let pkgs = ws.members_with_features(
&opts.to_package.to_package_id_specs(ws)?,
&opts.cli_features,
)?;
let mut dsts = Vec::with_capacity(pkgs.len());
if ws.root().join("Cargo.lock").exists() {
// Make sure the Cargo.lock is up-to-date and valid.
let _ = ops::resolve_ws(&ws)?;
// If Cargo.lock does not exist, it will be generated by `build_lock`
// below, and will be validated during the verification step.
}
for (pkg, cli_features) in pkgs {
let result = package_one(
ws,
pkg,
&PackageOpts {
config: opts.config,
list: opts.list,
check_metadata: opts.check_metadata,
allow_dirty: opts.allow_dirty,
verify: opts.verify,
jobs: opts.jobs,
to_package: ops::Packages::Default,
targets: opts.targets.clone(),
cli_features: cli_features,
},
)?;
if !opts.list {
dsts.push(result.unwrap());
}
}
if opts.list {
// We're just listing, so there's no file output
Ok(None)
} else {
Ok(Some(dsts))
}
Ok(Some(dst))
}
/// Builds list of files to archive.
@ -265,12 +308,11 @@ fn build_ar_list(
}
/// Construct `Cargo.lock` for the package to be published.
fn build_lock(ws: &Workspace<'_>) -> CargoResult<String> {
fn build_lock(ws: &Workspace<'_>, orig_pkg: &Package) -> CargoResult<String> {
let config = ws.config();
let orig_resolve = ops::load_pkg_lockfile(ws)?;
// Convert Package -> TomlManifest -> Manifest -> Package
let orig_pkg = ws.current()?;
let toml_manifest = Rc::new(
orig_pkg
.manifest()
@ -473,6 +515,7 @@ fn check_repo_state(
fn tar(
ws: &Workspace<'_>,
pkg: &Package,
ar_files: Vec<ArchiveFile>,
dst: &File,
filename: &str,
@ -485,7 +528,6 @@ fn tar(
// Put all package files into a compressed archive.
let mut ar = Builder::new(encoder);
let pkg = ws.current()?;
let config = ws.config();
let base_name = format!("{}-{}", pkg.name(), pkg.version());
@ -519,7 +561,7 @@ fn tar(
FileContents::Generated(generated_kind) => {
let contents = match generated_kind {
GeneratedFile::Manifest => pkg.to_registry_toml(ws)?,
GeneratedFile::Lockfile => build_lock(ws)?,
GeneratedFile::Lockfile => build_lock(ws, pkg)?,
GeneratedFile::VcsInfo(s) => s,
};
header.set_entry_type(EntryType::file());
@ -647,9 +689,13 @@ fn check_yanked(config: &Config, pkg_set: &PackageSet<'_>, resolve: &Resolve) ->
Ok(())
}
fn run_verify(ws: &Workspace<'_>, tar: &FileLock, opts: &PackageOpts<'_>) -> CargoResult<()> {
fn run_verify(
ws: &Workspace<'_>,
pkg: &Package,
tar: &FileLock,
opts: &PackageOpts<'_>,
) -> CargoResult<()> {
let config = ws.config();
let pkg = ws.current()?;
config.shell().status("Verifying", pkg)?;

View File

@ -13,7 +13,7 @@ pub use self::cargo_generate_lockfile::UpdateOptions;
pub use self::cargo_install::{install, install_list};
pub use self::cargo_new::{init, new, NewOptions, VersionControl};
pub use self::cargo_output_metadata::{output_metadata, ExportInfo, OutputMetadataOptions};
pub use self::cargo_package::{package, PackageOpts};
pub use self::cargo_package::{package, package_one, PackageOpts};
pub use self::cargo_pkgid::pkgid;
pub use self::cargo_read_manifest::{read_package, read_packages};
pub use self::cargo_run::run;

View File

@ -50,6 +50,7 @@ pub struct PublishOpts<'cfg> {
pub verify: bool,
pub allow_dirty: bool,
pub jobs: Option<u32>,
pub to_publish: ops::Packages,
pub targets: Vec<String>,
pub dry_run: bool,
pub registry: Option<String>,
@ -57,9 +58,12 @@ pub struct PublishOpts<'cfg> {
}
pub fn publish(ws: &Workspace<'_>, opts: &PublishOpts<'_>) -> CargoResult<()> {
let pkg = ws.current()?;
let mut publish_registry = opts.registry.clone();
let specs = opts.to_publish.to_package_id_specs(ws)?;
let mut pkgs = ws.members_with_features(&specs, &opts.cli_features)?;
let (pkg, cli_features) = pkgs.pop().unwrap();
let mut publish_registry = opts.registry.clone();
if let Some(ref allowed_registries) = *pkg.publish() {
if publish_registry.is_none() && allowed_registries.len() == 1 {
// If there is only one allowed registry, push to that one directly,
@ -101,22 +105,23 @@ pub fn publish(ws: &Workspace<'_>, opts: &PublishOpts<'_>) -> CargoResult<()> {
// Prepare a tarball, with a non-suppressible warning if metadata
// is missing since this is being put online.
let tarball = ops::package(
ws,
let tarball = ops::package_one(
&ws,
pkg,
&ops::PackageOpts {
config: opts.config,
verify: opts.verify,
list: false,
check_metadata: true,
allow_dirty: opts.allow_dirty,
to_package: ops::Packages::Default,
targets: opts.targets.clone(),
jobs: opts.jobs,
cli_features: opts.cli_features.clone(),
cli_features: cli_features.clone(),
},
)?
.unwrap();
// Upload said tarball to the specified destination
opts.config
.shell()
.status("Uploading", pkg.package_id().to_string())?;

View File

@ -2105,3 +2105,59 @@ src/main.rs
.run();
p.cargo("package --allow-dirty").run();
}
#[cargo_test]
fn in_workspace() {
let p = project()
.file(
"Cargo.toml",
r#"
[project]
name = "foo"
version = "0.0.1"
authors = []
license = "MIT"
description = "foo"
[workspace]
members = ["bar"]
"#,
)
.file("src/main.rs", "fn main() {}")
.file(
"bar/Cargo.toml",
r#"
[project]
name = "bar"
version = "0.0.1"
authors = []
license = "MIT"
description = "bar"
workspace = ".."
"#,
)
.file("bar/src/main.rs", "fn main() {}")
.build();
p.cargo("package --workspace")
.with_stderr(
"\
[WARNING] manifest has no documentation, [..]
See [..]
[PACKAGING] bar v0.0.1 ([CWD]/bar)
[VERIFYING] bar v0.0.1 ([CWD]/bar)
[COMPILING] bar v0.0.1 ([CWD][..])
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
[WARNING] manifest has no documentation, [..]
See [..]
[PACKAGING] foo v0.0.1 ([CWD])
[VERIFYING] foo v0.0.1 ([CWD])
[COMPILING] foo v0.0.1 ([CWD][..])
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
",
)
.run();
assert!(p.root().join("target/package/foo-0.0.1.crate").is_file());
assert!(p.root().join("target/package/bar-0.0.1.crate").is_file());
}

View File

@ -56,6 +56,34 @@ fn validate_upload_foo() {
);
}
fn validate_upload_bar() {
publish::validate_upload(
r#"
{
"authors": [],
"badges": {},
"categories": [],
"deps": [],
"description": "bar",
"documentation": null,
"features": {},
"homepage": null,
"keywords": [],
"license": "MIT",
"license_file": null,
"links": null,
"name": "bar",
"readme": null,
"readme_file": null,
"repository": null,
"vers": "0.0.1"
}
"#,
"bar-0.0.1.crate",
&["Cargo.lock", "Cargo.toml", "Cargo.toml.orig", "src/main.rs"],
);
}
fn validate_upload_foo_clean() {
publish::validate_upload(
CLEAN_FOO_JSON,
@ -1704,3 +1732,71 @@ Caused by:
t.join().unwrap();
}
#[cargo_test]
fn in_workspace() {
registry::init();
let p = project()
.file(
"Cargo.toml",
r#"
[workspace]
members = ["foo", "bar"]
"#,
)
.file(
"foo/Cargo.toml",
r#"
[project]
name = "foo"
version = "0.0.1"
authors = []
license = "MIT"
description = "foo"
"#,
)
.file("foo/src/main.rs", "fn main() {}")
.file(
"bar/Cargo.toml",
r#"
[project]
name = "bar"
version = "0.0.1"
authors = []
license = "MIT"
description = "bar"
workspace = ".."
"#,
)
.file("bar/src/main.rs", "fn main() {}")
.build();
p.cargo("publish --no-verify --token sekrit -p foo")
.with_stderr(&format!(
"\
[UPDATING] [..]
[WARNING] manifest has no documentation, [..]
See [..]
[PACKAGING] foo v0.0.1 ([CWD]/foo)
[UPLOADING] foo v0.0.1 ([CWD]/foo)
"
))
.run();
validate_upload_foo();
p.cargo("publish --no-verify --token sekrit -p bar")
.with_stderr(&format!(
"\
[UPDATING] [..]
[WARNING] manifest has no documentation, [..]
See [..]
[PACKAGING] bar v0.0.1 ([CWD]/bar)
[UPLOADING] bar v0.0.1 ([CWD]/bar)
"
))
.run();
validate_upload_bar();
}