diff options
-rw-r--r-- | .cargo/config.toml | 2 | ||||
-rw-r--r-- | Cargo.lock | 368 | ||||
-rw-r--r-- | xtask/Cargo.toml | 10 | ||||
-rw-r--r-- | xtask/src/lib.rs | 20 | ||||
-rw-r--r-- | xtask/src/main.rs | 3 | ||||
-rw-r--r-- | xtask/src/release.rs | 174 | ||||
-rw-r--r-- | xtask/src/release/version.rs | 302 |
7 files changed, 862 insertions, 17 deletions
diff --git a/.cargo/config.toml b/.cargo/config.toml index 35049cb..8c7aa77 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,2 +1,2 @@ [alias] -xtask = "run --package xtask --" +xtask = "run --config profile.dev.debug=false --package xtask --" @@ -27,6 +27,21 @@ dependencies = [ ] [[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[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 = "anstream" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -62,7 +77,7 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" dependencies = [ - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -72,7 +87,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" dependencies = [ "anstyle", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -82,6 +97,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" [[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -89,6 +110,17 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bstr" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" +dependencies = [ + "lazy_static", + "memchr", + "regex-automata", +] + +[[package]] +name = "bstr" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3d4260bcc2e8fc9df1eac4919a720effeb63a3f0952f5bf4944adfa18897f09" @@ -98,6 +130,12 @@ dependencies = [ ] [[package]] +name = "bumpalo" +version = "3.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" + +[[package]] name = "cc" version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -113,6 +151,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] +name = "chrono" +version = "0.4.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "num-traits", + "winapi", +] + +[[package]] name = "clap" version = "4.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -180,6 +230,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" [[package]] +name = "console" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c926e00cc70edefdc64d3a5ff31cc65bb97a3460097762bd23afb4d8145fccf8" +dependencies = [ + "encode_unicode", + "lazy_static", + "libc", + "windows-sys 0.45.0", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" + +[[package]] name = "crc32fast" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -189,6 +257,12 @@ dependencies = [ ] [[package]] +name = "encode_unicode" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" + +[[package]] name = "errno" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -196,7 +270,7 @@ checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" dependencies = [ "errno-dragonfly", "libc", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -218,7 +292,7 @@ dependencies = [ "cfg-if", "libc", "redox_syscall", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -266,13 +340,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "029d74589adefde59de1a0c4f4732695c32805624aec7b68d91503d4dba79afc" dependencies = [ "aho-corasick 0.7.20", - "bstr", + "bstr 1.4.0", "fnv", "log", "regex", ] [[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] name = "heck" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -285,6 +365,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" [[package]] +name = "iana-time-zone" +version = "0.1.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0722cd7114b7de04316e7ea5456a0bbb20e4adb46fd27a3697adb812cff0f37c" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] name = "idna" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -312,6 +415,16 @@ dependencies = [ ] [[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] name = "io-lifetimes" version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -319,7 +432,7 @@ checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" dependencies = [ "hermit-abi", "libc", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -331,7 +444,7 @@ dependencies = [ "hermit-abi", "io-lifetimes", "rustix", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -344,6 +457,15 @@ dependencies = [ ] [[package]] +name = "js-sys" +version = "0.3.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f37a4a5928311ac501dee68b3c7613a1037d0edb30c8e5427bd832d55d1b790" +dependencies = [ + "wasm-bindgen", +] + +[[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -429,6 +551,15 @@ dependencies = [ ] [[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.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -548,7 +679,7 @@ dependencies = [ "io-lifetimes", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -561,12 +692,27 @@ dependencies = [ ] [[package]] +name = "semver" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" + +[[package]] name = "serde" version = "1.0.163" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2" [[package]] +name = "serde_spanned" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93107647184f6027e3b7dcb2e11034cf95ffa1e3a682c67951963ac69c1c007d" +dependencies = [ + "serde", +] + +[[package]] name = "sharded-slab" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -576,6 +722,26 @@ dependencies = [ ] [[package]] +name = "similar" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "420acb44afdae038210c99e69aae24109f32f15500aa708e81d46c9f29d55fcf" +dependencies = [ + "bstr 0.2.17", + "unicode-segmentation", +] + +[[package]] +name = "similar-asserts" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbf644ad016b75129f01a34a355dcb8d66a5bc803e417c7a77cc5d5ee9fa0f18" +dependencies = [ + "console", + "similar", +] + +[[package]] name = "smallvec" version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -635,6 +801,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] +name = "toml_datetime" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a76a9312f5ba4c2dec6b9161fdf25d87ad8a09256ccea5a556fef03c706a10f" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.19.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2380d56e8670370eee6566b0bfd4265f65b3f432e8c6d85623f728d4fa31f739" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] name = "tracing" version = "0.1.37" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -718,6 +906,12 @@ dependencies = [ ] [[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + +[[package]] name = "url" version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -757,6 +951,60 @@ dependencies = [ ] [[package]] +name = "wasm-bindgen" +version = "0.2.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bba0e8cb82ba49ff4e229459ff22a191bbe9a1cb3a341610c9c33efc27ddf73" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b04bc93f9d6bdee709f6bd2118f57dd6679cf1176a1af464fca3ab0d66d8fb" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14d6b024f1a526bb0234f52840389927257beb670610081360e5a03c5df9c258" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e128beba882dd1eb6200e1dc92ae6c5dbaa4311aa7bb211ca035779e5efc39f8" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed9d5b4305409d1fc9482fee2d7f9bcbf24b3972bf59817ef757e23982242a93" + +[[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -788,12 +1036,45 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] +name = "windows" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +dependencies = [ + "windows-targets 0.48.0", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] name = "windows-sys" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets", + "windows-targets 0.48.0", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", ] [[package]] @@ -802,58 +1083,109 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", ] [[package]] name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" [[package]] name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" [[package]] name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" [[package]] name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" [[package]] name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" [[package]] name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" [[package]] name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" [[package]] +name = "winnow" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61de7bac303dc551fe038e2b3cef0f571087a47571ea6e79a87692ac99b99699" +dependencies = [ + "memchr", +] + +[[package]] name = "xattr" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -867,7 +1199,11 @@ name = "xtask" version = "0.3.2" dependencies = [ "anyhow", + "chrono", "clap", "flate2", + "semver", + "similar-asserts", "tar", + "toml_edit", ] diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml index 5040f0a..1694407 100644 --- a/xtask/Cargo.toml +++ b/xtask/Cargo.toml @@ -13,3 +13,13 @@ anyhow = { workspace = true } clap = { workspace = true } tar = "0.4.38" flate2 = "1.0.26" +semver = "1.0.17" +toml_edit = { version = "0.19.10", features = ["serde"] } + +[dependencies.chrono] +version = "0.4.26" +default-features = false +features = ["std", "clock"] + +[dev-dependencies] +similar-asserts = "1.4.2" diff --git a/xtask/src/lib.rs b/xtask/src/lib.rs index f01fe7b..26dcbd1 100644 --- a/xtask/src/lib.rs +++ b/xtask/src/lib.rs @@ -8,3 +8,23 @@ const PKG_INCLUDE: &[&str] = &[ ]; pub mod dist; +pub mod release; + +/// Parse version from git describe output. +pub fn git_version() -> anyhow::Result<semver::Version> { + let stdout = std::process::Command::new("git") + .arg("describe") + .arg("--long") + .arg("--abbrev=7") + .output()? + .stdout; + + std::str::from_utf8(&stdout)? + .trim() + .trim_start_matches('v') + .replacen("-g", ".g", 1) + .replacen('-', "-r", 1) + .parse() + .map_err(Into::into) +} + diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 51d5491..7454bf0 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -26,6 +26,7 @@ fn main() -> Result<()> { let out_dir = dist::find_out_dir(&cli.profile)?; println!("{}", out_dir.display()); } + Commands::Release(release) => release.run()?, }; Ok(()) @@ -57,6 +58,8 @@ enum Commands { #[arg(short, long)] tag: Option<String>, }, + + Release(release::Release), } fn get_package_dir() -> PathBuf { diff --git a/xtask/src/release.rs b/xtask/src/release.rs new file mode 100644 index 0000000..8bf4444 --- /dev/null +++ b/xtask/src/release.rs @@ -0,0 +1,174 @@ +use std::process::{Command, Stdio}; + +use anyhow::Result; +use clap::{Args, Subcommand}; +use semver::Version; + +use self::version::{Bump, Level, Replacement}; + +mod version; + +#[derive(Debug, Clone, Args)] +pub struct Release { + #[command(subcommand)] + step: Option<Step>, + + /// Level of version bump version. + #[arg(global = true, required = false)] + level: version::Level, + + /// Options passed to git commit. + #[arg(global = true, last = true)] + git_commit_args: Vec<String>, +} + +impl Release { + pub fn run(self) -> Result<()> { + match self.step { + Some(step) => step.run(), + None => { + let bump = Step::bump(self.level)?; + + println!("Bumped version: {bump}"); + + Ok(()) + } + } + } +} + +#[derive(Debug, Clone, Subcommand)] +pub enum Step { + /// Bump version in package files and commit changes. + Bump { + #[arg(from_global)] + level: version::Level, + }, + + /// Make a release commit. + Commit { + #[arg(from_global)] + git_commit_args: Vec<String>, + }, + + /// Create git tag for release. + Tag { + #[arg(from_global)] + level: version::Level, + }, +} + +impl Step { + pub fn run(self) -> Result<()> { + match self { + Step::Bump { level } => { + let bump = Self::bump(level)?; + println!("Bumped version: {bump}"); + } + Step::Commit { git_commit_args } => Self::commit(git_commit_args)?, + Step::Tag { level } => { + let stdout = Command::new("git") + .arg("describe") + .arg("--abbrev=0") + .output()? + .stdout; + + let prev = std::str::from_utf8(&stdout)?.parse()?; + let next = level.bump(&prev); + Self::tag(prev, next)?; + } + }; + + Ok(()) + } + + pub fn bump(level: Level) -> Result<Bump> { + let mut bump = Bump::from(level); + + bump.bump_file("README.md", &[Replacement::Version])?; + bump.bump_file("pkg/archlinux/projectr/PKGBUILD", &[Replacement::Version])?; + bump.bump_file( + "pkg/archlinux/projectr-bin/PKGBUILD", + &[Replacement::Version], + )?; + + let stdout = std::process::Command::new("git") + .arg("describe") + .arg("--long") + .arg("--abbrev=7") + .output()? + .stdout; + let pkgver = std::str::from_utf8(&stdout)? + .trim() + .trim_start_matches('v') + .replacen("-g", ".g", 1) + .replacen('-', "-r", 1) + .replace('-', "."); + + bump.bump_file( + "pkg/archlinux/projectr-git/PKGBUILD", + &[Replacement::FindLine( + |l| l.starts_with("pkgver="), + format!("pkgver={pkgver}"), + )], + )?; + + bump.bump_file( + "CHANGELOG.md", + &[ + Replacement::Append( + "## [Unreleased]".to_string(), + format!( + "\n## [{}] - {}", + bump.next, + chrono::Utc::now().format("%Y-%m-%d") + ), + ), + Replacement::Append( + "[Unreleased]: https://git.sr.ht/~tobyvin/projectr/log/HEAD".to_string(), + format!( + "[{0}]: https://git.sr.ht/~tobyvin/projectr/log/v{0}", + bump.next + ), + ), + ], + )?; + + Ok(bump) + } + + pub fn commit(git_commit_args: Vec<String>) -> Result<()> { + let git_commit = Command::new("git") + .arg("commit") + .args(git_commit_args) + .status()?; + + anyhow::ensure!(git_commit.success(), "Failed to commit changes"); + + Ok(()) + } + + pub fn tag(from: Version, to: Version) -> Result<String> { + let tag_name = format!("v{}", to); + + let shortlog_child = Command::new("git") + .arg("shortlog") + .arg(format!("v{}..HEAD", from)) + .arg("--abbrev=7") + .stdout(Stdio::piped()) + .spawn()?; + + let git_commit = Command::new("git") + .arg("tag") + .arg("-s") + .arg(&tag_name) + .arg("--file") + .arg("-") + .stdin(Stdio::from(shortlog_child.stdout.unwrap())) // Pipe through. + .status()?; + + anyhow::ensure!(git_commit.success(), "Failed to commit changes"); + + Ok(tag_name) + } +} diff --git a/xtask/src/release/version.rs b/xtask/src/release/version.rs new file mode 100644 index 0000000..0c8cffd --- /dev/null +++ b/xtask/src/release/version.rs @@ -0,0 +1,302 @@ +use std::{ + fmt::Display, + fs::File, + io::{Read, Write}, + path::Path, + process::Command, + str::FromStr, +}; + +use anyhow::Result; +use semver::Version; + +use crate::PKG_VER; + +#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord)] +pub enum Level { + Major, + Minor, + #[default] + Patch, +} + +impl Level { + pub fn bump(&self, version: &Version) -> Version { + match self { + Self::Major => Version::new(version.major + 1, 0, 0), + Self::Minor => Version::new(version.major, version.minor + 1, 0), + Self::Patch => Version::new(version.major, version.minor, version.patch + 1), + } + } +} + +impl FromStr for Level { + type Err = anyhow::Error; + + fn from_str(s: &str) -> std::result::Result<Self, Self::Err> { + match s.to_lowercase().as_str() { + "major" => Ok(Level::Major), + "minor" => Ok(Level::Minor), + "patch" => Ok(Level::Patch), + s => Err(anyhow::anyhow!("Invalid bump level: {s}")), + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub struct Bump { + pub version: Version, + pub next: Version, +} + +impl Bump { + pub fn new(major: u64, minor: u64, patch: u64, level: Level) -> Self { + let version = Version::new(major, minor, patch); + let next = level.bump(&version); + Self { version, next } + } + + fn check_modified<P>(path: P) -> Result<bool> + where + P: AsRef<Path>, + { + Ok(Command::new("git") + .arg("diff-index") + .arg("--quiet") + .arg("HEAD") + .arg(path.as_ref()) + .output()? + .status + .success()) + } + + pub fn bump_file<P>(&mut self, path: P, replacements: &[Replacement]) -> Result<()> + where + P: AsRef<Path>, + { + let path = path.as_ref(); + + anyhow::ensure!( + Self::check_modified(path)?, + "{} has uncommited changes. Aborting.", + path.display() + ); + + let file = File::open(path)?; + + self.bump(&file, &file, replacements)?; + + let git_added = Command::new("git").arg("add").arg(path).status()?; + + anyhow::ensure!(git_added.success(), "Failed to add bumped files to git"); + + Ok(()) + } + + fn bump<R, W>(&self, mut reader: R, mut writer: W, replacements: &[Replacement]) -> Result<()> + where + R: Read, + W: Write, + { + let mut buf = String::new(); + reader.read_to_string(&mut buf)?; + + let buf = replacements + .iter() + .try_fold(buf, |acc, r| r.replace(self, acc))?; + + writer.write_all(buf.as_bytes()).map_err(Into::into) + } +} + +impl Display for Bump { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{} -> {}", self.version, self.next) + } +} + +impl Default for Bump { + fn default() -> Self { + let version = PKG_VER.parse().unwrap_or_else(|_| Version::new(0, 1, 0)); + Self { + next: Level::default().bump(&version), + version, + } + } +} + +impl From<Level> for Bump { + fn from(level: Level) -> Self { + let mut v = Self::default(); + v.next = level.bump(&v.version); + v + } +} + +#[derive(Default)] +pub enum Replacement { + #[default] + Version, + CargoVersion, + FindLine(fn(&&str) -> bool, String), + Replace(String, String), + Append(String, String), +} + +impl Replacement { + pub fn replace(&self, bump: &Bump, buf: String) -> Result<String> { + let buf = match self { + Replacement::Version => buf.replace(&bump.version.to_string(), &bump.next.to_string()), + Replacement::Replace(from, to) => buf.replace(from, to), + Replacement::Append(line, append) => buf.replace(line, &format!("{line}\n{append}")), + Replacement::FindLine(find, replace) => match buf.lines().find(find) { + Some(line) => buf.replace(line, replace), + None => buf, + }, + Replacement::CargoVersion => { + let mut cargo_toml: toml_edit::Document = buf.parse()?; + cargo_toml["workspace"]["package"]["version"] = + toml_edit::value(bump.next.to_string()); + cargo_toml.to_string() + } + }; + + Ok(buf) + } +} + +#[cfg(test)] +mod test { + + use similar_asserts::SimpleDiff; + + use super::*; + + fn setup_test() -> Result<(), std::io::Error> { + let project_root = std::path::Path::new(&env!("CARGO_MANIFEST_DIR")) + .parent() + .expect("Failed to get parent directory.") + .to_path_buf(); + + std::env::set_current_dir(project_root) + } + + fn print_diff(reader: &[u8], writer: &[u8]) { + let left = std::str::from_utf8(reader).unwrap(); + let right = std::str::from_utf8(writer).unwrap(); + let diff = SimpleDiff::from_str(left, right, "old", "new"); + + println!("{diff}") + } + + #[test] + fn test_bump_cargo() { + setup_test().unwrap(); + + let bump = Bump::default(); + let file = "Cargo.toml"; + + let content = std::fs::read_to_string(file).unwrap(); + let reader = content.as_bytes(); + let mut writer = Vec::new(); + + let replacements = &[Replacement::CargoVersion]; + + bump.bump(reader, &mut writer, replacements).unwrap(); + + println!("{file}: {bump}"); + print_diff(reader, &writer) + } + + #[test] + fn test_bump_changelog() { + setup_test().unwrap(); + + let bump = Bump::default(); + let file = "CHANGELOG.md"; + + let content = std::fs::read_to_string(file).unwrap(); + let reader = content.as_bytes(); + let mut writer = Vec::new(); + + let replacements = &[ + Replacement::Append( + "## [Unreleased]".to_string(), + format!( + "\n## [{}] - {}", + bump.next, + chrono::Utc::now().format("%Y-%m-%d") + ), + ), + Replacement::Append( + "[Unreleased]: https://git.sr.ht/~tobyvin/projectr/log/HEAD".to_string(), + format!( + "[{0}]: https://git.sr.ht/~tobyvin/projectr/log/v{0}", + bump.next + ), + ), + ]; + + bump.bump(reader, &mut writer, replacements).unwrap(); + + println!("{file}: {bump}"); + print_diff(reader, &writer) + } + + #[test] + fn test_bump_readme() { + setup_test().unwrap(); + + let bump = Bump::default(); + let file = "README.md"; + + let content = std::fs::read_to_string(file); + let reader = content.as_deref().unwrap().as_bytes(); + let mut writer = Vec::new(); + + let replacements = &[Replacement::Version]; + + bump.bump(reader, &mut writer, replacements).unwrap(); + + println!("{file}: {bump}"); + print_diff(reader, &writer) + } + + #[test] + fn test_bump_vsc_pkgbuild() { + setup_test().unwrap(); + + let bump = Bump::default(); + let file = "pkg/archlinux/projectr-git/PKGBUILD"; + + let content = std::fs::read_to_string(file).unwrap(); + let reader = content.as_bytes(); + let mut writer = Vec::new(); + + let stdout = std::process::Command::new("git") + .arg("describe") + .arg("--long") + .arg("--abbrev=7") + .output() + .unwrap() + .stdout; + + let pkgver = std::str::from_utf8(&stdout) + .unwrap() + .trim() + .trim_start_matches('v') + .replacen("-g", ".g", 1) + .replacen('-', "-r", 1) + .replace('-', "."); + + let replacements = &[Replacement::FindLine( + |l| l.starts_with("pkgver="), + format!("pkgver={pkgver}"), + )]; + + bump.bump(reader, &mut writer, replacements).unwrap(); + + println!("{file}: {bump}"); + print_diff(reader, &writer) + } +} |