From d3e73ec16da197322e668dce1cea8eebfccdbe48 Mon Sep 17 00:00:00 2001 From: "Carol (Nichols || Goulding)" Date: Sun, 29 Jan 2017 18:06:34 -0500 Subject: [PATCH 1/5] Extract a function for getting the build target from config --- src/cargo/ops/cargo_compile.rs | 2 +- src/cargo/util/config.rs | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/cargo/ops/cargo_compile.rs b/src/cargo/ops/cargo_compile.rs index cc3400268f9..48cb70fbca5 100644 --- a/src/cargo/ops/cargo_compile.rs +++ b/src/cargo/ops/cargo_compile.rs @@ -716,7 +716,7 @@ fn scrape_build_config(config: &Config, None => None, }; let jobs = jobs.or(cfg_jobs).unwrap_or(::num_cpus::get() as u32); - let cfg_target = config.get_string("build.target")?.map(|s| s.val); + let cfg_target = config.build_target_triple()?; let target = target.or(cfg_target); let mut base = ops::BuildConfig { host_triple: config.rustc()?.host.clone(), diff --git a/src/cargo/util/config.rs b/src/cargo/util/config.rs index 8d1d75e8613..0f992d237c9 100644 --- a/src/cargo/util/config.rs +++ b/src/cargo/util/config.rs @@ -223,6 +223,10 @@ impl Config { pub fn cwd(&self) -> &Path { &self.cwd } + pub fn build_target_triple(&self) -> CargoResult> { + self.get_string("build.target").map(|r| r.map(|s| s.val)) + } + pub fn target_dir(&self) -> CargoResult> { if let Some(dir) = env::var_os("CARGO_TARGET_DIR") { Ok(Some(Filesystem::new(self.cwd.join(dir)))) From 304d32995911d5aea3078ecc1037ba0fe52b6673 Mon Sep 17 00:00:00 2001 From: "Carol (Nichols || Goulding)" Date: Sun, 29 Jan 2017 18:10:33 -0500 Subject: [PATCH 2/5] Add a convenience function for getting rustc info --- src/cargo/util/rustc.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/cargo/util/rustc.rs b/src/cargo/util/rustc.rs index a9b65c959ca..d8da5ef1d20 100644 --- a/src/cargo/util/rustc.rs +++ b/src/cargo/util/rustc.rs @@ -59,4 +59,8 @@ impl Rustc { util::process(&self.path) } } + + pub fn medium_version(&self) -> Option<&str> { + self.verbose_version.lines().next() + } } From 3335fe82435663746fc74ed34f665fcda4780f69 Mon Sep 17 00:00:00 2001 From: "Carol (Nichols || Goulding)" Date: Sun, 29 Jan 2017 18:26:38 -0500 Subject: [PATCH 3/5] Add a publish-build-info command for reporting build status --- src/bin/cargo.rs | 1 + src/bin/publish_build_info.rs | 74 +++++++++++++++++++++++++++++++++++ src/cargo/ops/mod.rs | 1 + src/cargo/ops/registry.rs | 57 ++++++++++++++++++++++++++- src/cargo/util/rustc.rs | 4 +- src/crates-io/lib.rs | 21 ++++++++++ 6 files changed, 155 insertions(+), 3 deletions(-) create mode 100644 src/bin/publish_build_info.rs diff --git a/src/bin/cargo.rs b/src/bin/cargo.rs index 1f282f82d3e..2e27730704e 100644 --- a/src/bin/cargo.rs +++ b/src/bin/cargo.rs @@ -124,6 +124,7 @@ macro_rules! each_subcommand{ $mac!(package); $mac!(pkgid); $mac!(publish); + $mac!(publish_build_info); $mac!(read_manifest); $mac!(run); $mac!(rustc); diff --git a/src/bin/publish_build_info.rs b/src/bin/publish_build_info.rs new file mode 100644 index 00000000000..59f4861abc5 --- /dev/null +++ b/src/bin/publish_build_info.rs @@ -0,0 +1,74 @@ +use cargo::core::Workspace; +use cargo::ops; +use cargo::util::{CliResult, Config}; +use cargo::util::important_paths::find_root_manifest_for_wd; + +#[derive(Deserialize)] +pub struct Options { + flag_target: Option, + flag_host: Option, + flag_token: Option, + flag_manifest_path: Option, + flag_verbose: u32, + flag_quiet: Option, + flag_color: Option, + flag_dry_run: bool, + flag_frozen: bool, + flag_locked: bool, + cmd_pass: bool, + #[allow(dead_code)] // Pass and fail are mutually exclusive + cmd_fail: bool, +} + +pub const USAGE: &'static str = " +Upload a package's build info to the registry: whether the crate built +successfully on a particular target with a particular version of Rust. + +Usage: + cargo publish-build-info [options] (pass|fail) + +Options: + -h, --help Print this message + --target TRIPLE Build for the target triple + --host HOST Host to upload the package to + --token TOKEN Token to use when uploading + --manifest-path PATH Path to the manifest of the package to publish + --dry-run Perform all checks without uploading + -v, --verbose ... Use verbose output (-vv very verbose/build.rs output) + -q, --quiet No output printed to stdout + --color WHEN Coloring: auto, always, never + --frozen Require Cargo.lock and cache are up to date + --locked Require Cargo.lock is up to date + +"; + +pub fn execute(options: Options, config: &Config) -> CliResult { + config.configure(options.flag_verbose, + options.flag_quiet, + &options.flag_color, + options.flag_frozen, + options.flag_locked)?; + + let Options { + flag_token: token, + flag_host: host, + flag_manifest_path, + flag_dry_run: dry_run, + flag_target: target, + cmd_pass, + .. + } = options; + + let root = find_root_manifest_for_wd(flag_manifest_path.clone(), config.cwd())?; + let ws = Workspace::new(&root, config)?; + ops::publish_build_info(&ws, ops::PublishBuildInfoOpts { + config: config, + token: token, + index: host, + dry_run: dry_run, + rust_version: config.rustc()?.version_channel_date()?.to_string(), + target: target, + passed: cmd_pass, + })?; + Ok(()) +} diff --git a/src/cargo/ops/mod.rs b/src/cargo/ops/mod.rs index 7c4a33d4ba2..ffbd2fc8eb1 100644 --- a/src/cargo/ops/mod.rs +++ b/src/cargo/ops/mod.rs @@ -20,6 +20,7 @@ pub use self::registry::{publish, registry_configuration, RegistryConfig}; pub use self::registry::{registry_login, search, needs_custom_http_transport, http_handle}; pub use self::registry::{modify_owners, yank, OwnersOptions, PublishOpts}; pub use self::registry::configure_http_handle; +pub use self::registry::{publish_build_info, PublishBuildInfoOpts}; pub use self::cargo_fetch::fetch; pub use self::cargo_pkgid::pkgid; pub use self::resolve::{resolve_ws, resolve_ws_precisely, resolve_with_previous}; diff --git a/src/cargo/ops/registry.rs b/src/cargo/ops/registry.rs index daf80a6241e..96f9ed0d6d6 100644 --- a/src/cargo/ops/registry.rs +++ b/src/cargo/ops/registry.rs @@ -5,7 +5,7 @@ use std::time::Duration; use curl::easy::{Easy, SslOpt}; use git2; -use registry::{Registry, NewCrate, NewCrateDependency}; +use registry::{Registry, NewCrate, NewCrateDependency, NewVersionBuildInfo}; use url::percent_encoding::{percent_encode, QUERY_ENCODE_SET}; @@ -225,6 +225,61 @@ fn transmit(config: &Config, } } +pub struct PublishBuildInfoOpts<'cfg> { + pub config: &'cfg Config, + pub token: Option, + pub index: Option, + pub dry_run: bool, + pub rust_version: String, + pub target: Option, + pub passed: bool, +} + +pub fn publish_build_info(ws: &Workspace, opts: PublishBuildInfoOpts) -> CargoResult<()> { + let pkg = ws.current()?; + + let (mut registry, _reg_id) = registry(opts.config, opts.token, opts.index)?; + + // Upload build info to the specified destination + opts.config.shell().status("Uploading build info", pkg.package_id().to_string())?; + + let target = opts.target; + let cfg_target = opts.config.build_target_triple()?; + let target = target.or(cfg_target); + let target = target.unwrap_or(opts.config.rustc()?.host.clone()); + + transmit_build_info( + opts.config, &pkg, &mut registry, opts.dry_run, + &opts.rust_version, &target, opts.passed)?; + + Ok(()) +} + +fn transmit_build_info(config: &Config, + pkg: &Package, + registry: &mut Registry, + dry_run: bool, + rust_version: &str, + target: &str, + passed: bool) -> CargoResult<()> { + + // Do not upload if performing a dry run + if dry_run { + config.shell().warn("aborting upload due to dry run")?; + return Ok(()); + } + + registry.publish_build_info(&NewVersionBuildInfo { + name: pkg.name().to_string(), + vers: pkg.version().to_string(), + rust_version: rust_version.to_string(), + target: target.to_string(), + passed: passed, + }).map_err(|e| { + CargoError::from(e.to_string()) + }) +} + pub fn registry_configuration(config: &Config, registry: Option) -> CargoResult { diff --git a/src/cargo/util/rustc.rs b/src/cargo/util/rustc.rs index d8da5ef1d20..b1a08466efe 100644 --- a/src/cargo/util/rustc.rs +++ b/src/cargo/util/rustc.rs @@ -60,7 +60,7 @@ impl Rustc { } } - pub fn medium_version(&self) -> Option<&str> { - self.verbose_version.lines().next() + pub fn version_channel_date(&self) -> CargoResult<&str> { + self.verbose_version.lines().next().ok_or(internal("rustc -v didn't have any lines")) } } diff --git a/src/crates-io/lib.rs b/src/crates-io/lib.rs index 19d3e700c60..924a50bb9cc 100644 --- a/src/crates-io/lib.rs +++ b/src/crates-io/lib.rs @@ -72,6 +72,15 @@ pub struct NewCrateDependency { pub registry: Option, } +#[derive(Serialize)] +pub struct NewVersionBuildInfo { + pub name: String, + pub vers: String, + pub rust_version: String, + pub target: String, + pub passed: bool, +} + #[derive(Deserialize)] pub struct User { pub id: u32, @@ -202,6 +211,18 @@ impl Registry { }) } + pub fn publish_build_info(&mut self, build_info: &NewVersionBuildInfo) + -> Result<()> { + let body = serde_json::to_string(build_info)?; + let url = format!("/crates/{}/{}/build_info", build_info.name, build_info.vers); + + let body = self.put(url, body.as_bytes())?; + + assert!(serde_json::from_str::(&body)?.ok); + + Ok(()) + } + pub fn search(&mut self, query: &str, limit: u8) -> Result<(Vec, u32)> { let formated_query = percent_encode(query.as_bytes(), QUERY_ENCODE_SET); let body = self.req( From 0c252abd74f75187a3285085dc5231190b708ba7 Mon Sep 17 00:00:00 2001 From: "Carol (Nichols || Goulding)" Date: Tue, 31 Jan 2017 20:57:56 -0500 Subject: [PATCH 4/5] Documentation for publish-build-info --- src/doc/src/reference/publishing.md | 75 +++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/src/doc/src/reference/publishing.md b/src/doc/src/reference/publishing.md index 05439354cb9..ca3cac5d695 100644 --- a/src/doc/src/reference/publishing.md +++ b/src/doc/src/reference/publishing.md @@ -168,6 +168,81 @@ The syntax for teams is currently `github:org:team` (see examples above). In order to add a team as an owner one must be a member of that team. No such restriction applies to removing a team as an owner. +#### `cargo publish-build-info` + +The `cargo publish-build-info` command is intended to help automate reporting +on which versions of Rust your crate's released versions work successfully +with. It is meant to work with the results of continuous integration runs. It +will work with any CI service; below are instructions for Travis CI, but the +idea should be generalizable to any setup. + +`cargo publish-build-info` will report the version of rustc, the version of +your crate, and the target that you run the command with. The target may +optionally be specified as something other than the operating system the +command is running on by specifying the `--target` flag. + +When CI runs on a tagged (released) version of your crate, run this command +with the value `pass` or `fail` depending on the results of your CI script. + +Results with a particular crate version, rustc version, and target can only be +reported once. A possible enhancement is to allow overwriting in the future. +Until then, only report on your final tagged release version. + +Crates.io must already know about a crate and version in order for you to +publish build information about them, so intended workflow is: + +1. Run regular CI to verify your crate compiles and passes tests +2. Bump to the version you want to release in `Cargo.toml` and commit +3. Tag that commit since the CI setup recommended below will only run on tagged + versions +4. Publish to crates.io +5. Push the tag in order to run CI on the tagged version, which will then run + `cargo publish-build-info` with the results. + +Yes, you can report possibly-incorrect results manually, but your users +will probably report a bug if they can't reproduce your reported results. + +On crate list pages such as search results, your crate will have a badge if you +have reported `pass` results for the max version of your crate. If you have +reported that it passes on stable, the version of stable will be displayed in a +green badge. If no stable versions have a reported pass result, but a beta +version of Rust has, the date of the latest beta that passed will be displayed +in a yellow badge. If there have been no pass results on stable or beta but +there have been for nightly, the date of the latest nightly that passed will be +displayed in an orange badge. If there have been no pass results reported for +any Rust version, no badge will be shown for that crate. + +If there have been any results reported for the Tier 1 targets on 64 bit +architectures for a version of a crate, there will be a section on that +version's page titled "Build info" that will display more detailed results for +the latest version of each of the stable, beta, and nightly channels for those +targets. + +##### Travis configuration to automatically report build info + +First, make an [encrypted environment variable][travis-env] named TOKEN with +your crates.io API key. + +Then add this to your `.travis.yml`, substituting in your secure environment +variable value where indicated: + +```yml +env: + - secure: [your secure env var value here] + +after_script: > + if [ -n "$TRAVIS_TAG" ] ; then + result=$([[ $TRAVIS_TEST_RESULT = 0 ]] && echo pass || echo fail) + cargo publish-build-info $result --token TOKEN + fi +``` + +The code in `after_script` checks to see if you're building a tagged commit, +and if so, checks to see if the build passed or failed, then runs the `cargo +publish-build-info` command to send the build result to crates.io. + +[travis-env]: https://docs.travis-ci.com/user/environment-variables/#Defining-encrypted-variables-in-.travis.yml + ### GitHub permissions Team membership is not something GitHub provides simple public access to, and it From b9ee56276f6629f4a3bb4ca0719fc1617107c778 Mon Sep 17 00:00:00 2001 From: Justin Geibel Date: Wed, 28 Feb 2018 18:50:42 -0500 Subject: [PATCH 5/5] Update to include standard options and --host is now --index --- src/bin/publish_build_info.rs | 28 ++++++++++++++++++---------- src/cargo/ops/registry.rs | 5 ++--- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/src/bin/publish_build_info.rs b/src/bin/publish_build_info.rs index 59f4861abc5..57ed3548634 100644 --- a/src/bin/publish_build_info.rs +++ b/src/bin/publish_build_info.rs @@ -6,7 +6,7 @@ use cargo::util::important_paths::find_root_manifest_for_wd; #[derive(Deserialize)] pub struct Options { flag_target: Option, - flag_host: Option, + flag_index: Option, flag_token: Option, flag_manifest_path: Option, flag_verbose: u32, @@ -15,6 +15,9 @@ pub struct Options { flag_dry_run: bool, flag_frozen: bool, flag_locked: bool, + #[serde(rename = "flag_Z")] + flag_z: Vec, + flag_registry: Option, cmd_pass: bool, #[allow(dead_code)] // Pass and fail are mutually exclusive cmd_fail: bool, @@ -30,7 +33,7 @@ Usage: Options: -h, --help Print this message --target TRIPLE Build for the target triple - --host HOST Host to upload the package to + --index INDEX Registry index to publish build info to --token TOKEN Token to use when uploading --manifest-path PATH Path to the manifest of the package to publish --dry-run Perform all checks without uploading @@ -39,22 +42,26 @@ Options: --color WHEN Coloring: auto, always, never --frozen Require Cargo.lock and cache are up to date --locked Require Cargo.lock is up to date + -Z FLAG ... Unstable (nightly-only) flags to Cargo + --registry REGISTRY Registry to use "; -pub fn execute(options: Options, config: &Config) -> CliResult { +pub fn execute(options: Options, config: &mut Config) -> CliResult { config.configure(options.flag_verbose, options.flag_quiet, &options.flag_color, options.flag_frozen, - options.flag_locked)?; + options.flag_locked, + &options.flag_z)?; let Options { flag_token: token, - flag_host: host, + flag_index: index, flag_manifest_path, flag_dry_run: dry_run, flag_target: target, + flag_registry: registry, cmd_pass, .. } = options; @@ -62,12 +69,13 @@ pub fn execute(options: Options, config: &Config) -> CliResult { let root = find_root_manifest_for_wd(flag_manifest_path.clone(), config.cwd())?; let ws = Workspace::new(&root, config)?; ops::publish_build_info(&ws, ops::PublishBuildInfoOpts { - config: config, - token: token, - index: host, - dry_run: dry_run, + config, + token, + index, + dry_run, rust_version: config.rustc()?.version_channel_date()?.to_string(), - target: target, + registry, + target, passed: cmd_pass, })?; Ok(()) diff --git a/src/cargo/ops/registry.rs b/src/cargo/ops/registry.rs index 96f9ed0d6d6..33097c5d35f 100644 --- a/src/cargo/ops/registry.rs +++ b/src/cargo/ops/registry.rs @@ -233,12 +233,13 @@ pub struct PublishBuildInfoOpts<'cfg> { pub rust_version: String, pub target: Option, pub passed: bool, + pub registry: Option, } pub fn publish_build_info(ws: &Workspace, opts: PublishBuildInfoOpts) -> CargoResult<()> { let pkg = ws.current()?; - let (mut registry, _reg_id) = registry(opts.config, opts.token, opts.index)?; + let (mut registry, _reg_id) = registry(opts.config, opts.token, opts.index, opts.registry)?; // Upload build info to the specified destination opts.config.shell().status("Uploading build info", pkg.package_id().to_string())?; @@ -275,8 +276,6 @@ fn transmit_build_info(config: &Config, rust_version: rust_version.to_string(), target: target.to_string(), passed: passed, - }).map_err(|e| { - CargoError::from(e.to_string()) }) }