Skip to content

Commit

Permalink
Adding feature-flags to release configuration
Browse files Browse the repository at this point in the history
This PR adds `features` and `all_features` as both flags to the CLI
as well as the `release.toml` configuration. This change allows you
to tell `cargo-release` to verify the package with certain features
enabled, in order to avoid needing to use `no-verify` for crates
that depend on certain features being enabled (or simply any
feature) before they can be uploaded.

This change is dependent on rust-lang/cargo#6453, which adds this
feature to the base `cargo publish` command.

The motivation behind this PR is the same as the above mentioned
cargo PR: reducing the amount of packaging errors that can happen
due to people not being able to verify their code because it has
some feature requirements for compilation, ultimately resulting in
a more healthy package ecosystem.
  • Loading branch information
spacekookie committed Jan 29, 2019
1 parent b4b13dd commit 3239298
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 21 deletions.
12 changes: 10 additions & 2 deletions src/cargo.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
use cmd::call;
use error::FatalError;
use Features;

pub fn publish(dry_run: bool) -> Result<bool, FatalError> {
call(vec![env!("CARGO"), "publish"], dry_run)
pub fn publish(dry_run: bool, features: Features) -> Result<bool, FatalError> {
match features {
Features::None => call(vec![env!("CARGO"), "publish"], dry_run),
Features::Selective(vec) => call(
vec![env!("CARGO"), "publish", "--features", &vec.join(" ")],
dry_run,
),
Features::All => call(vec![env!("CARGO"), "publish", "--all-features"], dry_run),
}
}

pub fn update(dry_run: bool) -> Result<bool, FatalError> {
Expand Down
4 changes: 4 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ pub static TAG_MESSAGE: &'static str = "tag-message";
pub static TAG_PREFIX: &'static str = "tag-prefix";
pub static DOC_COMMIT_MESSAGE: &'static str = "doc-commit-message";
pub static DISABLE_TAG: &'static str = "disable-tag";
pub static ENABLE_FEATURES: &'static str = "enable-features";
pub static ENABLE_ALL_FEATURES: &'static str = "all-features";

fn load_from_file(path: &Path) -> io::Result<String> {
let mut file = File::open(path)?;
Expand Down Expand Up @@ -123,6 +125,8 @@ pub fn verify_release_config(config: &Table) -> Option<Vec<&str>> {
TAG_PREFIX,
DOC_COMMIT_MESSAGE,
DISABLE_TAG,
ENABLE_FEATURES,
ENABLE_ALL_FEATURES
];
let mut invalid_keys = Vec::new();
for i in config.keys() {
Expand Down
98 changes: 79 additions & 19 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,7 @@ fn get_string_option(
config::get_release_config(config_file, config_file_key)
.and_then(|f| f.as_str())
.map(|f| f.to_owned())
})
.unwrap_or(default_value.to_owned())
}).unwrap_or(default_value.to_owned())
}

