mirror of
https://github.com/esp-rs/esp-hal.git
synced 2025-09-28 21:00:59 +00:00
Add subcommand to rollover migration guides (#3606)
* add subcommand to create migration guides * make git changes
This commit is contained in:
parent
1f1e120dd2
commit
71fe3f0e46
@ -5,6 +5,8 @@ pub mod bump_version;
|
|||||||
pub mod execute_plan;
|
pub mod execute_plan;
|
||||||
#[cfg(feature = "release")]
|
#[cfg(feature = "release")]
|
||||||
pub mod plan;
|
pub mod plan;
|
||||||
|
#[cfg(feature = "release")]
|
||||||
|
pub mod post_release;
|
||||||
pub mod publish;
|
pub mod publish;
|
||||||
#[cfg(feature = "release")]
|
#[cfg(feature = "release")]
|
||||||
pub mod publish_plan;
|
pub mod publish_plan;
|
||||||
@ -21,6 +23,10 @@ pub use publish::*;
|
|||||||
pub use publish_plan::*;
|
pub use publish_plan::*;
|
||||||
pub use semver_check::*;
|
pub use semver_check::*;
|
||||||
pub use tag_releases::*;
|
pub use tag_releases::*;
|
||||||
|
#[cfg(feature = "release")]
|
||||||
|
pub use post_release::*;
|
||||||
|
|
||||||
|
pub const PLACEHOLDER: &str = "{{currentVersion}}";
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// Subcommands
|
// Subcommands
|
||||||
@ -47,6 +53,10 @@ pub enum Release {
|
|||||||
/// the release and pushes the tags.
|
/// the release and pushes the tags.
|
||||||
#[cfg(feature = "release")]
|
#[cfg(feature = "release")]
|
||||||
PublishPlan(PublishPlanArgs),
|
PublishPlan(PublishPlanArgs),
|
||||||
|
/// Rollover migrations steps post release.
|
||||||
|
/// - Create new migration guides for packages that have a migration guide
|
||||||
|
#[cfg(feature = "release")]
|
||||||
|
PostRelease,
|
||||||
/// Bump the version of the specified package(s).
|
/// Bump the version of the specified package(s).
|
||||||
///
|
///
|
||||||
/// This command will, for each specified package:
|
/// This command will, for each specified package:
|
||||||
|
@ -11,7 +11,7 @@ use serde::{Deserialize, Serialize};
|
|||||||
use strum::IntoEnumIterator;
|
use strum::IntoEnumIterator;
|
||||||
use toml_edit::{Item, TableLike, Value};
|
use toml_edit::{Item, TableLike, Value};
|
||||||
|
|
||||||
use crate::{Package, Version, cargo::CargoToml, changelog::Changelog};
|
use crate::{cargo::CargoToml, changelog::Changelog, commands::PLACEHOLDER, Package, Version};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||||
pub enum VersionBump {
|
pub enum VersionBump {
|
||||||
@ -308,8 +308,6 @@ fn finalize_placeholders(
|
|||||||
new_version: &semver::Version,
|
new_version: &semver::Version,
|
||||||
dry_run: bool,
|
dry_run: bool,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
const PLACEHOLDER: &str = "{{currentVersion}}";
|
|
||||||
|
|
||||||
let skip_paths = [bumped_package.package_path().join("target")];
|
let skip_paths = [bumped_package.package_path().join("target")];
|
||||||
|
|
||||||
fn walk_dir(dir: &Path, skip_paths: &[PathBuf], callback: &mut impl FnMut(&Path)) {
|
fn walk_dir(dir: &Path, skip_paths: &[PathBuf], callback: &mut impl FnMut(&Path)) {
|
||||||
|
@ -98,7 +98,10 @@ pub fn execute_plan(workspace: &Path, args: ApplyPlanArgs) -> Result<()> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
make_git_changes(!args.no_dry_run, &plan_source, &plan)?;
|
let branch = make_git_changes(!args.no_dry_run, "release-branch", "Finalize crate releases")?;
|
||||||
|
|
||||||
|
open_pull_request(&branch, !args.no_dry_run, &plan_source, &plan)
|
||||||
|
.with_context(|| "Failed to open pull request")?;
|
||||||
|
|
||||||
if !args.no_dry_run {
|
if !args.no_dry_run {
|
||||||
println!(
|
println!(
|
||||||
@ -109,12 +112,16 @@ pub fn execute_plan(workspace: &Path, args: ApplyPlanArgs) -> Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_git_changes(dry_run: bool, release_plan_str: &str, release_plan: &Plan) -> Result<()> {
|
pub(crate) struct Branch {
|
||||||
|
pub name: String,
|
||||||
|
pub upstream: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn make_git_changes(dry_run: bool, branch_name: &str, commit: &str) -> Result<Branch> {
|
||||||
// Find an available branch name
|
// Find an available branch name
|
||||||
let branch_name = format!(
|
let branch_name = format!(
|
||||||
"{branch_name}-{}",
|
"{branch_name}-{}",
|
||||||
jiff::Timestamp::now().strftime("%Y-%m-%d"),
|
jiff::Timestamp::now().strftime("%Y-%m-%d"),
|
||||||
branch_name = "release-branch",
|
|
||||||
);
|
);
|
||||||
|
|
||||||
let upstream = get_remote_name_for("esp-rs/esp-hal")?;
|
let upstream = get_remote_name_for("esp-rs/esp-hal")?;
|
||||||
@ -140,10 +147,11 @@ fn make_git_changes(dry_run: bool, release_plan_str: &str, release_plan: &Plan)
|
|||||||
if dry_run {
|
if dry_run {
|
||||||
println!("Dry run: would commit changes to branch: {branch_name}");
|
println!("Dry run: would commit changes to branch: {branch_name}");
|
||||||
} else {
|
} else {
|
||||||
|
Command::new("git").arg("add").arg(".").status().context("Failed to stage changes")?;
|
||||||
Command::new("git")
|
Command::new("git")
|
||||||
.arg("commit")
|
.arg("commit")
|
||||||
.arg("-am")
|
.arg("-m")
|
||||||
.arg("Finalize crates for release")
|
.arg(commit)
|
||||||
.status()
|
.status()
|
||||||
.context("Failed to commit changes")?;
|
.context("Failed to commit changes")?;
|
||||||
}
|
}
|
||||||
@ -174,6 +182,18 @@ fn make_git_changes(dry_run: bool, release_plan_str: &str, release_plan: &Plan)
|
|||||||
extract_url_from_push(&String::from_utf8_lossy(&message.stderr)) // git outputs to stderr
|
extract_url_from_push(&String::from_utf8_lossy(&message.stderr)) // git outputs to stderr
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Ok(Branch {
|
||||||
|
name: branch_name,
|
||||||
|
upstream: url,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn open_pull_request(
|
||||||
|
branch: &Branch,
|
||||||
|
dry_run: bool,
|
||||||
|
release_plan_str: &str,
|
||||||
|
release_plan: &Plan,
|
||||||
|
) -> Result<()> {
|
||||||
// Open a pull request
|
// Open a pull request
|
||||||
|
|
||||||
let packages_to_release = release_plan
|
let packages_to_release = release_plan
|
||||||
@ -216,7 +236,7 @@ cargo xrelease publish-plan --no-dry-run
|
|||||||
// TODO: don't forget to update the PR text once we have the `publish` command
|
// TODO: don't forget to update the PR text once we have the `publish` command
|
||||||
// updated.
|
// updated.
|
||||||
|
|
||||||
let pr_url_base = comparison_url(&release_plan.base, &url, &branch_name)?;
|
let pr_url_base = comparison_url(&release_plan.base, &branch.upstream, &branch.name)?;
|
||||||
|
|
||||||
// Query string options are documented at: https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/using-query-parameters-to-create-a-pull-request
|
// Query string options are documented at: https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/using-query-parameters-to-create-a-pull-request
|
||||||
let mut open_pr_url = format!(
|
let mut open_pr_url = format!(
|
||||||
@ -254,7 +274,6 @@ cargo xrelease publish-plan --no-dry-run
|
|||||||
}
|
}
|
||||||
|
|
||||||
println!("Create the release PR and follow the next steps laid out there.");
|
println!("Create the release PR and follow the next steps laid out there.");
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -268,7 +287,7 @@ fn extract_url_from_push(output: &str) -> String {
|
|||||||
.to_string()
|
.to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn comparison_url(base: &str, url: &str, branch_name: &str) -> Result<String> {
|
pub(crate) fn comparison_url(base: &str, url: &str, branch_name: &str) -> Result<String> {
|
||||||
let url = if url.starts_with("https://github.com/esp-rs/") {
|
let url = if url.starts_with("https://github.com/esp-rs/") {
|
||||||
format!("https://github.com/esp-rs/esp-hal/compare/{base}...{branch_name}")
|
format!("https://github.com/esp-rs/esp-hal/compare/{base}...{branch_name}")
|
||||||
} else {
|
} else {
|
||||||
|
81
xtask/src/commands/release/post_release.rs
Normal file
81
xtask/src/commands/release/post_release.rs
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
use std::fs;
|
||||||
|
|
||||||
|
use anyhow::{Context, Result};
|
||||||
|
use semver::Version;
|
||||||
|
use super::execute_plan::make_git_changes;
|
||||||
|
use super::PLACEHOLDER;
|
||||||
|
use super::Plan;
|
||||||
|
use crate::commands::comparison_url;
|
||||||
|
|
||||||
|
pub fn post_release(workspace: &std::path::Path) -> Result<()> {
|
||||||
|
// Read the release plan
|
||||||
|
let plan_path = workspace.join("release_plan.jsonc");
|
||||||
|
let plan_path = crate::windows_safe_path(&plan_path);
|
||||||
|
|
||||||
|
let plan = Plan::from_path(&plan_path)
|
||||||
|
.with_context(|| format!("Failed to read release plan from {}", plan_path.display()))?;
|
||||||
|
|
||||||
|
// Process packages from the plan that have migration guides
|
||||||
|
for package_plan in plan.packages.iter() {
|
||||||
|
let package = package_plan.package;
|
||||||
|
|
||||||
|
if !package.has_migration_guide(workspace) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the package's directory path
|
||||||
|
let package_path = workspace.join(package.to_string());
|
||||||
|
let cargo_toml_path = package_path.join("Cargo.toml");
|
||||||
|
|
||||||
|
// Read and parse Cargo.toml
|
||||||
|
let cargo_toml_content = fs::read_to_string(&cargo_toml_path)?;
|
||||||
|
let cargo_toml = cargo_toml_content.parse::<toml_edit::DocumentMut>()?;
|
||||||
|
|
||||||
|
// Extract version from Cargo.toml
|
||||||
|
let version_str = cargo_toml["package"]["version"].as_str().ok_or_else(|| {
|
||||||
|
anyhow::anyhow!(
|
||||||
|
"Could not find version in Cargo.toml for package {:?}",
|
||||||
|
package
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// Parse version using semver and zero out patch version
|
||||||
|
let mut version = Version::parse(version_str)?;
|
||||||
|
version.patch = 0;
|
||||||
|
|
||||||
|
// Generate migration guide filename
|
||||||
|
let migration_file_name = format!("MIGRATING-{}.md", version);
|
||||||
|
let migration_file_path = package_path.join(&migration_file_name);
|
||||||
|
|
||||||
|
// Create the migration guide file if it doesn't exist
|
||||||
|
if !migration_file_path.exists() {
|
||||||
|
// Create the title content
|
||||||
|
let title = format!("# Migration Guide from {} to {}\n", version, PLACEHOLDER);
|
||||||
|
fs::write(&migration_file_path, title)?;
|
||||||
|
log::info!("Created migration guide: {}", migration_file_path.display());
|
||||||
|
} else {
|
||||||
|
log::info!(
|
||||||
|
"Migration guide already exists: {}",
|
||||||
|
migration_file_path.display()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let branch = make_git_changes(false, "post-release-branch", "Post release rollover")?;
|
||||||
|
|
||||||
|
println!("Post-release migration guides created successfully.");
|
||||||
|
|
||||||
|
let pr_url_base = comparison_url(&plan.base, &branch.upstream, &branch.name)?;
|
||||||
|
|
||||||
|
let open_pr_url = format!(
|
||||||
|
"{pr_url_base}?quick_pull=1&title=Post+release+rollover&labels={labels}",
|
||||||
|
labels = "skip-changelog",
|
||||||
|
);
|
||||||
|
|
||||||
|
if opener::open(&open_pr_url).is_err() {
|
||||||
|
println!("Open the following URL to create a pull request:");
|
||||||
|
println!("{open_pr_url}");
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
@ -106,6 +106,26 @@ impl Package {
|
|||||||
.any(|line| line.contains("asm_experimental_arch"))
|
.any(|line| line.contains("asm_experimental_arch"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn has_migration_guide(&self, workspace: &Path) -> bool {
|
||||||
|
let package_path = workspace.join(self.to_string());
|
||||||
|
|
||||||
|
// Check if the package directory exists
|
||||||
|
let Ok(entries) = std::fs::read_dir(&package_path) else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Look for files matching the pattern "MIGRATING-*.md"
|
||||||
|
for entry in entries.flatten() {
|
||||||
|
if let Some(file_name) = entry.file_name().to_str() {
|
||||||
|
if file_name.starts_with("MIGRATING-") && file_name.ends_with(".md") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
pub fn needs_build_std(&self) -> bool {
|
pub fn needs_build_std(&self) -> bool {
|
||||||
use Package::*;
|
use Package::*;
|
||||||
|
|
||||||
|
@ -133,6 +133,8 @@ fn main() -> Result<()> {
|
|||||||
Release::ExecutePlan(args) => execute_plan(&workspace, args),
|
Release::ExecutePlan(args) => execute_plan(&workspace, args),
|
||||||
#[cfg(feature = "release")]
|
#[cfg(feature = "release")]
|
||||||
Release::PublishPlan(args) => publish_plan(&workspace, args),
|
Release::PublishPlan(args) => publish_plan(&workspace, args),
|
||||||
|
#[cfg(feature = "release")]
|
||||||
|
Release::PostRelease => post_release(&workspace),
|
||||||
},
|
},
|
||||||
|
|
||||||
Cli::Ci(args) => run_ci_checks(&workspace, args),
|
Cli::Ci(args) => run_ci_checks(&workspace, args),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user