diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..77b4505f --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,56 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +## [0.10.0] - 2021-11-15 + +Unified all version numbers and removed the zzz-release tool. + +### ic-agent + +#### Fixed: rewrite all *.ic0.app domains to ic0.app to avoid redirects. + +### icx-cert + +#### New feature: Add --accept-encoding parameter + +It's now possible to specify which encodings will be accepted. The default (and previous) behavior +is to accept only the identity encoding. Specifying encodings that browsers more commonly accept +demonstrates the difference in the returned data and certificate. + +For example, here is the data and certificate returned when only accepting the identity encoding. + +``` +$ cargo run -p icx-cert -- print 'http://localhost:8000/index.js?canisterId=ryjl3-tyaaa-aaaaa-aaaba-cai' +DATA HASH: 1495cd574831c23b4db97bc3860666ea495386f0ef0dab73c23ef31db5aa2765 + Label("/index.js", Leaf(0x1495cd574831c23b4db97bc3860666ea495386f0ef0dab73c23ef31db5aa2765)), +``` + +Here is an example accepting the gzip encoding (as most browsers do), showing that the canister +responded with different data having a different data hash. + +``` +$ cargo run -p icx-cert -- print --accept-encoding gzip 'http://localhost:8000/index.js?canisterId=ryjl3-tyaaa-aaaaa-aaaba-cai' +DATA HASH: 1770e76af0816ba951320c03eab1263c43de7ac4b0558dd9049cc532b7d6cd01 + Label("/index.js", Leaf(0x1495cd574831c23b4db97bc3860666ea495386f0ef0dab73c23ef31db5aa2765)), +``` + +### icx-proxy + +This project moved to https://github.com/dfinity/icx-proxy. + +## [0.9.0] - 2021-10-06 + +### ic-agent + +#### Added + +- Added field `replica_health_status` to `Status`. + - typical values + - `healthy` + - `waiting_for_certified_state` diff --git a/Cargo.lock b/Cargo.lock index 1a4a0763..8537ac35 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -197,15 +197,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040" -[[package]] -name = "camino" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4648c6d00a709aa069a236adcaae4f605a6241c72bf5bee79331a4b625921a9" -dependencies = [ - "serde", -] - [[package]] name = "candid" version = "0.7.7" @@ -245,36 +236,11 @@ dependencies = [ "syn", ] -[[package]] -name = "cargo-platform" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0226944a63d1bf35a3b5f948dd7c59e263db83695c9e8bffc4037de02e30f1d7" -dependencies = [ - "serde", -] - -[[package]] -name = "cargo_metadata" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c297bd3135f558552f99a0daa180876984ea2c4ffa7470314540dff8c654109a" -dependencies = [ - "camino", - "cargo-platform", - "semver", - "serde", - "serde_json", -] - [[package]] name = "cc" version = "1.0.68" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a72c244c1ff497a746a7e1fb3d14bd08420ecda70c8f25c7112f2781652d787" -dependencies = [ - "jobserver", -] [[package]] name = "cfg-if" @@ -661,21 +627,6 @@ version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0a01e0497841a3b2db4f8afa483cce65f7e96a3498bd6c541734792aeac8fe7" -[[package]] -name = "git2" -version = "0.13.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c1cbbfc9a1996c6af82c2b4caf828d2c653af4fcdbb0e5674cc966eee5a4197" -dependencies = [ - "bitflags", - "libc", - "libgit2-sys", - "log", - "openssl-probe", - "openssl-sys", - "url", -] - [[package]] name = "h2" version = "0.3.3" @@ -701,12 +652,6 @@ version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62aca2aba2d62b4a7f5b33f3712cb1b0692779a56fb510499d5c0aa594daeaf3" -[[package]] -name = "hashbrown" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" - [[package]] name = "hashbrown" version = "0.11.2" @@ -834,7 +779,7 @@ dependencies = [ [[package]] name = "ic-agent" -version = "0.9.0" +version = "0.10.0" dependencies = [ "async-trait", "base32", @@ -868,7 +813,7 @@ dependencies = [ [[package]] name = "ic-asset" -version = "0.4.0" +version = "0.10.0" dependencies = [ "anyhow", "candid", @@ -892,7 +837,7 @@ dependencies = [ [[package]] name = "ic-identity-hsm" -version = "0.3.6" +version = "0.10.0" dependencies = [ "hex", "ic-agent", @@ -920,7 +865,7 @@ dependencies = [ [[package]] name = "ic-utils" -version = "0.7.0" +version = "0.10.0" dependencies = [ "async-trait", "candid", @@ -937,7 +882,7 @@ dependencies = [ [[package]] name = "icx" -version = "0.6.0" +version = "0.10.0" dependencies = [ "anyhow", "candid", @@ -958,7 +903,7 @@ dependencies = [ [[package]] name = "icx-asset" -version = "0.4.0" +version = "0.10.0" dependencies = [ "anyhow", "candid", @@ -985,7 +930,7 @@ dependencies = [ [[package]] name = "icx-cert" -version = "0.5.0" +version = "0.10.0" dependencies = [ "anyhow", "base64", @@ -1020,7 +965,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5" dependencies = [ "autocfg", - "hashbrown 0.11.2", + "hashbrown", ] [[package]] @@ -1053,15 +998,6 @@ version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" -[[package]] -name = "jobserver" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "972f5ae5d1cb9c6ae417789196c803205313edde988685da5e3aae0827b9e7fd" -dependencies = [ - "libc", -] - [[package]] name = "js-sys" version = "0.3.51" @@ -1141,20 +1077,6 @@ dependencies = [ "rle-decode-fast", ] -[[package]] -name = "libgit2-sys" -version = "0.12.23+1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29730a445bae719db3107078b46808cc45a5b7a6bae3f31272923af969453356" -dependencies = [ - "cc", - "libc", - "libssh2-sys", - "libz-sys", - "openssl-sys", - "pkg-config", -] - [[package]] name = "libloading" version = "0.5.2" @@ -1165,32 +1087,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "libssh2-sys" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0186af0d8f171ae6b9c4c90ec51898bad5d08a2d5e470903a50d9ad8959cbee" -dependencies = [ - "cc", - "libc", - "libz-sys", - "openssl-sys", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "libz-sys" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de5435b8549c16d423ed0c03dbaafe57cf6c3344744f1242520d59c9d8ecec66" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] - [[package]] name = "lock_api" version = "0.4.4" @@ -1245,15 +1141,6 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc" -[[package]] -name = "memoffset" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9" -dependencies = [ - "autocfg", -] - [[package]] name = "mime" version = "0.3.16" @@ -1320,12 +1207,6 @@ dependencies = [ "serde_urlencoded", ] -[[package]] -name = "muncher" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0aeee23e39d46940d01b843da99d6e26fb8b3659e33f3c48272aef45d978671" - [[package]] name = "native-tls" version = "0.2.7" @@ -1889,31 +1770,12 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cabe4fa914dec5870285fa7f71f602645da47c486e68486d2b4ceb4a343e90ac" -[[package]] -name = "rowan" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a0734142c18710f7214dc21908e2f054e973b908dbb1a602a3e6691615aaaae" -dependencies = [ - "hashbrown 0.9.1", - "rustc-hash", - "smol_str", - "text-size", - "triomphe", -] - [[package]] name = "rustc-demangle" version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dead70b0b5e03e9c814bcb6b01e03e68f7c57a80aa48c72ec92152ab3e818d49" -[[package]] -name = "rustc-hash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" - [[package]] name = "rustls" version = "0.19.1" @@ -2009,15 +1871,6 @@ dependencies = [ "libc", ] -[[package]] -name = "semver" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "568a8e6258aa33c13358f81fd834adb854c6f7c9468520910a9b1e8fac068012" -dependencies = [ - "serde", -] - [[package]] name = "serde" version = "1.0.130" @@ -2132,12 +1985,6 @@ version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" -[[package]] -name = "smol_str" -version = "0.1.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b203e79e90905594272c1c97c7af701533d42adaab0beb3859018e477d54a3b0" - [[package]] name = "socket2" version = "0.4.0" @@ -2154,12 +2001,6 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" -[[package]] -name = "stable_deref_trait" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" - [[package]] name = "string_cache" version = "0.8.1" @@ -2241,12 +2082,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "text-size" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "288cb548dbe72b652243ea797201f3d481a0609a967980fcc5b2315ea811560a" - [[package]] name = "textwrap" version = "0.12.1" @@ -2404,17 +2239,6 @@ dependencies = [ "serde", ] -[[package]] -name = "toml-parse" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afb896bce41dd65209bc25172528068af0d5d62a15c1c30c8b59a8f35de8d46e" -dependencies = [ - "chrono", - "muncher", - "rowan", -] - [[package]] name = "tower-service" version = "0.3.1" @@ -2441,17 +2265,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "triomphe" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46bddb80ec09585c58022a2c55e6e7c11e713d9bb26895f1b56cd945c4040c54" -dependencies = [ - "memoffset", - "serde", - "stable_deref_trait", -] - [[package]] name = "try-lock" version = "0.2.3" @@ -2729,15 +2542,3 @@ checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69" dependencies = [ "winapi", ] - -[[package]] -name = "zzz-release" -version = "0.0.0" -dependencies = [ - "anyhow", - "cargo_metadata", - "clap", - "clap_derive", - "git2", - "toml-parse", -] diff --git a/Cargo.toml b/Cargo.toml index 0a7c6064..e361d367 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,5 @@ members = [ "ic-utils", "icx", "icx-asset", - "ref-tests", - "zzz-release", + "ref-tests" ] diff --git a/README.md b/README.md index 6f5b3254..110a1e52 100644 --- a/README.md +++ b/README.md @@ -18,20 +18,8 @@ There are two suites of tests that can be executed from this repo; the regular c the ic-ref tests. In order to run the ic-ref tests, you will need a running local reference server. If you do not have one, those tests will be ignored. -## Release Script -Using the `zzz-release` binary here (not available on crates.io), one can automatically update -the versions of packages based on the Git history. For example, any commit that starts with -`feat:` will update the minor version of packages which files were changed. Commits starting -with `fix:` or which dependencies were changed will update their patch version. - -To run, simply do - -```sh -cargo run --bin zzz-release && cargo build -``` - -If there are any errors (e.g. the workspace is modified locally), an error will be given. Otherwise -by default a git commit will be made, which needs to be pushed (and a PR created). +## Release +To release, increase the version number in all crates and run `cargo build` to update the lock file. ## Packages This repo has multiple packages in its Cargo workspace. diff --git a/ic-agent/CHANGELOG.md b/ic-agent/CHANGELOG.md deleted file mode 100644 index de5f272e..00000000 --- a/ic-agent/CHANGELOG.md +++ /dev/null @@ -1,17 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - -## [Unreleased] - -## [0.9.0] - 2021-10-06 - -### Added - -- Added field `replica_health_status` to `Status`. - - typical values - - `healthy` - - `waiting_for_certified_state` diff --git a/ic-agent/Cargo.toml b/ic-agent/Cargo.toml index bc2ae342..dfa761b9 100644 --- a/ic-agent/Cargo.toml +++ b/ic-agent/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ic-agent" -version = "0.9.0" +version = "0.10.0" authors = ["DFINITY Stiftung "] edition = "2018" description = "Agent library to communicate with the Internet Computer, following the Public Specification." diff --git a/ic-asset/Cargo.toml b/ic-asset/Cargo.toml index a68d9e0f..c0fbd925 100644 --- a/ic-asset/Cargo.toml +++ b/ic-asset/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ic-asset" -version = "0.4.0" +version = "0.10.0" authors = ["DFINITY Stiftung "] edition = "2018" description = "Library for storing files in an asset canister." @@ -20,9 +20,9 @@ futures = "0.3.17" futures-intrusive = "0.4.0" garcon = { version = "0.2", features = ["async"] } hex = {version = "0.4.2", features = ["serde"] } -ic-agent = { path = "../ic-agent", version = "0.9", features = [ "pem" ] } +ic-agent = { path = "../ic-agent", version = "0.10", features = [ "pem" ] } ic-types = { version = "0.2.2", features = [ "serde" ] } -ic-utils = { path = "../ic-utils", version = "0.7" } +ic-utils = { path = "../ic-utils", version = "0.10" } mime = "0.3.16" mime_guess = "2.0.3" openssl = "0.10.32" diff --git a/ic-identity-hsm/Cargo.toml b/ic-identity-hsm/Cargo.toml index 83aae187..b4576126 100644 --- a/ic-identity-hsm/Cargo.toml +++ b/ic-identity-hsm/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ic-identity-hsm" -version = "0.3.6" +version = "0.10.0" authors = ["DFINITY Stiftung "] description = "Identity implementation for HSM for the ic-agent package." homepage = "https://docs.rs/ic-identity-hsm" @@ -14,7 +14,7 @@ include = ["src", "Cargo.toml", "../LICENSE", "README.md"] [dependencies] hex = "0.4.2" -ic-agent = { path = "../ic-agent", version = "0.9", features = [ "pem" ] } +ic-agent = { path = "../ic-agent", version = "0.10", features = [ "pem" ] } num-bigint = "0.4.1" openssl = "0.10.30" pkcs11 = "0.5.0" diff --git a/ic-utils/Cargo.toml b/ic-utils/Cargo.toml index 26c117b6..74f4f7dc 100644 --- a/ic-utils/Cargo.toml +++ b/ic-utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ic-utils" -version = "0.7.0" +version = "0.10.0" authors = ["DFINITY Stiftung "] edition = "2018" description = "Collection of utilities for Rust, on top of ic-agent, to communicate with the Internet Computer, following the Public Specification." @@ -18,7 +18,7 @@ include = ["src", "Cargo.toml", "../LICENSE", "README.md"] async-trait = "0.1.40" candid = "0.7.7" garcon = { version = "0.2", features = ["async"] } -ic-agent = { path = "../ic-agent", version = "0.9" } +ic-agent = { path = "../ic-agent", version = "0.10" } serde = "1.0.115" serde_bytes = "0.11" strum = "0.21" diff --git a/icx-asset/Cargo.toml b/icx-asset/Cargo.toml index dc678c10..bf8cad88 100644 --- a/icx-asset/Cargo.toml +++ b/icx-asset/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "icx-asset" -version = "0.4.0" +version = "0.10.0" authors = ["DFINITY Stiftung "] edition = "2018" description = "CLI tool to manage assets on an asset canister on the Internet Computer." @@ -23,10 +23,10 @@ clap_derive = "=3.0.0-beta.2" delay = "0.3.1" garcon = "0.2.2" humantime = "2.0.1" -ic-agent = { path = "../ic-agent", version = "0.9" } -ic-asset = { path = "../ic-asset", version = "0.4" } +ic-agent = { path = "../ic-agent", version = "0.10" } +ic-asset = { path = "../ic-asset", version = "0.10" } ic-types = "0.2.2" -ic-utils = { path = "../ic-utils", version = "0.7" } +ic-utils = { path = "../ic-utils", version = "0.10" } libflate = "1.1.1" num-traits = "0.2" pem = "0.8.1" diff --git a/icx-cert/Cargo.toml b/icx-cert/Cargo.toml index c270c452..39e767fa 100644 --- a/icx-cert/Cargo.toml +++ b/icx-cert/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "icx-cert" -version = "0.5.0" +version = "0.10.0" authors = ["DFINITY Stiftung "] edition = "2018" description = "CLI tool to download a document from the Internet Computer and pretty-print the contents of its IC-Certificate header." @@ -21,7 +21,7 @@ clap = "=3.0.0-beta.2" clap_derive = "=3.0.0-beta.2" chrono = "0.4.19" hex = "0.4.2" -ic-agent = { path = "../ic-agent", version = "0.9" } +ic-agent = { path = "../ic-agent", version = "0.10" } leb128 = "0.2.4" reqwest = { version = "0.11",features = [ "blocking", "rustls-tls" ] } sha2 = "0.9.8" diff --git a/icx/Cargo.toml b/icx/Cargo.toml index c766da50..86c4baa1 100644 --- a/icx/Cargo.toml +++ b/icx/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "icx" -version = "0.6.0" +version = "0.10.0" authors = ["DFINITY Stiftung "] edition = "2018" description = "CLI tool to call canisters on the Internet Computer." @@ -24,8 +24,8 @@ clap_derive = "=3.0.0-beta.2" garcon = { version = "0.2.3", features = ["async"] } hex = "0.4.2" humantime = "2.0.1" -ic-agent = { path = "../ic-agent", version = "0.9" } -ic-utils = { path = "../ic-utils", version = "0.7" } +ic-agent = { path = "../ic-agent", version = "0.10" } +ic-utils = { path = "../ic-utils", version = "0.10" } pem = "0.8.1" ring = "0.16.11" serde = "1.0.115" diff --git a/zzz-release/Cargo.toml b/zzz-release/Cargo.toml deleted file mode 100644 index 4ac34499..00000000 --- a/zzz-release/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "zzz-release" -version = "0.0.0" -edition = "2018" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -anyhow = "1.0.40" -cargo_metadata = "0.14.0" -clap = "=3.0.0-beta.2" -clap_derive = "=3.0.0-beta.2" -git2 = "0.13.22" -toml-parse = "0.2.11" diff --git a/zzz-release/src/main.rs b/zzz-release/src/main.rs deleted file mode 100644 index 0e81b118..00000000 --- a/zzz-release/src/main.rs +++ /dev/null @@ -1,533 +0,0 @@ -use anyhow::{anyhow, Result}; -use cargo_metadata::{Package, Version}; -use clap::Clap; -use git2::{Commit, DescribeFormatOptions, Repository, RepositoryState}; -use std::{ - collections::HashMap, - ops::Range, - path::{Path, PathBuf}, - str::FromStr, -}; -use toml_parse::{walk, SyntaxNode, SyntaxToken, TomlKind}; - -#[derive(Clap, Debug)] -struct Options { - /// If true, will print out what would be done, but not do it. - #[clap(long = "dry-run")] - dry_run: bool, - - /// Skip creating a git commit (will still output all messages and logs). - #[clap(long = "skip-git")] - skip_git: bool, - - /// If true, will ignore status and branch of the repo. - #[clap(long = "force")] - force: bool, - - /// The release tag, by default the most recent tag is used if it starts - /// with `release-`. - #[clap(long = "release")] - tag: Option, - - /// A list of forced patch or minor for packages. This will bypass automatic - /// detection of changes. The format is `--package name=<1.2.3|minor|patch>`. - #[clap(long = "package")] - package: Vec, -} - -fn check_repo_status(repo: &Repository) -> Result<()> { - if RepositoryState::Clean != repo.state() { - return Err(anyhow!( - "The repo is not in a clean state. State: {:?}", - repo.state() - )); - } - - for s in repo.statuses(None)?.iter() { - let status = s.status(); - - if status.is_ignored() { - continue; - } - if status.is_empty() { - continue; - } - - return Err(anyhow!("The repo has local changes. Cannot proceed.")); - } - - if repo.head()?.is_branch() && repo.head()?.name() == Some("main") { - return Err(anyhow!( - "Cannot run this on main branch. You can use `--force`." - )); - } - - Ok(()) -} - -fn package_changed( - repo: &Repository, - workspace_root: &Path, - package_root: &Path, - commit: &Commit, -) -> Result { - let commit_tree = commit.tree()?; - let parent_tree = commit.parent(0)?.tree()?; - - let diff = repo.diff_tree_to_tree(Some(&parent_tree), Some(&commit_tree), None)?; - - for delta in diff.deltas() { - let old_path = workspace_root.join(delta.old_file().path().unwrap()); - let new_path = workspace_root.join(delta.new_file().path().unwrap()); - - if old_path.starts_with(package_root) || new_path.starts_with(package_root) { - return Ok(true); - } - } - - Ok(false) -} - -type ApplyChangeFn = dyn FnMut(bool) -> std::io::Result<()>; - -type VersionMap = HashMap; - -#[derive(Debug)] -struct PackageDependency { - pub name: String, - pub version_node: SyntaxToken, -} - -fn get_ident_from_str(value: &SyntaxNode) -> Option { - // Get the ident - walk(value) - .find(|x| x.kind() == TomlKind::Ident)? - .into_token() -} - -/// Get a value -fn get_value_from_table_for_key(node: &SyntaxNode, key: &str) -> Option { - // Children and their children. - node.children() - .filter(|x| x.kind() == TomlKind::KeyValue) - .filter_map(|n| { - if get_ident_from_str(&n.first_child()?)?.to_string() == key { - Some(n) - } else { - None - } - }) - .filter_map(|n| get_ident_from_str(&n.last_child()?)) - .last() -} - -#[test] -fn get_value_from_table_test() { - let parsed = toml_parse::parse_it( - r#"# comment - [some] - field_1 = "ignored" - abc = """REGULAR TABLE""" # Will return this. - field_2 = "ignored" - inline = { field_2 = "ignored", def = "INLINE TABLE", field_3 = "ignored" } - "#, - ) - .unwrap(); - - let some_table = walk(&parsed.syntax()) - .find(|x| x.kind() == TomlKind::Table) - .unwrap() - .into_node() - .unwrap(); - - assert_eq!( - get_value_from_table_for_key(&some_table, "abc") - .unwrap() - .to_string(), - "REGULAR TABLE" - ); - - // Chcek with inline table. - let inline_table = walk(&parsed.syntax()) - .find(|x| x.kind() == TomlKind::InlineTable) - .unwrap() - .into_node() - .unwrap(); - - assert_eq!( - get_value_from_table_for_key(&inline_table, "def") - .unwrap() - .to_string(), - "INLINE TABLE" - ); -} - -fn package_dependencies_from_dependencies_section( - node: &SyntaxNode, -) -> Result> { - Ok(node - .children() - // Skip heading. - .skip(1) - .filter_map(|x| { - if x.kind() != TomlKind::KeyValue { - return None; - } - - let key = x.first_token().unwrap(); - let value = x.children().last().unwrap().first_child().unwrap(); - - let version_node: SyntaxToken = match value.kind() { - TomlKind::InlineTable => get_value_from_table_for_key(&value, "version")?, - TomlKind::Str => get_ident_from_str(&value)?, - _ => return None, - }; - - Some(PackageDependency { - name: key.to_string(), - version_node, - }) - }) - .collect()) -} - -fn package_dependencies_from_dependency_dot(node: &SyntaxNode) -> Option> { - let title = node.first_token()?.next_sibling_or_token()?.to_string(); - let name = title.split_at("dependencies.".len()).1.to_string(); - let version_node = get_value_from_table_for_key(node, "version")?; - Some(vec![PackageDependency { name, version_node }]) -} - -fn update_manifest(package_name: &str, version_map: &VersionMap) -> Result> { - let (package, version) = &version_map.get(package_name).unwrap(); - eprintln!(" {:20} {} => {}", package_name, package.version, version); - let cargo_path = package.manifest_path.as_path().as_os_str().to_os_string(); - let mut cargo_toml = std::fs::read_to_string(&cargo_path)?; - - let parsed = toml_parse::parse_it(&cargo_toml)?; - let root = parsed.syntax(); - - let mut changes: Vec<(Range, String)> = Vec::new(); - - // Find the `[package]` object and the key pair for version = ..., - // then add a change to the vector above to update the value to the new version. - let value = walk(&root) - // Filter key value pairs. - .filter(|element| element.kind() == TomlKind::KeyValue) - // That are part of a table hat has the heading `package`. - .filter_map(|element| { - if element.parent()?.kind() == TomlKind::Table - && get_ident_from_str(&element.parent()?.first_child()?)?.to_string() == "package" - { - element.into_node() - } else { - None - } - }) - // And find the first one which has its key set to `version`. - .find_map(|version_kv| { - if get_ident_from_str(&version_kv.first_child()?)?.to_string() == "version" { - get_ident_from_str(&version_kv.last_child()?) - } else { - None - } - }) - .unwrap(); - - let range = value.text_range(); - let start: usize = range.start().into(); - let end: usize = range.end().into(); - changes.push(((start..end), version.to_string())); - - // Find the location of all version = ... of dependencies. This is a bit more touchy. - // This strategy is O(n^m) where `m` is the number of elements we look for, basically - // 4 right now, and `n` is the number of nodes in the graph. We don't actually care - // about performance but keep in mind this might be very slow. - walk(&root) - .filter_map(|element| { - // Find all tables that have name `dependencies` or `dev-dependencies`. - if element.kind() == TomlKind::Table { - let node = element.as_node()?; - let title = node.first_token()?.next_sibling_or_token()?.to_string(); - - if title.starts_with("dependencies.") { - package_dependencies_from_dependency_dot(node).map(Result::Ok) - } else if title == "dependencies" { - Some(package_dependencies_from_dependencies_section(node)) - } else { - None - } - } else { - None - } - }) - .collect::>>>()? - .into_iter() - .flatten() - .for_each(|pnv: PackageDependency| { - if let Some((_pkg, version)) = version_map.get(&pnv.name) { - // Add a change to the version number of this. - // This might be a shorter version so we need to check for that. - let short = format!("{}.{}", version.major, version.minor); - let long = version.to_string(); - - // Count number of dots... Easiest way to know. - let new_version = match pnv - .version_node - .to_string() - .chars() - .filter(|x| *x == '.') - .count() - { - 0 | 1 => short, - _ => long, - }; - - let range = pnv.version_node.text_range(); - let start: usize = range.start().into(); - let end: usize = range.end().into(); - changes.push((start..end, new_version)); - } - }); - - // Sort and apply all changes and return the save function. - changes.sort_by(|a, b| b.0.start.cmp(&a.0.start)); - for (Range { start, end }, str) in changes { - cargo_toml.replace_range(start..end, &str); - } - - let version = version.clone(); - let cargo_path = cargo_path; - Ok(Box::new(move |dry_run| { - if dry_run { - println!( - " Would update manifest '{}' to version {} (but dry-run is on)", - cargo_path.to_string_lossy(), - version - ); - Ok(()) - } else { - // Update the cargo file. - std::fs::write(&cargo_path, &cargo_toml) - } - })) -} - -fn main() -> Result<()> { - let opts: Options = Options::parse(); - eprintln!("{:#?}\n", opts); - - let repo = Repository::open(std::env::current_dir()?)?; - - if !opts.force { - check_repo_status(&repo)?; - } - - eprintln!("Finding the last release tag..."); - let tag = if let Some(tag) = opts.tag { - tag - } else { - let d = repo.describe(&git2::DescribeOptions::new().describe_tags())?; - let tag = d.format(Some(&DescribeFormatOptions::new().abbreviated_size(0)))?; - if !tag.starts_with("release-") { - return Err(anyhow!("Invalid release tag name: {}", tag)); - } - tag - }; - eprintln!("Found '{}'...", tag); - - eprintln!("Checking all packages..."); - let metadata = cargo_metadata::MetadataCommand::new() - .features(cargo_metadata::CargoOpt::AllFeatures) - .exec()?; - - eprintln!( - " Found {} packages ({} workspace member)...", - metadata.packages.len(), - metadata.workspace_members.len() - ); - - eprintln!("Checking git history to see the changes..."); - let mut revwalk = repo.revwalk()?; - let revspec = repo.revparse(&tag)?; - revwalk.push_head()?; - revwalk.hide(revspec.from().unwrap().id())?; - - let mut packages: HashMap = HashMap::new(); - let mut packages_minor: HashMap = HashMap::new(); - let mut packages_patch: HashMap = HashMap::new(); - - for package in metadata.packages { - if !metadata.workspace_members.contains(&package.id) { - continue; - } - - if package.version.eq(&Version::from_str("0.0.0")?) { - eprintln!("Skipping '{}' because it's version 0.0.0...", package.name); - continue; - } - - let cargo_toml: PathBuf = package.manifest_path.to_path_buf().into(); - packages.insert(cargo_toml.parent().unwrap().to_path_buf(), package); - } - - let workspace_root: PathBuf = metadata.workspace_root.into(); - - for rev in revwalk { - let oid = rev?; - let commit = repo.find_commit(oid)?; - if oid == revspec.from().unwrap().id() { - break; - } - - let message = commit.message().unwrap_or(""); - if message.starts_with("feat") { - for (root, package) in &packages { - if package_changed(&repo, &workspace_root, root, &commit)? { - packages_minor.insert(package.name.clone(), package.clone()); - } - } - } else if message.starts_with("feat") { - for (root, package) in &packages { - if package_changed(&repo, &workspace_root, root, &commit)? { - packages_patch.insert(package.name.clone(), package.clone()); - } - } - } - } - - // Increment the patch of all packages that depend on a package that's going to be updated. - let mut changed = true; - while changed { - changed = false; - - for (_root, package) in packages.iter() { - for d in &package.dependencies { - if packages_minor.values().any(|p| p.name == d.name) { - if packages_patch - .insert(package.name.clone(), package.clone()) - .is_none() - { - changed = true; - } - } else if packages_patch.values().any(|p| p.name == d.name) - && packages_patch - .insert(package.name.clone(), package.clone()) - .is_none() - { - changed = true; - } - } - } - } - - for k in packages_minor.keys() { - packages_patch.remove(k); - } - - let mut version_map = VersionMap::new(); - for (name, package) in packages_minor.iter() { - let incremented = Version::new(package.version.major, package.version.minor + 1, 0); - version_map.insert(name.clone(), (package.clone(), incremented)); - } - for (name, package) in packages_patch.iter() { - let incremented = Version::new( - package.version.major, - package.version.minor, - package.version.patch + 1, - ); - version_map.insert(name.clone(), (package.clone(), incremented)); - } - - for patch in opts.package { - // Split using `=`. - let c: Vec<_> = patch.split('=').take(2).collect(); - let name = c[0].to_string(); - let version = Version::from_str(c[1])?; - - version_map - .entry(name.to_string()) - .and_modify(|e| e.1 = version); - } - - eprintln!("\nVersions will be updated to:"); - - // Find all update functions and if any of them failed, bail out without doing any changes. - let changes: Result>> = packages_minor - .iter() - .chain(packages_patch.iter()) - .map(|(name, _package)| update_manifest(name, &version_map)) - .collect(); - - eprintln!("\nApplying changes..."); - for mut ch in changes? { - ch(opts.dry_run)?; - } - - eprintln!(); - - let commit_message = format!( - "chore: release of ic-agent\n\n{}", - version_map - .iter() - .map(|(name, (pkg, v))| { - format!("{}: {} => {}", name, pkg.version.to_string(), v.to_string()) - }) - .collect::>() - .join("\n") - ); - eprintln!( - "Will create a commit with message:\n===\n{}\n===\n", - commit_message - ); - - if opts.skip_git || opts.dry_run { - eprintln!("Skipping creating a git commit..."); - } else { - let mut index = repo.index()?; - index.add_all(["*"].iter(), git2::IndexAddOption::DEFAULT, None)?; - index.write()?; - repo.commit( - Some("HEAD"), - &repo.signature()?, - &repo.signature()?, - &commit_message, - &repo.find_tree(index.write_tree()?)?, - &[&repo.find_commit(repo.head()?.target().unwrap())?], - )?; - eprintln!("Commit made. All you need to do is `git push` now and create a PR."); - } - - // Figure out the dependency order. Using bubble sort because it's simple, it saves us - // time trying to figure out transitive dependencies. - // Basically if we were to use, say, merge sort, comparing two packages would mean - // comparing all the transitive dependencies of both to know if one is directly or - // indirectly depending on the other (if so, greater or less than depending on sort - // order, and if not, equals). That's too complicated. Proper solution is to use - // petgraph and topological sort, but I'm lazy. Bubblesort works already, with just - // comparing intransitively if two dependencies are directly related. - let mut cargo_update_order: Vec<&Package> = version_map.values().map(|x| &x.0).collect(); - - let mut changed = cargo_update_order.len() > 1; // Nothing to sort if there's only 1. - while changed { - changed = false; - for i in 0..(cargo_update_order.len() - 1) { - for j in (i + 1)..cargo_update_order.len() { - let (a, b) = (cargo_update_order[i], cargo_update_order[j]); - - if a.dependencies.iter().any(|dep| dep.name == b.name) { - cargo_update_order.swap(i, j); - changed = true; - }; - } - } - } - - eprintln!("\nOnce the PR is approved, run `cargo publish` commands in the following order:"); - for p in cargo_update_order { - eprintln!(" {}", p.name); - } - eprintln!("\n"); - - Ok(()) -}