fn get_bool_option(cli: bool, config_file: Option<&Table>, config_file_key: &str) -> bool {
Expand All @@ -52,6 +51,25 @@ fn get_bool_option(cli: bool, config_file: Option<&Table>, config_file_key: &str
.unwrap_or(false)
}

/// Takes a list in form `"a,b,d,e"` and returns a Vec: `["a", "b", "c", "d"]`
fn get_list_option(
cli: &Option<Vec<String>>,
config_file: Option<&Table>,
config_file_key: &str,
) -> Option<Vec<String>> {
cli.clone().or_else(|| {
config::get_release_config(config_file, config_file_key)
.and_then(|f| f.as_array())
.and_then(|a| {
Some(
a.iter()
.map(|i| String::from(i.as_str().unwrap()))
.collect::<Vec<String>>(),
)
})
})
}

fn execute(args: &ReleaseOpt) -> Result<i32, error::FatalError> {
let cargo_file = config::parse_cargo_config()?;
let custom_config_path_option = args.config.as_ref();
Expand Down Expand Up @@ -120,20 +138,20 @@ fn execute(args: &ReleaseOpt) -> Result<i32, error::FatalError> {
.unwrap_or("(cargo-release) start next development iteration {{version}}");
let pre_release_replacements =
config::get_release_config(release_config.as_ref(), config::PRE_RELEASE_REPLACEMENTS);
let pre_release_hook =
config::get_release_config(release_config.as_ref(), config::PRE_RELEASE_HOOK).and_then(
|h| match h {
&Value::String(ref s) => Some(vec![s.as_ref()]),
&Value::Array(ref a) => Some(
a.iter()
.map(|v| v.as_str())
.filter(|o| o.is_some())
.map(|s| s.unwrap())
.collect(),
),
_ => None,
},
);
let pre_release_hook = config::get_release_config(
release_config.as_ref(),
config::PRE_RELEASE_HOOK,
).and_then(|h| match h {
&Value::String(ref s) => Some(vec![s.as_ref()]),
&Value::Array(ref a) => Some(
a.iter()
.map(|v| v.as_str())
.filter(|o| o.is_some())
.map(|s| s.unwrap())
.collect(),
),
_ => None,
});
let tag_msg = config::get_release_config(release_config.as_ref(), config::TAG_MESSAGE)
.and_then(|f| f.as_str())
.unwrap_or("(cargo-release) {{prefix}} version {{version}}");
Expand All @@ -150,6 +168,29 @@ fn execute(args: &ReleaseOpt) -> Result<i32, error::FatalError> {
.and_then(|f| f.as_bool())
.unwrap_or(!skip_publish);
let metadata = args.metadata.as_ref();
let feature_list = get_list_option(
&if args.features.is_empty() {
None
} else {
Some(args.features.clone())
},
release_config.as_ref(),
config::ENABLE_FEATURES,
);
let all_features = get_bool_option(
args.all_features,
release_config.as_ref(),
config::ENABLE_FEATURES,
);

let features = if all_features {
Features::All
} else {
match feature_list {
Some(vec) => Features::Selective(vec),
None => Features::None,
}
};

// STEP 0: Check if working directory is clean
if !git::status()? {
Expand Down Expand Up @@ -234,7 +275,7 @@ fn execute(args: &ReleaseOpt) -> Result<i32, error::FatalError> {
// STEP 3: cargo publish
if publish {
shell::log_info("Running cargo publish");
if !cargo::publish(dry_run)? {
if !cargo::publish(dry_run, features)? {
return Ok(103);
}
}
Expand Down Expand Up @@ -267,11 +308,12 @@ fn execute(args: &ReleaseOpt) -> Result<i32, error::FatalError> {
.and_then(|f| f.as_str())
.map(|f| f.to_string())
.map(|f| replace_in(&f, &replacements))
})
.or_else(|| Some(format!("{}-", crate_name)));
}).or_else(|| Some(format!("{}-", crate_name)));

if let Some(p) = tag_prefix.clone() {
replacements.insert("{{prefix}}", p.clone());
}

let current_version = version.to_string();
let tag_name = tag_prefix.as_ref().map_or_else(
|| current_version.clone(),
Expand Down Expand Up @@ -322,6 +364,16 @@ fn execute(args: &ReleaseOpt) -> Result<i32, error::FatalError> {
Ok(0)
}

/// Expresses what features flags should be used
pub enum Features {
/// None - don't use special features
None,
/// Only use selected features
Selective(Vec<String>),
/// Use all features via `all-features`
All,
}

#[derive(Debug, StructOpt)]
struct ReleaseOpt {
/// Release level: bumping major|minor|patch|rc|beta|alpha version on release or removing prerelease extensions by default
Expand Down Expand Up @@ -382,6 +434,14 @@ struct ReleaseOpt {
#[structopt(long = "no-confirm")]
/// Skip release confirmation and version preview
no_confirm: bool,

#[structopt(long = "features")]
/// Provide a set of features that need to be enabled
features: Vec<String>,

#[structopt(long = "all-features")]
/// Enable all features via `all-features`. Overrides `features`
all_features: bool,
}

#[derive(Debug, StructOpt)]
Expand Down

0 comments on commit 3239298

Please sign in to comment.