mirror of
https://github.com/italicsjenga/agb.git
synced 2025-01-11 09:31:34 +11:00
Merge pull request #315 from gwilymk/release-script-in-rust
Release script in rust
This commit is contained in:
commit
416f238062
|
@ -20,7 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
- Fixed the fast magnitude function in agb_fixnum. This is also used in fast_normalise. Previously only worked for positive (x, y).
|
||||
- Fixed formatting of fixed point numbers in the range (-1, 0), which previously appeared positive.
|
||||
|
||||
## Changed
|
||||
### Changed
|
||||
- `testing` is now a default feature, so you no longer need to add a separate `dev-dependencies` line for `agb` in order to enable unit tests for your project.
|
||||
|
||||
## [0.11.1] - 2022/08/02
|
||||
|
|
5
justfile
5
justfile
|
@ -68,8 +68,11 @@ update-linker-scripts:
|
|||
|
||||
publish: (_run-tool "publish")
|
||||
|
||||
release +args: (_run-tool "release" args)
|
||||
|
||||
_run-tool +tool:
|
||||
cargo run --manifest-path "{{justfile_directory() + "/tools/Cargo.toml"}}" -- {{tool}}
|
||||
(cd tools && cargo build)
|
||||
"$CARGO_TARGET_DIR/debug/tools" {{tool}}
|
||||
|
||||
_build-rom folder name:
|
||||
#!/usr/bin/env bash
|
||||
|
|
96
release.sh
96
release.sh
|
@ -1,96 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# Fail if any command fails
|
||||
set -e
|
||||
set -x
|
||||
|
||||
VERSION=$1
|
||||
NO_COMMIT=$2
|
||||
|
||||
# Sanity check that we actually have a version
|
||||
if [ "$VERSION" = "" ]; then
|
||||
echo "Usage $0 <version> [--no-commit]"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check the format of version
|
||||
if echo "$VERSION" | grep -q -Ev "^[0-9]+\.[0-9]+\.[0-9]+$"; then
|
||||
echo "Version must be of the form x.y.z, got $VERSION"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if no commit option is valid
|
||||
if [ ! "$NO_COMMIT" = "" ] && [ ! "$NO_COMMIT" = "--no-commit" ]; then
|
||||
echo "Must pass either no last argument or --no-commit"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
function maybe_git() {
|
||||
if [ "$NO_COMMIT" = "--no-commit" ]; then
|
||||
echo "Would run: git $*"
|
||||
else
|
||||
git "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
# Check that no out-standing changes in git
|
||||
if [ "$NO_COMMIT" = "" ] && [ -n "$(git status --porcelain)" ]; then
|
||||
echo "Uncommitted changes, please commit first"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check that we are in the master branch, but only if actually committing
|
||||
if [ ! "$NO_COMMIT" = "--no-commit" ] && [ "$(git symbolic-ref --short HEAD)" != "master" ]; then
|
||||
echo "You must be in the master branch before releasing"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
TAGNAME="v$VERSION"
|
||||
|
||||
for PROJECT_TOML_FILE in agb/Cargo.toml agb-*/Cargo.toml; do
|
||||
DIRECTORY=$(dirname "$PROJECT_TOML_FILE")
|
||||
|
||||
# Update the version in Cargo.toml
|
||||
sed -i -e "s/^version = \".*\"/version = \"$VERSION\"/" "$DIRECTORY/Cargo.toml"
|
||||
|
||||
# Also update the lock file
|
||||
(cd "$DIRECTORY" && cargo update)
|
||||
|
||||
if [ "$DIRECTORY" = "agb" ]; then
|
||||
# also update the agb version in the template and the examples
|
||||
sed -i -e "s/^agb = \".*\"/agb = \"$VERSION\"/" template/Cargo.toml
|
||||
|
||||
for EXAMPLE_TOML_FILE in examples/*/Cargo.toml book/games/*/Cargo.toml template/Cargo.toml; do
|
||||
EXAMPLE_DIR=$(dirname "$EXAMPLE_TOML_FILE")
|
||||
sed -E -i -e "/agb =/ s/version = \"[^\"]+\"/version = \"$VERSION\"/" "$EXAMPLE_DIR/Cargo.toml"
|
||||
done
|
||||
for EXAMPLE_TOML_FILE in examples/*/Cargo.toml book/games/*/Cargo.toml; do
|
||||
EXAMPLE_DIR=$(dirname "$EXAMPLE_TOML_FILE")
|
||||
(cd "$EXAMPLE_DIR" && cargo update)
|
||||
done
|
||||
else
|
||||
PROJECT_NAME_WITH_UNDERSCORES=$(echo -n "$DIRECTORY" | tr - _)
|
||||
|
||||
for CARGO_TOML_FILE in agb-*/Cargo.toml agb/Cargo.toml examples/*/Cargo.toml book/games/*/Cargo.toml; do
|
||||
sed -i -E -e "s/($PROJECT_NAME_WITH_UNDERSCORES = .*version = \")[^\"]+(\".*)/\1$VERSION\2/" "$CARGO_TOML_FILE"
|
||||
(cd "$(dirname "$CARGO_TOML_FILE")" && cargo generate-lockfile)
|
||||
done
|
||||
fi
|
||||
done
|
||||
|
||||
# Sanity check to make sure the build works
|
||||
just ci
|
||||
|
||||
for EXAMPLE_TOML_FILE in examples/*/Cargo.toml book/games/*/Cargo.toml; do
|
||||
EXAMPLE_DIR=$(dirname "$EXAMPLE_TOML_FILE")
|
||||
(cd "$EXAMPLE_DIR" && cargo check --release)
|
||||
done
|
||||
|
||||
# Commit the Cargo.toml changes
|
||||
maybe_git commit -am "Release v$VERSION"
|
||||
|
||||
# Tag the version
|
||||
maybe_git tag -a "$TAGNAME" -m "v$VERSION"
|
||||
|
||||
echo "Done! Push with"
|
||||
echo "git push --atomic origin master $TAGNAME"
|
212
tools/Cargo.lock
generated
212
tools/Cargo.lock
generated
|
@ -2,6 +2,15 @@
|
|||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "android_system_properties"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
|
@ -25,12 +34,39 @@ version = "1.3.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfd4d1b31faaa3a89d7934dbded3111da0d2ef28e3ebccdb4f0179f5929d1ef1"
|
||||
dependencies = [
|
||||
"iana-time-zone",
|
||||
"js-sys",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
"time",
|
||||
"wasm-bindgen",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.0.7"
|
||||
|
@ -63,12 +99,24 @@ dependencies = [
|
|||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
|
||||
|
||||
[[package]]
|
||||
name = "glob"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.12.3"
|
||||
|
@ -84,6 +132,19 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone"
|
||||
version = "0.1.50"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd911b35d940d2bd0bea0f9100068e5b97b51a1cbe13d13382f132e0365257a0"
|
||||
dependencies = [
|
||||
"android_system_properties",
|
||||
"core-foundation-sys",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.9.1"
|
||||
|
@ -103,30 +164,102 @@ dependencies = [
|
|||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.60"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.134"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "329c933548736bc49fd575ee68c89e8be4d260064184389a5b77517cddd99ffb"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1"
|
||||
|
||||
[[package]]
|
||||
name = "os_str_bytes"
|
||||
version = "6.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.46"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94e2ef8dbfc347b10c094890f778ee2e36ca9bb4262e86dc99cd217e35f3470b"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.101"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e90cde112c4b9690b8cbe810cba9ddd8bc1d7472e2cae317b69e9438c1cba7d2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.1.3"
|
||||
|
@ -136,6 +269,17 @@ dependencies = [
|
|||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.1.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"wasi",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.14.4"
|
||||
|
@ -151,10 +295,78 @@ dependencies = [
|
|||
name = "tools"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"clap",
|
||||
"glob",
|
||||
"toml_edit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.10.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"wasm-bindgen-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"log",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
|
|
|
@ -8,3 +8,5 @@ edition = "2021"
|
|||
[dependencies]
|
||||
clap = "4"
|
||||
toml_edit = "0.14"
|
||||
glob = "0.3"
|
||||
chrono = "0.4"
|
||||
|
|
|
@ -2,19 +2,35 @@
|
|||
use clap::Command;
|
||||
|
||||
mod publish;
|
||||
mod release;
|
||||
mod utils;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
PublishError(publish::Error),
|
||||
ReleaseError(release::Error),
|
||||
}
|
||||
|
||||
fn cli() -> Command {
|
||||
Command::new("Agb tools")
|
||||
.subcommand_required(true)
|
||||
.arg_required_else_help(true)
|
||||
.subcommand(publish::command())
|
||||
.subcommand(release::command())
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let matches = cli().get_matches();
|
||||
|
||||
let result = match matches.subcommand() {
|
||||
Some(("publish", arg_matches)) => publish::publish(arg_matches),
|
||||
Some(("publish", arg_matches)) => {
|
||||
publish::publish(arg_matches).map_err(Error::PublishError)
|
||||
}
|
||||
|
||||
Some(("release", arg_matches)) => {
|
||||
release::release(arg_matches).map_err(Error::ReleaseError)
|
||||
}
|
||||
|
||||
_ => unreachable!("Exhausted list of subcommands and subcommand_required prevents `None`"),
|
||||
};
|
||||
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
use clap::{Arg, ArgAction, ArgMatches};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::path::Path;
|
||||
use std::process::Command;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
use std::{env, thread};
|
||||
use toml_edit::Document;
|
||||
|
||||
use crate::utils::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
FindRootDirectory,
|
||||
|
@ -31,7 +33,7 @@ pub fn command() -> clap::Command {
|
|||
pub fn publish(matches: &ArgMatches) -> Result<(), Error> {
|
||||
let dry_run = matches.get_one::<bool>("Dry run").expect("defined by clap");
|
||||
|
||||
let root_directory = find_agb_root_directory()?;
|
||||
let root_directory = find_agb_root_directory().map_err(|_| Error::FindRootDirectory)?;
|
||||
|
||||
let mut fully_published_crates: HashSet<String> = HashSet::new();
|
||||
let mut published_crates: HashSet<String> = HashSet::new();
|
||||
|
@ -60,11 +62,12 @@ pub fn publish(matches: &ArgMatches) -> Result<(), Error> {
|
|||
if *dry_run {
|
||||
println!("Would execute cargo publish for {publishable_crate}");
|
||||
} else {
|
||||
Command::new("cargo")
|
||||
assert!(Command::new("cargo")
|
||||
.arg("publish")
|
||||
.current_dir(&root_directory.join(publishable_crate))
|
||||
.spawn()
|
||||
.map_err(|_| Error::PublishCrate)?;
|
||||
.status()
|
||||
.map_err(|_| Error::PublishCrate)?
|
||||
.success());
|
||||
}
|
||||
|
||||
published_crates.insert(publishable_crate.to_string());
|
||||
|
@ -86,19 +89,6 @@ pub fn publish(matches: &ArgMatches) -> Result<(), Error> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn find_agb_root_directory() -> Result<PathBuf, Error> {
|
||||
let mut current_path = env::current_dir().map_err(|_| Error::FindRootDirectory)?;
|
||||
|
||||
while !current_path.clone().join("justfile").exists() {
|
||||
current_path = current_path
|
||||
.parent()
|
||||
.ok_or(Error::FindRootDirectory)?
|
||||
.to_owned();
|
||||
}
|
||||
|
||||
Ok(current_path)
|
||||
}
|
||||
|
||||
fn check_if_released(crate_to_publish: &str, expected_version: &str) -> Result<bool, Error> {
|
||||
let url_to_poll = &get_url_to_poll(crate_to_publish);
|
||||
|
||||
|
@ -220,16 +210,9 @@ mod test {
|
|||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_find_root_directory() -> Result<(), Error> {
|
||||
assert_ne!(find_agb_root_directory()?.to_string_lossy(), "");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_read_version() -> Result<(), Error> {
|
||||
let root_directory = find_agb_root_directory()?;
|
||||
let root_directory = crate::utils::find_agb_root_directory().unwrap();
|
||||
let my_version = read_cargo_toml_version(&root_directory.join("tools"))?;
|
||||
|
||||
assert_eq!(my_version, "0.1.0");
|
||||
|
@ -238,7 +221,7 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn should_detect_dependencies() -> Result<(), Error> {
|
||||
let root_directory = find_agb_root_directory()?;
|
||||
let root_directory = crate::utils::find_agb_root_directory().unwrap();
|
||||
let deps = get_agb_dependencies(&root_directory.join("agb"))?;
|
||||
|
||||
assert_eq!(
|
||||
|
|
305
tools/src/release.rs
Normal file
305
tools/src/release.rs
Normal file
|
@ -0,0 +1,305 @@
|
|||
use std::{path::Path, process::Command};
|
||||
|
||||
use crate::utils::find_agb_root_directory;
|
||||
|
||||
pub fn command() -> clap::Command {
|
||||
clap::Command::new("release")
|
||||
.about("Prepares and commits the changes required to release agb")
|
||||
.arg(
|
||||
clap::Arg::new("version")
|
||||
.required(true)
|
||||
.help("New version to release")
|
||||
.value_parser(version_parser),
|
||||
)
|
||||
.arg(
|
||||
clap::Arg::new("Dry run")
|
||||
.long("dry-run")
|
||||
.help("Don't do anything with git (but does everything else)")
|
||||
.action(clap::ArgAction::SetTrue),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn release(matches: &clap::ArgMatches) -> Result<(), Error> {
|
||||
let dry_run = matches.get_one::<bool>("Dry run").expect("defined by clap");
|
||||
let version = matches
|
||||
.get_one::<Version>("version")
|
||||
.expect("defined by clap");
|
||||
|
||||
let root_directory = find_agb_root_directory().map_err(|_| Error::FindRootDirectory)?;
|
||||
|
||||
// if not dry run, check that there are no out-standing changes in git
|
||||
if !dry_run && !execute_git_command(&root_directory, &["status", "--porcelain"])?.is_empty() {
|
||||
println!("Uncommitted changes, please commit first");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Check that we are in the master branch
|
||||
if !dry_run
|
||||
&& execute_git_command(&root_directory, &["symbolic-ref", "--short", "HEAD"])? != "master"
|
||||
{
|
||||
println!("You must be on the master branch before releasing");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let project_toml_files = glob_many(&root_directory, &["agb-*/Cargo.toml"])?;
|
||||
let agb_cargo_toml = root_directory.join("agb/Cargo.toml");
|
||||
|
||||
update_to_version(&root_directory, &agb_cargo_toml, version)?;
|
||||
|
||||
for toml_file in &project_toml_files {
|
||||
update_to_version(&root_directory, toml_file, version)?;
|
||||
}
|
||||
|
||||
assert!(Command::new("just")
|
||||
.arg("ci")
|
||||
.current_dir(&root_directory)
|
||||
.status()
|
||||
.map_err(|_| Error::JustCiFailed)?
|
||||
.success());
|
||||
|
||||
let changelog_text = update_changelog(&root_directory, version)?;
|
||||
|
||||
println!("Content of changelog:\n{changelog_text}");
|
||||
|
||||
if !dry_run {
|
||||
execute_git_command(
|
||||
&root_directory,
|
||||
&["commit", "-am", &format!("Release v{version}")],
|
||||
)?;
|
||||
execute_git_command(
|
||||
&root_directory,
|
||||
&[
|
||||
"tag",
|
||||
"-a",
|
||||
&version.to_string(),
|
||||
"-m",
|
||||
&format!("#v{version}\n{changelog_text}"),
|
||||
],
|
||||
)?;
|
||||
}
|
||||
|
||||
println!("Done! Push with");
|
||||
println!("git push --atomic origin master v{version}");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn update_to_version(
|
||||
root_directory: &Path,
|
||||
toml_file: &Path,
|
||||
new_version: &Version,
|
||||
) -> Result<(), Error> {
|
||||
let directory_name = toml_file.parent().unwrap().file_name().unwrap();
|
||||
let project_name = directory_name.to_string_lossy().replace('-', "_");
|
||||
|
||||
let toml_file_content = std::fs::read_to_string(toml_file).map_err(|_| Error::ReadTomlFile)?;
|
||||
let mut cargo_toml = toml_file_content
|
||||
.parse::<toml_edit::Document>()
|
||||
.map_err(|_| Error::InvalidToml(toml_file.to_string_lossy().into_owned()))?;
|
||||
|
||||
let new_version = format!("{new_version}");
|
||||
cargo_toml["package"]["version"] = toml_edit::value(&new_version);
|
||||
|
||||
std::fs::write(toml_file, cargo_toml.to_string()).map_err(|_| Error::WriteTomlFile)?;
|
||||
|
||||
for cargo_toml_file in glob_many(
|
||||
root_directory,
|
||||
&[
|
||||
"agb-*/Cargo.toml",
|
||||
"agb/Cargo.toml",
|
||||
"examples/*/Cargo.toml",
|
||||
"book/games/*/Cargo.toml",
|
||||
"template/Cargo.toml",
|
||||
],
|
||||
)? {
|
||||
let toml_file_content =
|
||||
std::fs::read_to_string(&cargo_toml_file).map_err(|_| Error::ReadTomlFile)?;
|
||||
let mut cargo_toml = toml_file_content
|
||||
.parse::<toml_edit::Document>()
|
||||
.map_err(|_| Error::InvalidToml(cargo_toml_file.to_string_lossy().into_owned()))?;
|
||||
|
||||
if let Some(this_dep) = cargo_toml["dependencies"].get_mut(&project_name) {
|
||||
match this_dep {
|
||||
toml_edit::Item::Value(s @ toml_edit::Value::String(_)) => {
|
||||
*s = new_version.clone().into()
|
||||
}
|
||||
toml_edit::Item::Value(toml_edit::Value::InlineTable(t)) => {
|
||||
t["version"] = new_version.clone().into()
|
||||
}
|
||||
toml_edit::Item::None => continue,
|
||||
_ => {
|
||||
return Err(Error::InvalidToml(format!(
|
||||
"{:?} while seaching dependencies in {}",
|
||||
this_dep,
|
||||
cargo_toml_file.to_string_lossy()
|
||||
)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::fs::write(cargo_toml_file, cargo_toml.to_string())
|
||||
.map_err(|_| Error::WriteTomlFile)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn update_changelog(root_directory: &Path, new_version: &Version) -> Result<String, Error> {
|
||||
use chrono::Datelike;
|
||||
|
||||
let changelog_file = root_directory.join("CHANGELOG.md");
|
||||
let changelog_content =
|
||||
std::fs::read_to_string(&changelog_file).map_err(|_| Error::FailedToReadChangelog)?;
|
||||
|
||||
let today = chrono::Local::today();
|
||||
let formatted_date = format!(
|
||||
"{:04}/{:02}/{:02}",
|
||||
today.year(),
|
||||
today.month(),
|
||||
today.day()
|
||||
);
|
||||
|
||||
const UNRELEASED_HEADER: &str = "## [Unreleased]";
|
||||
|
||||
let unreleased_bit_start = changelog_content
|
||||
.find(UNRELEASED_HEADER)
|
||||
.ok_or(Error::FailedToParseChangelog)?
|
||||
+ UNRELEASED_HEADER.len();
|
||||
let unreleased_bit_end = changelog_content[unreleased_bit_start..]
|
||||
.find("\n## [") // the start of the next entry
|
||||
.ok_or(Error::FailedToParseChangelog)?
|
||||
+ unreleased_bit_start;
|
||||
|
||||
let change_content = changelog_content[unreleased_bit_start..unreleased_bit_end].to_owned();
|
||||
|
||||
let changelog_content = changelog_content.replacen(
|
||||
UNRELEASED_HEADER,
|
||||
&format!("{UNRELEASED_HEADER}\n\n## [{new_version}] - {formatted_date}"),
|
||||
1,
|
||||
);
|
||||
|
||||
std::fs::write(&changelog_file, &changelog_content)
|
||||
.map_err(|_| Error::FailedToWriteChangelog)?;
|
||||
|
||||
Ok(change_content)
|
||||
}
|
||||
|
||||
fn execute_git_command(root_directory: &Path, args: &[&str]) -> Result<String, Error> {
|
||||
let git_cmd = Command::new("git")
|
||||
.args(args)
|
||||
.current_dir(root_directory)
|
||||
.output()
|
||||
.map_err(|_| Error::Git("Failed to run command"))?;
|
||||
|
||||
assert!(git_cmd.status.success());
|
||||
|
||||
String::from_utf8(git_cmd.stdout).map_err(|_| Error::Git("Output not utf-8"))
|
||||
}
|
||||
|
||||
fn glob_many(root_directory: &Path, globs: &[&str]) -> Result<Vec<std::path::PathBuf>, Error> {
|
||||
let mut result = vec![];
|
||||
|
||||
for g in globs.iter() {
|
||||
for path in glob::glob(&root_directory.join(g).to_string_lossy()).expect("Invalid glob") {
|
||||
result.push(path.map_err(|_| Error::Glob)?);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
FindRootDirectory,
|
||||
Git(&'static str),
|
||||
Glob,
|
||||
ReadTomlFile,
|
||||
InvalidToml(String),
|
||||
WriteTomlFile,
|
||||
JustCiFailed,
|
||||
CargoUpdateFailed,
|
||||
FailedToReadChangelog,
|
||||
FailedToWriteChangelog,
|
||||
FailedToParseChangelog,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
struct Version {
|
||||
major: u32,
|
||||
minor: u32,
|
||||
patch: u32,
|
||||
}
|
||||
|
||||
impl Version {
|
||||
#[cfg(test)]
|
||||
pub fn new(major: u32, minor: u32, patch: u32) -> Self {
|
||||
Self {
|
||||
major,
|
||||
minor,
|
||||
patch,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Version {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}.{}.{}", self.major, self.minor, self.patch)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
struct ParseVersionError;
|
||||
|
||||
impl std::str::FromStr for Version {
|
||||
type Err = ParseVersionError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let version_array: Vec<_> = s
|
||||
.split('.')
|
||||
.map(|v| v.parse())
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
.map_err(|_| ParseVersionError)?;
|
||||
|
||||
if version_array.len() > 3 || version_array.is_empty() {
|
||||
return Err(ParseVersionError);
|
||||
}
|
||||
|
||||
Ok(Version {
|
||||
major: version_array[0],
|
||||
minor: *version_array.get(1).unwrap_or(&0),
|
||||
patch: *version_array.get(2).unwrap_or(&0),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn version_parser(maybe_version: &str) -> Result<Version, &'static str> {
|
||||
maybe_version
|
||||
.parse()
|
||||
.map_err(|_| "Failed to parse version, must be of the format x.y.z")
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::str::FromStr;
|
||||
|
||||
#[test]
|
||||
fn verify_cli() {
|
||||
command().debug_assert();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_parse_versions() {
|
||||
assert_eq!(Version::from_str("0.1.2").unwrap(), Version::new(0, 1, 2));
|
||||
assert_eq!(Version::from_str("0.1").unwrap(), Version::new(0, 1, 0));
|
||||
assert_eq!(
|
||||
Version::from_str("33.23.4000").unwrap(),
|
||||
Version::new(33, 23, 4000)
|
||||
);
|
||||
|
||||
assert_eq!(Version::from_str("abc").unwrap_err(), ParseVersionError);
|
||||
assert_eq!(Version::from_str("").unwrap_err(), ParseVersionError);
|
||||
assert_eq!(Version::from_str("0.2.4.5").unwrap_err(), ParseVersionError);
|
||||
assert_eq!(Version::from_str("0.2.4a").unwrap_err(), ParseVersionError);
|
||||
}
|
||||
}
|
27
tools/src/utils.rs
Normal file
27
tools/src/utils.rs
Normal file
|
@ -0,0 +1,27 @@
|
|||
use std::{env, path::PathBuf};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct FindRootDirectoryError;
|
||||
|
||||
pub fn find_agb_root_directory() -> Result<PathBuf, FindRootDirectoryError> {
|
||||
let mut current_path = env::current_dir().map_err(|_| FindRootDirectoryError)?;
|
||||
|
||||
while !current_path.clone().join("justfile").exists() {
|
||||
current_path = current_path
|
||||
.parent()
|
||||
.ok_or(FindRootDirectoryError)?
|
||||
.to_owned();
|
||||
}
|
||||
|
||||
Ok(current_path)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::find_agb_root_directory;
|
||||
|
||||
#[test]
|
||||
fn find_agb_root_directory_works() {
|
||||
find_agb_root_directory().unwrap();
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue