Move release-related code to their own module (#3501)

This commit is contained in:
Dániel Buga 2025-05-20 15:15:53 +02:00 committed by GitHub
parent fe25980e53
commit 74c1f13e1f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 182 additions and 147 deletions

View File

@ -10,12 +10,11 @@ Usage: xtask <COMMAND>
Commands:
build Build-related subcommands
run Run-related subcommands
bump-version Bump the version of the specified package(s)
release Release-related subcommands
ci Perform (parts of) the checks done in CI
fmt-packages Format all packages in the workspace with rustfmt
lint-packages Lint all packages in the workspace with clippy
publish Attempt to publish the specified package
tag-releases Generate git tags for all new package releases
semver-check Semver Checks
check-changelog Check the changelog for packages
help Print this message or the help of the given subcommand(s)

View File

@ -488,8 +488,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
changelog.finalize(
Package::EspHal,
semver::Version::new(0, 2, 0),
jiff::Timestamp::now(),
&semver::Version::new(0, 2, 0),
"2025-05-08 08:36-04".parse().unwrap(),
);
let expected = "# Changelog

View File

@ -1,17 +1,16 @@
use std::path::Path;
use anyhow::{bail, Result};
use anyhow::{Result, bail};
use clap::Args;
use esp_metadata::Chip;
pub use self::{build::*, bump_version::*, check_changelog::*, run::*, semver_check::*};
use crate::{cargo::CargoAction, Package};
pub use self::{build::*, check_changelog::*, release::*, run::*};
use crate::{Package, cargo::CargoAction};
mod build;
mod bump_version;
mod check_changelog;
mod release;
mod run;
mod semver_check;
// ----------------------------------------------------------------------------
// Subcommand Arguments

View File

@ -0,0 +1,34 @@
use clap::Subcommand;
pub mod bump_version;
pub mod publish;
pub mod semver_check;
pub mod tag_releases;
pub use bump_version::*;
pub use publish::*;
pub use semver_check::*;
pub use tag_releases::*;
// ----------------------------------------------------------------------------
// Subcommands
#[derive(Debug, Subcommand)]
pub enum Release {
/// Bump the version of the specified package(s).
///
/// This command will, for each specified package:
/// - Verify that the crate can be released (e.g. it doesn't refer to git
/// dependencies)
/// - Update the version in `Cargo.toml` files
/// - Update the version in dependencies' `Cargo.toml` files
/// - Check if the changelog can be finalized
/// - Update the version in the changelog
/// - Replaces `{{currentVersion}}` markers in source files and the
/// migration guide.
BumpVersion(BumpVersionArgs),
/// Attempt to publish the specified package.
Publish(PublishArgs),
/// Generate git tags for all new package releases.
TagReleases(TagReleasesArgs),
}

View File

@ -0,0 +1,53 @@
use std::path::Path;
use anyhow::{Result, bail};
use clap::Args;
use crate::{Package, cargo::CargoArgsBuilder, windows_safe_path};
#[derive(Debug, Args)]
pub struct PublishArgs {
/// Package to publish (performs a dry-run by default).
#[arg(value_enum)]
package: Package,
/// Do not pass the `--dry-run` argument, actually try to publish.
#[arg(long)]
no_dry_run: bool,
}
pub fn publish(workspace: &Path, args: PublishArgs) -> Result<()> {
let package_name = args.package.to_string();
let package_path = windows_safe_path(&workspace.join(&package_name));
use Package::*;
let mut publish_args = match args.package {
Examples | HilTest | QaTest => {
bail!(
"Invalid package '{}' specified, this package should not be published!",
args.package
)
}
EspBacktrace | EspHal | EspHalEmbassy | EspIeee802154 | EspLpHal | EspPrintln
| EspRiscvRt | EspStorage | EspWifi | XtensaLxRt => vec!["--no-verify"],
_ => vec![],
};
if !args.no_dry_run {
publish_args.push("--dry-run");
}
let builder = CargoArgsBuilder::default()
.subcommand("publish")
.args(&publish_args);
let args = builder.build();
log::debug!("{args:#?}");
// Execute `cargo publish` command from the package root:
crate::cargo::run(&args, &package_path)?;
Ok(())
}

View File

@ -0,0 +1,75 @@
use std::{path::Path, process::Command};
use anyhow::Result;
use clap::Args;
use strum::IntoEnumIterator;
use crate::{Package, package_version};
#[derive(Debug, Args)]
pub struct TagReleasesArgs {
/// Package(s) to tag.
#[arg(long, value_enum, value_delimiter = ',', default_values_t = Package::iter())]
packages: Vec<Package>,
/// Actually try and create the tags
#[arg(long)]
no_dry_run: bool,
}
pub fn tag_releases(workspace: &Path, mut args: TagReleasesArgs) -> Result<()> {
args.packages.sort();
#[derive(serde::Serialize)]
struct DocumentationItem {
name: String,
tag: String,
}
let mut created = Vec::new();
for package in args.packages {
// If a package does not require documentation, this also means that it is not
// published (maybe this function needs a better name), so we can skip tagging
// it:
if !package.is_published() {
continue;
}
let version = package_version(workspace, package)?;
let tag = package.tag(&version);
if args.no_dry_run {
let output = Command::new("git")
.arg("tag")
.arg(&tag)
.current_dir(workspace)
.output()?;
if output.stderr.is_empty() {
log::info!("Created tag '{tag}'");
} else {
let err = String::from_utf8_lossy(&output.stderr);
let err = err.trim_start_matches("fatal: ");
log::warn!("{}", err);
}
} else {
log::info!("Would create '{tag}' if `--no-dry-run` was passed.")
}
created.push(DocumentationItem {
name: package.to_string(),
tag,
});
}
if args.no_dry_run {
log::info!("Created {} tags", created.len());
log::info!("IMPORTANT: Don't forget to push the tags to the correct remote!");
}
log::info!(
"Documentation workflow input for these packages:\r\n\r\n {:#}",
serde_json::to_string(&created)?
);
Ok(())
}

View File

@ -1,18 +1,17 @@
use std::{
fs,
path::{Path, PathBuf},
process::Command,
time::Instant,
};
use anyhow::{bail, Result};
use anyhow::{Result, bail};
use clap::{Args, Parser};
use esp_metadata::{Chip, Config};
use strum::IntoEnumIterator;
use xtask::{
Package,
cargo::{CargoAction, CargoArgsBuilder},
commands::*,
Package,
};
// ----------------------------------------------------------------------------
@ -26,19 +25,10 @@ enum Cli {
/// Run-related subcommands
#[clap(subcommand)]
Run(Run),
/// Release-related subcommands
#[clap(subcommand)]
Release(Release),
/// Bump the version of the specified package(s).
///
/// This command will, for each specified package:
/// - Verify that the crate can be released (e.g. it doesn't refer to git
/// dependencies)
/// - Update the version in `Cargo.toml` files
/// - Update the version in dependencies' `Cargo.toml` files
/// - Check if the changelog can be finalized
/// - Update the version in the changelog
/// - Replaces `{{currentVersion}}` markers in source files and the
/// migration guide.
BumpVersion(BumpVersionArgs),
/// Perform (parts of) the checks done in CI
Ci(CiArgs),
/// Format all packages in the workspace with rustfmt
@ -46,10 +36,6 @@ enum Cli {
FmtPackages(FmtPackagesArgs),
/// Lint all packages in the workspace with clippy
LintPackages(LintPackagesArgs),
/// Attempt to publish the specified package.
Publish(PublishArgs),
/// Generate git tags for all new package releases.
TagReleases(TagReleasesArgs),
/// Semver Checks
SemverCheck(SemverCheckArgs),
/// Check the changelog for packages.
@ -89,28 +75,6 @@ struct LintPackagesArgs {
fix: bool,
}
#[derive(Debug, Args)]
struct PublishArgs {
/// Package to publish (performs a dry-run by default).
#[arg(value_enum)]
package: Package,
/// Do not pass the `--dry-run` argument, actually try to publish.
#[arg(long)]
no_dry_run: bool,
}
#[derive(Debug, Args)]
struct TagReleasesArgs {
/// Package(s) to tag.
#[arg(long, value_enum, value_delimiter = ',', default_values_t = Package::iter())]
packages: Vec<Package>,
/// Actually try and create the tags
#[arg(long)]
no_dry_run: bool,
}
#[derive(Debug, Args)]
struct CheckChangelogArgs {
/// Package(s) to tag.
@ -156,12 +120,16 @@ fn main() -> Result<()> {
Run::Tests(args) => tests(&workspace, args, CargoAction::Run),
},
Cli::BumpVersion(args) => bump_version(&workspace, args),
// Release-related subcommands:
Cli::Release(release) => match release {
Release::BumpVersion(args) => bump_version(&workspace, args),
Release::TagReleases(args) => tag_releases(&workspace, args),
Release::Publish(args) => publish(&workspace, args),
},
Cli::Ci(args) => run_ci_checks(&workspace, args),
Cli::FmtPackages(args) => fmt_packages(&workspace, args),
Cli::LintPackages(args) => lint_packages(&workspace, args),
Cli::Publish(args) => publish(&workspace, args),
Cli::TagReleases(args) => tag_releases(&workspace, args),
Cli::SemverCheck(args) => semver_checks(&workspace, args),
Cli::CheckChangelog(args) => check_changelog(&workspace, &args.packages, args.normalize),
}
@ -304,42 +272,6 @@ fn lint_package(
Ok(())
}
fn publish(workspace: &Path, args: PublishArgs) -> Result<()> {
let package_name = args.package.to_string();
let package_path = xtask::windows_safe_path(&workspace.join(&package_name));
use Package::*;
let mut publish_args = match args.package {
Examples | HilTest | QaTest => {
bail!(
"Invalid package '{}' specified, this package should not be published!",
args.package
)
}
EspBacktrace | EspHal | EspHalEmbassy | EspIeee802154 | EspLpHal | EspPrintln
| EspRiscvRt | EspStorage | EspWifi | XtensaLxRt => vec!["--no-verify"],
_ => vec![],
};
if !args.no_dry_run {
publish_args.push("--dry-run");
}
let builder = CargoArgsBuilder::default()
.subcommand("publish")
.args(&publish_args);
let args = builder.build();
log::debug!("{args:#?}");
// Execute `cargo publish` command from the package root:
xtask::cargo::run(&args, &package_path)?;
Ok(())
}
fn run_ci_checks(workspace: &Path, args: CiArgs) -> Result<()> {
println!("::add-matcher::.github/rust-matchers.json");
@ -506,60 +438,3 @@ fn run_ci_checks(workspace: &Path, args: CiArgs) -> Result<()> {
Ok(())
}
fn tag_releases(workspace: &Path, mut args: TagReleasesArgs) -> Result<()> {
args.packages.sort();
#[derive(serde::Serialize)]
struct DocumentationItem {
name: String,
tag: String,
}
let mut created = Vec::new();
for package in args.packages {
// If a package does not require documentation, this also means that it is not
// published (maybe this function needs a better name), so we can skip tagging
// it:
if !package.is_published() {
continue;
}
let version = xtask::package_version(workspace, package)?;
let tag = package.tag(&version);
if args.no_dry_run {
let output = Command::new("git")
.arg("tag")
.arg(&tag)
.current_dir(workspace)
.output()?;
if output.stderr.is_empty() {
log::info!("Created tag '{tag}'");
} else {
let err = String::from_utf8_lossy(&output.stderr);
let err = err.trim_start_matches("fatal: ");
log::warn!("{}", err);
}
} else {
log::info!("Would create '{tag}' if `--no-dry-run` was passed.")
}
created.push(DocumentationItem {
name: package.to_string(),
tag,
});
}
if args.no_dry_run {
log::info!("Created {} tags", created.len());
log::info!("IMPORTANT: Don't forget to push the tags to the correct remote!");
}
log::info!(
"Documentation workflow input for these packages:\r\n\r\n {:#}",
serde_json::to_string(&created)?
);
Ok(())
}