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

improve CI for tag publishing #1053

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
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: 5 additions & 0 deletions .changes/1053.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"description": "add automatic tagging of new releases.",
"issues": [1050],
"type": "internal"
}
4 changes: 3 additions & 1 deletion .github/actions/cargo-install-upload-artifacts/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ runs:
echo "::set-output name=out-dir::${out_dir}"
echo "::set-output name=artifacts-dir::${artifacts_dir}"
shell: bash
- run: rm -rf .git
- run: mv .git .git.bak
shell: bash
- name: Build with all features
run:
Expand Down Expand Up @@ -82,3 +82,5 @@ runs:
name: ${{ steps.archive.outputs.name }}
path: ${{ steps.archive.outputs.path }}
if-no-files-found: error
- run: mv .git.bak .git
shell: bash
8 changes: 7 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
on:
pull_request:
push:
branches: [main, staging, trying]
branches: [main, staging, trying, v*.*.*]
tags:
- "v*.*.*"

Expand Down Expand Up @@ -351,7 +351,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0 # fetch tags for publish
ssh-key: "${{ secrets.COMMIT_KEY }}" # use deploy key to trigger workflow
- uses: ./.github/actions/setup-rust
- run: cargo xtask ci-job release
env:
DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
- uses: ./.github/actions/cargo-publish
with:
cargo-registry-token: ${{ secrets.CARGO_REGISTRY_TOKEN }}
Expand Down
2 changes: 1 addition & 1 deletion Cargo.lock

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

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ keywords = ["cross", "compilation", "testing", "tool"]
license = "MIT OR Apache-2.0"
name = "cross"
repository = "https://github.com/cross-rs/cross"
version = "0.2.4"
version = "0.2.5"
edition = "2021"
include = [
"src/**/*",
Expand Down
5 changes: 5 additions & 0 deletions xtask/src/ci.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
mod release;
mod target_matrix;

use crate::util::gha_output;
Expand Down Expand Up @@ -32,6 +33,7 @@ pub enum CiJob {
#[clap(long, env = "COMMIT_AUTHOR")]
author: String,
},
Release(release::Release),
}

pub fn ci(args: CiJob, metadata: CargoMetadata) -> cross::Result<()> {
Expand Down Expand Up @@ -117,6 +119,9 @@ pub fn ci(args: CiJob, metadata: CargoMetadata) -> cross::Result<()> {
CiJob::TargetMatrix { message, author } => {
target_matrix::run(message, author)?;
}
CiJob::Release(r) => {
r.run(&mut cross::shell::Verbosity::Verbose(1).into())?;
}
}
Ok(())
}
140 changes: 140 additions & 0 deletions xtask/src/ci/release.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
// TODO: More details/ensure accuracy
//!
//! # The `publish` job + `build`
//!
//! The `publish` job is triggered in five cases.
//! All of these need the event to be a push (since the `build` job is a need for `publish`,
//! and that need is gated on `if: github.event_name == 'push'`
//!
//! 1. on the default_branch, or `main` in our case
//! 2. on the `staging` branch
//! 3. on the `try` branch
//! 4. on branches matching `v*.*.*`
//! 5. on tags matching `v*.*.*`
//!
//! ## In `default_branch`/`main`
//!
//! In the case of `main`, the workflow does the following.
//!
//! 1. `build` builds and publishes images for these targets with the tag `main` and `edge`. It also assembles binaries for artifacting
//! 2. If all ok, the `publish` job triggers
//! 3. this calls `cargo xtask ci-job release` which
//! 1. inspects the package version
//! 2. if the version does not exist as a tag, create a new tag for that version and push it.
//! this tag will trigger the `CI` workflow again, but with `ref_type == "tag"`
//! if the version does exist, exit quietly.
//! 4. `publish` now calls `cargo-publish` which creates a new release with draft tag `Unreleased`, attaches binaries from step 1, and does a `cargo publish --dry-run`, this tag uses the standard github token for workflows, and should not be able to trigger any other workflows.
//!
//! ## In `staging` / `try` branch
//!
//! In `staging` or `try`, we need to make sure that nothing goes out.
//! This includes tags, releases and `cargo publish`
//!
//! 1. `build` builds (but does not publish) images for these targets with the tag `try`/`staging`. It also assembles binaries for artifacting
//! 2. If all ok, the `publish` job triggers
//! 4. this calls `cargo xtask ci-job release` which
//! 1. inspects the package version
//! 2. if the version does not exist as a tag, "dry-run" creating the tag and push it.
//! if the version does exist, exit quietly.
//! 5. `publish` now calls `cargo-publish` which does a `cargo publish --dry-run`
//!
//! ## On branches matching `v*.*.*`
//!
//! 1. `build` builds (but does not publish) images for these targets with the tag `vx.y.z` and `edge`. It also assembles binaries for artifacting
//! 2. If all ok, the `publish` job triggers
//! 3. this calls `cargo xtask ci-job release` which
//! 1. inspects the package version
//! 2. since the `ref_type == "branch"`, if the version does not exist as a tag,
//! create a new tag for that version and push it.
//! this tag will trigger the `CI` workflow again, but with `ref_type == "tag"`
//! if the version does exist, exit quietly.
//! 4. `publish` now calls `cargo-publish` which does nothing
//!
//! ## On tags matching `v*.*.*`
//!
//! In this case, we need to make sure that the created release does not trigger a workflow.
//!
//! 1. `build` builds and publishes images for these targets with the tag `vx.y.z`. It also assembles binaries for artifacting
//! 2. If all ok, the `publish` job triggers
//! 4. this calls `cargo xtask ci-job release` which
//! 1. inspects the package version
//! 2. since the `ref_type == "tag"`, the program exits quietly.
//! 5. `publish` now calls `cargo-publish` which creates a new release with tag `vx.y.z`, attaches binaries from step 1, and does a `cargo publish`, this release tag uses the standard github token for workflows, and should not be able to trigger any other workflows.
use clap::Args;
use cross::{shell::MessageInfo, CommandExt};

