Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Check all relevant pre-release versions, not just the latest. #346

Merged
merged 8 commits into from
Jan 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ dialoguer = "0.10.3"
toml = "0.5.10"
serde_json = { version = "1.0.91", features = ["preserve_order"] }
git2 = { version = "0.15.0", default-features = false }
semver = "1.0.16"
execute = "0.2.11"
platform-dirs = "0.3.0"
git-conventional = "0.12.1"
Expand Down
20 changes: 12 additions & 8 deletions src/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,10 @@ fn replace_variables(
let release = if state.releases.is_empty() {
command = command.replace(
&var_name,
&get_version(package.clone())?.latest_version().to_string(),
&get_version(package)?
.into_latest()
.ok_or(StepError::NoCurrentVersion)?
.to_string(),
);
continue;
} else {
Expand Down Expand Up @@ -126,9 +129,8 @@ mod test_run_command {
#[cfg(test)]
mod test_replace_variables {
use crate::issues::Issue;
use crate::releases::{Package, Release};
use crate::releases::{semver::Version, Package, Release};
use crate::state;
use semver::Version;
use std::path::PathBuf;

use super::*;
Expand Down Expand Up @@ -168,9 +170,10 @@ mod test_replace_variables {
command,
format!(
"blah {} {}",
get_version(state.packages[0].clone())
get_version(&state.packages[0])
.unwrap()
.latest_version(),
.into_latest()
.unwrap(),
expected_branch_name
)
);
Expand All @@ -189,9 +192,10 @@ mod test_replace_variables {
command,
format!(
"blah {} other blah",
get_version(state.packages[0].clone())
get_version(&state.packages[0])
.unwrap()
.latest_version(),
.into_latest()
.unwrap(),
)
);
}
Expand All @@ -202,7 +206,7 @@ mod test_replace_variables {
let mut variables = HashMap::new();
variables.insert("$$".to_string(), Variable::Version);
let mut state = State::new(None, None, packages());
let version = Version::new(1, 2, 3);
let version = Version::new(1, 2, 3, None);
state.releases.push(state::Release::Prepared(Release {
version: version.clone(),
changelog: String::new(),
Expand Down
20 changes: 8 additions & 12 deletions src/git.rs
Original file line number Diff line number Diff line change
Expand Up @@ -246,18 +246,14 @@ mod test_branch_name_from_issue {
pub(crate) fn get_commit_messages_after_last_stable_version(
package_name: &Option<String>,
) -> Result<Vec<String>, StepError> {
let target_version = get_current_versions_from_tag(package_name.as_deref())?
.map(|current_version| current_version.stable);
let reference = match &target_version {
Some(version) => {
let tag = tag_name(version, package_name);
debug!("Processing all commits since tag {tag}");
Some(format!("refs/tags/{tag}"))
}
None => {
warn!("No stable version tag found, processing all commits.");
None
}
let target_version = get_current_versions_from_tag(package_name.as_deref())?.stable;
let reference = if let Some(version) = target_version {
let tag = tag_name(&version.into(), package_name);
debug!("Processing all commits since tag {tag}");
Some(format!("refs/tags/{tag}"))
} else {
warn!("No stable version tag found, processing all commits.");
None
};
let repo = git_repository::open(".").map_err(|_| StepError::NotAGitRepo)?;
let tag_ref = reference
Expand Down
8 changes: 4 additions & 4 deletions src/releases/conventional_commits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use git_conventional::{Commit, Type};
use log::debug;

use crate::git::{add_files, get_commit_messages_after_last_stable_version};
use crate::releases::semver::PackageVersion;
use crate::releases::semver::{Label, PackageVersion};
use crate::releases::Package;
use crate::step::StepError;
use crate::{state, step, RunType};
Expand Down Expand Up @@ -367,7 +367,7 @@ pub(crate) fn update_project_from_conventional_commits(
fn prepare_release_for_package(
package: Package,
consider_scopes: bool,
prerelease_label: Option<&String>,
prerelease_label: Option<&Label>,
dry_run_stdout: Option<&mut Box<dyn Write>>,
) -> Result<Option<Release>, StepError> {
let ConventionalCommits {
Expand All @@ -392,12 +392,12 @@ fn prepare_release_for_package(
};
let PackageVersion { package, version } =
bump_version(&rule, dry_run_stdout.is_some(), package)?;
let new_version_string = version.latest().to_string();
let new_version_string = version.to_string();
let new_changes =
new_changelog_lines(&new_version_string, &fixes, &features, &breaking_changes);

let release = Release {
version: version.into_latest(),
version,
changelog: new_changes.join("\n"),
package_name: package.name,
};
Expand Down
34 changes: 7 additions & 27 deletions src/releases/git.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
use std::env::current_dir;
use std::io::Write;
use std::str::FromStr;

use git_repository::object::Kind;
use git_repository::open;
use git_repository::refs::transaction::PreviousValue;
use semver::Version;

use crate::releases::semver::Version;
use crate::releases::{CurrentVersions, Release};
use crate::step::StepError;

Expand Down Expand Up @@ -46,20 +47,9 @@ pub(crate) fn release(
Ok(())
}

fn replace_version_if_newer(current: &mut Option<Version>, new: Version) {
match current.as_ref() {
None => *current = Some(new),
Some(version) => {
if &new > version {
*current = Some(new);
}
}
}
}

pub(crate) fn get_current_versions_from_tag(
prefix: Option<&str>,
) -> Result<Option<CurrentVersions>, StepError> {
) -> Result<CurrentVersions, StepError> {
let repo = open(current_dir()?).map_err(|_e| StepError::NotAGitRepo)?;
let references = repo.references().map_err(|_e| StepError::NotAGitRepo)?;
let tags = references
Expand All @@ -74,8 +64,7 @@ pub(crate) fn get_current_versions_from_tag(
.replace("refs/tags/", "")
})
});
let mut stable = None;
let mut prerelease = None;
let mut current_versions = CurrentVersions::default();
let pattern = prefix
.as_ref()
.map_or_else(|| String::from("v"), |prefix| format!("{prefix}/v"));
Expand All @@ -84,19 +73,10 @@ pub(crate) fn get_current_versions_from_tag(
continue;
}
let version_string = tag.replace(&pattern, "");
if let Ok(version) = Version::parse(version_string.as_str()) {
if version.pre.is_empty() {
replace_version_if_newer(&mut stable, version);
} else {
replace_version_if_newer(&mut prerelease, version);
}
if let Ok(version) = Version::from_str(version_string.as_str()) {
current_versions.update_version(version);
}
}

// Don't consider prereleases older than the stable version.
if prerelease < stable {
prerelease = None;
}

Ok(stable.map(|stable| CurrentVersions { stable, prerelease }))
Ok(current_versions)
}
2 changes: 1 addition & 1 deletion src/releases/github.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ pub(crate) fn release(
tag_name: &tag_name,
name: &name,
body: changelog,
prerelease: !release.version.pre.is_empty(),
prerelease: release.version.is_prerelease(),
};

if let Some(stdout) = dry_run_stdout {
Expand Down
11 changes: 6 additions & 5 deletions src/releases/go.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use crate::releases::semver::Version;
use crate::step::StepError;
use semver::Version;

pub(crate) fn set_version(go_mod: String, new_version: &Version) -> Result<String, StepError> {
if new_version.major == 0 || new_version.major == 1 {
let new_major = new_version.stable_component().major;
if new_major == 0 || new_major == 1 {
// These major versions aren't recorded in go.mod
return Ok(go_mod);
}
Expand All @@ -29,18 +30,18 @@ pub(crate) fn set_version(go_mod: String, new_version: &Version) -> Result<Strin
None
};
if let Some(existing_version) = existing_version {
if existing_version == new_version.major {
if existing_version == new_version.stable_component().major {
// Major version has not changed—keep existing content
return Ok(go_mod);
}
let index = parts.len() - 1;
let new_version_string = format!("v{}", new_version.major);
let new_version_string = format!("v{new_major}");
parts[index] = new_version_string.as_str();
let new_module_line = format!("module {}", parts.join("/"));
Ok(go_mod.replace(module_line, &new_module_line))
} else {
// No existing version found—add new line
let new_module_line = format!("module {module}/v{}", new_version.major);
let new_module_line = format!("module {module}/v{new_major}");
Ok(go_mod.replace(module_line, &new_module_line))
}
}
81 changes: 68 additions & 13 deletions src/releases/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use ::semver::Version;
use std::collections::BTreeMap;

pub(crate) use conventional_commits::update_project_from_conventional_commits as prepare_release;

use crate::releases::semver::{Label, PreVersion, Prerelease, StableVersion, Version};
use crate::state::Release::{Bumped, Prepared};
use crate::step::StepError;
use crate::RunType;
Expand All @@ -19,7 +21,7 @@ mod go;
mod package;
mod package_json;
mod pyproject;
mod semver;
pub(crate) mod semver;

#[derive(Clone, Debug)]
pub(crate) struct Release {
Expand All @@ -28,31 +30,84 @@ pub(crate) struct Release {
pub(crate) package_name: Option<String>,
}

#[derive(Clone, Debug, Eq, PartialEq)]
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub(crate) struct CurrentVersions {
pub(crate) stable: Version,
pub(crate) prerelease: Option<Version>,
pub(crate) stable: Option<StableVersion>,
pub(crate) prereleases: Prereleases,
}

type Prereleases = BTreeMap<StableVersion, BTreeMap<Label, Prerelease>>;

impl CurrentVersions {
pub(crate) fn latest(&self) -> &Version {
self.prerelease.as_ref().unwrap_or(&self.stable)
pub(crate) fn into_latest(mut self) -> Option<Version> {
self.prereleases
.pop_last()
.map(|(stable_component, mut pres)| {
let pre_component = pres
.pop_last()
.expect("This map is never allowed to be empty")
.1;
Version::Pre(PreVersion {
stable_component,
pre_component,
})
})
.or_else(|| self.stable.map(Version::Stable))
}

pub(crate) fn into_latest(self) -> Version {
self.prerelease.unwrap_or(self.stable)
/// Replace or insert the version in the correct location if it's newer than the current
/// equivalent version. If the version is a newer stable version, it will update `stable`.
/// If the version is a newer prerelease, it will overwrite the prerelease with
/// the same stable component and label.
pub(crate) fn update_version(&mut self, version: Version) {
match version {
Version::Stable(new) => {
if let Some(existing) = &self.stable {
if existing >= &new {
return;
}
}
self.stable = Some(new);
}
Version::Pre(PreVersion {
stable_component,
pre_component,
}) => {
let recorded_pre = self
.prereleases
.get(&stable_component)
.and_then(|pres| pres.get(&pre_component.label));
if let Some(recorded_pre) = recorded_pre {
if recorded_pre >= &pre_component {
return;
}
}
self.prereleases
.entry(stable_component)
.or_default()
.insert(pre_component.label.clone(), pre_component);
}
}
}
}

impl Default for CurrentVersions {
fn default() -> Self {
impl From<StableVersion> for CurrentVersions {
fn from(version: StableVersion) -> Self {
Self {
stable: Version::new(0, 0, 0),
prerelease: None,
stable: Some(version),
prereleases: BTreeMap::new(),
}
}
}

impl From<Version> for CurrentVersions {
fn from(version: Version) -> Self {
let mut new = Self::default();
new.update_version(version);
new
}
}

/// Create a release for the package.
///
/// If GitHub config is present, this creates a GitHub release. Otherwise, it tags the Git repo.
Expand Down
4 changes: 2 additions & 2 deletions src/releases/package.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ use std::path::{Path, PathBuf};

use itertools::Itertools;
use log::trace;
use semver::Version;

use crate::config::Package as PackageConfig;
use crate::releases::semver::Version;
use crate::releases::{cargo, get_current_versions_from_tag, go, package_json, pyproject};
use crate::step::StepError;
use crate::step::StepError::InvalidCargoToml;
Expand Down Expand Up @@ -141,8 +141,8 @@ impl PackageFormat {
.map_err(|_| StepError::InvalidPackageJson(path.into())),
PackageFormat::Go => get_current_versions_from_tag(name).map(|current_versions| {
current_versions
.unwrap_or_default()
.into_latest()
.unwrap_or_default()
.to_string()
}),
}
Expand Down
Loading