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 Dec 17, 2018
1 parent 31d55fa commit ec2a3a3
Show file tree
Hide file tree
Showing 3 changed files with 91 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
96 changes: 77 additions & 19 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,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 @@ -50,6 +49,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 @@ -118,20 +136,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 @@ -148,6 +166,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 @@ -224,7 +265,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 @@ -258,8 +299,7 @@ fn execute(args: &ReleaseOpt) -> Result<i32, error::FatalError> {
config::get_release_config(release_config.as_ref(), config::TAG_PREFIX)
.and_then(|f| f.as_str())
.map(|f| f.to_string())
})
.or_else(|| rel_path.as_ref().map(|t| format!("{}-", t)));
}).or_else(|| rel_path.as_ref().map(|t| format!("{}-", t)));

let current_version = version.to_string();
let tag_name = tag_prefix.as_ref().map_or_else(
Expand Down Expand Up @@ -313,6 +353,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 @@ -373,6 +423,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 ec2a3a3

Please sign in to comment.