diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..daa0b3a --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,2 @@ +[alias] +rpg = "run -q --bin rust-project-goals-cli --" \ No newline at end of file diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index f7d30d9..ed03860 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -1,4 +1,4 @@ -name: Rust +name: Validate markdown on: push: diff --git a/.github/workflows/compile.yml b/.github/workflows/compile.yml new file mode 100644 index 0000000..241e562 --- /dev/null +++ b/.github/workflows/compile.yml @@ -0,0 +1,20 @@ +name: Compile Rust code + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +env: + CARGO_TERM_COLOR: always + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - name: Build and check + run: cargo check --workspace diff --git a/Cargo.lock b/Cargo.lock index de8b33e..50bc76a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -60,15 +60,6 @@ dependencies = [ "libc", ] -[[package]] -name = "ansi_term" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" -dependencies = [ - "winapi", -] - [[package]] name = "anstream" version = "0.6.14" @@ -86,9 +77,9 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.7" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" [[package]] name = "anstyle-parse" @@ -120,9 +111,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.86" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" +checksum = "c1fd03a028ef38ba2276dce7e33fcd6369c158a1bca17946c4b1b701891c1ff7" [[package]] name = "atomic-waker" @@ -130,17 +121,6 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi 0.1.19", - "libc", - "winapi", -] - [[package]] name = "autocfg" version = "1.3.0" @@ -149,9 +129,9 @@ checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "aws-config" -version = "1.5.9" +version = "1.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d6448cfb224dd6a9b9ac734f58622dd0d4751f3589f3b777345745f46b2eb14" +checksum = "9b49afaa341e8dd8577e1a2200468f98956d6eda50bcf4a53246cc00174ba924" dependencies = [ "aws-credential-types", "aws-runtime", @@ -160,7 +140,7 @@ dependencies = [ "aws-sdk-sts", "aws-smithy-async", "aws-smithy-http", - "aws-smithy-json", + "aws-smithy-json 0.60.7", "aws-smithy-runtime", "aws-smithy-runtime-api", "aws-smithy-types", @@ -191,9 +171,9 @@ dependencies = [ [[package]] name = "aws-runtime" -version = "1.4.3" +version = "1.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a10d5c055aa540164d9561a0e2e74ad30f0dcf7393c3a92f6733ddf9c5762468" +checksum = "b5ac934720fbb46206292d2c75b57e67acfc56fe7dfd34fb9a02334af08409ea" dependencies = [ "aws-credential-types", "aws-sigv4", @@ -216,15 +196,15 @@ dependencies = [ [[package]] name = "aws-sdk-bedrock" -version = "1.60.0" +version = "1.64.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aae150c500b71de8e46329e5bc8c1630a009b9a88b090245151870bb89f9649" +checksum = "73778742601b8345891eb5a3733d48e447f61601438d18222c4ba399cb75560c" dependencies = [ "aws-credential-types", "aws-runtime", "aws-smithy-async", "aws-smithy-http", - "aws-smithy-json", + "aws-smithy-json 0.61.1", "aws-smithy-runtime", "aws-smithy-runtime-api", "aws-smithy-types", @@ -239,21 +219,22 @@ dependencies = [ [[package]] name = "aws-sdk-bedrockruntime" -version = "1.59.0" +version = "1.64.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b257117450ded4c42fda3da48b686bacc9b1eac59e44108e80ade005c4029c2" +checksum = "37ed488cf4e3f3acd56ead725dc01a60afcad0161b46c692aa605d63c679e7d0" dependencies = [ "aws-credential-types", "aws-runtime", "aws-smithy-async", "aws-smithy-eventstream", "aws-smithy-http", - "aws-smithy-json", + "aws-smithy-json 0.61.1", "aws-smithy-runtime", "aws-smithy-runtime-api", "aws-smithy-types", "aws-types", "bytes", + "fastrand", "http 0.2.12", "once_cell", "regex-lite", @@ -262,15 +243,15 @@ dependencies = [ [[package]] name = "aws-sdk-sso" -version = "1.48.0" +version = "1.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ded855583fa1d22e88fe39fd6062b062376e50a8211989e07cf5e38d52eb3453" +checksum = "05ca43a4ef210894f93096039ef1d6fa4ad3edfabb3be92b80908b9f2e4b4eab" dependencies = [ "aws-credential-types", "aws-runtime", "aws-smithy-async", "aws-smithy-http", - "aws-smithy-json", + "aws-smithy-json 0.61.1", "aws-smithy-runtime", "aws-smithy-runtime-api", "aws-smithy-types", @@ -284,15 +265,15 @@ dependencies = [ [[package]] name = "aws-sdk-ssooidc" -version = "1.49.0" +version = "1.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9177ea1192e6601ae16c7273385690d88a7ed386a00b74a6bc894d12103cd933" +checksum = "abaf490c2e48eed0bb8e2da2fb08405647bd7f253996e0f93b981958ea0f73b0" dependencies = [ "aws-credential-types", "aws-runtime", "aws-smithy-async", "aws-smithy-http", - "aws-smithy-json", + "aws-smithy-json 0.61.1", "aws-smithy-runtime", "aws-smithy-runtime-api", "aws-smithy-types", @@ -306,15 +287,15 @@ dependencies = [ [[package]] name = "aws-sdk-sts" -version = "1.48.0" +version = "1.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "823ef553cf36713c97453e2ddff1eb8f62be7f4523544e2a5db64caf80100f0a" +checksum = "b68fde0d69c8bfdc1060ea7da21df3e39f6014da316783336deff0a9ec28f4bf" dependencies = [ "aws-credential-types", "aws-runtime", "aws-smithy-async", "aws-smithy-http", - "aws-smithy-json", + "aws-smithy-json 0.61.1", "aws-smithy-query", "aws-smithy-runtime", "aws-smithy-runtime-api", @@ -329,9 +310,9 @@ dependencies = [ [[package]] name = "aws-sigv4" -version = "1.2.5" +version = "1.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5619742a0d8f253be760bfbb8e8e8368c69e3587e4637af5754e488a611499b1" +checksum = "7d3820e0c08d0737872ff3c7c1f21ebbb6693d832312d6152bf18ef50a5471c2" dependencies = [ "aws-credential-types", "aws-smithy-http", @@ -402,6 +383,15 @@ dependencies = [ "aws-smithy-types", ] +[[package]] +name = "aws-smithy-json" +version = "0.61.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee4e69cc50921eb913c6b662f8d909131bb3e6ad6cb6090d3a39b66fc5c52095" +dependencies = [ + "aws-smithy-types", +] + [[package]] name = "aws-smithy-query" version = "0.60.7" @@ -414,9 +404,9 @@ dependencies = [ [[package]] name = "aws-smithy-runtime" -version = "1.7.3" +version = "1.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be28bd063fa91fd871d131fc8b68d7cd4c5fa0869bea68daca50dcb1cbd76be2" +checksum = "9f20685047ca9d6f17b994a07f629c813f08b5bce65523e47124879e60103d45" dependencies = [ "aws-smithy-async", "aws-smithy-http", @@ -587,6 +577,31 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bon" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f265cdb2e8501f1c952749e78babe8f1937be92c98120e5f78fc72d634682bad" +dependencies = [ + "bon-macros", + "rustversion", +] + +[[package]] +name = "bon-macros" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38aa5c627cd7706490e5b003d685f8b9d69bc343b1a00b9fdd01e75fdf6827cf" +dependencies = [ + "darling", + "ident_case", + "prettyplease", + "proc-macro2", + "quote", + "rustversion", + "syn", +] + [[package]] name = "bstr" version = "1.9.1" @@ -650,38 +665,24 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.38" +version = "0.4.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" dependencies = [ "android-tzdata", "iana-time-zone", "js-sys", "num-traits", + "serde", "wasm-bindgen", "windows-targets 0.52.6", ] [[package]] name = "clap" -version = "2.34.0" +version = "4.5.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" -dependencies = [ - "ansi_term", - "atty", - "bitflags 1.3.2", - "strsim 0.8.0", - "textwrap", - "unicode-width", - "vec_map", -] - -[[package]] -name = "clap" -version = "4.5.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64acc1846d54c1fe936a78dc189c34e28d3f5afc348403f28ecf53660b9b8462" +checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84" dependencies = [ "clap_builder", "clap_derive", @@ -689,14 +690,14 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.9" +version = "4.5.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fb8393d67ba2e7bfaf28a23458e4e2b543cc73a99595511eb207fdb8aede942" +checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838" dependencies = [ "anstream", "anstyle", "clap_lex", - "strsim 0.11.1", + "strsim", "terminal_size", ] @@ -706,26 +707,26 @@ version = "4.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d598e88f6874d4b888ed40c71efbcbf4076f1dfbae128a08a8c9e45f710605d" dependencies = [ - "clap 4.5.9", + "clap", ] [[package]] name = "clap_derive" -version = "4.5.8" +version = "4.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bac35c6dafb060fd4d275d9a4ffae97917c13a6327903a8be2153cd964f7085" +checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" dependencies = [ - "heck 0.5.0", + "heck", "proc-macro2", "quote", - "syn 2.0.70", + "syn", ] [[package]] name = "clap_lex" -version = "0.7.1" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" [[package]] name = "colorchoice" @@ -735,13 +736,13 @@ checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" [[package]] name = "comrak" -version = "0.29.0" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8c32ff8b21372fab0e9ecc4e42536055702dc5faa418362bffd1544f9d12637" +checksum = "453dcb42e33f7b474d7e0db12e0b8d82802c88f35cf5a1d8c297d0dfcecb154f" dependencies = [ + "bon", "caseless", - "clap 4.5.9", - "derive_builder", + "clap", "entities", "memchr", "once_cell", @@ -852,8 +853,8 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "strsim 0.11.1", - "syn 2.0.70", + "strsim", + "syn", ] [[package]] @@ -864,7 +865,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn 2.0.70", + "syn", ] [[package]] @@ -893,37 +894,6 @@ dependencies = [ "powerfmt", ] -[[package]] -name = "derive_builder" -version = "0.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947" -dependencies = [ - "derive_builder_macro", -] - -[[package]] -name = "derive_builder_core" -version = "0.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8" -dependencies = [ - "darling", - "proc-macro2", - "quote", - "syn 2.0.70", -] - -[[package]] -name = "derive_builder_macro" -version = "0.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" -dependencies = [ - "derive_builder_core", - "syn 2.0.70", -] - [[package]] name = "deunicode" version = "1.6.0" @@ -1075,9 +1045,9 @@ dependencies = [ [[package]] name = "flate2" -version = "1.0.34" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0" +checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" dependencies = [ "crc32fast", "miniz_oxide 0.8.0", @@ -1150,9 +1120,9 @@ checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] name = "futures-io" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-macro" @@ -1162,7 +1132,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn", ] [[package]] @@ -1272,20 +1242,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "handlebars" -version = "5.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d08485b96a0e6393e9e4d1b8d48cf74ad6c063cd905eb33f42c1ce3f0377539b" -dependencies = [ - "log", - "pest", - "pest_derive", - "serde", - "serde_json", - "thiserror", -] - [[package]] name = "handlebars" version = "6.2.0" @@ -1332,30 +1288,12 @@ dependencies = [ "http 0.2.12", ] -[[package]] -name = "heck" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" -dependencies = [ - "unicode-segmentation", -] - [[package]] name = "heck" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - [[package]] name = "hermit-abi" version = "0.3.9" @@ -1388,7 +1326,7 @@ dependencies = [ "markup5ever", "proc-macro2", "quote", - "syn 2.0.70", + "syn", ] [[package]] @@ -1799,19 +1737,19 @@ dependencies = [ [[package]] name = "mdbook" -version = "0.4.40" +version = "0.4.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b45a38e19bd200220ef07c892b0157ad3d2365e5b5a267ca01ad12182491eea5" +checksum = "fe1f98b8d66e537d2f0ba06e7dec4f44001deec539a2d18bfc102d6a86189148" dependencies = [ "ammonia", "anyhow", "chrono", - "clap 4.5.9", + "clap", "clap_complete", "elasticlunr-rs", "env_logger", "futures-util", - "handlebars 5.1.2", + "handlebars", "ignore", "log", "memchr", @@ -1838,25 +1776,12 @@ name = "mdbook-goals" version = "0.1.0" dependencies = [ "anyhow", - "aws-config", - "aws-sdk-bedrock", - "aws-sdk-bedrockruntime", - "chrono", - "comrak", - "disk-persist", - "handlebars 6.2.0", - "lazy_static", + "clap", "mdbook", - "progress_bar", "regex", - "reqwest", - "rust_team_data", + "rust-project-goals", "semver", - "serde", "serde_json", - "structopt", - "tokio", - "walkdir", ] [[package]] @@ -1917,7 +1842,7 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" dependencies = [ - "hermit-abi 0.3.9", + "hermit-abi", "libc", "wasi", "windows-sys 0.52.0", @@ -2096,7 +2021,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn", ] [[package]] @@ -2189,7 +2114,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.70", + "syn", ] [[package]] @@ -2277,7 +2202,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn", ] [[package]] @@ -2330,43 +2255,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" [[package]] -name = "proc-macro-error" -version = "1.0.4" +name = "prettyplease" +version = "0.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033" dependencies = [ - "proc-macro-error-attr", "proc-macro2", - "quote", - "syn 1.0.109", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", + "syn", ] [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] [[package]] name = "progress_bar" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94320ad982441d1294a56ba407d963c09fad89c70def47288603350b88cd6b94" +checksum = "990190dcd3c05bde8d044682f33cab9b194c44b1ea809b9d4e7457d48d24649d" dependencies = [ "lazy_static", ] @@ -2468,9 +2379,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.5" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", @@ -2480,9 +2391,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", @@ -2497,15 +2408,15 @@ checksum = "53a49587ad06b26609c52e423de037e7f57f20d53535d66e08c695f347df952a" [[package]] name = "regex-syntax" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" -version = "0.12.5" +version = "0.12.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7d6d2a27d57148378eb5e111173f4276ad26340ecc5c49a4a2152167a2d6a37" +checksum = "a77c62af46e79de0a562e1a9849205ffcb7fc1238876e9bd743357570e04046f" dependencies = [ "base64 0.22.1", "bytes", @@ -2542,7 +2453,7 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "winreg", + "windows-registry", ] [[package]] @@ -2560,6 +2471,77 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rust-project-goals" +version = "0.1.0" +dependencies = [ + "anyhow", + "chrono", + "disk-persist", + "lazy_static", + "regex", + "reqwest", + "rust-project-goals-json", + "rust_team_data", + "serde", + "serde_json", + "walkdir", +] + +[[package]] +name = "rust-project-goals-cli" +version = "0.1.0" +dependencies = [ + "anyhow", + "chrono", + "clap", + "progress_bar", + "regex", + "rust-project-goals", + "rust-project-goals-json", + "rust-project-goals-llm", + "serde", + "serde_json", + "walkdir", +] + +[[package]] +name = "rust-project-goals-cli-llm" +version = "0.1.0" +dependencies = [ + "anyhow", + "aws-config", + "aws-sdk-bedrock", + "aws-sdk-bedrockruntime", + "chrono", + "clap", + "comrak", + "handlebars", + "progress_bar", + "rust-project-goals", + "rust-project-goals-json", + "rust-project-goals-llm", + "serde", + "serde_json", + "tokio", +] + +[[package]] +name = "rust-project-goals-json" +version = "0.1.0" +dependencies = [ + "serde", +] + +[[package]] +name = "rust-project-goals-llm" +version = "0.1.0" +dependencies = [ + "chrono", + "clap", + "serde", +] + [[package]] name = "rust_team_data" version = "1.0.0" @@ -2680,6 +2662,12 @@ dependencies = [ "untrusted", ] +[[package]] +name = "rustversion" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" + [[package]] name = "ryu" version = "1.0.18" @@ -2757,31 +2745,32 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "serde" -version = "1.0.204" +version = "1.0.216" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" +checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.204" +version = "1.0.216" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" +checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn", ] [[package]] name = "serde_json" -version = "1.0.120" +version = "1.0.133" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5" +checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" dependencies = [ "itoa", + "memchr", "ryu", "serde", ] @@ -2914,42 +2903,12 @@ dependencies = [ "quote", ] -[[package]] -name = "strsim" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" - [[package]] name = "strsim" version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" -[[package]] -name = "structopt" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10" -dependencies = [ - "clap 2.34.0", - "lazy_static", - "structopt-derive", -] - -[[package]] -name = "structopt-derive" -version = "0.4.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" -dependencies = [ - "heck 0.3.3", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "subtle" version = "2.6.1" @@ -2958,20 +2917,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.70" +version = "2.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f0209b68b3613b093e0ec905354eccaedcfe83b8cb37cbdeae64026c3064c16" +checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" dependencies = [ "proc-macro2", "quote", @@ -2983,6 +2931,9 @@ name = "sync_wrapper" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" +dependencies = [ + "futures-core", +] [[package]] name = "syntect" @@ -3009,20 +2960,20 @@ dependencies = [ [[package]] name = "system-configuration" -version = "0.5.1" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.6.0", "core-foundation", "system-configuration-sys", ] [[package]] name = "system-configuration-sys" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" dependencies = [ "core-foundation-sys", "libc", @@ -3053,21 +3004,12 @@ dependencies = [ [[package]] name = "terminal_size" -version = "0.3.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7" +checksum = "5352447f921fda68cf61b4101566c0bdb5104eff6804d0678e5227580ab6a4e9" dependencies = [ "rustix", - "windows-sys 0.48.0", -] - -[[package]] -name = "textwrap" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" -dependencies = [ - "unicode-width", + "windows-sys 0.59.0", ] [[package]] @@ -3087,7 +3029,7 @@ checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn", ] [[package]] @@ -3138,9 +3080,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.41.0" +version = "1.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "145f3413504347a2be84393cc8a7d2fb4d863b375909ea59f2158261aa258bbb" +checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551" dependencies = [ "backtrace", "bytes", @@ -3162,7 +3104,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn", ] [[package]] @@ -3277,13 +3219,13 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.27" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn", ] [[package]] @@ -3368,18 +3310,6 @@ dependencies = [ "tinyvec", ] -[[package]] -name = "unicode-segmentation" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" - -[[package]] -name = "unicode-width" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" - [[package]] name = "unicode_categories" version = "0.1.1" @@ -3433,12 +3363,6 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" -[[package]] -name = "vec_map" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" - [[package]] name = "version_check" version = "0.9.4" @@ -3525,7 +3449,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.70", + "syn", "wasm-bindgen-shared", ] @@ -3559,7 +3483,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -3620,6 +3544,36 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-registry" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" +dependencies = [ + "windows-result", + "windows-strings", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result", + "windows-targets 0.52.6", +] + [[package]] name = "windows-sys" version = "0.48.0" @@ -3638,6 +3592,15 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + [[package]] name = "windows-targets" version = "0.48.5" @@ -3759,16 +3722,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" -[[package]] -name = "winreg" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5" -dependencies = [ - "cfg-if", - "windows-sys 0.48.0", -] - [[package]] name = "xdg" version = "2.5.2" diff --git a/Cargo.toml b/Cargo.toml index 744a91c..00fd7ab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [workspace] -members = ["mdbook-goals"] +members = ["crates/mdbook-goals", "crates/rust-project-goals", "crates/rust-project-goals-cli", "crates/rust-project-goals-cli-llm", "crates/rust-project-goals-json", "crates/rust-project-goals-llm"] resolver = "2" [profile.dev] diff --git a/mdbook-goals/.gitignore b/crates/mdbook-goals/.gitignore similarity index 100% rename from mdbook-goals/.gitignore rename to crates/mdbook-goals/.gitignore diff --git a/mdbook-goals/2024h2.md b/crates/mdbook-goals/2024h2.md similarity index 100% rename from mdbook-goals/2024h2.md rename to crates/mdbook-goals/2024h2.md diff --git a/mdbook-goals/Cargo.lock b/crates/mdbook-goals/Cargo.lock similarity index 100% rename from mdbook-goals/Cargo.lock rename to crates/mdbook-goals/Cargo.lock diff --git a/crates/mdbook-goals/Cargo.toml b/crates/mdbook-goals/Cargo.toml new file mode 100644 index 0000000..3099db0 --- /dev/null +++ b/crates/mdbook-goals/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "mdbook-goals" +version = "0.1.0" +edition = "2021" + +[dependencies] +anyhow = "1.0.94" +clap = "4.5.23" +mdbook = "0.4.43" +regex = "1.11.1" +rust-project-goals = { version = "0.1.0", path = "../rust-project-goals" } +semver = "1.0.23" +serde_json = "1.0.133" diff --git a/mdbook-goals/README.md b/crates/mdbook-goals/README.md similarity index 100% rename from mdbook-goals/README.md rename to crates/mdbook-goals/README.md diff --git a/crates/mdbook-goals/src/main.rs b/crates/mdbook-goals/src/main.rs new file mode 100644 index 0000000..2d636b9 --- /dev/null +++ b/crates/mdbook-goals/src/main.rs @@ -0,0 +1,72 @@ +use clap::Parser; +use mdbook::preprocess::{CmdPreprocessor, Preprocessor}; +use mdbook_preprocessor::GoalPreprocessor; +use semver::{Version, VersionReq}; +use std::io; + +mod mdbook_preprocessor; + +#[derive(clap::Parser, Debug)] +#[structopt(about = "Project goal preprocessor")] +struct Opt { + #[command(subcommand)] + cmd: Option, +} + +#[derive(clap::Subcommand, Debug)] +#[allow(dead_code)] +enum Command { + /// Command used by mdbook to check if the preprocessor supports a renderer + Supports { renderer: String }, +} + +fn main() -> anyhow::Result<()> { + let opt = Opt::parse(); + + let Some(cmd) = &opt.cmd else { + return handle_preprocessing(&GoalPreprocessor); + }; + + match cmd { + Command::Supports { renderer } => { + handle_supports(&GoalPreprocessor, renderer)?; + } + } + + Ok(()) +} + +// from https://github.com/rust-lang/mdBook/blob/master/examples/nop-preprocessor.rs +fn handle_supports(pre: &dyn Preprocessor, renderer: &str) -> anyhow::Result<()> { + let supported = pre.supports_renderer(renderer); + + // Signal whether the renderer is supported by exiting with 1 or 0. + if supported { + Ok(()) + } else { + anyhow::bail!("renderer `{}` unsupported", renderer) + } +} + +// from https://github.com/rust-lang/mdBook/blob/master/examples/nop-preprocessor.rs +fn handle_preprocessing(pre: &dyn Preprocessor) -> anyhow::Result<()> { + let (ctx, book) = CmdPreprocessor::parse_input(io::stdin())?; + + let book_version = Version::parse(&ctx.mdbook_version)?; + let version_req = VersionReq::parse(mdbook::MDBOOK_VERSION)?; + + if !version_req.matches(&book_version) { + eprintln!( + "Warning: The {} plugin was built against version {} of mdbook, \ + but we're being called from version {}", + pre.name(), + mdbook::MDBOOK_VERSION, + ctx.mdbook_version + ); + } + + let processed_book = pre.run(&ctx, book)?; + serde_json::to_writer(io::stdout(), &processed_book)?; + + Ok(()) +} diff --git a/mdbook-goals/src/mdbook_preprocessor.rs b/crates/mdbook-goals/src/mdbook_preprocessor.rs similarity index 98% rename from mdbook-goals/src/mdbook_preprocessor.rs rename to crates/mdbook-goals/src/mdbook_preprocessor.rs index 7ec46ab..0d9f430 100644 --- a/mdbook-goals/src/mdbook_preprocessor.rs +++ b/crates/mdbook-goals/src/mdbook_preprocessor.rs @@ -8,10 +8,12 @@ use mdbook::book::{Book, Chapter}; use mdbook::preprocess::{Preprocessor, PreprocessorContext}; use mdbook::BookItem; use regex::{Captures, Regex}; +use rust_project_goals::util::GithubUserInfo; -use crate::goal::{self, format_team_asks, GoalDocument, Status, TeamAsk}; -use crate::util::GithubUserInfo; -use crate::{re, team}; +use rust_project_goals::{ + goal::{self, format_team_asks, GoalDocument, Status, TeamAsk}, + re, team, +}; const LINKS: &str = "links"; const LINKIFIERS: &str = "linkifiers"; diff --git a/crates/rust-project-goals-cli-llm/Cargo.toml b/crates/rust-project-goals-cli-llm/Cargo.toml new file mode 100644 index 0000000..cd1d222 --- /dev/null +++ b/crates/rust-project-goals-cli-llm/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "rust-project-goals-cli-llm" +version = "0.1.0" +edition = "2021" + +[dependencies] +anyhow = "1.0.94" +aws-config = "1.5.8" +aws-sdk-bedrock = "1.57.0" +aws-sdk-bedrockruntime = "1.55.0" +chrono = "0.4.39" +clap = { version = "4.5.23", features = ["derive"] } +rust-project-goals = { version = "0.1.0", path = "../rust-project-goals" } +rust-project-goals-llm = { version = "0.1.0", path = "../rust-project-goals-llm" } +handlebars = { version = "6.2.0", features = ["dir_source"] } +serde = "1.0.216" +serde_json = "1.0.133" +comrak = "0.31.0" +progress_bar = "1.0.6" +tokio = { version = "1.42.0", features = ["full"] } +rust-project-goals-json = { version = "0.1.0", path = "../rust-project-goals-json" } diff --git a/mdbook-goals/src/llm.rs b/crates/rust-project-goals-cli-llm/src/llm.rs similarity index 98% rename from mdbook-goals/src/llm.rs rename to crates/rust-project-goals-cli-llm/src/llm.rs index 4d8d1bc..11de503 100644 --- a/mdbook-goals/src/llm.rs +++ b/crates/rust-project-goals-cli-llm/src/llm.rs @@ -1,6 +1,3 @@ -//! Code to invoke a LLM to summarize content and generate blog posts. -//! Currently based on AWS bedrock. - use anyhow::Context; use aws_config::{ environment::EnvironmentVariableCredentialsProvider, diff --git a/crates/rust-project-goals-cli-llm/src/main.rs b/crates/rust-project-goals-cli-llm/src/main.rs new file mode 100644 index 0000000..25829c7 --- /dev/null +++ b/crates/rust-project-goals-cli-llm/src/main.rs @@ -0,0 +1,48 @@ +//! Code to invoke a LLM to summarize content and generate blog posts. +//! Currently based on AWS bedrock. + +use clap::Parser; +use rust_project_goals::gh::issue_id::Repository; +use rust_project_goals_llm::UpdateArgs; + +mod llm; +mod templates; +mod updates; + +#[derive(clap::Parser, Debug)] +#[structopt(about = "Project goal preprocessor")] +struct Opt { + repository: Repository, + updates_json: String, +} + +#[tokio::main] +async fn main() -> anyhow::Result<()> { + let Opt { + repository, + updates_json, + } = Opt::parse(); + let UpdateArgs { + milestone, + quick, + vscode, + output_file, + start_date, + end_date, + model_id, + region, + } = &serde_json::from_str(&updates_json)?; + updates::updates( + &repository, + milestone, + output_file.as_deref(), + start_date, + end_date, + *quick, + *vscode, + model_id.as_deref(), + region.as_deref(), + ) + .await?; + Ok(()) +} diff --git a/mdbook-goals/src/templates.rs b/crates/rust-project-goals-cli-llm/src/templates.rs similarity index 97% rename from mdbook-goals/src/templates.rs rename to crates/rust-project-goals-cli-llm/src/templates.rs index f3c1448..12d44e3 100644 --- a/mdbook-goals/src/templates.rs +++ b/crates/rust-project-goals-cli-llm/src/templates.rs @@ -3,7 +3,7 @@ use std::path::{Path, PathBuf}; use handlebars::{DirectorySourceOptions, Handlebars}; use serde::Serialize; -use crate::json::Progress; +use rust_project_goals_json::Progress; pub struct Templates<'h> { reg: Handlebars<'h>, @@ -11,7 +11,7 @@ pub struct Templates<'h> { impl<'h> Templates<'h> { pub fn new() -> anyhow::Result { - let templates = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../templates"); + let templates = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../templates"); Self::from_templates_dir(&templates) } diff --git a/mdbook-goals/src/updates.rs b/crates/rust-project-goals-cli-llm/src/updates.rs similarity index 94% rename from mdbook-goals/src/updates.rs rename to crates/rust-project-goals-cli-llm/src/updates.rs index b7b6523..1827fc6 100644 --- a/mdbook-goals/src/updates.rs +++ b/crates/rust-project-goals-cli-llm/src/updates.rs @@ -1,21 +1,18 @@ use anyhow::Context; use chrono::{Datelike, NaiveDate}; +use rust_project_goals::util::comma; +use rust_project_goals_json::GithubIssueState; use std::collections::BTreeMap; use std::io::Write; use std::path::Path; use std::process::{Command, Stdio}; -use crate::gh::issues::ExistingGithubIssue; -use crate::templates::{Updates, UpdatesGoal}; -use crate::{ - gh::{ - issue_id::{IssueId, Repository}, - issues::{list_issue_titles_in_milestone, ExistingGithubComment, ExistingIssueState}, - }, - json::checkboxes, - llm::LargeLanguageModel, - templates, - util::comma, +use crate::llm::LargeLanguageModel; +use crate::templates::{self, Updates, UpdatesGoal}; +use rust_project_goals::gh::issues::ExistingGithubIssue; +use rust_project_goals::gh::{ + issue_id::{IssueId, Repository}, + issues::{checkboxes, list_issue_titles_in_milestone, ExistingGithubComment}, }; const QUICK_UPDATES: &[&str] = &[ @@ -174,7 +171,7 @@ async fn prepare_flagship_goals( } .url(), progress, - is_closed: issue.state == ExistingIssueState::Closed, + is_closed: issue.state == GithubIssueState::Closed, updates_markdown: summary, }); @@ -249,7 +246,7 @@ async fn prepare_other_goals( number: issue.number, } .url(), - is_closed: issue.state == ExistingIssueState::Closed, + is_closed: issue.state == GithubIssueState::Closed, updates_markdown: summary, progress: checkboxes(&issue), }; diff --git a/crates/rust-project-goals-cli/Cargo.toml b/crates/rust-project-goals-cli/Cargo.toml new file mode 100644 index 0000000..44b912e --- /dev/null +++ b/crates/rust-project-goals-cli/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "rust-project-goals-cli" +version = "0.1.0" +edition = "2021" + +[dependencies] +anyhow = "1.0.94" +regex = "1.11.1" +rust-project-goals = { version = "0.1.0", path = "../rust-project-goals" } +rust-project-goals-llm = { version = "0.1.0", path = "../rust-project-goals-llm" } +serde = "1.0.216" +walkdir = "2.5.0" +serde_json = "1.0.133" +chrono = "0.4.39" +progress_bar = "1.0.6" +clap = { version = "4.5.23", features = ["derive"] } +rust-project-goals-json = { version = "0.1.0", path = "../rust-project-goals-json" } diff --git a/crates/rust-project-goals-cli/src/generate_json.rs b/crates/rust-project-goals-cli/src/generate_json.rs new file mode 100644 index 0000000..4138b87 --- /dev/null +++ b/crates/rust-project-goals-cli/src/generate_json.rs @@ -0,0 +1,60 @@ +use std::path::PathBuf; + +use rust_project_goals::gh::{ + issue_id::Repository, + issues::{checkboxes, list_issue_titles_in_milestone, ExistingGithubComment}, +}; +use rust_project_goals_json::{TrackingIssue, TrackingIssueUpdate, TrackingIssues}; + +pub(super) fn generate_json( + repository: &Repository, + milestone: &str, + json_path: &Option, +) -> anyhow::Result<()> { + let issues = list_issue_titles_in_milestone(repository, milestone)?; + + let issues = TrackingIssues { + issues: issues + .into_iter() + .map(|(title, issue)| { + let progress = checkboxes(&issue); + TrackingIssue { + number: issue.number, + title, + flagship: issue.has_flagship_label(), + progress, + assignees: issue.assignees.into_iter().collect(), + updates: updates(issue.comments), + state: issue.state, + } + }) + .collect(), + repository: repository.to_string(), + milestone: milestone.to_string(), + }; + + if let Some(json_path) = json_path { + let json = serde_json::to_string(&issues)?; + if let Some(parent) = json_path.parent() { + std::fs::create_dir_all(parent)?; + } + std::fs::write(json_path, json)?; + } else { + println!("{}", serde_json::to_string_pretty(&issues)?); + } + + Ok(()) +} + +fn updates(comments: Vec) -> Vec { + comments + .into_iter() + .filter(|comment| !comment.is_automated_comment()) + .map(|comment| TrackingIssueUpdate { + author: comment.author, + body: comment.body, + created_at: comment.created_at, + url: comment.url, + }) + .collect() +} diff --git a/crates/rust-project-goals-cli/src/main.rs b/crates/rust-project-goals-cli/src/main.rs new file mode 100644 index 0000000..e44c5c6 --- /dev/null +++ b/crates/rust-project-goals-cli/src/main.rs @@ -0,0 +1,163 @@ +use anyhow::{bail, Context}; +use clap::Parser; +use regex::Regex; +use rust_project_goals::gh::issue_id::Repository; +use rust_project_goals_llm::UpdateArgs; +use std::path::PathBuf; +use walkdir::WalkDir; + +mod generate_json; +mod rfc; +mod team_repo; + +#[derive(clap::Parser, Debug)] +#[structopt(about = "Project goal preprocessor")] +struct Opt { + #[command(subcommand)] + cmd: Command, + + /// Repository to use if applicable + #[arg(long, default_value = "rust-lang/rust-project-goals")] + repository: Repository, +} + +#[derive(clap::Subcommand, Debug)] +#[allow(dead_code)] +enum Command { + /// Print the comment required to initiate FCP + FCP { path: PathBuf }, + + /// Print the RFC text to stdout + RFC { path: PathBuf }, + + /// Use `gh` CLI tool to create issues on the rust-lang/rust-project-goals repository + Issues { + path: PathBuf, + + /// Number of milliseconds to pause between github commands + /// to avoid rate limiting + #[arg(long, default_value = "500")] + sleep: u64, + + /// Without this option, no action is taken. + #[arg(long)] + commit: bool, + }, + + /// Generate the project-goal-owners team based on the owners found in `paths`. + TeamRepo { + /// Paths to the directories containing the goals (e.g., `src/2024h2`) + #[arg(required = true /* , min_values = 1 */)] + path: Vec, + + /// Paths to the teams repository checkout + #[arg(required = true, long = "team-repo")] + team_repo_path: PathBuf, + }, + + /// Checks that the goal documents are well-formed, intended for use within CI + Check {}, + + /// Generate json file with status from tracking issues. + /// This is intended for storing alongside the book for consumption by external tools. + Json { + /// Milestone for which we generate tracking issue data (e.g., `2024h2`). + milestone: String, + + /// Path to write the json (e.g., `book/html/api/milestone.json`). + /// If not provided, writes to stdout. + #[arg(long)] + json_path: Option, + }, + + /// Generate markdown with the list of updates for each tracking issue. + /// Collects updates + Updates { + #[command(flatten)] + updates: UpdateArgs, + }, +} + +fn main() -> anyhow::Result<()> { + let opt: Opt = Opt::parse(); + + match &opt.cmd { + Command::FCP { path } => { + rfc::generate_comment(&path)?; + } + + Command::Check {} => { + check()?; + } + + Command::RFC { path } => { + rfc::generate_rfc(&path)?; + } + + Command::Issues { + path, + commit, + sleep, + } => { + rfc::generate_issues(&opt.repository, path, *commit, *sleep) + .with_context(|| format!("failed to adjust issues; rerun command to resume"))?; + } + + Command::TeamRepo { + path, + team_repo_path, + } => { + team_repo::generate_team_repo(&path, team_repo_path)?; + } + + Command::Json { + milestone, + json_path, + } => { + generate_json::generate_json(&opt.repository, &milestone, json_path)?; + } + Command::Updates { updates } => { + // The updates command is compiled separately so that we don't have to + // build all the LLM stuff if we are not using it. + let status = std::process::Command::new("cargo") + .arg("run") + .arg("-p") + .arg("rust-project-goals-cli-llm") + .arg("-q") + .arg("--") + .arg(&opt.repository.to_string()) + .arg(&serde_json::to_string(updates).unwrap()) + .status()?; + if !status.success() { + bail!("subcommand failed"); + } + } + } + + Ok(()) +} + +fn check() -> anyhow::Result<()> { + // Look for all directories like `2024h2` or `2025h1` and load goals from those directories. + let regex = Regex::new(r"\d\d\d\dh[12]")?; + + for entry in WalkDir::new("src") { + let entry = entry?; + + if !entry.file_type().is_dir() { + continue; + } + + let Some(name) = entry.file_name().to_str() else { + continue; + }; + + if !regex.is_match(name) { + continue; + } + + let _goals = rust_project_goals::goal::goals_in_dir(entry.path())?; + } + + Ok(()) +} diff --git a/mdbook-goals/src/rfc.rs b/crates/rust-project-goals-cli/src/rfc.rs similarity index 98% rename from mdbook-goals/src/rfc.rs rename to crates/rust-project-goals-cli/src/rfc.rs index 857b967..c91cfc6 100644 --- a/mdbook-goals/src/rfc.rs +++ b/crates/rust-project-goals-cli/src/rfc.rs @@ -9,10 +9,13 @@ use std::{ use anyhow::Context; use regex::Regex; -use crate::{ +use rust_project_goals::{ gh::{ issue_id::{IssueId, Repository}, - issues::{create_issue, list_issue_titles_in_milestone, lock_issue, sync_assignees, FLAGSHIP_LABEL}, + issues::{ + create_issue, list_issue_titles_in_milestone, lock_issue, sync_assignees, + FLAGSHIP_LABEL, + }, labels::GhLabel, }, goal::{self, GoalDocument, ParsedOwners, PlanItem, Status}, diff --git a/mdbook-goals/src/team_repo.rs b/crates/rust-project-goals-cli/src/team_repo.rs similarity index 98% rename from mdbook-goals/src/team_repo.rs rename to crates/rust-project-goals-cli/src/team_repo.rs index c90dde3..17618da 100644 --- a/mdbook-goals/src/team_repo.rs +++ b/crates/rust-project-goals-cli/src/team_repo.rs @@ -4,7 +4,7 @@ use std::process::Command; use anyhow::Context; -use crate::{goal, team}; +use rust_project_goals::{goal, team}; pub(crate) fn generate_team_repo( paths: &[std::path::PathBuf], diff --git a/crates/rust-project-goals-json/Cargo.toml b/crates/rust-project-goals-json/Cargo.toml new file mode 100644 index 0000000..54f5d1d --- /dev/null +++ b/crates/rust-project-goals-json/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "rust-project-goals-json" +version = "0.1.0" +edition = "2021" + +[dependencies] +serde = { version = "1.0.216", features = ["derive"] } diff --git a/crates/rust-project-goals-json/src/lib.rs b/crates/rust-project-goals-json/src/lib.rs new file mode 100644 index 0000000..8e3d096 --- /dev/null +++ b/crates/rust-project-goals-json/src/lib.rs @@ -0,0 +1,82 @@ +//! This module contains types (e.g., [`TrackingIssues`]) that represent the +//! external API that is used by the website +//! and other tools to consume the tracking issue data. They are very similar +//! to the types in `gh` and so forth but because they represent +//! a versioned API, we copy them over here to insulate them from incidental changes. + +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize)] +pub struct TrackingIssues { + pub repository: String, + pub milestone: String, + pub issues: Vec, +} + +#[derive(Serialize, Deserialize)] +pub struct TrackingIssue { + /// Issue number on the repository + pub number: u64, + + /// Title of the tracking issue + pub title: String, + + /// True if this is a flagship goal + pub flagship: bool, + + /// State of progress + pub progress: Progress, + + /// Set of assigned people + pub assignees: Vec, + + /// Posts that we consider to be status updates, in chronological order + pub updates: Vec, + + /// Issue state + pub state: GithubIssueState, +} + +#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] +#[serde(rename_all = "UPPERCASE")] +pub enum GithubIssueState { + Open, + Closed, +} + +impl std::fmt::Display for GithubIssueState { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + GithubIssueState::Open => write!(f, "open"), + GithubIssueState::Closed => write!(f, "closed"), + } + } +} + +#[derive(Serialize, Deserialize, Debug)] +pub enum Progress { + /// We could not find any checkboxes or other details on the tracking issue. + /// So all we have is "open" or "closed". + Binary { + is_closed: bool, + }, + + /// We found checkboxes or issue listing. + Tracked { + completed: u32, + total: u32, + }, + + Error { + message: String, + }, +} + +#[derive(Serialize, Deserialize)] +pub struct TrackingIssueUpdate { + pub author: String, + pub body: String, + #[serde(rename = "createdAt")] + pub created_at: String, + pub url: String, +} diff --git a/crates/rust-project-goals-llm/Cargo.toml b/crates/rust-project-goals-llm/Cargo.toml new file mode 100644 index 0000000..04176bc --- /dev/null +++ b/crates/rust-project-goals-llm/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "rust-project-goals-llm" +version = "0.1.0" +edition = "2021" + +[dependencies] +chrono = { version = "0.4.39", features = ["serde"] } +clap = "4.5.23" +serde = "1.0.216" diff --git a/crates/rust-project-goals-llm/src/lib.rs b/crates/rust-project-goals-llm/src/lib.rs new file mode 100644 index 0000000..58666b0 --- /dev/null +++ b/crates/rust-project-goals-llm/src/lib.rs @@ -0,0 +1,41 @@ +//! Library for the LLM execution -- just encodes the command-line arguments. +//! Most of the work is in the `main.rs` binary. + +use std::path::PathBuf; + +use serde::{Deserialize, Serialize}; + +/// Updates struct +#[derive(clap::Args, Debug, Serialize, Deserialize)] +pub struct UpdateArgs { + /// Milestone for which we generate tracking issue data (e.g., `2024h2`). + pub milestone: String, + + /// Quick mode does not use an LLM to generate a summary. + #[arg(long)] + pub quick: bool, + + /// Quick mode does not use an LLM to generate a summary. + #[arg(long)] + pub vscode: bool, + + /// If specified, write the output into the given file. + #[arg(long)] + pub output_file: Option, + + /// Start date for comments. + /// If not given, defaults to 1 week before the start of this month. + pub start_date: Option, + + /// End date for comments. + /// If not given, no end date. + pub end_date: Option, + + /// Set a custom model id for the LLM. + #[arg(long)] + pub model_id: Option, + + /// Set a custom region. + #[arg(long)] + pub region: Option, +} diff --git a/crates/rust-project-goals/Cargo.toml b/crates/rust-project-goals/Cargo.toml new file mode 100644 index 0000000..4ce4691 --- /dev/null +++ b/crates/rust-project-goals/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "rust-project-goals" +version = "0.1.0" +edition = "2021" + +[dependencies] +anyhow = "1.0.94" +chrono = "0.4.39" +disk-persist = "0.1.0" +lazy_static = "1.5.0" +regex = "1.11.1" +reqwest = { version = "0.12.9", features = ["blocking", "json"] } +serde = "1.0.216" +serde_json = "1.0.133" +walkdir = "2.5.0" +rust_team_data = { git = "https://github.com/rust-lang/team" } +rust-project-goals-json = { version = "0.1.0", path = "../rust-project-goals-json" } diff --git a/mdbook-goals/src/gh.rs b/crates/rust-project-goals/src/gh.rs similarity index 100% rename from mdbook-goals/src/gh.rs rename to crates/rust-project-goals/src/gh.rs diff --git a/mdbook-goals/src/gh/issue_id.rs b/crates/rust-project-goals/src/gh/issue_id.rs similarity index 100% rename from mdbook-goals/src/gh/issue_id.rs rename to crates/rust-project-goals/src/gh/issue_id.rs diff --git a/mdbook-goals/src/gh/issues.rs b/crates/rust-project-goals/src/gh/issues.rs similarity index 70% rename from mdbook-goals/src/gh/issues.rs rename to crates/rust-project-goals/src/gh/issues.rs index b04f2f2..325740e 100644 --- a/mdbook-goals/src/gh/issues.rs +++ b/crates/rust-project-goals/src/gh/issues.rs @@ -1,13 +1,15 @@ use std::{ collections::{BTreeMap, BTreeSet}, process::Command, + str::FromStr, }; use anyhow::Context; use chrono::NaiveDate; +use rust_project_goals_json::{GithubIssueState, Progress}; use serde::{Deserialize, Serialize}; -use crate::util::comma; +use crate::{re, util::comma}; use super::{issue_id::Repository, labels::GhLabel}; @@ -18,7 +20,7 @@ pub struct ExistingGithubIssue { pub assignees: BTreeSet, pub comments: Vec, pub body: String, - pub state: ExistingIssueState, + pub state: GithubIssueState, pub labels: Vec, } @@ -38,7 +40,7 @@ struct ExistingGithubIssueJson { assignees: Vec, comments: Vec, body: String, - state: ExistingIssueState, + state: GithubIssueState, labels: Vec, } @@ -62,22 +64,6 @@ struct ExistingGithubAuthorJson { login: String, } -#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] -#[serde(rename_all = "UPPERCASE")] -pub enum ExistingIssueState { - Open, - Closed, -} - -impl std::fmt::Display for ExistingIssueState { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - ExistingIssueState::Open => write!(f, "open"), - ExistingIssueState::Closed => write!(f, "closed"), - } - } -} - pub struct CountIssues { pub open: u32, pub closed: u32, @@ -89,7 +75,7 @@ pub fn count_issues_matching_search( ) -> anyhow::Result { #[derive(Deserialize)] struct JustState { - state: ExistingIssueState, + state: GithubIssueState, } let output = Command::new("gh") @@ -111,8 +97,8 @@ pub fn count_issues_matching_search( for issue in &existing_issues { match issue.state { - ExistingIssueState::Open => count_issues.open += 1, - ExistingIssueState::Closed => count_issues.closed += 1, + GithubIssueState::Open => count_issues.open += 1, + GithubIssueState::Closed => count_issues.closed += 1, } } @@ -327,3 +313,90 @@ impl From for ExistingGithubIssue { } } } + +/// Identify how many sub-items have been completed. +/// These can be encoded in two different ways: +/// +/// * Option A, the most common, is to have checkboxes in the issue. We just count the number that are checked. +/// * Option B is to include a metadata line called "Tracked issues" that lists a search query. We count the number of open vs closed issues in that query. +/// +/// Returns a tuple (completed, total) with the number of completed items and the total number of items. +pub fn checkboxes(issue: &ExistingGithubIssue) -> Progress { + match try_checkboxes(&issue) { + Ok(pair) => pair, + Err(e) => Progress::Error { + message: e.to_string(), + }, + } +} + +fn try_checkboxes(issue: &ExistingGithubIssue) -> anyhow::Result { + let mut completed = 0; + let mut total = 0; + + for line in issue.body.lines() { + // Does this match TRACKED_ISSUES? + if let Some(c) = re::TRACKED_ISSUES_QUERY.captures(line) { + let repo = Repository::from_str(&c["repo"])?; + let query = &c["query"]; + + let CountIssues { open, closed } = count_issues_matching_search(&repo, query)?; + completed += closed; + total += open + closed; + continue; + } + + if let Some(c) = re::SEE_ALSO_QUERY.captures(line) { + let issue_urls = c["issues"].split(&[',', ' ']).filter(|s| !s.is_empty()); + + for issue_url in issue_urls { + let c = match ( + re::SEE_ALSO_ISSUE1.captures(issue_url), + re::SEE_ALSO_ISSUE2.captures(issue_url), + ) { + (Some(c), _) => c, + (None, Some(c)) => c, + (None, None) => anyhow::bail!("invalid issue URL `{issue_url}`"), + }; + let repository = Repository::new(&c["org"], &c["repo"]); + let issue_number = c["issue"].parse::()?; + let issue = fetch_issue(&repository, issue_number)?; + match try_checkboxes(&issue)? { + Progress::Binary { is_closed } => { + if is_closed { + completed += 1; + } + total += 1; + } + + Progress::Tracked { + completed: c, + total: t, + } => { + completed += c; + total += t; + } + + Progress::Error { message } => { + anyhow::bail!("error parsing {repository}#{issue_number}: {message}") + } + } + } + } + + if re::CHECKED_CHECKBOX.is_match(line) { + total += 1; + completed += 1; + } else if re::CHECKBOX.is_match(line) { + total += 1; + } + } + + if total == 0 && completed == 0 { + Ok(Progress::Binary { + is_closed: issue.state == GithubIssueState::Closed, + }) + } else { + Ok(Progress::Tracked { completed, total }) + } +} diff --git a/mdbook-goals/src/gh/labels.rs b/crates/rust-project-goals/src/gh/labels.rs similarity index 100% rename from mdbook-goals/src/gh/labels.rs rename to crates/rust-project-goals/src/gh/labels.rs diff --git a/mdbook-goals/src/goal.rs b/crates/rust-project-goals/src/goal.rs similarity index 98% rename from mdbook-goals/src/goal.rs rename to crates/rust-project-goals/src/goal.rs index 97e0842..c1bf17c 100644 --- a/mdbook-goals/src/goal.rs +++ b/crates/rust-project-goals/src/goal.rs @@ -7,13 +7,10 @@ use anyhow::Context; use regex::Regex; use crate::gh::issue_id::{IssueId, Repository}; +use crate::markwaydown::{self, Section, Table}; use crate::re::USERNAME; use crate::team::{self, TeamName}; -use crate::util::{commas, markdown_files}; -use crate::{ - markwaydown::{self, Section, Table}, - util::{self, ARROW}, -}; +use crate::util::{self, commas, markdown_files, ARROW}; /// Data parsed from a goal file in the expected format #[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] @@ -153,7 +150,7 @@ impl GoalDocument { } /// True if this goal is a candidate (may yet be accepted) - pub(crate) fn is_not_not_accepted(&self) -> bool { + pub fn is_not_not_accepted(&self) -> bool { match self.metadata.status { Status::Flagship | Status::Accepted | Status::Proposed | Status::Orphaned => true, Status::NotAccepted => false, @@ -161,7 +158,7 @@ impl GoalDocument { } /// Modify the goal document on disk to link to the given issue number in the metadata. - pub(crate) fn link_issue(&self, number: IssueId) -> anyhow::Result<()> { + pub fn link_issue(&self, number: IssueId) -> anyhow::Result<()> { let mut metadata_table = self.metadata.table.clone(); metadata_table.add_key_value_row(TRACKING_ISSUE_ROW, &number); self.metadata @@ -234,7 +231,7 @@ pub fn format_team_asks(asks_of_any_team: &[&TeamAsk]) -> anyhow::Result pub fn format_goal_table(goals: &[&GoalDocument]) -> anyhow::Result { // If any of the goals have tracking issues, include those in the table. let goals_are_proposed = goals.iter().any(|g| g.metadata.status == Status::Proposed); - + let mut table; if !goals_are_proposed { diff --git a/crates/rust-project-goals/src/lib.rs b/crates/rust-project-goals/src/lib.rs new file mode 100644 index 0000000..693c60d --- /dev/null +++ b/crates/rust-project-goals/src/lib.rs @@ -0,0 +1,6 @@ +pub mod gh; +pub mod goal; +pub mod markwaydown; +pub mod re; +pub mod team; +pub mod util; diff --git a/mdbook-goals/src/markwaydown.rs b/crates/rust-project-goals/src/markwaydown.rs similarity index 100% rename from mdbook-goals/src/markwaydown.rs rename to crates/rust-project-goals/src/markwaydown.rs diff --git a/mdbook-goals/src/re.rs b/crates/rust-project-goals/src/re.rs similarity index 100% rename from mdbook-goals/src/re.rs rename to crates/rust-project-goals/src/re.rs diff --git a/mdbook-goals/src/team.rs b/crates/rust-project-goals/src/team.rs similarity index 100% rename from mdbook-goals/src/team.rs rename to crates/rust-project-goals/src/team.rs diff --git a/mdbook-goals/src/util.rs b/crates/rust-project-goals/src/util.rs similarity index 100% rename from mdbook-goals/src/util.rs rename to crates/rust-project-goals/src/util.rs diff --git a/gh-cache/@1c3t3a.bincode b/gh-cache/@1c3t3a.bincode new file mode 100644 index 0000000..7a1dff0 Binary files /dev/null and b/gh-cache/@1c3t3a.bincode differ diff --git a/gh-cache/@vabr-g.bincode b/gh-cache/@vabr-g.bincode new file mode 100644 index 0000000..f76dd23 Binary files /dev/null and b/gh-cache/@vabr-g.bincode differ diff --git a/justfile b/justfile index 5265ea3..6289721 100644 --- a/justfile +++ b/justfile @@ -1,5 +1,5 @@ api: - cargo run -- json 2024h2 --json-path src/api/2024h2.json + cargo rpg json 2024h2 --json-path src/api/2024h2.json serve: api mdbook serve @@ -8,6 +8,5 @@ build: api mdbook build check: - cargo build - cargo run -- check + cargo rpg check \ No newline at end of file diff --git a/mdbook-goals/Cargo.toml b/mdbook-goals/Cargo.toml deleted file mode 100644 index 660b0d9..0000000 --- a/mdbook-goals/Cargo.toml +++ /dev/null @@ -1,26 +0,0 @@ -[package] -name = "mdbook-goals" -version = "0.1.0" -edition = "2021" - -[dependencies] -anyhow = "1.0.86" -disk-persist = "0.1.0" -mdbook = "0.4.40" -regex = "1.10.5" -reqwest = { version = "0.12.5", features = ["blocking", "json"] } -semver = "1.0.23" -serde = "1.0.204" -serde_json = "1.0.120" -structopt = "0.3.26" -walkdir = "2.5.0" -rust_team_data = { git = "https://github.com/rust-lang/team" } -lazy_static = "1.5.0" -progress_bar = "1.0.5" -chrono = "0.4.38" -aws-config = "1.5.8" -aws-sdk-bedrock = "1.57.0" -aws-sdk-bedrockruntime = "1.55.0" -tokio = { version = "1.41.0", features = ["full"] } -handlebars = { version = "6.1.0", features = ["dir_source"] } -comrak = "0.29.0" diff --git a/mdbook-goals/src/json.rs b/mdbook-goals/src/json.rs deleted file mode 100644 index 7e33c6f..0000000 --- a/mdbook-goals/src/json.rs +++ /dev/null @@ -1,221 +0,0 @@ -//! Generate JSON summarizing the tracking issues. -//! -//! This module contains types (e.g., [`TrackingIssues`]) that represent the -//! external API that is used by the website -//! and other tools to consume the tracking issue data. They are very similar -//! to the types in `gh` and so forth but because they represent -//! a versioned API, we copy them over here to insulate them from incidental changes. - -use std::{path::PathBuf, str::FromStr}; - -use serde::{Deserialize, Serialize}; - -use crate::{ - gh::{ - issue_id::Repository, - issues::{ - count_issues_matching_search, fetch_issue, list_issue_titles_in_milestone, CountIssues, - ExistingGithubComment, ExistingGithubIssue, ExistingIssueState, - }, - }, - re, -}; - -pub(super) fn generate_json( - repository: &Repository, - milestone: &str, - json_path: &Option, -) -> anyhow::Result<()> { - let issues = list_issue_titles_in_milestone(repository, milestone)?; - - let issues = TrackingIssues { - issues: issues - .into_iter() - .map(|(title, issue)| { - let progress = checkboxes(&issue); - TrackingIssue { - number: issue.number, - title, - flagship: issue.has_flagship_label(), - progress, - assignees: issue.assignees.into_iter().collect(), - updates: updates(issue.comments), - state: issue.state, - } - }) - .collect(), - repository: repository.to_string(), - milestone: milestone.to_string(), - }; - - if let Some(json_path) = json_path { - let json = serde_json::to_string(&issues)?; - if let Some(parent) = json_path.parent() { - std::fs::create_dir_all(parent)?; - } - std::fs::write(json_path, json)?; - } else { - println!("{}", serde_json::to_string_pretty(&issues)?); - } - - Ok(()) -} - -#[derive(Serialize)] -struct TrackingIssues { - repository: String, - milestone: String, - issues: Vec, -} - -#[derive(Serialize)] -struct TrackingIssue { - /// Issue number on the repository - number: u64, - - /// Title of the tracking issue - title: String, - - /// True if this is a flagship goal - flagship: bool, - - /// State of progress - progress: Progress, - - /// Set of assigned people - assignees: Vec, - - /// Posts that we consider to be status updates, in chronological order - updates: Vec, - - /// Issue state - state: ExistingIssueState, -} - -#[derive(Serialize, Deserialize, Debug)] -pub enum Progress { - /// We could not find any checkboxes or other details on the tracking issue. - /// So all we have is "open" or "closed". - Binary { - is_closed: bool, - }, - - /// We found checkboxes or issue listing. - Tracked { - completed: u32, - total: u32, - }, - - Error { - message: String, - }, -} - -#[derive(Serialize)] -struct TrackingIssueUpdate { - pub author: String, - pub body: String, - #[serde(rename = "createdAt")] - pub created_at: String, - pub url: String, -} - -/// Identify how many sub-items have been completed. -/// These can be encoded in two different ways: -/// -/// * Option A, the most common, is to have checkboxes in the issue. We just count the number that are checked. -/// * Option B is to include a metadata line called "Tracked issues" that lists a search query. We count the number of open vs closed issues in that query. -/// -/// Returns a tuple (completed, total) with the number of completed items and the total number of items. -pub fn checkboxes(issue: &ExistingGithubIssue) -> Progress { - match try_checkboxes(&issue) { - Ok(pair) => pair, - Err(e) => Progress::Error { - message: e.to_string(), - }, - } -} - -fn try_checkboxes(issue: &ExistingGithubIssue) -> anyhow::Result { - let mut completed = 0; - let mut total = 0; - - for line in issue.body.lines() { - // Does this match TRACKED_ISSUES? - if let Some(c) = re::TRACKED_ISSUES_QUERY.captures(line) { - let repo = Repository::from_str(&c["repo"])?; - let query = &c["query"]; - - let CountIssues { open, closed } = count_issues_matching_search(&repo, query)?; - completed += closed; - total += open + closed; - continue; - } - - if let Some(c) = re::SEE_ALSO_QUERY.captures(line) { - let issue_urls = c["issues"].split(&[',', ' ']).filter(|s| !s.is_empty()); - - for issue_url in issue_urls { - let c = match ( - re::SEE_ALSO_ISSUE1.captures(issue_url), - re::SEE_ALSO_ISSUE2.captures(issue_url), - ) { - (Some(c), _) => c, - (None, Some(c)) => c, - (None, None) => anyhow::bail!("invalid issue URL `{issue_url}`"), - }; - let repository = Repository::new(&c["org"], &c["repo"]); - let issue_number = c["issue"].parse::()?; - let issue = fetch_issue(&repository, issue_number)?; - match try_checkboxes(&issue)? { - Progress::Binary { is_closed } => { - if is_closed { - completed += 1; - } - total += 1; - } - - Progress::Tracked { - completed: c, - total: t, - } => { - completed += c; - total += t; - } - - Progress::Error { message } => { - anyhow::bail!("error parsing {repository}#{issue_number}: {message}") - } - } - } - } - - if re::CHECKED_CHECKBOX.is_match(line) { - total += 1; - completed += 1; - } else if re::CHECKBOX.is_match(line) { - total += 1; - } - } - - if total == 0 && completed == 0 { - Ok(Progress::Binary { - is_closed: issue.state == ExistingIssueState::Closed, - }) - } else { - Ok(Progress::Tracked { completed, total }) - } -} - -fn updates(comments: Vec) -> Vec { - comments - .into_iter() - .filter(|comment| !comment.is_automated_comment()) - .map(|comment| TrackingIssueUpdate { - author: comment.author, - body: comment.body, - created_at: comment.created_at, - url: comment.url, - }) - .collect() -} diff --git a/mdbook-goals/src/main.rs b/mdbook-goals/src/main.rs deleted file mode 100644 index 750d9ae..0000000 --- a/mdbook-goals/src/main.rs +++ /dev/null @@ -1,257 +0,0 @@ -use anyhow::Context; -use gh::issue_id::Repository; -use mdbook::preprocess::{CmdPreprocessor, Preprocessor}; -use mdbook_preprocessor::GoalPreprocessor; -use regex::Regex; -use semver::{Version, VersionReq}; -use std::{io, path::PathBuf}; -use structopt::StructOpt; -use walkdir::WalkDir; - -mod gh; -mod goal; -mod json; -mod llm; -mod markwaydown; -mod mdbook_preprocessor; -mod re; -mod rfc; -mod team; -mod team_repo; -mod templates; -mod updates; -mod util; - -#[derive(StructOpt, Debug)] -#[structopt(about = "Project goal preprocessor")] -struct Opt { - #[structopt(subcommand)] - cmd: Option, - - /// Repository to use if applicable - #[structopt(long, default_value = "rust-lang/rust-project-goals")] - repository: Repository, -} - -#[derive(StructOpt, Debug)] -#[allow(dead_code)] -enum Command { - /// Command used by mdbook to check if the preprocessor supports a renderer - Supports { renderer: String }, - - /// Print the comment required to initiate FCP - FCP { path: PathBuf }, - - /// Print the RFC text to stdout - RFC { path: PathBuf }, - - /// Use `gh` CLI tool to create issues on the rust-lang/rust-project-goals repository - Issues { - path: PathBuf, - - /// Number of milliseconds to pause between github commands - /// to avoid rate limiting - #[structopt(long, default_value = "500")] - sleep: u64, - - /// Without this option, no action is taken. - #[structopt(long)] - commit: bool, - }, - - /// Generate the project-goal-owners team based on the owners found in `paths`. - TeamRepo { - /// Paths to the directories containing the goals (e.g., `src/2024h2`) - #[structopt(required = true, min_values = 1)] - path: Vec, - - /// Paths to the teams repository checkout - #[structopt(required = true, long = "team-repo")] - team_repo_path: PathBuf, - }, - - /// Checks that the goal documents are well-formed, intended for use within CI - Check {}, - - /// Generate json file with status from tracking issues. - /// This is intended for storing alongside the book for consumption by external tools. - Json { - /// Milestone for which we generate tracking issue data (e.g., `2024h2`). - milestone: String, - - /// Path to write the json (e.g., `book/html/api/milestone.json`). - /// If not provided, writes to stdout. - #[structopt(long)] - json_path: Option, - }, - - /// Generate markdown with the list of updates for each tracking issue. - /// Collects updates - Updates { - /// Milestone for which we generate tracking issue data (e.g., `2024h2`). - milestone: String, - - /// Quick mode does not use an LLM to generate a summary. - #[structopt(long)] - quick: bool, - - /// Quick mode does not use an LLM to generate a summary. - #[structopt(long)] - vscode: bool, - - /// If specified, write the output into the given file. - #[structopt(long)] - output_file: Option, - - /// Start date for comments. - /// If not given, defaults to 1 week before the start of this month. - start_date: Option, - - /// End date for comments. - /// If not given, no end date. - end_date: Option, - - /// Set a custom model id for the LLM. - #[structopt(long)] - model_id: Option, - - /// Set a custom region. - #[structopt(long)] - region: Option, - }, -} - -#[tokio::main] -async fn main() -> anyhow::Result<()> { - let opt = Opt::from_args(); - - let Some(cmd) = &opt.cmd else { - return handle_preprocessing(&GoalPreprocessor); - }; - - match cmd { - Command::Supports { renderer } => { - handle_supports(&GoalPreprocessor, renderer)?; - } - - Command::FCP { path } => { - rfc::generate_comment(&path)?; - } - - Command::Check {} => { - check()?; - } - - Command::RFC { path } => { - rfc::generate_rfc(&path)?; - } - - Command::Issues { - path, - commit, - sleep, - } => { - rfc::generate_issues(&opt.repository, path, *commit, *sleep) - .with_context(|| format!("failed to adjust issues; rerun command to resume"))?; - } - - Command::TeamRepo { - path, - team_repo_path, - } => { - team_repo::generate_team_repo(&path, team_repo_path)?; - } - - Command::Json { - milestone, - json_path, - } => { - json::generate_json(&opt.repository, &milestone, json_path)?; - } - Command::Updates { - milestone, - output_file, - start_date, - end_date, - quick, - vscode, - model_id, - region, - } => { - updates::updates( - &opt.repository, - milestone, - output_file.as_deref(), - start_date, - end_date, - *quick, - *vscode, - model_id.as_deref(), - region.as_deref(), - ) - .await?; - } - } - - Ok(()) -} - -// from https://github.com/rust-lang/mdBook/blob/master/examples/nop-preprocessor.rs -fn handle_supports(pre: &dyn Preprocessor, renderer: &str) -> anyhow::Result<()> { - let supported = pre.supports_renderer(renderer); - - // Signal whether the renderer is supported by exiting with 1 or 0. - if supported { - Ok(()) - } else { - anyhow::bail!("renderer `{}` unsupported", renderer) - } -} - -// from https://github.com/rust-lang/mdBook/blob/master/examples/nop-preprocessor.rs -fn handle_preprocessing(pre: &dyn Preprocessor) -> anyhow::Result<()> { - let (ctx, book) = CmdPreprocessor::parse_input(io::stdin())?; - - let book_version = Version::parse(&ctx.mdbook_version)?; - let version_req = VersionReq::parse(mdbook::MDBOOK_VERSION)?; - - if !version_req.matches(&book_version) { - eprintln!( - "Warning: The {} plugin was built against version {} of mdbook, \ - but we're being called from version {}", - pre.name(), - mdbook::MDBOOK_VERSION, - ctx.mdbook_version - ); - } - - let processed_book = pre.run(&ctx, book)?; - serde_json::to_writer(io::stdout(), &processed_book)?; - - Ok(()) -} - -fn check() -> anyhow::Result<()> { - // Look for all directories like `2024h2` or `2025h1` and load goals from those directories. - let regex = Regex::new(r"\d\d\d\dh[12]")?; - - for entry in WalkDir::new("src") { - let entry = entry?; - - if !entry.file_type().is_dir() { - continue; - } - - let Some(name) = entry.file_name().to_str() else { - continue; - }; - - if !regex.is_match(name) { - continue; - } - - let _goals = goal::goals_in_dir(entry.path())?; - } - - Ok(()) -} diff --git a/src/admin/commands.md b/src/admin/commands.md index e9f51ea..df67052 100644 --- a/src/admin/commands.md +++ b/src/admin/commands.md @@ -1,5 +1,3 @@ # Commands -The `mdbook-goals` plugin can also be run with `cargo run --` and it has a number of useful commands: - -* `cargo run -- --help` \ No newline at end of file +There is a CLI for manipulating and checking project goals. You can run it with `cargo rpg` and it has a number of useful commands, described in this section. Use `cargo rpg --help` to get a summary. \ No newline at end of file diff --git a/src/admin/issues.md b/src/admin/issues.md index 096a8ef..679192f 100644 --- a/src/admin/issues.md +++ b/src/admin/issues.md @@ -3,7 +3,7 @@ Usage: ``` -> cargo run -- issues +> cargo rpg issues ``` The `issues` command is used to create tracking issues at the start of a project goal session. When you first run it, it will simply tell you what actions it plans to take. @@ -11,7 +11,7 @@ The `issues` command is used to create tracking issues at the start of a project To actually commit and create the issues, supply the `--commit` flag: ``` -> cargo run -- issues --commit +> cargo rpg issues --commit ``` This will also edit the goal documents to include a link to each created tracking issue. You should commit those edits. diff --git a/src/admin/updates.md b/src/admin/updates.md index 6a1d489..e03cd5a 100644 --- a/src/admin/updates.md +++ b/src/admin/updates.md @@ -3,13 +3,13 @@ Usage: ``` -> cargo run -- updates --help +> cargo rpg updates --help ``` The `updates` command generates the starting point for a monthly blog post. The output is based on the handlebars templates found in the `templates` directory. The command you probably want most often is something like this ``` -> cargo run -- updates YYYYhN --vscode +> cargo rpg updates YYYYhN --vscode ``` which will open the blogpost in a tab in VSCode. This makes it easy to copy-and-paste over to the main Rust blog.