From c444c915fc349ee345080aa4d151f9416b5c0ab8 Mon Sep 17 00:00:00 2001 From: Lars Wirzenius Date: Sun, 28 Mar 2021 13:19:02 +0300 Subject: feat: create git tag for release --- Cargo.lock | 294 +++++++++++++++++++++++++++++++++++++++++++++++++++- Cargo.toml | 5 +- bumper.md | 27 +++++ src/bin/bumper.rs | 26 ++++- src/errors.rs | 15 +++ src/git.rs | 25 +++++ src/lib.rs | 3 +- subplot/bumper.py | 41 ++++++++ subplot/bumper.yaml | 12 +++ 9 files changed, 441 insertions(+), 7 deletions(-) create mode 100644 src/errors.rs create mode 100644 src/git.rs diff --git a/Cargo.lock b/Cargo.lock index 1b89008..778f1fa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,23 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "aho-corasick" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5" +dependencies = [ + "memchr", +] + +[[package]] +name = "ansi_term" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +dependencies = [ + "winapi", +] + [[package]] name = "anyhow" version = "1.0.39" @@ -7,23 +25,104 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81cddc5f91628367664cc7c69714ff08deee8a3efc54623011c772544d7b2767" [[package]] -name = "either" -version = "1.6.1" +name = "atty" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] [[package]] -name = "git" +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "bumper" version = "0.1.0" dependencies = [ "anyhow", + "log", "pandoc", "pandoc_ast", + "pretty_env_logger", "serde", "serde_json", + "structopt", "thiserror", ] +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clap" +version = "2.33.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" +dependencies = [ + "ansi_term", + "atty", + "bitflags", + "strsim", + "textwrap", + "unicode-width", + "vec_map", +] + +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + +[[package]] +name = "env_logger" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "heck" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cbf45460356b7deeb5e3415b5563308c0a9b057c85e12b06ad551f98d0a6ac" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "hermit-abi" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" +dependencies = [ + "libc", +] + +[[package]] +name = "humantime" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" +dependencies = [ + "quick-error", +] + [[package]] name = "itertools" version = "0.8.2" @@ -39,6 +138,33 @@ version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8916b1f6ca17130ec6568feccee27c156ad12037880833a3b842a823236502e7" + +[[package]] +name = "log" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "memchr" +version = "2.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" + [[package]] name = "pandoc" version = "0.8.4" @@ -59,6 +185,40 @@ dependencies = [ "serde_json", ] +[[package]] +name = "pretty_env_logger" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "926d36b9553851b8b0005f1275891b392ee4d2d833852c417ed025477350fb9d" +dependencies = [ + "env_logger", + "log", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + [[package]] name = "proc-macro2" version = "1.0.24" @@ -68,6 +228,12 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + [[package]] name = "quote" version = "1.0.9" @@ -77,6 +243,23 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "regex" +version = "1.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "957056ecddbeba1b26965114e191d2e8589ce74db242b6ea25fc4062427a5c19" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5f089152e60f62d28b835fbff2cd2e8dc0baf1ac13343bef92ab7eed84548" + [[package]] name = "ryu" version = "1.0.5" @@ -111,6 +294,36 @@ dependencies = [ "serde", ] +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + +[[package]] +name = "structopt" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5277acd7ee46e63e5168a80734c9f6ee81b1367a7d8772a2d765df2a3705d28c" +dependencies = [ + "clap", + "lazy_static", + "structopt-derive", +] + +[[package]] +name = "structopt-derive" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ba9cdfda491b814720b6b06e0cac513d922fc407582032e8706e9f137976f90" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "syn" version = "1.0.64" @@ -122,6 +335,24 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "termcolor" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + [[package]] name = "thiserror" version = "1.0.24" @@ -142,8 +373,63 @@ dependencies = [ "syn", ] +[[package]] +name = "unicode-segmentation" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796" + +[[package]] +name = "unicode-width" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" + [[package]] name = "unicode-xid" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" + +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + +[[package]] +name = "version_check" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml index 098f345..93a77d7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "git" +name = "bumper" version = "0.1.0" authors = ["Lars Wirzenius "] edition = "2018" @@ -8,8 +8,11 @@ edition = "2018" [dependencies] anyhow = "1" +log = "0.4" pandoc = "0.8.4" pandoc_ast = "0.8.0" +pretty_env_logger = "0.4" serde = "1.0.124" serde_json = "1.0.64" +structopt = "0.3" thiserror = "1" diff --git a/bumper.md b/bumper.md index fcf4005..a1bb929 100644 --- a/bumper.md +++ b/bumper.md @@ -81,6 +81,31 @@ then only files random.txt exist Some random text file Bumper knows nothing about. ~~~ +## Creates a release tag + +This scenario verifies that Bumper creates a git tag to mark a +release. For this to work, Bumper needs to recognize the type of +project. We use a Rust project for simplicity. + +~~~scenario +given an installed Bumper +given file foo/Cargo.toml from Cargo.toml +given all files in foo are committed to git +given the HEAD commit in foo is COMMIT +when I run, in foo, bumper 1.2 +then in foo, git tag v1.2 is a signed tag pointing at +~~~ + +~~~{#Cargo.toml .file .ini} +[package] +name = "foo" +version = "0.1.0" +authors = ["J. Random Hacker "] +edition = "2018" + +[dependencies] +~~~ + --- @@ -96,4 +121,6 @@ functions: - subplot/bumper.py - subplot/vendored/files.py - subplot/vendored/runcmd.py +classes: +- ini ... diff --git a/src/bin/bumper.rs b/src/bin/bumper.rs index 081ca86..1a2e567 100644 --- a/src/bin/bumper.rs +++ b/src/bin/bumper.rs @@ -1,3 +1,27 @@ +use bumper::errors::BumperError; +use bumper::git; +use log::{error, info}; +use std::process::exit; +use structopt::StructOpt; + fn main() { - std::process::exit(1); + if let Err(err) = bumper() { + error!("{}", err); + exit(1); + } +} + +fn bumper() -> Result<(), BumperError> { + pretty_env_logger::init_custom_env("BUMPER_LOG"); + info!("Bumper starts"); + let opt = Opt::from_args(); + git::tag(&opt.version)?; + info!("Bumper ends OK"); + Ok(()) +} + +#[derive(Debug, StructOpt)] +struct Opt { + #[structopt(help = "version number of new release")] + version: String, } diff --git a/src/errors.rs b/src/errors.rs new file mode 100644 index 0000000..607eecb --- /dev/null +++ b/src/errors.rs @@ -0,0 +1,15 @@ +#[derive(Debug, thiserror::Error)] +pub enum BumperError { + #[error("Failed to run git: {0}")] + GitInvoke(#[source] std::io::Error), + + #[error("Command 'git tag' failed: {0}")] + GitTag(String), +} + +impl BumperError { + pub fn git_tag(stderr: &[u8]) -> Self { + let stderr = String::from_utf8_lossy(stderr).into_owned(); + Self::GitTag(stderr) + } +} diff --git a/src/git.rs b/src/git.rs new file mode 100644 index 0000000..76f6da1 --- /dev/null +++ b/src/git.rs @@ -0,0 +1,25 @@ +use crate::errors::BumperError; +use log::{debug, info}; +use std::process::Command; + +pub fn tag(version: &str) -> Result<(), BumperError> { + let tag_name = format!("v{}", version); + info!("Create git tag {}", tag_name); + let output = Command::new("git") + .arg("tag") + .arg("-am") + .arg(format!("release version {}", version)) + .arg(format!("v{}", version)) + .output(); + let output = match output { + Ok(output) => output, + Err(err) => { + return Err(BumperError::GitInvoke(err)); + } + }; + debug!("git exit code was {:?}", output.status.code()); + if !output.status.success() { + return Err(BumperError::git_tag(&output.stderr)); + } + Ok(()) +} diff --git a/src/lib.rs b/src/lib.rs index 8b13789..7d4548a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1 +1,2 @@ - +pub mod errors; +pub mod git; diff --git a/subplot/bumper.py b/subplot/bumper.py index 66d2564..0e6a0ef 100644 --- a/subplot/bumper.py +++ b/subplot/bumper.py @@ -1,4 +1,6 @@ +import logging import os +import shlex def install_bumper(ctx): @@ -7,3 +9,42 @@ def install_bumper(ctx): # Add the directory with built Rust binaries to the path. runcmd_prepend_to_path(ctx, dirname=os.path.join(srcdir, "target", "debug")) + + +def git_init_and_commit_everything(ctx, dirname=None): + runcmd_run = globals()["runcmd_run"] + runcmd_run(ctx, ["git", "init", dirname]) + runcmd_run(ctx, ["git", "add", "."], cwd=dirname) + runcmd_run(ctx, ["git", "commit", "-minitial"], cwd=dirname) + + +def remember_HEAD(ctx, dirname=None, varname=None): + runcmd_run = globals()["runcmd_run"] + runcmd_get_stdout = globals()["runcmd_get_stdout"] + runcmd_run(ctx, ["git", "rev-parse", "HEAD"], cwd=dirname) + ctx[varname] = runcmd_get_stdout(ctx).strip() + + +def run_command_in_directory(ctx, dirname=None, argv0=None, args=None): + runcmd_run = globals()["runcmd_run"] + runcmd_exit_code_is_zero = globals()["runcmd_exit_code_is_zero"] + + argv = [shlex.quote(argv0)] + shlex.split(args) + runcmd_run(ctx, argv, cwd=dirname) + runcmd_exit_code_is_zero(ctx) + + +def git_tag_points_at(ctx, dirname=None, tag=None, varname=None): + runcmd_run = globals()["runcmd_run"] + runcmd_get_stdout = globals()["runcmd_get_stdout"] + runcmd_exit_code_is_zero = globals()["runcmd_exit_code_is_zero"] + + runcmd_run(ctx, ["git", "show", "--raw", tag], cwd=dirname) + runcmd_exit_code_is_zero(ctx) + + output = runcmd_get_stdout(ctx) + commit = ctx[varname] + logging.debug(f"expecting tag {tag} to point at {commit}") + logging.debug(f"tag: {output!r}") + assert output.startswith(f"tag {tag}\n") + assert f"\ncommit {commit}" in output diff --git a/subplot/bumper.yaml b/subplot/bumper.yaml index f74c28e..7b95dac 100644 --- a/subplot/bumper.yaml +++ b/subplot/bumper.yaml @@ -1,2 +1,14 @@ - given: "an installed Bumper" function: install_bumper + +- given: "all files in {dirname} are committed to git" + function: git_init_and_commit_everything + +- given: "the HEAD commit in {dirname} is {varname}" + function: remember_HEAD + +- when: "I run, in {dirname}, {argv0}{args:text}" + function: run_command_in_directory + +- then: "in {dirname}, git tag {tag} is a signed tag pointing at <{varname}>" + function: git_tag_points_at -- cgit v1.2.1