#[derive(Debug, Args)]
pub struct Release {
#[clap(long, default_value = "main", env = "DEFAULT_BRANCH")]
default_branch: String,
#[clap(long, hide = true, env = "GITHUB_REF_TYPE")]
pub ref_type: Option<String>,
#[clap(long, hide = true, env = "GITHUB_REF_NAME")]
ref_name: Option<String>,
}

impl Release {
pub fn run(&self, msg_info: &mut MessageInfo) -> Result<(), color_eyre::Report> {
if self.ref_type.as_deref() == Some("branch") {
self.tag(msg_info)?;
}
Ok(())
}

pub fn tag(&self, msg_info: &mut MessageInfo) -> Result<(), color_eyre::Report> {
color_eyre::eyre::ensure!(
self.ref_type.as_deref() == Some("branch"),
"tag() should only be called on a branch"
);
let current_branch = self.ref_name.as_deref().unwrap();
let version = pkgid()?.rsplit_once('#').unwrap().1.trim().to_string();
let tag = format!("v{version}");

let has_tag = std::process::Command::new("git")
.args(["tag", "--list"])
.run_and_get_stdout(msg_info)?
.lines()
.any(|it| it.trim() == tag);
if !has_tag {
let dry_run = std::env::var("CI").is_err()
|| (current_branch != self.default_branch
&& !wildmatch::WildMatch::new("v*.*.*").matches(current_branch));

eprint!("Taging!");
let mut tagging = std::process::Command::new("git");
tagging.args(["tag", &tag]);
let mut push = std::process::Command::new("git");
push.args(["push", "--tags"]);
if dry_run {
eprintln!(" (dry run)");
tagging.print(msg_info)?;
push.print(msg_info)?;
} else {
eprintln!();
tagging.run(msg_info, false)?;
push.run(msg_info, false)?;
}
}
Ok(())
}
}

#[track_caller]
fn pkgid() -> Result<String, color_eyre::Report> {
cross::cargo_command()
.arg("pkgid")
.current_dir(crate::util::get_cargo_workspace())
.run_and_get_stdout(&mut cross::shell::Verbosity::Verbose(1).into())
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn assert_pkgid_hashtag() {
let pkgid = pkgid().unwrap();
assert!(!pkgid.contains('@'));
assert!(pkgid.contains("cross"));
}
}