diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000000..d8c2032b49 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,2 @@ +[alias] +xtask = "run --manifest-path ./xtask/Cargo.toml --" diff --git a/.github/codecov.yaml b/.github/codecov.yaml index 2bfdd7105a..51b60cee9a 100644 --- a/.github/codecov.yaml +++ b/.github/codecov.yaml @@ -25,3 +25,4 @@ ignore: - "opentelemetry-appender-log/examples" # stress test - "stress" + - "xtask" diff --git a/Cargo.toml b/Cargo.toml index 37e52f90bc..05afd9cf68 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ members = [ "opentelemetry-otlp/tests/*", "examples/*", "stress", + "xtask", ] resolver = "2" diff --git a/scripts/RELEASING.md b/scripts/RELEASING.md index 5c95c1daa2..0c5c9ea8c8 100644 --- a/scripts/RELEASING.md +++ b/scripts/RELEASING.md @@ -14,10 +14,10 @@ releases. Perhaps once we've established a 1.0 we can consider adopting a consis A draft PR can be created, but before releasing consider the following: -* Are there any pending pull requests which should be included in the next release? - * Are they blockers? -* Are there any unresolved issues which should be resolved before the next release? Check the release [blockers milestone](https://github.com/open-telemetry/opentelemetry-rust/milestones) for every release -* Bring it up at a SIG meeting, this can usually get some of these questions answered sooner than later. It will also +- Are there any pending pull requests which should be included in the next release? + - Are they blockers? +- Are there any unresolved issues which should be resolved before the next release? Check the release [blockers milestone](https://github.com/open-telemetry/opentelemetry-rust/milestones) for every release +- Bring it up at a SIG meeting, this can usually get some of these questions answered sooner than later. It will also help establish a person to perform the release. Ideally this can be someone different each time to ensure that the process is documented. @@ -25,12 +25,13 @@ A draft PR can be created, but before releasing consider the following: 1. Create a release PR -* For each crate - * Bump appropriate version - * Update change logs to reflect release version. - * Update API/SDK version as necessary - * Attach `integration test` label to the PR to run integration tests -* If there's a large enough set of changes, consider writing a migration guide. +- For each crate + - Bump appropriate version + - Update change logs to reflect release version. + - Update API/SDK version as necessary + - Attach `integration test` label to the PR to run integration tests +- If there's a large enough set of changes, consider writing a migration guide. +- Perform a dryrun of the publish using `cargo xtask publish`. This will validate that crates are valid for publishing. 2. Merge the PR @@ -38,11 +39,11 @@ A draft PR can be created, but before releasing consider the following: * Ensure that there haven't been any interfering PRs 3. Tag the release commit based on the [tagging convention](#tagging-convention). It should usually be a bump on minor version before 1.0 -4. Create Github Release -5. [Publish](#publishing-crates) to crates.io using the version as of the release commit +4. [Publish](#publishing-crates) to crates.io using the version as of the release commit +5. Create Github Release 6. Post to [#otel-rust](https://cloud-native.slack.com/archives/C03GDP0H023) on CNCF Slack. -[Publish.sh](./publish.sh) may be used to automate steps 3 and 5. +[`cargo xtask publish --force`](../xtask) may be used to automate steps 3 and 4. ## Tagging Convention diff --git a/scripts/publish.sh b/scripts/publish.sh deleted file mode 100755 index e106fec06b..0000000000 --- a/scripts/publish.sh +++ /dev/null @@ -1,62 +0,0 @@ -#!/bin/bash - -packages=( - "opentelemetry" - "opentelemetry-http" - "opentelemetry-semantic-conventions" - "opentelemetry-jaeger-propagator" - "opentelemetry-sdk" - "opentelemetry-proto" - "opentelemetry-otlp" - "opentelemetry-stdout" - "opentelemetry-zipkin" - "opentelemetry-prometheus" - "opentelemetry-appender-log" - "opentelemetry-appender-tracing" - - # Add more packages as needed, in the right order. A package should only be published after all it's dependencies have been published -) - -# Set the current directory to one level above the scripts directory -current_dir=$(pwd)/.. -cd "$current_dir" # Change to the current directory - -# Iterate over the list of packages -for package in "${packages[@]}"; do - if [ -d "$package" ]; then - echo "==================================================" - echo "Processing package: $package" - cd "$package" # Change to the directory of package - - # Extract the name and version from Cargo.toml - name=$(grep -m1 '^name =' Cargo.toml | cut -d'"' -f2) - version=$(grep -m1 '^version =' Cargo.toml | cut -d'"' -f2) - - if [[ -n "$name" && -n "$version" ]]; then - echo "Found package $name with version $version" in cargo.toml - - # Tag the version in git - tag="${name}-${version}" - tag_message="${name} ${version} release." - # uncomment the following lines after verifying all looks good. - # git tag -a "$tag" -m "\"$tag_message\"" - # git push origin "$tag" - echo "git tag -a "$tag" -m "$tag_message"" - - # Run cargo publish - # uncomment the following line after verifying all looks good. - # cargo publish - echo "Published $name $version" - else - echo "Error: Unable to extract name or version from Cargo.toml in $package" - fi - - cd "$current_dir" # Return to the original directory - echo "Sleeping for 15 seconds before next package..." - sleep 15 # Sleep for 15 seconds to allow crates.io to index the previous package - else - echo "Skipping: $package is not a valid package directory." - fi -done - -echo "Finished publishing all packages." diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml new file mode 100644 index 0000000000..7c3b0ffdcb --- /dev/null +++ b/xtask/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "xtask" +version = "0.1.0" +edition = "2021" + +[dependencies] +anyhow = "1.0.89" +json = "0.12.4" +serde = { workspace = true, features = ["derive"] } +toml = "0.8.19" +xshell = "0.2.6" diff --git a/xtask/README.md b/xtask/README.md new file mode 100644 index 0000000000..ed0e79e059 --- /dev/null +++ b/xtask/README.md @@ -0,0 +1,5 @@ +# xtask + +This is following the scheme laid out here. + +https://github.com/matklad/cargo-xtask?tab=readme-ov-file diff --git a/xtask/src/lib.rs b/xtask/src/lib.rs new file mode 100644 index 0000000000..f490bff04e --- /dev/null +++ b/xtask/src/lib.rs @@ -0,0 +1,29 @@ +use anyhow::Result; +use std::fs; +use std::path::PathBuf; +use xshell::{cmd, Shell}; + +pub mod publish; + +#[derive(serde::Deserialize)] +pub struct Cargo { + pub package: Package, +} + +#[derive(serde::Deserialize)] +pub struct Package { + pub name: String, + pub version: String, +} + +pub fn project_root(sh: &Shell) -> Result { + Ok(cmd!(sh, "git rev-parse --show-toplevel").read()?) +} + +pub fn read_cargo_toml(p: PathBuf) -> Result { + let contents = fs::read_to_string(&p)?; + match toml::from_str(&contents) { + Ok(d) => Ok(d), + Err(e) => anyhow::bail!("Failed to read {}: {e}", p.to_string_lossy()), + } +} diff --git a/xtask/src/main.rs b/xtask/src/main.rs new file mode 100644 index 0000000000..d59f9eff13 --- /dev/null +++ b/xtask/src/main.rs @@ -0,0 +1,33 @@ +use anyhow::Result; +use xshell::Shell; + +fn main() { + if let Err(e) = try_main() { + eprintln!("error: {e:#}"); + std::process::exit(1); + } +} + +fn try_main() -> Result<()> { + let task = std::env::args().nth(1); + let sh = xshell::Shell::new()?; + if let Some(cmd) = task.as_deref() { + let f = match cmd { + "publish" => xtask::publish::publish, + _ => print_help, + }; + f(&sh)?; + } else { + print_help(&sh)?; + } + Ok(()) +} + +fn print_help(_sh: &Shell) -> Result<()> { + eprintln!( + "Tasks: + - publish +" + ); + Ok(()) +} diff --git a/xtask/src/publish.rs b/xtask/src/publish.rs new file mode 100644 index 0000000000..6452a80a40 --- /dev/null +++ b/xtask/src/publish.rs @@ -0,0 +1,83 @@ +use crate::{project_root, read_cargo_toml}; +use anyhow::Result; +use core::time; +use std::{path::Path, thread::sleep}; +use xshell::{cmd, Shell}; + +// Add more packages as needed, in the right order. +// A package should only be published after all it's dependencies have been published +const PUBLISH_PACKAGES: &[&str] = &[ + "opentelemetry", + "opentelemetry-http", + "opentelemetry-semantic-conventions", + "opentelemetry-jaeger-propagator", + "opentelemetry-sdk", + "opentelemetry-proto", + "opentelemetry-otlp", + "opentelemetry-stdout", + "opentelemetry-zipkin", + "opentelemetry-prometheus", + "opentelemetry-appender-log", + "opentelemetry-appender-tracing", +]; + +pub fn publish(sh: &Shell) -> Result<()> { + let root = project_root(sh)?; + // Is a try run unless forced. + let dry_run = std::env::args() + .filter(|e| e.eq("--force")) + .collect::>() + .is_empty(); + + for package in PUBLISH_PACKAGES.to_owned().iter() { + // Check for the Dihectory + let path = Path::new(&root).join(package); + + if !path.is_dir() { + eprintln!("Skipping: {package} is not a valid package directory."); + continue; + } + // Set the current directory to the project root. + sh.change_dir(&path); + + println!("=================================================="); + println!("Processing package: {package}"); + let cargo = match read_cargo_toml(path.join("Cargo.toml")) { + Ok(c) => c, + Err(e) => { + eprintln!("{e}"); + continue; + } + }; + + let name = cargo.package.name.as_str(); + let version = cargo.package.version.as_str(); + if name.is_empty() || version.is_empty() { + eprintln!("Empty Name or Version"); + continue; + } + let tag = format!("{name}-{version}"); + let tag_message = format!("{name} {version} release."); + let tag_command = format!("git tag -a \"{tag}\" -m \"{tag_message}\""); + let push_tag_command = format!("git push origin \"{tag}\""); + + if dry_run { + let _ = cmd!(sh, "cargo publish --dry-run").run(); + println!("DRY RUN: {tag_command}"); + println!("DRY RUN: {push_tag_command}"); + } else { + let mut pub_cmd = cmd!(sh, "cargo publish"); + pub_cmd.set_ignore_stdout(false); + let _ = pub_cmd.run(); + let _ = cmd!(sh, "{tag_command}").run(); + let _ = cmd!(sh, "{push_tag_command}").run(); + } + println!("Published {name} {version}"); + + println!("Sleeping for 15 seconds before next package..."); + // Sleep for 15 seconds to allow crates.io to index the previous package + sleep(time::Duration::from_secs(15)); + } + + Ok(()) +}