From 59e76a3344dc2e8df83353ad543eac07c52372fa Mon Sep 17 00:00:00 2001 From: Julian Schuler Date: Sun, 12 Jan 2025 11:29:31 +0100 Subject: [PATCH 1/3] Squashed commit of the following: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 196123679d27f26abcef112003404f21a078b467 Author: Julian Schuler Date: Sun Jan 12 10:53:51 2025 +0100 Directly build tokio command instead of converting commit 406d0286bfcb6945da21d3efb0b74cd47ab7ab2f Author: Julian Schuler Date: Sun Jan 12 10:47:11 2025 +0100 Fix storing incorrect address in twix commit 39e7ebd411721a97a937f337768d63b8de83dc57 Author: Konrad Nölle Date: Sat Jan 11 20:44:00 2025 +0100 Fix relative pwd for remote cargo actions commit 860ab8bb04c46af3d1bfb6329b534524bcab3948 Author: Maximilian Schmidt Date: Sat Jan 11 19:04:13 2025 +0100 fix duplicated os check commit 713a93c5af65a1d2c91f71f38f49d8b2a0b35961 Author: Maximilian Schmidt Date: Sat Jan 11 13:00:59 2025 +0100 rework commands commit 3d12ade3ccb6327f9e5913e29031da3b6ae07d74 Author: Maximilian Schmidt Date: Sat Jan 11 10:31:30 2025 +0100 streamline logging commit abb991f6bcd7ed86bf89d793d34801b1a9e8f482 Author: Julian Schuler Date: Sat Jan 11 00:00:51 2025 +0100 Fix using incorrect manifest for update check commit 24a52a862296a1e591f64e566dcc2ecbb18ecdf2 Author: Julian Schuler Date: Fri Jan 10 23:55:41 2025 +0100 Use manifest path for checking lock files commit 1def48f9204be0d6c91d2aafcfb50d7c95791b9c Author: Julian Schuler Date: Fri Jan 10 23:55:15 2025 +0100 Update Cargo.lock files commit d5abc7bceed9cf77ff1e571257b42dbbeb27ac17 Author: Julian Schuler Date: Fri Jan 10 23:40:36 2025 +0100 Remove unused tests and implementations commit 9f46d8ddde064f8c84cb96c0a7b19a83fd4fd5b6 Author: Julian Schuler Date: Fri Jan 10 23:19:18 2025 +0100 Update CI to make use of new pepsi functionality commit 7b75e88308efce99ae4b58e530e33f3359215b9d Author: Julian Schuler Date: Fri Jan 10 23:09:35 2025 +0100 Add manifest shortcuts for all tools and services commit d6bdd5f3ef3b0dd275c18e68240c60685d4add50 Author: Julian Schuler Date: Fri Jan 10 23:07:55 2025 +0100 Remove unused fallback URL for HULK OS image commit 1d49c7428f37e6aef96e4485ac1da128e1fa2347 Author: Julian Schuler Date: Fri Jan 10 23:07:14 2025 +0100 Move parameter tester to tools commit b9d5d5a86f96b000f4465d725efc706cfbda47df Author: Julian Schuler Date: Fri Jan 10 22:35:05 2025 +0100 Update documentation commit 4a7e7f42435c8358139a3b422a98844e85e1af02 Author: Julian Schuler Date: Fri Jan 10 17:15:58 2025 +0100 Reenable softbank NAO login script commit 42e62afa484ef9aaf3ece066087dc7c549a55aa1 Author: Julian Schuler Date: Fri Jan 10 17:02:34 2025 +0100 Implement remote host commit 2f9396997127bbd22e8ab75fbb79888378742bbd Author: Julian Schuler Date: Fri Jan 10 16:45:04 2025 +0100 Use relative manifest path commit 172e0c3de02379da4ed9bbc289f6b939b7f926a6 Author: Julian Schuler Date: Fri Jan 10 16:41:54 2025 +0100 Bump SDK and OS version in hulk.toml commit 81a595fe05ad6c774f01be0e2de5caba9c27067d Author: Julian Schuler Date: Fri Jan 10 16:37:05 2025 +0100 Fix incorrect paths to hula types commit 81ff8ad4d0e05bbd210a44b1e1b001df22971462 Author: Julian Schuler Date: Fri Jan 10 15:04:45 2025 +0100 Add pepsi install commit acf09b40cf18a72cfca956849b42d6b4dddedd5b Author: Julian Schuler Date: Thu Jan 9 23:32:44 2025 +0100 Fix incorrect error message Co-authored-by: Maximilian Schmidt commit 8a117ef25de6254cf3466016713a8766713ea4d0 Author: Julian Schuler Date: Thu Jan 9 23:27:42 2025 +0100 Implement pepsi test Co-authored-by: Maximilian Schmidt commit fe916185e16fb058464f4323bc6e868518bd138b Author: Julian Schuler Date: Thu Jan 9 23:26:48 2025 +0100 Apply manifest path directly after sub command Co-authored-by: Maximilian Schmidt commit 415d9f191cb063e4df182f18b0a8eeb7940db98d Author: Julian Schuler Date: Thu Jan 9 23:04:43 2025 +0100 Update pepsi subcommand docstrings Co-authored-by: Maximilian Schmidt commit 0338dd91a7e64ca4047b7ce138100a3d6baca07b Author: Julian Schuler Date: Thu Jan 9 22:55:17 2025 +0100 Output used cargo environment Co-authored-by: Maximilian Schmidt commit 004b23b3f182a1c67fc776c50f9d4d3345609aab Author: Julian Schuler Date: Thu Jan 9 22:54:44 2025 +0100 Parse cargo environment from package metadata Co-authored-by: Maximilian Schmidt commit bf171e8bee9d9cfe2a7ac71055f24869e6c9dfa3 Author: Julian Schuler Date: Thu Jan 9 21:59:22 2025 +0100 Fix finding repository root in bevyhavior simulator commit 68553e392804910ae18443bfaca80edd234da6fe Author: Julian Schuler Date: Thu Jan 9 21:33:49 2025 +0100 Move cargo arguments to corresponding module commit 2d78f2805f9002869d664c003e9546205cf752e6 Author: Julian Schuler Date: Thu Jan 9 21:28:29 2025 +0100 Setup cargo environment before executing cargo Co-authored-by: Maximilian Schmidt commit 8a6cdb1887eab5d8f8e563c672251de6a05e5f0a Author: Julian Schuler Date: Thu Jan 9 21:28:02 2025 +0100 Remove deprecated fallback URL Co-authored-by: Maximilian Schmidt commit 74867c1d71012a28afd28496f4055f95b5be603e Author: Julian Schuler Date: Thu Jan 9 21:15:07 2025 +0100 Do not expose manifest path for pre game and upload Co-authored-by: Maximilian Schmidt commit b81cae5cd19a73959146449a2c80bc8de56c1737 Author: Julian Schuler Date: Thu Jan 9 21:14:36 2025 +0100 Use qualified paths instead of renaming Co-authored-by: Maximilian Schmidt commit 32489f846649da71869e08790a533a0fbd0e7ee3 Author: Julian Schuler Date: Thu Jan 9 21:12:31 2025 +0100 Use OsString instead of String for manifest Co-authored-by: Maximilian Schmidt commit f53c7854d5cc451758acc934d1c4cc97b9088e0a Author: Julian Schuler Date: Thu Jan 9 18:18:48 2025 +0100 Remove unused imports Co-authored-by: Maximilian Schmidt commit f31170e533fddfece7db32f36a8b2563be52fa5d Author: Julian Schuler Date: Thu Jan 9 18:17:13 2025 +0100 Add manifest flag for cargo commands Co-authored-by: Maximilian Schmidt commit dbe6bf4b9a7f5adec665f25183c509b5a9cb53f6 Author: Julian Schuler Date: Thu Jan 9 17:31:14 2025 +0100 Finish pepsi pregame implementation Co-authored-by: Maximilian Schmidt commit 032a27ee2140ac08351f722591d18d5490b7e0d9 Author: Julian Schuler Date: Thu Jan 9 17:30:33 2025 +0100 Reimplement pepsi upload Co-authored-by: Maximilian Schmidt commit 19418357b5e92e200a2f6d337ebd7e50278e990d Author: Julian Schuler Date: Thu Jan 9 17:02:00 2025 +0100 Update cargo profiles Co-authored-by: Maximilian Schmidt commit 7a25ebc9afeda36a8368d6513e990dcad9f47c4d Author: Julian Schuler Date: Thu Jan 9 14:57:42 2025 +0100 Skip downloading SDK installer if already present commit 43f0db997cbf94aae7c7d86e4056d75696634d7c Author: Julian Schuler Date: Thu Jan 9 13:46:40 2025 +0100 Implement resolving environment from CLI arguments commit 06acbbe3671c3d55c850edb4a53700428032a8da Author: Julian Schuler Date: Thu Jan 9 12:39:56 2025 +0100 Deduplicate cargo commands with shared trait commit d1118c2252cefe787dc78f5c1d50beca4eb84877 Author: Julian Schuler Date: Thu Jan 9 12:22:50 2025 +0100 Implement cargo commands commit c4202e73bc727631639af57f48de06a06333d4ea Author: Maximilian Schmidt Date: Tue Dec 17 16:47:30 2024 +0100 --wip-- commit 5e75b837cfe57481b254a60657aa2b1e2d28f7cc Author: Maximilian Schmidt Date: Sun Nov 3 22:13:32 2024 +0100 rework documentation and error messages commit 0ec71880ea952435aaa22950d9f80fd95b845034 Author: Maximilian Schmidt Date: Sun Nov 3 13:27:14 2024 +0100 Things work --- .github/workflows/pull-request.yml | 83 +- .gitignore | 1 - Cargo.lock | 177 ++++- Cargo.toml | 33 +- crates/aliveness/Cargo.toml | 2 +- crates/bevyhavior_simulator/src/simulator.rs | 14 +- .../types => crates/hula_types}/Cargo.toml | 2 +- .../hula_types}/src/control_frame.rs | 0 .../types => crates/hula_types}/src/lib.rs | 0 .../types => crates/hula_types}/src/lola.rs | 0 .../hula_types}/src/robot_state.rs | 0 crates/hulk_nao/Cargo.toml | 4 +- crates/hulk_nao/src/hula_wrapper.rs | 3 +- crates/nao/Cargo.toml | 1 + crates/nao/src/lib.rs | 93 ++- crates/parameters/src/directory.rs | 6 +- crates/repository/Cargo.toml | 6 +- crates/repository/src/cargo.rs | 190 +++++ crates/repository/src/communication.rs | 35 + crates/repository/src/configuration.rs | 32 + crates/repository/src/data_home.rs | 17 + crates/repository/src/download.rs | 44 ++ crates/repository/src/find_root.rs | 25 + crates/repository/src/image.rs | 41 + crates/repository/src/inspect_version.rs | 51 ++ crates/repository/src/lib.rs | 728 +----------------- crates/repository/src/location.rs | 113 +++ crates/repository/src/modify_json.rs | 36 + crates/repository/src/player_number.rs | 37 + crates/repository/src/recording.rs | 31 + crates/repository/src/sdk.rs | 107 +++ crates/repository/src/symlink.rs | 20 + crates/repository/src/team.rs | 30 + crates/repository/src/upload.rs | 39 + crates/source_analyzer/src/pretty.rs | 2 +- docs/tooling/pepsi.md | 4 +- docs/tooling/recording_and_replay.md | 4 +- docs/workflow/checks.md | 6 +- hulk.toml | 2 + pepsi | 4 +- scripts/remoteWorkspace | 19 +- {tools => services}/aliveness/Cargo.lock | 454 ++++++----- {tools => services}/aliveness/Cargo.toml | 6 +- {tools => services}/aliveness/src/main.rs | 0 .../aliveness/src/robot_info.rs | 3 +- {tools => services}/breeze/Cargo.lock | 233 +++--- {tools => services}/breeze/Cargo.toml | 3 + {tools => services}/breeze/src/main.rs | 0 {tools => services}/hula/Cargo.lock | 570 ++++++++++---- {tools => services}/hula/Cargo.toml | 19 +- .../hula/proxy => services/hula}/src/dbus.rs | 4 +- .../hula/proxy => services/hula}/src/idle.rs | 0 .../hula/proxy => services/hula}/src/main.rs | 0 .../hula/proxy => services/hula}/src/proxy.rs | 2 +- tools/hula/proxy/Cargo.toml | 18 - {crates => tools}/parameter_tester/Cargo.toml | 0 .../parameter_tester/src/main.rs | 0 tools/pepsi/Cargo.toml | 8 +- tools/pepsi/src/aliveness.rs | 62 +- tools/pepsi/src/analyze.rs | 44 +- tools/pepsi/src/cargo.rs | 311 +++++--- tools/pepsi/src/cargo/build.rs | 201 +++++ tools/pepsi/src/cargo/check.rs | 199 +++++ tools/pepsi/src/cargo/clippy.rs | 73 ++ tools/pepsi/src/cargo/common.rs | 185 +++++ tools/pepsi/src/cargo/environment.rs | 61 ++ tools/pepsi/src/cargo/install.rs | 170 ++++ tools/pepsi/src/cargo/run.rs | 94 +++ tools/pepsi/src/cargo/test.rs | 224 ++++++ tools/pepsi/src/communication.rs | 10 +- tools/pepsi/src/completions.rs | 6 +- tools/pepsi/src/gammaray.rs | 27 +- tools/pepsi/src/location.rs | 16 +- tools/pepsi/src/main.rs | 262 ++++--- tools/pepsi/src/ping.rs | 15 +- tools/pepsi/src/player_number.rs | 53 +- tools/pepsi/src/post_game.rs | 71 +- tools/pepsi/src/power_off.rs | 15 +- tools/pepsi/src/pre_game.rs | 248 ++++-- tools/pepsi/src/recording.rs | 17 +- tools/pepsi/src/sdk.rs | 50 +- tools/pepsi/src/upload.rs | 152 ++-- tools/pepsi/src/{wireless.rs => wifi.rs} | 4 +- tools/twix/src/main.rs | 64 +- tools/twix/src/nao.rs | 30 +- twix | 2 +- 86 files changed, 4069 insertions(+), 1959 deletions(-) rename {tools/hula/types => crates/hula_types}/Cargo.toml (89%) rename {tools/hula/types => crates/hula_types}/src/control_frame.rs (100%) rename {tools/hula/types => crates/hula_types}/src/lib.rs (100%) rename {tools/hula/types => crates/hula_types}/src/lola.rs (100%) rename {tools/hula/types => crates/hula_types}/src/robot_state.rs (100%) create mode 100644 crates/repository/src/cargo.rs create mode 100644 crates/repository/src/communication.rs create mode 100644 crates/repository/src/configuration.rs create mode 100644 crates/repository/src/data_home.rs create mode 100644 crates/repository/src/download.rs create mode 100644 crates/repository/src/find_root.rs create mode 100644 crates/repository/src/image.rs create mode 100644 crates/repository/src/inspect_version.rs create mode 100644 crates/repository/src/location.rs create mode 100644 crates/repository/src/modify_json.rs create mode 100644 crates/repository/src/player_number.rs create mode 100644 crates/repository/src/recording.rs create mode 100644 crates/repository/src/sdk.rs create mode 100644 crates/repository/src/symlink.rs create mode 100644 crates/repository/src/team.rs create mode 100644 crates/repository/src/upload.rs create mode 100644 hulk.toml rename {tools => services}/aliveness/Cargo.lock (84%) rename {tools => services}/aliveness/Cargo.toml (83%) rename {tools => services}/aliveness/src/main.rs (100%) rename {tools => services}/aliveness/src/robot_info.rs (98%) rename {tools => services}/breeze/Cargo.lock (55%) rename {tools => services}/breeze/Cargo.toml (77%) rename {tools => services}/breeze/src/main.rs (100%) rename {tools => services}/hula/Cargo.lock (79%) rename {tools => services}/hula/Cargo.toml (64%) rename {tools/hula/proxy => services/hula}/src/dbus.rs (93%) rename {tools/hula/proxy => services/hula}/src/idle.rs (100%) rename {tools/hula/proxy => services/hula}/src/main.rs (100%) rename {tools/hula/proxy => services/hula}/src/proxy.rs (99%) delete mode 100644 tools/hula/proxy/Cargo.toml rename {crates => tools}/parameter_tester/Cargo.toml (100%) rename {crates => tools}/parameter_tester/src/main.rs (100%) create mode 100644 tools/pepsi/src/cargo/build.rs create mode 100644 tools/pepsi/src/cargo/check.rs create mode 100644 tools/pepsi/src/cargo/clippy.rs create mode 100644 tools/pepsi/src/cargo/common.rs create mode 100644 tools/pepsi/src/cargo/environment.rs create mode 100644 tools/pepsi/src/cargo/install.rs create mode 100644 tools/pepsi/src/cargo/run.rs create mode 100644 tools/pepsi/src/cargo/test.rs rename tools/pepsi/src/{wireless.rs => wifi.rs} (97%) diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 50a2474dd6..d773cfbf1a 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -7,12 +7,12 @@ env: CARGO_HOME: /__w/hulk/cargo CARGO_TARGET_DIR: /__w/hulk/target CARGO_TERM_COLOR: always - NAOSDK_HOME: /__w/hulk/naosdk + HULK_DATA_HOME: /__w/hulk/data NAOSDK_AUTOMATIC_YES: 1 jobs: check: - name: Check + name: Check with clippy runs-on: - self-hosted - v3 @@ -24,14 +24,17 @@ jobs: with: lfs: true - name: Check - run: | - ./pepsi clippy --workspace + run: ./pepsi clippy check-cargo-lock: name: Check Cargo.lock strategy: matrix: - path: [., tools/aliveness, tools/hula] + path: + - . + - services/aliveness + - services/breeze + - services/hula runs-on: - self-hosted - v3 @@ -43,9 +46,7 @@ jobs: with: lfs: true - name: Check - run: | - cd ${{ matrix.path }} - cargo update --locked --workspace + run: cargo update --locked --manifest-path ${{ matrix.path }}/Cargo.toml check-parameters: name: Check parameters @@ -60,8 +61,7 @@ jobs: with: lfs: true - name: Check - run: | - cargo run --manifest-path=crates/parameter_tester/Cargo.toml + run: ./pepsi run parameter_tester format: name: Format @@ -76,8 +76,7 @@ jobs: with: lfs: true - name: Check - run: | - cargo fmt --check + run: cargo fmt --check format-toml: name: Format TOML files @@ -108,16 +107,21 @@ jobs: with: lfs: true - name: Test - run: | - cargo test --profile incremental --all-features --workspace + run: ./pepsi test --all-features build: name: Build strategy: fail-fast: true matrix: - target: [imagine, nao, replayer, webots] - profile: [release, dev] + target: + - imagine + - nao + - replayer + - webots + profile: + - dev + - release runs-on: - self-hosted - v3 @@ -129,24 +133,41 @@ jobs: with: lfs: true - name: Build - run: | - ./pepsi build --target ${{ matrix.target }} --profile ${{ matrix.profile }} + run: ./pepsi build --profile ${{ matrix.profile }} ${{ matrix.target }} + + build_services: + name: Build + strategy: + matrix: + service: + - aliveness + - breeze + - hula + runs-on: + - self-hosted + - v3 + container: + image: ghcr.io/hulks/hulk-ci:1.81.0 + options: --user=1000:1000 + steps: + - uses: actions/checkout@v4 + with: + lfs: true + - name: Build + run: ./pepsi build --release ${{ matrix.service }} build_tools: name: Build strategy: matrix: - path: - [ - aliveness, - annotato, - camera_matrix_extractor, - depp, - fanta, - hula, - pepsi, - twix, - ] + tool: + - annotato + - camera_matrix_extractor + - depp + - fanta + - pepsi + - twix + - widget_gallery runs-on: - self-hosted - v3 @@ -158,9 +179,7 @@ jobs: with: lfs: true - name: Build - run: | - cd tools/${{ matrix.path }} - cargo build --release + run: ./pepsi build --release ${{ matrix.tool }} build_mkdocs: name: Build mkdocs diff --git a/.gitignore b/.gitignore index 1ee9339eb9..d6862407d1 100644 --- a/.gitignore +++ b/.gitignore @@ -6,7 +6,6 @@ /.wiki/ settings.json -/naosdk *.edited *~ diff --git a/Cargo.lock b/Cargo.lock index 27832ac8e9..650f873110 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -167,7 +167,7 @@ name = "aliveness" version = "0.1.0" dependencies = [ "futures-util", - "hula-types", + "hula_types", "regex", "serde", "serde_json", @@ -1836,15 +1836,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32b13ea120a812beba79e34316b3942a857c86ec1593cb34f27bb28272ce2cca" -[[package]] -name = "constants" -version = "0.1.0" -dependencies = [ - "lazy_static", - "serde", - "toml", -] - [[package]] name = "content_inspector" version = "0.2.4" @@ -3489,7 +3480,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] -name = "hula-types" +name = "hula_types" version = "0.1.0" dependencies = [ "serde", @@ -3592,7 +3583,6 @@ dependencies = [ "chrono", "clap 4.5.26", "color-eyre", - "constants", "ctrlc", "enum-iterator", "fern", @@ -4346,9 +4336,12 @@ dependencies = [ [[package]] name = "log" -version = "0.4.22" +version = "0.4.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +checksum = "3d6ea2a48c204030ee31a7d7fc72c93294c92fe87ecb1789881c9543516e1a0d" +dependencies = [ + "value-bag", +] [[package]] name = "lua-src" @@ -4629,6 +4622,7 @@ name = "nao" version = "0.1.0" dependencies = [ "color-eyre", + "serde", "tokio", ] @@ -5433,6 +5427,12 @@ dependencies = [ "syn 2.0.96", ] +[[package]] +name = "pathdiff" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" + [[package]] name = "pathfinding" version = "4.10.0" @@ -5455,7 +5455,7 @@ checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" [[package]] name = "pepsi" -version = "3.17.0" +version = "4.0.0" dependencies = [ "aliveness", "argument_parsers", @@ -5463,21 +5463,25 @@ dependencies = [ "clap 4.5.26", "clap_complete", "color-eyre", - "constants", "futures-util", + "glob", "indicatif", "lazy_static", "nao", "opn", + "pathdiff", "regex", "repository", "serde", "serde_json", "source_analyzer", "spl_network_messages", + "tempfile", "thiserror", "tokio", "toml", + "tracing", + "tracing-subscriber", ] [[package]] @@ -5949,12 +5953,12 @@ name = "repository" version = "0.1.0" dependencies = [ "color-eyre", - "constants", "futures-util", - "glob", "home", "itertools 0.10.5", + "nao", "parameters", + "pathdiff", "semver", "serde", "serde_json", @@ -5962,7 +5966,9 @@ dependencies = [ "tempfile", "tokio", "toml", + "tracing", "types", + "xdg", ] [[package]] @@ -6241,6 +6247,15 @@ dependencies = [ "syn 2.0.96", ] +[[package]] +name = "serde_fmt" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d4ddca14104cd60529e8c7f7ba71a2c8acd8f7f5cfcdc2faf97eeb7c3010a4" +dependencies = [ + "serde", +] + [[package]] name = "serde_ignored" version = "0.1.10" @@ -6598,6 +6613,84 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "sval" +version = "2.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6dc0f9830c49db20e73273ffae9b5240f63c42e515af1da1fceefb69fceafd8" + +[[package]] +name = "sval_buffer" +version = "2.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "429922f7ad43c0ef8fd7309e14d750e38899e32eb7e8da656ea169dd28ee212f" +dependencies = [ + "sval", + "sval_ref", +] + +[[package]] +name = "sval_dynamic" +version = "2.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f16ff5d839396c11a30019b659b0976348f3803db0626f736764c473b50ff4" +dependencies = [ + "sval", +] + +[[package]] +name = "sval_fmt" +version = "2.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c01c27a80b6151b0557f9ccbe89c11db571dc5f68113690c1e028d7e974bae94" +dependencies = [ + "itoa", + "ryu", + "sval", +] + +[[package]] +name = "sval_json" +version = "2.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0deef63c70da622b2a8069d8600cf4b05396459e665862e7bdb290fd6cf3f155" +dependencies = [ + "itoa", + "ryu", + "sval", +] + +[[package]] +name = "sval_nested" +version = "2.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a39ce5976ae1feb814c35d290cf7cf8cd4f045782fe1548d6bc32e21f6156e9f" +dependencies = [ + "sval", + "sval_buffer", + "sval_ref", +] + +[[package]] +name = "sval_ref" +version = "2.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb7c6ee3751795a728bc9316a092023529ffea1783499afbc5c66f5fabebb1fa" +dependencies = [ + "sval", +] + +[[package]] +name = "sval_serde" +version = "2.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a5572d0321b68109a343634e3a5d576bf131b82180c6c442dee06349dfc652a" +dependencies = [ + "serde", + "sval", + "sval_nested", +] + [[package]] name = "syn" version = "1.0.109" @@ -6944,7 +7037,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "winnow 0.6.22", + "winnow 0.6.24", ] [[package]] @@ -7280,6 +7373,42 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +[[package]] +name = "value-bag" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ef4c4aa54d5d05a279399bfa921ec387b7aba77caf7a682ae8d86785b8fdad2" +dependencies = [ + "value-bag-serde1", + "value-bag-sval2", +] + +[[package]] +name = "value-bag-serde1" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bb773bd36fd59c7ca6e336c94454d9c66386416734817927ac93d81cb3c5b0b" +dependencies = [ + "erased-serde 0.4.5", + "serde", + "serde_fmt", +] + +[[package]] +name = "value-bag-sval2" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53a916a702cac43a88694c97657d449775667bcd14b70419441d05b7fea4a83a" +dependencies = [ + "sval", + "sval_buffer", + "sval_dynamic", + "sval_fmt", + "sval_json", + "sval_ref", + "sval_serde", +] + [[package]] name = "vcpkg" version = "0.2.15" @@ -8146,9 +8275,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.6.22" +version = "0.6.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39281189af81c07ec09db316b302a3e67bf9bd7cbf6c820b50e35fee9c2fa980" +checksum = "c8d71a593cc5c42ad7876e2c1fda56f314f3754c084128833e64f1345ff8a03a" dependencies = [ "memchr", ] @@ -8213,6 +8342,12 @@ version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ef33da6b1660b4ddbfb3aef0ade110c8b8a781a3b6382fa5f2b5b040fd55f61" +[[package]] +name = "xdg" +version = "2.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213b7324336b53d2414b2db8537e56544d981803139155afa84f76eeebb7a546" + [[package]] name = "xdg-home" version = "1.3.0" diff --git a/Cargo.toml b/Cargo.toml index 76dcfcb110..81d7576cc5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,6 @@ members = [ "crates/calibration", "crates/code_generation", "crates/communication", - "crates/constants", "crates/context_attribute", "crates/control", "crates/coordinate_systems", @@ -19,6 +18,7 @@ members = [ "crates/framework", "crates/geometry", "crates/hardware", + "crates/hula_types", "crates/hulk", "crates/hulk_imagine", "crates/hulk_manifest", @@ -34,7 +34,6 @@ members = [ "crates/nao_camera", "crates/object_detection", "crates/opn", - "crates/parameter_tester", "crates/parameters", "crates/path_serde", "crates/path_serde_derive", @@ -51,14 +50,18 @@ members = [ "tools/camera_matrix_extractor", "tools/depp", "tools/fanta", - "tools/hula/types", + "tools/parameter_tester", "tools/pepsi", "tools/twix", "tools/widget_gallery", ] resolver = "2" -# HuLA and Aliveness are built independently by yocto -exclude = ["tools/aliveness", "tools/breeze", "tools/hula"] +# services are built independently by yocto +exclude = [ + "services/aliveness", + "services/breeze", + "services/hula", +] [workspace.package] version = "0.1.0" @@ -88,13 +91,12 @@ build_script_helpers = { path = "crates/build_script_helpers" } byteorder = "1.4.3" calibration = { path = "crates/calibration" } chrono = "0.4.23" -clap = { version = "4.2.4", features = ["derive"] } +clap = { version = "4.2.4", features = ["derive", "env"] } clap_complete = "4.2.1" code_generation = { path = "crates/code_generation" } color-eyre = "0.6.2" communication = { path = "crates/communication" } compiled-nn = "0.12.0" -constants = { path = "crates/constants" } context_attribute = { path = "crates/context_attribute" } control = { path = "crates/control" } convert_case = "0.6.0" @@ -120,7 +122,7 @@ gilrs = "0.10.1" glob = "0.3.0" hardware = { path = "crates/hardware" } home = "=0.5.9" -hula-types = { path = "tools/hula/types" } +hula_types = { path = "crates/hula_types" } hulk = { path = "crates/hulk" } hulk_manifest = { path = "crates/hulk_manifest" } hulk_widgets = { path = "crates/hulk_widgets" } @@ -162,6 +164,7 @@ parameters = { path = "crates/parameters" } parking_lot = "0.12.1" path_serde = { path = "crates/path_serde" } path_serde_derive = { path = "crates/path_serde_derive" } +pathdiff = "0.2.3" pathfinding = "<=4.10.0" petgraph = "0.6.2" png = "0.17.6" @@ -206,6 +209,8 @@ tokio-tungstenite = "0.19.0" tokio-util = "0.7.4" toml = "0.8.8" toposort-scc = "0.5.4" +tracing = "0.1.40" +tracing-subscriber = "0.3.18" types = { path = "crates/types" } uuid = { version = "1.1.2", features = ["v4"] } v4l = { version = "0.12.1", git = "https://github.com/HULKs/libv4l-rs", rev = "be65819073514b193d082dd37dbcc2cfac3f6183" } @@ -214,6 +219,7 @@ walkdir = "2.3.2" walking_engine = { path = "crates/walking_engine" } watch = "0.2.3" webots = { version = "0.8.0" } +xdg = "2.5.2" zbus = { version = "3.7.0" } [patch.crates-io] @@ -222,10 +228,11 @@ serde = { git = "https://github.com/HULKs/serde.git", rev = "73d5e8e404a874ad900 # Pinned to forked serde version since https://github.com/serde-rs/serde/pull/2513 is not merged serde_derive = { git = "https://github.com/HULKs/serde.git", rev = "73d5e8e404a874ad90023810d634b985bef19478" } -[profile.incremental] -inherits = "release" -incremental = true +[profile.dev] +opt-level = 3 +debug = false +codegen-units = 16 # TODO: Evaluate performance for different values -[profile.release-with-debug] -inherits = "release" +[profile.with-debug] +inherits = "dev" debug = true diff --git a/crates/aliveness/Cargo.toml b/crates/aliveness/Cargo.toml index 30fbb5a09b..a227bba28e 100644 --- a/crates/aliveness/Cargo.toml +++ b/crates/aliveness/Cargo.toml @@ -7,7 +7,7 @@ homepage.workspace = true [dependencies] futures-util = { workspace = true } -hula-types = { path = "../../tools/hula/types/" } +hula_types = { workspace = true } regex = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } diff --git a/crates/bevyhavior_simulator/src/simulator.rs b/crates/bevyhavior_simulator/src/simulator.rs index 8af86c4ac7..ce31c9f844 100644 --- a/crates/bevyhavior_simulator/src/simulator.rs +++ b/crates/bevyhavior_simulator/src/simulator.rs @@ -1,3 +1,5 @@ +use std::env::current_dir; + use bevy::{ app::{App, AppExit, First, Plugin, Update}, core::{FrameCountPlugin, TaskPoolPlugin, TypeRegistrationPlugin}, @@ -8,11 +10,10 @@ use bevy::{ time::Time, }; use color_eyre::{ - eyre::{eyre, Context}, + eyre::{eyre, Context, ContextCompat}, Result, }; -use repository::get_repository_root; -use tokio::runtime::Runtime; +use repository::find_root::find_repository_root; use types::hardware::Ids; use crate::{ @@ -103,10 +104,9 @@ fn load_parameters() -> Result { body_id: "behavior_simulator".to_string(), head_id: "behavior_simulator".to_string(), }; - let runtime = Runtime::new().wrap_err("failed to build async runtime")?; - let repository_root = runtime - .block_on(get_repository_root()) - .wrap_err("failed to get repository root")?; + let current_directory = current_dir().wrap_err("failed to get current directory")?; + let repository_root = + find_repository_root(current_directory).wrap_err("failed to get repository root")?; let parameters_path = repository_root.join("crates/bevyhavior_simulator"); parameters::directory::deserialize(parameters_path, &ids, true) diff --git a/tools/hula/types/Cargo.toml b/crates/hula_types/Cargo.toml similarity index 89% rename from tools/hula/types/Cargo.toml rename to crates/hula_types/Cargo.toml index 7ea2e993e5..149facbf4b 100644 --- a/tools/hula/types/Cargo.toml +++ b/crates/hula_types/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "hula-types" +name = "hula_types" version = "0.1.0" edition.workspace = true license.workspace = true diff --git a/tools/hula/types/src/control_frame.rs b/crates/hula_types/src/control_frame.rs similarity index 100% rename from tools/hula/types/src/control_frame.rs rename to crates/hula_types/src/control_frame.rs diff --git a/tools/hula/types/src/lib.rs b/crates/hula_types/src/lib.rs similarity index 100% rename from tools/hula/types/src/lib.rs rename to crates/hula_types/src/lib.rs diff --git a/tools/hula/types/src/lola.rs b/crates/hula_types/src/lola.rs similarity index 100% rename from tools/hula/types/src/lola.rs rename to crates/hula_types/src/lola.rs diff --git a/tools/hula/types/src/robot_state.rs b/crates/hula_types/src/robot_state.rs similarity index 100% rename from tools/hula/types/src/robot_state.rs rename to crates/hula_types/src/robot_state.rs diff --git a/crates/hulk_nao/Cargo.toml b/crates/hulk_nao/Cargo.toml index c803c5de3e..49edff1a8e 100644 --- a/crates/hulk_nao/Cargo.toml +++ b/crates/hulk_nao/Cargo.toml @@ -5,13 +5,15 @@ edition.workspace = true license.workspace = true homepage.workspace = true +[package.metadata.pepsi] +cross-compile = true + [dependencies] alsa = { workspace = true } ball_filter = { workspace = true } chrono = { workspace = true } clap = { workspace = true } color-eyre = { workspace = true } -constants = { workspace = true } ctrlc = { workspace = true } enum-iterator = { workspace = true } fern = { workspace = true } diff --git a/crates/hulk_nao/src/hula_wrapper.rs b/crates/hulk_nao/src/hula_wrapper.rs index 6b41446d1d..5f1c4e4de1 100644 --- a/crates/hulk_nao/src/hula_wrapper.rs +++ b/crates/hulk_nao/src/hula_wrapper.rs @@ -12,7 +12,8 @@ use super::{ double_buffered_reader::{DoubleBufferedReader, SelectPoller}, hula::{read_from_hula, write_to_hula, ControlStorage, StateStorage}, }; -use constants::HULA_SOCKET_PATH; + +pub const HULA_SOCKET_PATH: &str = "/tmp/hula"; pub struct HulaWrapper { now: RwLock, diff --git a/crates/nao/Cargo.toml b/crates/nao/Cargo.toml index 7c931bcdf8..ca5d977e1e 100644 --- a/crates/nao/Cargo.toml +++ b/crates/nao/Cargo.toml @@ -7,4 +7,5 @@ homepage.workspace = true [dependencies] color-eyre = { workspace = true } +serde = { workspace = true } tokio = { workspace = true } diff --git a/crates/nao/src/lib.rs b/crates/nao/src/lib.rs index 1f96b7aeb9..4aeb876c5d 100644 --- a/crates/nao/src/lib.rs +++ b/crates/nao/src/lib.rs @@ -6,36 +6,62 @@ use std::{ os::unix::fs::PermissionsExt, path::{Path, PathBuf}, process::Stdio, + time::Duration, }; use color_eyre::{ - eyre::{bail, eyre, WrapErr}, + eyre::{self, bail, eyre, WrapErr}, Result, }; +use serde::Deserialize; use tokio::{ io::{AsyncBufReadExt, BufReader}, process::{Child, Command}, select, }; -pub const PING_TIMEOUT_SECONDS: u32 = 2; +const PING_TIMEOUT: Duration = Duration::from_secs(2); + +const NAO_SSH_FLAGS: &[&str] = &[ + "-lnao", + "-oLogLevel=quiet", + "-oStrictHostKeyChecking=no", + "-oUserKnownHostsFile=/dev/null", +]; + +#[derive(Debug, Deserialize, Hash, Eq, PartialEq)] +#[serde(try_from = "String")] +pub struct NaoNumber { + pub id: u8, +} + +impl TryFrom for NaoNumber { + type Error = eyre::Error; + + fn try_from(value: String) -> Result { + let id = value + .parse() + .wrap_err_with(|| format!("failed to parse `{value}` into Nao number"))?; + Ok(Self { id }) + } +} pub struct Nao { - pub host: Ipv4Addr, + pub address: Ipv4Addr, } impl Nao { - pub fn new(host: Ipv4Addr) -> Self { - Self { host } + pub fn new(address: Ipv4Addr) -> Self { + Self { address } } pub async fn try_new_with_ping(host: Ipv4Addr) -> Result { - Self::try_new_with_ping_and_arguments(host, PING_TIMEOUT_SECONDS).await + Self::try_new_with_ping_and_arguments(host, PING_TIMEOUT).await } pub async fn try_new_with_ping_and_arguments( host: Ipv4Addr, - timeout_seconds: u32, + timeout: Duration, ) -> Result { #[cfg(target_os = "macos")] const TIMEOUT_FLAG: &str = "-t"; @@ -46,7 +72,7 @@ impl Nao { .arg("-c") .arg("1") .arg(TIMEOUT_FLAG) - .arg(timeout_seconds.to_string()) + .arg(timeout.as_secs().to_string()) .arg(host.to_string()) .output() .await @@ -68,15 +94,6 @@ impl Nao { extract_version_number(&stdout).ok_or_else(|| eyre!("could not extract version number")) } - fn get_ssh_flags(&self) -> Vec { - vec![ - "-lnao".to_string(), - "-oLogLevel=quiet".to_string(), - "-oStrictHostKeyChecking=no".to_string(), - "-oUserKnownHostsFile=/dev/null".to_string(), - ] - } - fn create_login_script() -> Result { let path = temp_dir().join("nao_login_script"); @@ -93,24 +110,29 @@ impl Nao { } fn ssh_to_nao(&self) -> Result { - let temp_file = - Self::create_login_script().wrap_err("failed to create ssh login script")?; + let temp_file = Self::create_login_script().wrap_err("failed_to_create login script")?; let mut command = Command::new("ssh"); + command.env("SSH_ASKPASS", temp_file.as_os_str()); command.env("SSH_ASKPASS_REQUIRE", "force"); - for flag in self.get_ssh_flags() { + for flag in NAO_SSH_FLAGS { command.arg(flag); } - command.arg(self.host.to_string()); - + command.arg(self.address.to_string()); Ok(command) } - pub fn rsync_with_nao(&self, mkpath: bool) -> Command { + pub fn rsync_with_nao(&self, mkpath: bool) -> Result { let mut command = Command::new("rsync"); - let ssh_flags = self.get_ssh_flags().join(" "); + + let temp_file = Self::create_login_script().wrap_err("failed_to_create login script")?; + + command.env("SSH_ASKPASS", temp_file.as_os_str()); + command.env("SSH_ASKPASS_REQUIRE", "force"); + + let ssh_flags = NAO_SSH_FLAGS.join(" "); command .stdout(Stdio::piped()) .arg("--recursive") @@ -121,7 +143,7 @@ impl Nao { if mkpath { command.arg("--mkpath"); } - command + Ok(command) } pub async fn execute_shell(&self) -> Result<()> { @@ -206,9 +228,9 @@ impl Nao { } let rsync = self - .rsync_with_nao(true) + .rsync_with_nao(true)? .arg("--info=progress2") - .arg(format!("{}:hulk/logs/", self.host)) + .arg(format!("{}:hulk/logs/", self.address)) .arg(local_directory.as_ref().to_str().unwrap()) .spawn() .wrap_err("failed to execute rsync command")?; @@ -284,16 +306,21 @@ impl Nao { pub async fn upload( &self, local_directory: impl AsRef, + remote_directory: impl AsRef, delete_remaining: bool, progress_callback: impl Fn(&str), ) -> Result<()> { - let mut command = self.rsync_with_nao(true); + let mut command = self.rsync_with_nao(true)?; command .arg("--keep-dirlinks") .arg("--copy-links") .arg("--info=progress2") .arg(format!("{}/", local_directory.as_ref().display())) - .arg(format!("{}:hulk/", self.host)); + .arg(format!( + "{}:{}/", + self.address, + remote_directory.as_ref().display() + )); if delete_remaining { command.arg("--delete").arg("--delete-excluded"); @@ -360,7 +387,7 @@ impl Nao { Ok(()) } - pub async fn set_network(&self, network: Network) -> Result<()> { + pub async fn set_wifi(&self, network: Network) -> Result<()> { let command_string = [ Network::SplA, Network::SplB, @@ -410,11 +437,11 @@ impl Nao { progress_callback: impl Fn(&str), ) -> Result<()> { let rsync = self - .rsync_with_nao(false) + .rsync_with_nao(false)? .arg("--copy-links") .arg("--info=progress2") .arg(image_path.as_ref().to_str().unwrap()) - .arg(format!("{}:/data/.image/", self.host)) + .arg(format!("{}:/data/.image/", self.address)) .spawn() .wrap_err("failed to execute rsync command")?; @@ -425,7 +452,7 @@ impl Nao { impl Display for Nao { fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result { - Display::fmt(&self.host, formatter) + Display::fmt(&self.address, formatter) } } diff --git a/crates/parameters/src/directory.rs b/crates/parameters/src/directory.rs index 86568d9c74..e49fe73177 100644 --- a/crates/parameters/src/directory.rs +++ b/crates/parameters/src/directory.rs @@ -122,7 +122,7 @@ pub fn serialize( parameters: &Parameters, scope: Scope, path: &str, - parameters_root_path: impl AsRef, + parameters_root: impl AsRef, hardware_ids: &Ids, ) -> Result<(), DirectoryError> where @@ -131,7 +131,7 @@ where let mut parameters = to_value(parameters).map_err(DirectoryError::ParametersNotConvertedToJsonValue)?; let stored_parameters = to_value( - deserialize::(¶meters_root_path, hardware_ids, true).map_err(|error| { + deserialize::(¶meters_root, hardware_ids, true).map_err(|error| { println!("{:?}", error); error })?, @@ -143,7 +143,7 @@ where let Some(sparse_parameters_from_scope_path) = clone_nested_value(¶meters, path) else { return Ok(()); }; - let serialization_file_path = file_path_from_scope(scope, parameters_root_path, hardware_ids); + let serialization_file_path = file_path_from_scope(scope, parameters_root, hardware_ids); let mut parameters = if serialization_file_path.exists() { read_from_file(&serialization_file_path) .map_err(DirectoryError::HeadParametersOfLocationNotGet)? diff --git a/crates/repository/Cargo.toml b/crates/repository/Cargo.toml index f5e28cf0f0..c5f9bea54f 100644 --- a/crates/repository/Cargo.toml +++ b/crates/repository/Cargo.toml @@ -7,12 +7,12 @@ homepage.workspace = true [dependencies] color-eyre = { workspace = true } -constants = { workspace = true } futures-util = { workspace = true } -glob = { workspace = true } home = { workspace = true } itertools = { workspace = true } +nao = { workspace = true } parameters = { workspace = true } +pathdiff = { workspace = true } semver = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } @@ -20,4 +20,6 @@ spl_network_messages = { workspace = true } tempfile = { workspace = true } tokio = { workspace = true } toml = { workspace = true } +tracing = { workspace = true } types = { workspace = true } +xdg = { workspace = true } diff --git a/crates/repository/src/cargo.rs b/crates/repository/src/cargo.rs new file mode 100644 index 0000000000..c2726da122 --- /dev/null +++ b/crates/repository/src/cargo.rs @@ -0,0 +1,190 @@ +use core::fmt; +use std::{ + env::current_dir, + ffi::{OsStr, OsString}, + fmt::{Display, Formatter}, + path::Path, +}; + +use color_eyre::{ + eyre::{bail, Context, ContextCompat}, + Result, +}; +use pathdiff::diff_paths; +use tokio::process::Command; + +use crate::{data_home::get_data_home, sdk::download_and_install}; + +#[derive(Debug, Clone)] +pub enum Environment { + Native, + Sdk { version: String }, + Docker { image: String }, +} + +impl Display for Environment { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + Environment::Native => write!(f, "Native"), + Environment::Sdk { version } => write!(f, "SDK ({version})"), + Environment::Docker { image } => write!(f, "Docker ({image})"), + } + } +} + +pub enum Host { + Local, + Remote, +} + +pub struct Cargo { + host: Host, + environment: Environment, + arguments: Vec, +} + +impl Cargo { + pub fn local(environment: Environment) -> Self { + Self { + host: Host::Local, + environment, + arguments: Vec::new(), + } + } + + pub fn remote(environment: Environment) -> Self { + Self { + host: Host::Remote, + environment, + arguments: Vec::new(), + } + } + + pub async fn setup(&self, repository_root: impl AsRef) -> Result<()> { + if let Environment::Sdk { version } = &self.environment { + match self.host { + Host::Local => { + let data_home = get_data_home().wrap_err("failed to get data home")?; + + download_and_install(version, data_home) + .await + .wrap_err("failed to download and install SDK")?; + } + Host::Remote => { + let mut command = + Command::new(repository_root.as_ref().join("scripts/remoteWorkspace")); + + let status = command + .arg("pepsi") + .arg("sdk") + .arg("install") + .arg("--version") + .arg(version) + .status() + .await + .wrap_err("failed to run pepsi")?; + + if !status.success() { + bail!("pepsi failed with {status}"); + } + } + } + } + + Ok(()) + } + + pub fn arg(&mut self, argument: impl Into) -> &mut Self { + self.arguments.push(argument.into()); + self + } + + pub fn args(&mut self, arguments: impl IntoIterator>) -> &mut Self { + self.arguments.extend(arguments.into_iter().map(Into::into)); + self + } + + pub fn command( + self, + repository_root: impl AsRef, + compiler_artifacts: &[impl AsRef], + ) -> Result { + let repository_root = repository_root.as_ref(); + + let arguments = self.arguments.join(OsStr::new(" ")); + + let relative_pwd = diff_paths( + current_dir().wrap_err("failed to get current directory")?, + repository_root, + ) + .wrap_err("failed to express current directory relative to repository root")?; + + let command_string = match self.environment { + Environment::Native => { + let mut command = OsString::from("cargo "); + command.push(arguments); + command + } + Environment::Sdk { version } => { + let data_home = get_data_home().wrap_err("failed to get data home")?; + let environment_file = &data_home.join(format!( + "sdk/{version}/environment-setup-corei7-64-aldebaran-linux" + )); + let sdk_environment_setup = environment_file + .to_str() + .wrap_err("failed to convert sdk environment setup path to string")?; + let mut command = OsString::from(format!(". {sdk_environment_setup} && cargo ")); + command.push(arguments); + command + } + Environment::Docker { image } => { + let data_home = get_data_home().wrap_err("failed to get data home")?; + let cargo_home = data_home.join("container-cargo-home/"); + // FIXME: Pepsi only work in repository root + // TODO: Make image generic over SDK/native by modifying entry point; source SDK not here + let pwd = Path::new("/hulk").join(&relative_pwd); + let mut command = OsString::from(format!("\ + mkdir -p {cargo_home} && \ + docker run \ + --volume={repository_root}:/hulk:z \ + --volume={cargo_home}:/naosdk/sysroots/corei7-64-aldebaran-linux/home/cargo:z \ + --rm \ + --interactive \ + --tty {image} \ + /bin/sh -c '\ + cd {pwd} && \ + . /naosdk/environment-setup-corei7-64-aldebaran-linux && \ + echo $PATH && \ + cargo \ + ", + repository_root=repository_root.display(), + cargo_home=cargo_home.display(), + pwd=pwd.display(), + )); + command.push(arguments); + command.push(OsStr::new("'")); + command + } + }; + + let mut command = match self.host { + Host::Local => { + let mut command = Command::new("sh"); + command.arg("-c"); + command + } + Host::Remote => { + let mut command = Command::new(repository_root.join("scripts/remoteWorkspace")); + + for path in compiler_artifacts { + command.arg("--return-file").arg(path.as_ref()); + } + command.arg("--cd").arg(Path::new("./").join(relative_pwd)); + command + } + }; + command.arg(command_string); + + Ok(command) + } +} diff --git a/crates/repository/src/communication.rs b/crates/repository/src/communication.rs new file mode 100644 index 0000000000..173093da11 --- /dev/null +++ b/crates/repository/src/communication.rs @@ -0,0 +1,35 @@ +use std::path::Path; + +use color_eyre::{eyre::Context, Result}; +use serde_json::Value; + +use crate::modify_json::modify_json_inplace; + +pub async fn configure_communication( + enable: bool, + repository_root: impl AsRef, +) -> Result<()> { + let framework_json = repository_root + .as_ref() + .join("etc/parameters/framework.json"); + + let address = if enable { + Value::String("[::]:1337".to_string()) + } else { + Value::Null + }; + + modify_json_inplace(&framework_json, |mut hardware_json: Value| { + hardware_json["communication_addresses"] = address; + hardware_json + }) + .await + .wrap_err_with(|| { + format!( + "failed to configure communication address in {}", + framework_json.display() + ) + })?; + + Ok(()) +} diff --git a/crates/repository/src/configuration.rs b/crates/repository/src/configuration.rs new file mode 100644 index 0000000000..ebd15ea1d7 --- /dev/null +++ b/crates/repository/src/configuration.rs @@ -0,0 +1,32 @@ +use std::path::Path; + +use color_eyre::{eyre::Context, Result}; +use serde::Deserialize; +use tokio::fs::read_to_string; + +#[derive(Debug, Deserialize)] +pub struct Configuration { + pub os_version: String, + pub sdk_version: String, +} + +/// Get the OS version configured in the `hulk.toml`. +pub async fn read_os_version(repository_root: impl AsRef) -> Result { + let hulk = read_hulk_toml(repository_root).await?; + Ok(hulk.os_version) +} + +/// Get the SDK version configured in the `hulk.toml`. +pub async fn read_sdk_version(repository_root: impl AsRef) -> Result { + let hulk = read_hulk_toml(repository_root).await?; + Ok(hulk.sdk_version) +} + +pub async fn read_hulk_toml(repository_root: impl AsRef) -> Result { + let hulk_toml = repository_root.as_ref().join("hulk.toml"); + let hulk_toml = read_to_string(hulk_toml) + .await + .wrap_err("failed to read hulk.toml")?; + let hulk: Configuration = toml::from_str(&hulk_toml).wrap_err("failed to parse hulk.toml")?; + Ok(hulk) +} diff --git a/crates/repository/src/data_home.rs b/crates/repository/src/data_home.rs new file mode 100644 index 0000000000..e05c0fef34 --- /dev/null +++ b/crates/repository/src/data_home.rs @@ -0,0 +1,17 @@ +use std::{env, path::PathBuf}; + +use color_eyre::Result; + +/// Get the data home directory. +/// +/// This function returns the directory where hulk stores its data. The directory is determined by +/// the `HULK_DATA_HOME` environment variable. If the environment variable is not set, the +/// user-specific data directory (set by `XDG_DATA_HOME`) is used. +pub fn get_data_home() -> Result { + if let Ok(home) = env::var("HULK_DATA_HOME") { + return Ok(PathBuf::from(home)); + } + + let base_directories = xdg::BaseDirectories::with_prefix("hulk")?; + Ok(base_directories.get_data_home()) +} diff --git a/crates/repository/src/download.rs b/crates/repository/src/download.rs new file mode 100644 index 0000000000..de79331b7b --- /dev/null +++ b/crates/repository/src/download.rs @@ -0,0 +1,44 @@ +use std::{ffi::OsStr, time::Duration}; + +use color_eyre::{ + eyre::{bail, Context}, + Result, +}; +use tokio::process::Command; + +pub const CONNECT_TIMEOUT: Duration = Duration::from_secs(5); + +/// Download a file from a list of URLs using `curl`. +/// +/// This function takes a list of URLs to download from, a path to the output file, +/// and a connection timeout duration. It tries to download the file from each URL +/// in the list until it succeeds or runs out of URLs. +pub async fn download_with_fallback( + urls: impl IntoIterator>, + output_path: impl AsRef, + connect_timeout: Duration, +) -> Result<()> { + for url in urls.into_iter() { + let url = url.as_ref(); + eprintln!("Downloading from {url:?}"); + + let status = Command::new("curl") + .arg("--connect-timeout") + .arg(connect_timeout.as_secs_f32().to_string()) + .arg("--fail") + .arg("--location") + .arg("--progress-bar") + .arg("--output") + .arg(&output_path) + .arg(url) + .status() + .await + .wrap_err("failed to spawn command")?; + + if status.success() { + return Ok(()); + } + } + + bail!("failed to download from any URL"); +} diff --git a/crates/repository/src/find_root.rs b/crates/repository/src/find_root.rs new file mode 100644 index 0000000000..6d7c204193 --- /dev/null +++ b/crates/repository/src/find_root.rs @@ -0,0 +1,25 @@ +use std::{ + env::var_os, + path::{Path, PathBuf}, +}; + +/// Get the repository root directory. +/// +/// This function searches for the `hulk.toml` in the start directory and its ancestors. +/// If found, it returns the path to the directory containing the `hulk.toml`. If not, it falls +/// back to the HULK_DEFAULT_ROOT environment variable. +pub fn find_repository_root(start: impl AsRef) -> Option { + let ancestors = start.as_ref().ancestors(); + ancestors + .filter_map(|ancestor| std::fs::read_dir(ancestor).ok()) + .flatten() + .find_map(|entry| { + let entry = entry.ok()?; + if entry.file_name() == "hulk.toml" { + Some(entry.path().parent()?.to_path_buf()) + } else { + None + } + }) + .or_else(|| var_os("HULK_DEFAULT_ROOT").map(PathBuf::from)) +} diff --git a/crates/repository/src/image.rs b/crates/repository/src/image.rs new file mode 100644 index 0000000000..71454dd504 --- /dev/null +++ b/crates/repository/src/image.rs @@ -0,0 +1,41 @@ +use std::path::{Path, PathBuf}; + +use color_eyre::{eyre::Context, Result}; +use tokio::fs::{create_dir_all, rename}; + +use crate::download::{download_with_fallback, CONNECT_TIMEOUT}; + +/// Downloads the NAO image for a specified version. +/// +/// This function ensures the NAO image is downloaded. If the image is already exists, it will +/// do nothing. If not, it will try to download it. +/// +/// Returns the path to the downloaded image. +pub async fn download_image(version: &str, data_home: impl AsRef) -> Result { + let data_home = data_home.as_ref(); + let downloads_directory = data_home.join("image/"); + let image_name = format!("nao-image-HULKs-OS-{version}.ext3.gz.opn"); + let image_path = downloads_directory.join(&image_name); + let download_path = image_path.with_extension("tmp"); + + if image_path.exists() { + return Ok(image_path); + } + + create_dir_all(&downloads_directory) + .await + .wrap_err("failed to create download directory")?; + + let urls = [format!( + "https://github.com/HULKs/meta-nao/releases/download/{version}/{image_name}" + )]; + download_with_fallback(urls, &download_path, CONNECT_TIMEOUT) + .await + .wrap_err("failed to download image")?; + + rename(download_path, &image_path) + .await + .wrap_err("failed to rename image")?; + + Ok(image_path) +} diff --git a/crates/repository/src/inspect_version.rs b/crates/repository/src/inspect_version.rs new file mode 100644 index 0000000000..4314b6d148 --- /dev/null +++ b/crates/repository/src/inspect_version.rs @@ -0,0 +1,51 @@ +use std::{fs::read_to_string, path::Path}; + +use color_eyre::{eyre::Context, Result}; +use semver::Version; +use serde::Deserialize; +use tracing::warn; + +#[derive(Deserialize, Debug)] +struct Cargo { + package: Package, +} + +#[derive(Deserialize, Debug)] +struct Package { + version: String, +} + +/// Inspects and returns the version of a package from its `Cargo.toml` file. +pub fn inspect_version(toml_path: impl AsRef) -> Result { + let toml_path = toml_path.as_ref(); + let cargo_toml_text = read_to_string(toml_path).wrap_err("failed to read file")?; + let cargo_toml: Cargo = toml::from_str(&cargo_toml_text).wrap_err("failed to parse content")?; + let raw_version = &cargo_toml.package.version; + let version = Version::parse(raw_version) + .wrap_err_with(|| format!("failed to parse version '{raw_version}' as SemVer"))?; + Ok(version) +} + +/// Checks whether the package has a newer version than the provided version. +pub fn check_for_update(own_version: &str, cargo_toml: impl AsRef) -> Result<()> { + let own_version = Version::parse(own_version) + .wrap_err_with(|| format!("failed to parse own version '{own_version}' as SemVer"))?; + let cargo_toml_version = inspect_version(&cargo_toml).wrap_err_with(|| { + format!( + "failed to inspect version of package at {}", + cargo_toml.as_ref().display() + ) + })?; + if own_version < cargo_toml_version { + let crate_path = cargo_toml.as_ref().parent().unwrap(); + warn!( + "New version available! + Own version: {own_version} + New version: {cargo_toml_version} + To install new version use: + cargo install --path {}", + crate_path.display() + ); + } + Ok(()) +} diff --git a/crates/repository/src/lib.rs b/crates/repository/src/lib.rs index 0459653e4d..52902670cc 100644 --- a/crates/repository/src/lib.rs +++ b/crates/repository/src/lib.rs @@ -1,707 +1,21 @@ -use std::{ - collections::{BTreeMap, BTreeSet, HashMap}, - env::{self, consts::ARCH, current_dir}, - ffi::OsStr, - fmt::Display, - fs::Permissions, - io::{self, ErrorKind}, - os::unix::prelude::PermissionsExt, - path::{Path, PathBuf}, - time::Duration, -}; - -use color_eyre::{ - eyre::{bail, eyre, Context}, - Result, -}; -use futures_util::{stream::FuturesUnordered, StreamExt}; -use glob::glob; -use home::home_dir; -use itertools::intersperse; -use parameters::{ - directory::{serialize, Id, Location, Scope}, - json::nest_value_at_path, -}; -use semver::Version; -use serde::Deserialize; -use serde_json::{from_str, to_string_pretty, to_value, Value}; -use tempfile::{tempdir, TempDir}; -use tokio::{ - fs::{ - create_dir_all, read_dir, read_link, read_to_string, remove_dir_all, remove_file, rename, - set_permissions, symlink, try_exists, write, File, - }, - io::AsyncReadExt, - process::Command, -}; - -use constants::{Team, OS_IS_NOT_LINUX, SDK_VERSION}; -use spl_network_messages::PlayerNumber; -use types::hardware::Ids; - -const CONNECT_TIMEOUT: Duration = Duration::from_secs(5); - -#[derive(Clone)] -pub struct Repository { - root: PathBuf, -} - -impl Repository { - pub fn new(root: impl AsRef) -> Self { - Self { - root: root.as_ref().to_path_buf(), - } - } - - pub fn crates_directory(&self) -> PathBuf { - self.root.join("crates") - } - - pub fn parameters_root(&self) -> PathBuf { - self.root.join("etc/parameters") - } - - pub fn find_latest_file(&self, pattern: &str) -> Result { - let path = self.root.join(pattern); - let matching_paths: Vec<_> = glob( - path.to_str() - .ok_or_else(|| eyre!("failed to interpret path as Unicode"))?, - ) - .wrap_err("failed to execute glob() over target directory")? - .map(|entry| { - let path = entry.wrap_err("failed to get glob() entry")?; - let metadata = path - .metadata() - .wrap_err_with(|| format!("failed to get metadata of path {path:?}"))?; - let modified_time = metadata.modified().wrap_err_with(|| { - format!("failed to get modified time from metadata of path {path:?}") - })?; - Ok((path, modified_time)) - }) - .collect::>() - .wrap_err("failed to get matching paths")?; - let (path_with_maximal_modified_time, _modified_time) = matching_paths - .iter() - .max_by_key(|(_path, modified_time)| modified_time) - .ok_or_else(|| eyre!("failed to find any matching path"))?; - Ok(path_with_maximal_modified_time.to_path_buf()) - } - - pub fn check_new_version_available( - &self, - own_version: &str, - path: impl AsRef, - ) -> Result> { - #[derive(Deserialize, Debug)] - struct Cargo { - package: Package, - } - #[derive(Deserialize, Debug)] - struct Package { - version: String, - } - - let absolute_path = self.root.join(&path); - let own_version = Version::parse(own_version).wrap_err("failed to parse own version")?; - let cargo_toml_path = absolute_path.join("Cargo.toml"); - let cargo_toml_text = std::fs::read_to_string(&cargo_toml_path).wrap_err_with(|| { - format!( - "failed to load cargo toml at {}", - cargo_toml_path.to_str().unwrap() - ) - })?; - let cargo_toml: Cargo = toml::from_str(&cargo_toml_text).wrap_err_with(|| { - format!( - "failed to parse package version from {}", - cargo_toml_path.to_str().unwrap() - ) - })?; - let cargo_toml_version = Version::parse(&cargo_toml.package.version).unwrap(); - - if own_version < cargo_toml_version { - println!("New version available!"); - println!("Own version: {own_version}"); - println!("New version: {cargo_toml_version}"); - println!("To install new version use:"); - println!(); - println!(" cargo install --path {}", absolute_path.to_str().unwrap()); - println!(); - Ok(Some((own_version, cargo_toml_version))) - } else { - Ok(None) - } - } - - async fn cargo( - &self, - action: CargoAction, - workspace: bool, - profile: &str, - target: &str, - features: Option>, - passthrough_arguments: &[String], - ) -> Result<()> { - let use_docker = target == "nao" && OS_IS_NOT_LINUX; - - let cargo_command = format!("cargo {action} ") - + format!("--profile {profile} ").as_str() - + if let Some(features) = features { - let features = features.join(","); - format!("--features {features} ") - } else { - String::new() - } - .as_str() - + if workspace { - "--workspace --all-features --all-targets ".to_string() - } else { - let manifest = format!("crates/hulk_{target}/Cargo.toml"); - let root = if use_docker { - Path::new("/hulk") - } else { - &self.root - }; - format!("--manifest-path={} ", root.join(manifest).display()) - } - .as_str() - + "-- " - + match action { - CargoAction::Clippy => "--deny warnings ", - _ => "", - } - + passthrough_arguments.join(" ").as_str(); - - println!("Running: {cargo_command}"); - - let shell_command = if use_docker { - format!( - "docker run --volume={}:/hulk --volume={}:/naosdk/sysroots/corei7-64-aldebaran-linux/home/cargo \ - --rm --interactive --tty ghcr.io/hulks/naosdk:{SDK_VERSION} /bin/bash -c \ - '. /naosdk/environment-setup-corei7-64-aldebaran-linux && {cargo_command}'", - self.root.display(), - self.root.join("naosdk/cargo-home").join(SDK_VERSION).display() - ) - } else if target == "nao" { - format!( - ". {} && {cargo_command}", - self.root - .join(format!( - "naosdk/{SDK_VERSION}/environment-setup-corei7-64-aldebaran-linux" - )) - .display() - ) - } else { - cargo_command - }; - - let status = Command::new("sh") - .arg("-c") - .arg(shell_command) - .status() - .await - .wrap_err("failed to execute cargo command")?; - - if !status.success() { - bail!("cargo command exited with {status}"); - } - - Ok(()) - } - - pub async fn build( - &self, - workspace: bool, - profile: &str, - target: &str, - features: Option>, - passthrough_arguments: &[String], - ) -> Result<()> { - self.cargo( - CargoAction::Build, - workspace, - profile, - target, - features, - passthrough_arguments, - ) - .await - } - - pub async fn check(&self, workspace: bool, profile: &str, target: &str) -> Result<()> { - self.cargo(CargoAction::Check, workspace, profile, target, None, &[]) - .await - } - - pub async fn clippy(&self, workspace: bool, profile: &str, target: &str) -> Result<()> { - self.cargo(CargoAction::Clippy, workspace, profile, target, None, &[]) - .await - } - - pub async fn run( - &self, - profile: &str, - target: &str, - features: Option>, - passthrough_arguments: &[String], - ) -> Result<()> { - self.cargo( - CargoAction::Run, - false, - profile, - target, - features, - passthrough_arguments, - ) - .await - } - - pub async fn set_communication(&self, enable: bool) -> Result<()> { - let file_contents = read_to_string(self.root.join("etc/parameters/framework.json")) - .await - .wrap_err("failed to read framework.json")?; - let mut hardware_json: Value = - from_str(&file_contents).wrap_err("failed to deserialize framework.json")?; - - hardware_json["communication_addresses"] = if enable { - Value::String("[::]:1337".to_string()) - } else { - Value::Null - }; - { - let file_contents = to_string_pretty(&hardware_json) - .wrap_err("failed to serialize framework.json")? - + "\n"; - write( - self.root.join("etc/parameters/framework.json"), - file_contents.as_bytes(), - ) - .await - .wrap_err("failed to write framework.json")?; - } - Ok(()) - } - - pub async fn set_player_number( - &self, - head_id: &str, - player_number: PlayerNumber, - ) -> Result<()> { - let path = "player_number"; - let parameters = nest_value_at_path( - path, - to_value(player_number).wrap_err("failed to serialize player number")?, - ); - serialize( - ¶meters, - Scope { - location: Location::All, - id: Id::Head, - }, - path, - self.parameters_root(), - &Ids { - body_id: "unknown_body_id".to_string(), - head_id: head_id.to_string(), - }, - ) - .wrap_err("failed to serialize parameters directory") - } - - pub async fn set_recording_intervals( - &self, - recording_intervals: HashMap, - ) -> Result<()> { - let file_contents = read_to_string(self.root.join("etc/parameters/framework.json")) - .await - .wrap_err("failed to read framework.json")?; - let mut hardware_json: Value = - from_str(&file_contents).wrap_err("failed to deserialize framework.json")?; - - hardware_json["recording_intervals"] = to_value(recording_intervals) - .wrap_err("failed to convert recording intervals to JSON")?; - { - let file_contents = to_string_pretty(&hardware_json) - .wrap_err("failed to serialize framework.json")? - + "\n"; - write( - self.root.join("etc/parameters/framework.json"), - file_contents.as_bytes(), - ) - .await - .wrap_err("failed to write framework.json")?; - } - Ok(()) - } - - pub async fn link_sdk_home(&self, installation_directory: Option<&Path>) -> Result { - let symlink = self.root.join("naosdk"); - let environment_installation_directory = env::var("NAOSDK_HOME").ok().map(PathBuf::from); - let installation_directory = if let Some(directory) = - installation_directory.or(environment_installation_directory.as_deref()) - { - create_symlink(directory, &symlink).await?; - directory.to_path_buf() - } else if symlink.exists() { - symlink.clone() - } else { - let directory = home_dir() - .ok_or_else(|| eyre!("cannot find HOME directory"))? - .join(".naosdk"); - create_symlink(&directory, &symlink).await?; - directory - }; - - Ok(installation_directory) - } - - pub async fn install_sdk( - &self, - version: Option<&str>, - installation_directory: PathBuf, - ) -> Result<()> { - let version = version.unwrap_or(SDK_VERSION); - let sdk = installation_directory.join(version); - - let incomplete_marker = installation_directory.join(format!("{version}.incomplete")); - if sdk.exists() && incomplete_marker.exists() { - println!("Removing incomplete SDK directory..."); - remove_dir_all(&sdk) - .await - .wrap_err("failed to remove old SDK directory")?; - } - - if !sdk.exists() { - let downloads_directory = installation_directory.join("downloads"); - let installer_name = format!("HULKs-OS-{ARCH}-toolchain-{version}.sh"); - let installer_path = downloads_directory.join(&installer_name); - if !installer_path.exists() { - download_sdk(&downloads_directory, version, &installer_name) - .await - .wrap_err("failed to download SDK")?; - } - - File::create(&incomplete_marker) - .await - .wrap_err("failed to create marker")?; - install_sdk(installer_path, &sdk) - .await - .wrap_err("failed to install SDK")?; - remove_file(&incomplete_marker) - .await - .wrap_err("failed to remove marker")?; - } - Ok(()) - } - - pub async fn create_upload_directory(&self, profile: &str) -> Result<(TempDir, PathBuf)> { - let upload_directory = tempdir().wrap_err("failed to create temporary directory")?; - let hulk_directory = upload_directory.path().join("hulk"); - - // the target directory is "debug" with --profile dev... - let profile_directory = match profile { - "dev" => "debug", - other => other, - }; - - create_dir_all(hulk_directory.join("bin")) - .await - .wrap_err("failed to create directory")?; - - symlink(self.root.join("etc"), hulk_directory.join("etc")) - .await - .wrap_err("failed to link etc directory")?; - - symlink( - self.root.join(format!( - "target/x86_64-aldebaran-linux-gnu/{profile_directory}/hulk_nao" - )), - hulk_directory.join("bin/hulk"), - ) - .await - .wrap_err("failed to link executable")?; - - Ok((upload_directory, hulk_directory)) - } - - pub async fn get_configured_team(&self) -> Result { - let team_toml = self.root.join("etc/parameters/team.toml"); - let mut team_file = File::open(&team_toml) - .await - .wrap_err_with(|| format!("failed to open {}", team_toml.display()))?; - let mut contents = String::new(); - team_file.read_to_string(&mut contents).await?; - let team: Team = toml::from_str(&contents).wrap_err("failed to parse team.toml")?; - Ok(team) - } - - pub async fn get_configured_locations(&self) -> Result>> { - let results: Vec<_> = [ - "nao_location", - "webots_location", - "behavior_simulator_location", - ] - .into_iter() - .map(|target_name| async move { - ( - target_name, - read_link(self.parameters_root().join(target_name)) - .await - .wrap_err_with(|| format!("failed reading location symlink for {target_name}")), - ) - }) - .collect::>() - .collect() - .await; - - results - .into_iter() - .map(|(target_name, path)| match path { - Ok(path) => Ok(( - target_name.to_string(), - Some( - path.file_name() - .ok_or_else(|| eyre!("failed to get file name"))? - .to_str() - .ok_or_else(|| eyre!("failed to convert to UTF-8"))? - .to_string(), - ), - )), - Err(error) - if error.downcast_ref::().unwrap().kind() == ErrorKind::NotFound => - { - Ok((target_name.to_string(), None)) - } - Err(error) => Err(error), - }) - .collect() - } - - pub async fn set_location(&self, target: &str, location: &str) -> Result<()> { - let target_location = self.parameters_root().join(format!("{target}_location")); - let new_location = Path::new(location); - let new_location_path = self.parameters_root().join(location); - if !try_exists(new_location_path).await? { - let location_set = self.list_available_locations().await?; - let available_locations: String = intersperse( - location_set - .into_iter() - .map(|location| format!(" - {location}")), - "\n".to_string(), - ) - .collect(); - bail!("location {location} does not exist. \navailable locations are:\n{available_locations}"); - } - let _ = remove_file(&target_location).await; - symlink(&new_location, &target_location) - .await - .wrap_err_with(|| { - format!("failed creating symlink from {new_location:?} to {target_location:?}, does the location exist?" - ) - }) - } - - pub async fn list_available_locations(&self) -> Result> { - let parameters_path = self.root.join("etc/parameters"); - let mut locations = read_dir(parameters_path) - .await - .wrap_err("failed parameters root")?; - let mut results = BTreeSet::new(); - while let Ok(Some(entry)) = locations.next_entry().await { - if entry.path().is_dir() && !entry.path().is_symlink() { - results.insert( - entry - .path() - .file_name() - .ok_or_else(|| eyre!("failed getting file name for location"))? - .to_str() - .ok_or_else(|| eyre!("failed to convert to UTF-8"))? - .to_string(), - ); - } - } - Ok(results) - } -} - -async fn download_with_fallback( - output_path: impl AsRef, - urls: impl IntoIterator>, - connect_timeout: Duration, -) -> Result<()> { - for (i, url) in urls.into_iter().enumerate() { - let url = url.as_ref(); - if i > 0 { - println!("Falling back to downloading from {url:?}"); - } - - let status = Command::new("curl") - .arg("--connect-timeout") - .arg(connect_timeout.as_secs_f32().to_string()) - .arg("--fail") - .arg("--location") - .arg("--progress-bar") - .arg("--output") - .arg(&output_path) - .arg(url) - .status() - .await - .wrap_err("failed to spawn command")?; - - if status.success() { - return Ok(()); - } - } - - bail!("curl exited with error") -} - -async fn download_image( - downloads_directory: impl AsRef, - version: &str, - image_name: &str, -) -> Result<()> { - if !downloads_directory.as_ref().exists() { - create_dir_all(&downloads_directory) - .await - .wrap_err("failed to create download directory")?; - } - let image_path = downloads_directory.as_ref().join(image_name); - let download_path = image_path.with_extension("tmp"); - let urls = [ - format!("http://bighulk.hulks.dev/image/{image_name}"), - format!("https://github.com/HULKs/meta-nao/releases/download/{version}/{image_name}"), - ]; - - println!("Downloading image from {}", urls[0]); - download_with_fallback(&download_path, urls, CONNECT_TIMEOUT) - .await - .wrap_err("failed to download image")?; - - rename(download_path, image_path) - .await - .wrap_err("failed to rename image") -} - -pub async fn get_image_path(version: &str) -> Result { - let downloads_directory = home_dir() - .ok_or_else(|| eyre!("cannot find HOME directory"))? - .join(".naosdk/images"); - let image_name = format!("nao-image-HULKs-OS-{version}.ext3.gz.opn"); - let image_path = downloads_directory.join(&image_name); - - if !image_path.exists() { - download_image(downloads_directory, version, &image_name).await?; - } - Ok(image_path) -} - -async fn download_sdk( - downloads_directory: impl AsRef, - version: &str, - installer_name: &str, -) -> Result<()> { - if !downloads_directory.as_ref().exists() { - create_dir_all(&downloads_directory) - .await - .wrap_err("failed to create download directory")?; - } - let installer_path = downloads_directory.as_ref().join(installer_name); - let download_path = installer_path.with_extension("tmp"); - let urls = [ - format!("http://bighulk.hulks.dev/sdk/{installer_name}"), - format!("https://github.com/HULKs/meta-nao/releases/download/{version}/{installer_name}"), - ]; - - println!("Downloading SDK from {}", urls[0]); - download_with_fallback(&download_path, urls, CONNECT_TIMEOUT).await?; - - set_permissions(&download_path, Permissions::from_mode(0o755)) - .await - .wrap_err("failed to make installer executable")?; - - rename(download_path, installer_path) - .await - .wrap_err("failed to rename sdk installer") -} - -async fn install_sdk( - installer_path: impl AsRef, - installation_directory: impl AsRef, -) -> Result<()> { - let mut command = Command::new(installer_path.as_ref().as_os_str()); - command.arg("-d"); - command.arg(installation_directory.as_ref().as_os_str()); - if env::var("NAOSDK_AUTOMATIC_YES") - .map(|value| value == "1") - .unwrap_or(false) - { - command.arg("-y"); - } - let status = command.status().await.wrap_err("failed to spawn command")?; - - if !status.success() { - bail!("SDK installer exited with {status}"); - } - Ok(()) -} - -async fn create_symlink(source: &Path, destination: &Path) -> Result<()> { - if destination.read_link().is_ok() { - remove_file(&destination) - .await - .wrap_err("failed to remove current symlink")?; - } - symlink(&source, &destination) - .await - .wrap_err("failed to create symlink")?; - Ok(()) -} - -pub async fn get_repository_root() -> Result { - let path = current_dir().wrap_err("failed to get current directory")?; - let ancestors = path.as_path().ancestors(); - for ancestor in ancestors { - let mut directory = read_dir(ancestor) - .await - .wrap_err_with(|| format!("failed to read directory {}", ancestor.display()))?; - while let Some(child) = directory.next_entry().await.wrap_err_with(|| { - format!( - "failed to get next directory entry while iterating {}", - ancestor.display() - ) - })? { - if child.file_name() == ".git" { - return Ok(child - .path() - .parent() - .ok_or_else(|| eyre!("failed to get parent of {}", child.path().display()))? - .to_path_buf()); - } - } - } - - bail!("failed to find .git directory") -} - -#[derive(Debug, Clone, Copy)] -enum CargoAction { - Build, - Check, - Clippy, - Run, -} - -impl Display for CargoAction { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "{}", - match self { - CargoAction::Build => "build", - CargoAction::Check => "check", - CargoAction::Clippy => "clippy", - CargoAction::Run => "run", - } - ) - } -} +//! Tools and utilities for managing development workflows in the repository. +//! +//! This crate simplifies tasks like building for specific targets, handling SDKs, and setting up +//! configurations, making it easier to develop, configure, and deploy for NAO robots. + +pub mod cargo; +pub mod communication; +pub mod configuration; +pub mod data_home; +pub mod download; +pub mod find_root; +pub mod image; +pub mod inspect_version; +pub mod location; +pub mod modify_json; +pub mod player_number; +pub mod recording; +pub mod sdk; +pub mod symlink; +pub mod team; +pub mod upload; diff --git a/crates/repository/src/location.rs b/crates/repository/src/location.rs new file mode 100644 index 0000000000..4dec1c306a --- /dev/null +++ b/crates/repository/src/location.rs @@ -0,0 +1,113 @@ +use std::{io::ErrorKind, path::Path}; + +use color_eyre::{ + eyre::{bail, eyre, Context}, + Result, +}; +use futures_util::{stream::FuturesUnordered, StreamExt}; +use itertools::intersperse; +use tokio::{ + fs::{read_dir, read_link, remove_file, symlink, try_exists}, + io, +}; + +pub async fn list_configured_locations( + repository_root: impl AsRef, +) -> Result)>> { + let parameters_root = &repository_root.as_ref().join("etc/parameters"); + let results: Vec<_> = [ + "nao_location", + "webots_location", + "behavior_simulator_location", + ] + .into_iter() + .map(|target_name| async move { + ( + target_name, + read_link(parameters_root.join(target_name)) + .await + .wrap_err_with(|| format!("failed reading location symlink for {target_name}")), + ) + }) + .collect::>() + .collect() + .await; + + results + .into_iter() + .map(|(target_name, path)| match path { + Ok(path) => Ok(( + target_name.to_string(), + Some( + path.file_name() + .ok_or_else(|| eyre!("failed to get file name"))? + .to_str() + .ok_or_else(|| eyre!("failed to convert to UTF-8"))? + .to_string(), + ), + )), + Err(error) + if error.downcast_ref::().unwrap().kind() == ErrorKind::NotFound => + { + Ok((target_name.to_string(), None)) + } + Err(error) => Err(error), + }) + .collect() +} + +pub async fn set_location( + target: &str, + location: &str, + repository_root: impl AsRef, +) -> Result<()> { + let parameters_root = repository_root.as_ref().join("etc/parameters"); + if !try_exists(parameters_root.join(location)) + .await + .wrap_err_with(|| format!("failed checking if location '{location}' exists"))? + { + let location_set = list_available_locations(&repository_root) + .await + .unwrap_or_default(); + let available_locations: String = intersperse( + location_set + .into_iter() + .map(|location| format!(" - {location}")), + "\n".to_string(), + ) + .collect(); + bail!( + "location {location} does not exist.\navailable locations are:\n{available_locations}" + ); + } + let target_location = parameters_root.join(format!("{target}_location")); + let _ = remove_file(&target_location).await; + symlink(location, &target_location).await.wrap_err_with(|| { + format!( + "failed creating symlink named {target_location} pointing to {location}", + target_location = target_location.display() + ) + }) +} + +pub async fn list_available_locations(repository_root: impl AsRef) -> Result> { + let parameters_root = repository_root.as_ref().join("etc/parameters"); + let mut locations = read_dir(parameters_root) + .await + .wrap_err("failed to read parameters directory")?; + let mut results = Vec::new(); + while let Ok(Some(entry)) = locations.next_entry().await { + if entry.path().is_dir() && !entry.path().is_symlink() { + results.push( + entry + .path() + .file_name() + .ok_or_else(|| eyre!("failed getting file name for location"))? + .to_str() + .ok_or_else(|| eyre!("failed to convert to UTF-8"))? + .to_string(), + ); + } + } + Ok(results) +} diff --git a/crates/repository/src/modify_json.rs b/crates/repository/src/modify_json.rs new file mode 100644 index 0000000000..51a7a08918 --- /dev/null +++ b/crates/repository/src/modify_json.rs @@ -0,0 +1,36 @@ +use std::path::Path; + +use color_eyre::{eyre::Context, Result}; +use serde::{Deserialize, Serialize}; +use serde_json::to_string_pretty; +use tokio::fs::{read_to_string, write}; + +/// Modifies a JSON file in place. +/// +/// This function reads the contents of a JSON file, deserializes it into a value, applies a +/// modification to the value, serializes the modified value back into JSON, and writes the JSON +/// back to the file. +pub async fn modify_json_inplace( + path: impl AsRef, + modification: impl FnOnce(I) -> O, +) -> Result<()> +where + for<'de> I: Deserialize<'de>, + O: Serialize, +{ + let file_contents = read_to_string(&path) + .await + .wrap_err("failed to read contents")?; + + let value = serde_json::from_str(&file_contents).wrap_err("failed to deserialize value")?; + + let out = modification(value); + + let json = to_string_pretty(&out).wrap_err("failed to serialize value")?; + let file_contents = json + "\n"; + write(&path, file_contents.as_bytes()) + .await + .wrap_err("failed to write file contents back")?; + + Ok(()) +} diff --git a/crates/repository/src/player_number.rs b/crates/repository/src/player_number.rs new file mode 100644 index 0000000000..805550fcc2 --- /dev/null +++ b/crates/repository/src/player_number.rs @@ -0,0 +1,37 @@ +use std::path::Path; + +use color_eyre::{eyre::Context, Result}; +use parameters::{ + directory::{serialize, Id, Location, Scope}, + json::nest_value_at_path, +}; +use spl_network_messages::PlayerNumber; +use types::hardware::Ids; + +pub async fn configure_player_number( + head_id: &str, + player_number: PlayerNumber, + repository_root: impl AsRef, +) -> Result<()> { + let parameters_root = repository_root.as_ref().join("etc/parameters/"); + let path = "player_number"; + let parameters = nest_value_at_path( + path, + serde_json::to_value(player_number).wrap_err("failed to serialize player number")?, + ); + serialize( + ¶meters, + Scope { + location: Location::All, + id: Id::Head, + }, + path, + parameters_root, + &Ids { + body_id: "unknown_body_id".to_string(), + head_id: head_id.to_string(), + }, + ) + .wrap_err("failed to serialize parameters directory")?; + Ok(()) +} diff --git a/crates/repository/src/recording.rs b/crates/repository/src/recording.rs new file mode 100644 index 0000000000..29fbcaecd9 --- /dev/null +++ b/crates/repository/src/recording.rs @@ -0,0 +1,31 @@ +use std::{collections::HashMap, path::Path}; + +use color_eyre::{eyre::Context, Result}; +use serde_json::{to_value, Value}; + +use crate::modify_json::modify_json_inplace; + +pub async fn configure_recording_intervals( + recording_intervals: HashMap, + repository_root: impl AsRef, +) -> Result<()> { + let framework_json = repository_root + .as_ref() + .join("etc/parameters/framework.json"); + let serialized_intervals = + to_value(recording_intervals).wrap_err("failed to convert recording intervals to JSON")?; + + modify_json_inplace(&framework_json, |mut hardware_json: Value| { + hardware_json["recording_intervals"] = serialized_intervals; + hardware_json + }) + .await + .wrap_err_with(|| { + format!( + "failed to configure recording intervals in {}", + framework_json.display() + ) + })?; + + Ok(()) +} diff --git a/crates/repository/src/sdk.rs b/crates/repository/src/sdk.rs new file mode 100644 index 0000000000..07a2413957 --- /dev/null +++ b/crates/repository/src/sdk.rs @@ -0,0 +1,107 @@ +use std::{ + env::{self, consts::ARCH}, + fs::Permissions, + os::unix::fs::PermissionsExt, + path::{Path, PathBuf}, +}; + +use color_eyre::{ + eyre::{bail, Context}, + Result, +}; +use tokio::{ + fs::{create_dir_all, remove_dir_all, remove_file, rename, set_permissions, File}, + process::Command, +}; + +use crate::download::{download_with_fallback, CONNECT_TIMEOUT}; + +/// Downloads and installs a specified SDK version. +/// +/// This function ensures an installed SDK version. If it is not installed, it will download the +/// SDK installer and run it to install the SDK. If the SDK installation is incomplete, it will +/// remove the incomplete installation and try again. +pub async fn download_and_install(version: &str, data_home: impl AsRef) -> Result<()> { + let data_home = data_home.as_ref(); + let sdk_home = data_home.join("sdk/"); + let installation_directory = sdk_home.join(version); + + let incomplete_marker = sdk_home.join(format!("{version}.incomplete")); + if installation_directory.exists() && incomplete_marker.exists() { + eprintln!("Removing incomplete SDK ({version}) of previous installation attempt..."); + remove_dir_all(&installation_directory) + .await + .wrap_err("failed to remove incomplete SDK directory")?; + } + + if !installation_directory.exists() { + let installer_path = download_and_prepare(version, &sdk_home) + .await + .wrap_err("failed to download and prepare SDK")?; + + File::create(&incomplete_marker) + .await + .wrap_err("failed to create marker")?; + run_installer(installer_path, &installation_directory) + .await + .wrap_err("failed to install SDK")?; + remove_file(&incomplete_marker) + .await + .wrap_err("failed to remove marker")?; + } + Ok(()) +} + +async fn download_and_prepare(version: &str, sdk_home: impl AsRef) -> Result { + let downloads_directory = sdk_home.as_ref().join("downloads"); + let installer_name = format!("HULKs-OS-{ARCH}-toolchain-{version}.sh"); + let installer_path = downloads_directory.join(&installer_name); + let download_path = installer_path.with_extension("tmp"); + + if installer_path.exists() { + return Ok(installer_path); + } + + create_dir_all(&downloads_directory) + .await + .wrap_err("failed to create download directory")?; + + let urls = [format!( + "https://github.com/HULKs/meta-nao/releases/download/{version}/{installer_name}" + )]; + download_with_fallback(urls, &download_path, CONNECT_TIMEOUT) + .await + .wrap_err("failed to download SDK")?; + + set_permissions(&download_path, Permissions::from_mode(0o755)) + .await + .wrap_err("failed to mark installer executable")?; + + rename(download_path, &installer_path) + .await + .wrap_err("failed to rename sdk installer")?; + + Ok(installer_path) +} + +async fn run_installer( + installer: impl AsRef, + target_directory: impl AsRef, +) -> Result<()> { + let var_name = Command::new(installer.as_ref().as_os_str()); + let mut command = var_name; + command.arg("-d"); + command.arg(target_directory.as_ref().as_os_str()); + if env::var("NAOSDK_AUTOMATIC_YES") + .map(|value| value == "1") + .unwrap_or(false) + { + command.arg("-y"); + } + let status = command.status().await.wrap_err("failed to spawn command")?; + + if !status.success() { + bail!("SDK installer exited with {status}"); + } + Ok(()) +} diff --git a/crates/repository/src/symlink.rs b/crates/repository/src/symlink.rs new file mode 100644 index 0000000000..e515e03b07 --- /dev/null +++ b/crates/repository/src/symlink.rs @@ -0,0 +1,20 @@ +use std::path::Path; + +use color_eyre::{eyre::Context, Result}; +use tokio::fs::{remove_file, symlink}; + +/// Create or replace a symlink from the source path to the destination path. +/// +/// If a symlink already exists at the destination path, it is removed before creating the new symlink. +/// If the symlink cannot be created, an error is returned. +pub async fn create_symlink(source: &Path, destination: &Path) -> Result<()> { + if destination.read_link().is_ok() { + remove_file(&destination) + .await + .wrap_err("failed to remove current symlink")?; + } + symlink(&source, &destination) + .await + .wrap_err("failed to create symlink")?; + Ok(()) +} diff --git a/crates/repository/src/team.rs b/crates/repository/src/team.rs new file mode 100644 index 0000000000..b0972b5703 --- /dev/null +++ b/crates/repository/src/team.rs @@ -0,0 +1,30 @@ +use std::path::Path; + +use color_eyre::{eyre::Context, Result}; +use serde::{Deserialize, Serialize}; +use tokio::fs::read_to_string; + +#[derive(Serialize, Deserialize)] +pub struct Team { + pub team_number: u8, + pub naos: Vec, +} + +#[derive(Serialize, Deserialize)] +pub struct Nao { + pub number: u8, + pub hostname: String, + pub body_id: String, + pub head_id: String, +} + +pub async fn read_team_configuration(repository_root: impl AsRef) -> Result { + let team_toml = repository_root.as_ref().join("etc/parameters/team.toml"); + + let content = read_to_string(&team_toml) + .await + .wrap_err_with(|| format!("failed to read {}", team_toml.display()))?; + + let team = toml::from_str(&content).wrap_err("failed to parse team.toml")?; + Ok(team) +} diff --git a/crates/repository/src/upload.rs b/crates/repository/src/upload.rs new file mode 100644 index 0000000000..876c826ba0 --- /dev/null +++ b/crates/repository/src/upload.rs @@ -0,0 +1,39 @@ +use std::path::Path; + +use color_eyre::{eyre::Context, Result}; +use tokio::fs::{create_dir_all, symlink}; + +pub async fn populate_upload_directory( + upload_directory: impl AsRef, + repository_root: impl AsRef, + hulk_binary: impl AsRef, +) -> Result<()> { + let upload_directory = upload_directory.as_ref(); + let repository_root = repository_root.as_ref(); + + symlink(repository_root.join("etc"), upload_directory.join("etc")) + .await + .wrap_err("failed to link etc directory")?; + + create_dir_all(upload_directory.join("bin")) + .await + .wrap_err("failed to create directory for binaries")?; + symlink( + repository_root.join(hulk_binary), + upload_directory.join("bin/hulk"), + ) + .await + .wrap_err("failed to link executable")?; + + Ok(()) +} + +pub fn get_hulk_binary(profile: &str) -> String { + // the target directory is "debug" with --profile dev... + let profile_directory = match profile { + "dev" => "debug", + other => other, + }; + + format!("target/x86_64-aldebaran-linux-gnu/{profile_directory}/hulk_nao") +} diff --git a/crates/source_analyzer/src/pretty.rs b/crates/source_analyzer/src/pretty.rs index 2028e70aaf..3617982cb6 100644 --- a/crates/source_analyzer/src/pretty.rs +++ b/crates/source_analyzer/src/pretty.rs @@ -90,7 +90,7 @@ impl ToWriterPretty for Contexts { impl ToWriterPretty for Field { fn to_writer_pretty(&self, writer: &mut impl Write) -> fmt::Result { match self { - Field::AdditionalOutput { name, .. } => write!(writer, "{name}: AdditfmtnalOutput"), + Field::AdditionalOutput { name, .. } => write!(writer, "{name}: AdditionalOutput"), Field::CyclerState { name, .. } => write!(writer, "{name}: CyclerState"), Field::HardwareInterface { name, .. } => write!(writer, "{name}: HardwareInterface"), Field::HistoricInput { name, .. } => write!(writer, "{name}: HistoricInput"), diff --git a/docs/tooling/pepsi.md b/docs/tooling/pepsi.md index 968ff2370e..eae40fe011 100644 --- a/docs/tooling/pepsi.md +++ b/docs/tooling/pepsi.md @@ -11,7 +11,7 @@ For detailed usage instructions, run `pepsi --help` or `pepsi --hel This is pretty simple. Open Webots, load the `webots/worlds/penalized_extern.wbt` world file and execute ```bash -./pepsi run +./pepsi run webots ``` in your terminal. This will build (if necessary) and then run the webots binary. @@ -42,7 +42,7 @@ Many subcommands can act on multiple robots concurrently. `upload` builds a binary for the NAO target, and then uploads it and parameter files to one or more robot. -`wireless`, `reboot`, `poweroff`, and `hulk` directly interact with the robot(s), whereas `communication`, and `playernumber` only change the local configuration parameters. +`wifi`, `reboot`, `poweroff`, and `hulk` directly interact with the robot(s), whereas `communication`, and `playernumber` only change the local configuration parameters. `pregame` combines deactivating communication (to avoid sending illegal messages), assigning playernumbers, setting a wifi network, uploading, and restarting the HULK service. diff --git a/docs/tooling/recording_and_replay.md b/docs/tooling/recording_and_replay.md index a1efadeee8..17471dce9b 100644 --- a/docs/tooling/recording_and_replay.md +++ b/docs/tooling/recording_and_replay.md @@ -26,7 +26,7 @@ Assuming you already recorded some data on a robot, you can now use the "replaye - Download the logs into a `logs` directory within the repository via, e.g., `./pepsi postgame ... my_awesome_replay ...` - The `my_awesome_replay` directory now contains directories for each robot. Each robot directory contains one directory with the replay data from one execution of the `hulk` binary. All cycler instance files need to be present, regardless whether they were enabled during recording (they will be empty then). -- Start the replayer tool by pointing it to the log directory you want to replay, e.g., `./pepsi run --target replayer -- my_awesome_replay/10.1.24.42/12345678`. +- Start the replayer tool by pointing it to the log directory you want to replay, e.g., `./pepsi run replayer -- my_awesome_replay/10.1.24.42/12345678`. - Connect your Twix to `localhost` and open some panels - Use mouse and keyboard in replayer, as described below - ... @@ -51,5 +51,5 @@ To extract images from recording data, you can use the "imagine" tool. Example: ``` -./pepsi run --target imagine -- my_awesome_replay/10.1.24.42/12345678 path/to/output +./pepsi run imagine -- my_awesome_replay/10.1.24.42/12345678 path/to/output ``` diff --git a/docs/workflow/checks.md b/docs/workflow/checks.md index 52e2e3d407..0184a342e0 100644 --- a/docs/workflow/checks.md +++ b/docs/workflow/checks.md @@ -8,11 +8,11 @@ These are the most important checks: | Check | How to run | |-----------------------|----------------------------------------------------------| -| Run unit tests | `cargo test --workspace` | -| Lint Rust code | `./pepsi clippy --workspace` | +| Run unit tests | `./pepsi test` | +| Lint Rust code | `./pepsi clippy` | | Format Rust code | `cargo fmt` | | Format TOML files | Install [Taplo](https://taplo.tamasfe.dev/); `taplo fmt` | -| Check parameter files | `cargo run --bin parameter_tester` | +| Check parameter files | `./pepsi run parameter_tester` | The checks performed in our CI workflow are defined in `.github/workflows/pull-request.yml`. diff --git a/hulk.toml b/hulk.toml new file mode 100644 index 0000000000..b9f0550984 --- /dev/null +++ b/hulk.toml @@ -0,0 +1,2 @@ +os_version = "7.6.0" +sdk_version = "7.6.0" diff --git a/pepsi b/pepsi index 1d5778509f..237d71cfde 100755 --- a/pepsi +++ b/pepsi @@ -3,6 +3,6 @@ THIS_DIRECTORY="$(dirname $(readlink -f $0))" PEPSI_TARGET_DIRECTORY=${CARGO_TARGET_DIR:-${THIS_DIRECTORY}/target} -cargo build --profile incremental --manifest-path="${THIS_DIRECTORY}/tools/pepsi/Cargo.toml" +cargo build --manifest-path="${THIS_DIRECTORY}/tools/pepsi/Cargo.toml" # cargo sets LD_LIBRARY_PATH which we don't want, so let's call the binary directly -"${PEPSI_TARGET_DIRECTORY}/incremental/pepsi" "$@" +"${PEPSI_TARGET_DIRECTORY}/debug/pepsi" "$@" diff --git a/scripts/remoteWorkspace b/scripts/remoteWorkspace index 0dc5e4c536..de25373239 100755 --- a/scripts/remoteWorkspace +++ b/scripts/remoteWorkspace @@ -24,6 +24,7 @@ Options: --return-file Path of file to be returned. The file path must be relative to the repository. Can be repeated to return multiple files. + --cd Change directory on remote before executing command If no --remote is given, the path loaded from `.REMOTE_WORKSPACE` The remote workspace path should follow this format: @@ -38,6 +39,7 @@ __helpText__ remoteWorkspace="${REMOTE_WORKSPACE:-$(cat .REMOTE_WORKSPACE)}" files=() +path="." while true; do case "$1" in -[h?] | --help) @@ -52,7 +54,10 @@ while true; do shift files+=($1) ;; - + --cd) + shift + path=$1 + ;; *) break ;; @@ -81,11 +86,13 @@ ssh \ $address \ "sh -c \" \ cd \"$remotePath\" \ + && cd \"$path\" \ && $@ \"" -# fetch results -echo Returning files -printf ' %s\n' "${files[@]}" -printf '%s\n' "${files[@]}" | rsync -ah --info=progress --files-from=- "$remoteWorkspace" . - +if [ ${#files[@]} -gt 0 ]; then + # fetch results + echo Returning files + printf ' %s\n' "${files[@]}" + printf '%s\n' "${files[@]}" | rsync -ah --info=progress --files-from=- "$remoteWorkspace" . +fi diff --git a/tools/aliveness/Cargo.lock b/services/aliveness/Cargo.lock similarity index 84% rename from tools/aliveness/Cargo.lock rename to services/aliveness/Cargo.lock index 58520e8560..58d8540833 100644 --- a/tools/aliveness/Cargo.lock +++ b/services/aliveness/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "addr2line" @@ -31,7 +31,7 @@ name = "aliveness" version = "0.1.0" dependencies = [ "futures-util", - "hula-types", + "hula_types", "regex", "serde", "serde_json", @@ -47,11 +47,10 @@ dependencies = [ "aliveness", "color-eyre", "configparser", - "constants", "env_logger", "futures-util", "hostname", - "hula-types", + "hula_types", "log", "serde_json", "tokio", @@ -89,8 +88,8 @@ checksum = "30ca9a001c1e8ba5149f91a74362376cc6bc5b919d92d988668657bd570bdcec" dependencies = [ "async-task", "concurrent-queue", - "fastrand 2.1.1", - "futures-lite 2.4.0", + "fastrand 2.3.0", + "futures-lite 2.5.0", "slab", ] @@ -120,7 +119,7 @@ dependencies = [ "log", "parking", "polling 2.8.0", - "rustix 0.37.27", + "rustix 0.37.28", "slab", "socket2 0.4.10", "waker-fn", @@ -128,18 +127,18 @@ dependencies = [ [[package]] name = "async-io" -version = "2.3.4" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "444b0228950ee6501b3568d3c93bf1176a1fdbc3b758dcd9475046d30f4dc7e8" +checksum = "43a2b323ccce0a1d90b449fd71f2a06ca7faa7c54c2751f06c9bd851fc061059" dependencies = [ "async-lock 3.4.0", "cfg-if", "concurrent-queue", "futures-io", - "futures-lite 2.4.0", + "futures-lite 2.5.0", "parking", - "polling 3.7.3", - "rustix 0.38.38", + "polling 3.7.4", + "rustix 0.38.43", "slab", "tracing", "windows-sys 0.59.0", @@ -160,7 +159,7 @@ version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" dependencies = [ - "event-listener 5.3.1", + "event-listener 5.4.0", "event-listener-strategy", "pin-project-lite", ] @@ -178,7 +177,7 @@ dependencies = [ "cfg-if", "event-listener 3.1.0", "futures-lite 1.13.0", - "rustix 0.38.38", + "rustix 0.38.43", "windows-sys 0.48.0", ] @@ -190,7 +189,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.86", + "syn 2.0.96", ] [[package]] @@ -199,13 +198,13 @@ version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "637e00349800c0bdf8bfc21ebbc0b6524abea702b0da4168ac00d070d0c0b9f3" dependencies = [ - "async-io 2.3.4", + "async-io 2.4.0", "async-lock 3.4.0", "atomic-waker", "cfg-if", "futures-core", "futures-io", - "rustix 0.38.38", + "rustix 0.38.43", "signal-hook-registry", "slab", "windows-sys 0.59.0", @@ -219,13 +218,13 @@ checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" [[package]] name = "async-trait" -version = "0.1.83" +version = "0.1.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" +checksum = "3f934833b4b7233644e5848f235df3f57ed8c80f1528a26c3dfa13d2147fa056" dependencies = [ "proc-macro2", "quote", - "syn 2.0.86", + "syn 2.0.96", ] [[package]] @@ -263,9 +262,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +checksum = "1be3f42a67d6d345ecd59f675f3f012d6974981560836e938c22b424b85ce1be" [[package]] name = "block-buffer" @@ -285,7 +284,7 @@ dependencies = [ "async-channel", "async-task", "futures-io", - "futures-lite 2.4.0", + "futures-lite 2.5.0", "piper", ] @@ -297,15 +296,15 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" +checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" [[package]] name = "cc" -version = "1.1.33" +version = "1.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3788d6ac30243803df38a3e9991cf37e41210232916d41a8222ae378f912624" +checksum = "a012a0df96dd6d06ba9a1b29d6402d1a5d77c6befd2566afdc26e10603dc93d7" dependencies = [ "shlex", ] @@ -361,29 +360,20 @@ dependencies = [ "tokio", ] -[[package]] -name = "constants" -version = "0.1.0" -dependencies = [ - "lazy_static", - "serde", - "toml", -] - [[package]] name = "cpufeatures" -version = "0.2.14" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" dependencies = [ "libc", ] [[package]] name = "crossbeam-utils" -version = "0.8.20" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crypto-common" @@ -434,7 +424,7 @@ checksum = "de0d48a183585823424a4ce1aa132d174a6a81bd540895822eb4c8373a8e49e8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.86", + "syn 2.0.96", ] [[package]] @@ -456,14 +446,24 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +[[package]] +name = "erased-serde" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24e2389d65ab4fab27dc2a5de7b191e1f6617d1f1c8855c0dc569c94a4cbb18d" +dependencies = [ + "serde", + "typeid", +] + [[package]] name = "errno" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -485,9 +485,9 @@ dependencies = [ [[package]] name = "event-listener" -version = "5.3.1" +version = "5.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" +checksum = "3492acde4c3fc54c845eaab3eed8bd00c7a7d881f78bfc801e43a93dec1331ae" dependencies = [ "concurrent-queue", "parking", @@ -496,11 +496,11 @@ dependencies = [ [[package]] name = "event-listener-strategy" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" +checksum = "3c3e4e0dd3673c1139bf041f3008816d9cf2946bbfac2945c09e523b8d7b05b2" dependencies = [ - "event-listener 5.3.1", + "event-listener 5.4.0", "pin-project-lite", ] @@ -525,9 +525,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.1.1" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "futures-core" @@ -558,11 +558,11 @@ dependencies = [ [[package]] name = "futures-lite" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f1fa2f9765705486b33fd2acf1577f8ec449c2ba1f318ae5447697b7c08d210" +checksum = "cef40d21ae2c515b51041df9ed313ed21e572df340ea58a922a0aefe7e8891a1" dependencies = [ - "fastrand 2.1.1", + "fastrand 2.3.0", "futures-core", "futures-io", "parking", @@ -577,7 +577,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.86", + "syn 2.0.96", ] [[package]] @@ -638,9 +638,9 @@ checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" [[package]] name = "hashbrown" -version = "0.15.0" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" [[package]] name = "hermit-abi" @@ -672,7 +672,7 @@ dependencies = [ ] [[package]] -name = "hula-types" +name = "hula_types" version = "0.1.0" dependencies = [ "serde", @@ -693,9 +693,9 @@ checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" [[package]] name = "indexmap" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" +checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" dependencies = [ "equivalent", "hashbrown", @@ -734,9 +734,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.11" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" [[package]] name = "lazy_static" @@ -746,9 +746,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.161" +version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" [[package]] name = "linux-raw-sys" @@ -758,9 +758,9 @@ checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" [[package]] name = "linux-raw-sys" -version = "0.4.14" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "lock_api" @@ -774,9 +774,12 @@ dependencies = [ [[package]] name = "log" -version = "0.4.22" +version = "0.4.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +checksum = "3d6ea2a48c204030ee31a7d7fc72c93294c92fe87ecb1789881c9543516e1a0d" +dependencies = [ + "value-bag", +] [[package]] name = "match_cfg" @@ -819,11 +822,10 @@ dependencies = [ [[package]] name = "mio" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" dependencies = [ - "hermit-abi 0.3.9", "libc", "wasi", "windows-sys 0.52.0", @@ -903,9 +905,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] name = "pin-utils" @@ -920,7 +922,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" dependencies = [ "atomic-waker", - "fastrand 2.1.1", + "fastrand 2.3.0", "futures-io", ] @@ -942,15 +944,15 @@ dependencies = [ [[package]] name = "polling" -version = "3.7.3" +version = "3.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc2790cd301dec6cd3b7a025e4815cf825724a51c98dccfe6a3e55f05ffb6511" +checksum = "a604568c3202727d1507653cb121dbd627a58684eb09a820fd746bee38b4442f" dependencies = [ "cfg-if", "concurrent-queue", "hermit-abi 0.4.0", "pin-project-lite", - "rustix 0.38.38", + "rustix 0.38.43", "tracing", "windows-sys 0.59.0", ] @@ -971,23 +973,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" dependencies = [ "once_cell", - "toml_edit 0.19.15", + "toml_edit", ] [[package]] name = "proc-macro2" -version = "1.0.89" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" dependencies = [ "proc-macro2", ] @@ -1024,11 +1026,11 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" +checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.7.0", ] [[package]] @@ -1045,9 +1047,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", @@ -1068,9 +1070,9 @@ checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustix" -version = "0.37.27" +version = "0.37.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fea8ca367a3a01fe35e6943c400addf443c0f57670e6ec51196f71a4b8762dd2" +checksum = "519165d378b97752ca44bbe15047d5d3409e875f39327546b42ac81d7e18c1b6" dependencies = [ "bitflags 1.3.2", "errno", @@ -1082,15 +1084,15 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.38" +version = "0.38.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa260229e6538e52293eeb577aabd09945a09d6d9cc0fc550ed7529056c2e32a" +checksum = "a78891ee6bf2340288408954ac787aa063d8e8817e9f53abb37c695c6d834ef6" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.7.0", "errno", "libc", - "linux-raw-sys 0.4.14", - "windows-sys 0.52.0", + "linux-raw-sys 0.4.15", + "windows-sys 0.59.0", ] [[package]] @@ -1107,29 +1109,38 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" -version = "1.0.214" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5" +checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.214" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" +checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.86", + "syn 2.0.96", +] + +[[package]] +name = "serde_fmt" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d4ddca14104cd60529e8c7f7ba71a2c8acd8f7f5cfcdc2faf97eeb7c3010a4" +dependencies = [ + "serde", ] [[package]] name = "serde_json" -version = "1.0.132" +version = "1.0.135" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" +checksum = "2b0d7ba2887406110130a978386c4e1befb98c674b4fba677954e4db976630d9" dependencies = [ "itoa", "memchr", @@ -1145,16 +1156,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.86", -] - -[[package]] -name = "serde_spanned" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" -dependencies = [ - "serde", + "syn 2.0.96", ] [[package]] @@ -1219,9 +1221,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" dependencies = [ "libc", "windows-sys 0.52.0", @@ -1233,6 +1235,84 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "sval" +version = "2.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6dc0f9830c49db20e73273ffae9b5240f63c42e515af1da1fceefb69fceafd8" + +[[package]] +name = "sval_buffer" +version = "2.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "429922f7ad43c0ef8fd7309e14d750e38899e32eb7e8da656ea169dd28ee212f" +dependencies = [ + "sval", + "sval_ref", +] + +[[package]] +name = "sval_dynamic" +version = "2.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f16ff5d839396c11a30019b659b0976348f3803db0626f736764c473b50ff4" +dependencies = [ + "sval", +] + +[[package]] +name = "sval_fmt" +version = "2.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c01c27a80b6151b0557f9ccbe89c11db571dc5f68113690c1e028d7e974bae94" +dependencies = [ + "itoa", + "ryu", + "sval", +] + +[[package]] +name = "sval_json" +version = "2.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0deef63c70da622b2a8069d8600cf4b05396459e665862e7bdb290fd6cf3f155" +dependencies = [ + "itoa", + "ryu", + "sval", +] + +[[package]] +name = "sval_nested" +version = "2.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a39ce5976ae1feb814c35d290cf7cf8cd4f045782fe1548d6bc32e21f6156e9f" +dependencies = [ + "sval", + "sval_buffer", + "sval_ref", +] + +[[package]] +name = "sval_ref" +version = "2.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb7c6ee3751795a728bc9316a092023529ffea1783499afbc5c66f5fabebb1fa" +dependencies = [ + "sval", +] + +[[package]] +name = "sval_serde" +version = "2.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a5572d0321b68109a343634e3a5d576bf131b82180c6c442dee06349dfc652a" +dependencies = [ + "serde", + "sval", + "sval_nested", +] + [[package]] name = "syn" version = "1.0.109" @@ -1246,9 +1326,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.86" +version = "2.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89275301d38033efb81a6e60e3497e734dfcc62571f2854bf4b16690398824c" +checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" dependencies = [ "proc-macro2", "quote", @@ -1257,14 +1337,15 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.13.0" +version = "3.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" +checksum = "9a8a559c81686f576e8cd0290cd2a24a2a9ad80c98b3478856500fcbd7acd704" dependencies = [ "cfg-if", - "fastrand 2.1.1", + "fastrand 2.3.0", + "getrandom", "once_cell", - "rustix 0.38.38", + "rustix 0.38.43", "windows-sys 0.59.0", ] @@ -1279,22 +1360,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.66" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d171f59dbaa811dbbb1aee1e73db92ec2b122911a48e1390dfe327a821ddede" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.66" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b08be0f17bd307950653ce45db00cd31200d82b624b36e181337d9c7d92765b5" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.86", + "syn 2.0.96", ] [[package]] @@ -1309,9 +1390,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.41.0" +version = "1.43.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "145f3413504347a2be84393cc8a7d2fb4d863b375909ea59f2158261aa258bbb" +checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e" dependencies = [ "backtrace", "bytes", @@ -1320,7 +1401,7 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2 0.5.7", + "socket2 0.5.8", "tokio-macros", "tracing", "windows-sys 0.52.0", @@ -1328,20 +1409,20 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.86", + "syn 2.0.96", ] [[package]] name = "tokio-util" -version = "0.7.12" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" +checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078" dependencies = [ "bytes", "futures-core", @@ -1350,26 +1431,11 @@ dependencies = [ "tokio", ] -[[package]] -name = "toml" -version = "0.8.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit 0.22.22", -] - [[package]] name = "toml_datetime" version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" -dependencies = [ - "serde", -] [[package]] name = "toml_edit" @@ -1379,27 +1445,14 @@ checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ "indexmap", "toml_datetime", - "winnow 0.5.40", -] - -[[package]] -name = "toml_edit" -version = "0.22.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" -dependencies = [ - "indexmap", - "serde", - "serde_spanned", - "toml_datetime", - "winnow 0.6.20", + "winnow", ] [[package]] name = "tracing" -version = "0.1.40" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "pin-project-lite", "tracing-attributes", @@ -1408,20 +1461,20 @@ 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.86", + "syn 2.0.96", ] [[package]] name = "tracing-core" -version = "0.1.32" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" dependencies = [ "once_cell", "valuable", @@ -1429,9 +1482,9 @@ dependencies = [ [[package]] name = "tracing-error" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d686ec1c0f384b1277f097b2f279a2ecc11afe8c133c1aabf036a27cb4cd206e" +checksum = "8b1581020d7a273442f5b45074a6a57d5757ad0a47dac0e9f0bd57b81936f3db" dependencies = [ "tracing", "tracing-subscriber", @@ -1439,15 +1492,21 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.18" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" dependencies = [ "sharded-slab", "thread_local", "tracing-core", ] +[[package]] +name = "typeid" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e13db2e0ccd5e14a544e8a246ba2312cd25223f616442d7f2cb0e3db614236e" + [[package]] name = "typenum" version = "1.17.0" @@ -1467,9 +1526,9 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" [[package]] name = "valuable" @@ -1477,6 +1536,42 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +[[package]] +name = "value-bag" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ef4c4aa54d5d05a279399bfa921ec387b7aba77caf7a682ae8d86785b8fdad2" +dependencies = [ + "value-bag-serde1", + "value-bag-sval2", +] + +[[package]] +name = "value-bag-serde1" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bb773bd36fd59c7ca6e336c94454d9c66386416734817927ac93d81cb3c5b0b" +dependencies = [ + "erased-serde", + "serde", + "serde_fmt", +] + +[[package]] +name = "value-bag-sval2" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53a916a702cac43a88694c97657d449775667bcd14b70419441d05b7fea4a83a" +dependencies = [ + "sval", + "sval_buffer", + "sval_dynamic", + "sval_fmt", + "sval_json", + "sval_ref", + "sval_serde", +] + [[package]] name = "version_check" version = "0.9.5" @@ -1683,15 +1778,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "winnow" -version = "0.6.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" -dependencies = [ - "memchr", -] - [[package]] name = "xdg-home" version = "1.3.0" @@ -1787,7 +1873,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.86", + "syn 2.0.96", ] [[package]] diff --git a/tools/aliveness/Cargo.toml b/services/aliveness/Cargo.toml similarity index 83% rename from tools/aliveness/Cargo.toml rename to services/aliveness/Cargo.toml index 700c019ad2..908f2a7a5c 100644 --- a/tools/aliveness/Cargo.toml +++ b/services/aliveness/Cargo.toml @@ -5,15 +5,17 @@ edition = "2021" license = "GPL-3.0-only" homepage = "https://github.com/hulks/hulk" +[package.metadata.pepsi] +cross-compile = true + [dependencies] aliveness = { path = "../../crates/aliveness" } color-eyre = "0.6.2" configparser = { version = "3.0.2", features = ["async-std"] } -constants = { path = "../../crates/constants" } env_logger = "0.10.0" futures-util = "0.3.25" hostname = "0.3.1" -hula-types = { path = "../hula/types/" } +hula_types = { path = "../../crates/hula_types/" } log = "0.4.17" serde_json = "1.0.89" tokio = { version = "1.22.0", features = ["full"] } diff --git a/tools/aliveness/src/main.rs b/services/aliveness/src/main.rs similarity index 100% rename from tools/aliveness/src/main.rs rename to services/aliveness/src/main.rs diff --git a/tools/aliveness/src/robot_info.rs b/services/aliveness/src/robot_info.rs similarity index 98% rename from tools/aliveness/src/robot_info.rs rename to services/aliveness/src/robot_info.rs index 3636910e8e..a8652cab5a 100644 --- a/tools/aliveness/src/robot_info.rs +++ b/services/aliveness/src/robot_info.rs @@ -1,11 +1,12 @@ use color_eyre::eyre::{eyre, Context, Result}; use configparser::ini::Ini; -use constants::OS_RELEASE_PATH; use hula_types::{Battery, JointsArray}; use tokio::process::Command; use zbus::{dbus_proxy, zvariant::Optional, Connection}; +const OS_RELEASE_PATH: &str = "/etc/os-release"; + #[dbus_proxy( default_service = "org.hulks.hula", interface = "org.hulks.hula", diff --git a/tools/breeze/Cargo.lock b/services/breeze/Cargo.lock similarity index 55% rename from tools/breeze/Cargo.lock rename to services/breeze/Cargo.lock index 68f5093e67..0f0e12bc34 100644 --- a/tools/breeze/Cargo.lock +++ b/services/breeze/Cargo.lock @@ -1,12 +1,12 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "aho-corasick" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] @@ -41,9 +41,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.1" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +checksum = "1be3f42a67d6d345ecd59f675f3f012d6974981560836e938c22b424b85ce1be" [[package]] name = "breeze" @@ -80,9 +80,9 @@ dependencies = [ [[package]] name = "clang-sys" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" dependencies = [ "glob", "libc", @@ -91,40 +91,40 @@ dependencies = [ [[package]] name = "either" -version = "1.9.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "errno" -version = "0.3.8" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] name = "glob" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" [[package]] name = "home" -version = "0.5.9" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" dependencies = [ - "windows-sys 0.52.0", + "windows-sys", ] [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "lazycell" @@ -134,37 +134,37 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.152" +version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" [[package]] name = "libloading" -version = "0.8.1" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c571b676ddfc9a8c12f1f3d3085a7b163966a8fd8098a90640953ce5f6170161" +checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" dependencies = [ "cfg-if", - "windows-sys 0.48.0", + "windows-targets", ] [[package]] name = "linux-raw-sys" -version = "0.4.12" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "log" -version = "0.4.20" +version = "0.4.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +checksum = "3d6ea2a48c204030ee31a7d7fc72c93294c92fe87ecb1789881c9543516e1a0d" [[package]] name = "memchr" -version = "2.7.1" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "minimal-lexical" @@ -184,9 +184,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "peeking_take_while" @@ -196,27 +196,27 @@ checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" [[package]] name = "proc-macro2" -version = "1.0.76" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.35" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" dependencies = [ "proc-macro2", ] [[package]] name = "regex" -version = "1.10.2" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", @@ -226,9 +226,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.3" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", @@ -237,9 +237,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.2" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "rustc-hash" @@ -249,22 +249,22 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustix" -version = "0.38.28" +version = "0.38.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" +checksum = "a78891ee6bf2340288408954ac787aa063d8e8817e9f53abb37c695c6d834ef6" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.7.0", "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] name = "shlex" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "syn" @@ -279,9 +279,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.48" +version = "2.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" dependencies = [ "proc-macro2", "quote", @@ -290,29 +290,29 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.56" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.56" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.96", ] [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" [[package]] name = "which" @@ -328,132 +328,73 @@ dependencies = [ [[package]] name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", -] - -[[package]] -name = "windows-sys" -version = "0.52.0" +version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets 0.52.0", + "windows-targets", ] [[package]] name = "windows-targets" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", -] - -[[package]] -name = "windows-targets" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" -dependencies = [ - "windows_aarch64_gnullvm 0.52.0", - "windows_aarch64_msvc 0.52.0", - "windows_i686_gnu 0.52.0", - "windows_i686_msvc 0.52.0", - "windows_x86_64_gnu 0.52.0", - "windows_x86_64_gnullvm 0.52.0", - "windows_x86_64_msvc 0.52.0", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] -name = "windows_i686_msvc" -version = "0.48.5" +name = "windows_i686_gnullvm" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" diff --git a/tools/breeze/Cargo.toml b/services/breeze/Cargo.toml similarity index 77% rename from tools/breeze/Cargo.toml rename to services/breeze/Cargo.toml index 6ae6690b7b..5f37fdabde 100644 --- a/tools/breeze/Cargo.toml +++ b/services/breeze/Cargo.toml @@ -5,5 +5,8 @@ edition = "2021" license = "GPL-3.0-only" homepage = "https://github.com/hulks/hulk" +[package.metadata.pepsi] +cross-compile = true + [dependencies] cgos = "0.2.0" diff --git a/tools/breeze/src/main.rs b/services/breeze/src/main.rs similarity index 100% rename from tools/breeze/src/main.rs rename to services/breeze/src/main.rs diff --git a/tools/hula/Cargo.lock b/services/hula/Cargo.lock similarity index 79% rename from tools/hula/Cargo.lock rename to services/hula/Cargo.lock index b6ff82e6a4..eff4409f30 100644 --- a/tools/hula/Cargo.lock +++ b/services/hula/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "addr2line" @@ -26,11 +26,26 @@ dependencies = [ "memchr", ] +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "anstream" -version = "0.6.17" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23a1e53f0f5d86382dafe1cf314783b2044280f406e7e1506368220ad11b1338" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" dependencies = [ "anstyle", "anstyle-parse", @@ -105,8 +120,8 @@ checksum = "30ca9a001c1e8ba5149f91a74362376cc6bc5b919d92d988668657bd570bdcec" dependencies = [ "async-task", "concurrent-queue", - "fastrand 2.1.1", - "futures-lite 2.4.0", + "fastrand 2.3.0", + "futures-lite 2.5.0", "slab", ] @@ -136,7 +151,7 @@ dependencies = [ "log", "parking", "polling 2.8.0", - "rustix 0.37.27", + "rustix 0.37.28", "slab", "socket2", "waker-fn", @@ -144,18 +159,18 @@ dependencies = [ [[package]] name = "async-io" -version = "2.3.4" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "444b0228950ee6501b3568d3c93bf1176a1fdbc3b758dcd9475046d30f4dc7e8" +checksum = "43a2b323ccce0a1d90b449fd71f2a06ca7faa7c54c2751f06c9bd851fc061059" dependencies = [ "async-lock 3.4.0", "cfg-if", "concurrent-queue", "futures-io", - "futures-lite 2.4.0", + "futures-lite 2.5.0", "parking", - "polling 3.7.3", - "rustix 0.38.38", + "polling 3.7.4", + "rustix 0.38.43", "slab", "tracing", "windows-sys 0.59.0", @@ -176,7 +191,7 @@ version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" dependencies = [ - "event-listener 5.3.1", + "event-listener 5.4.0", "event-listener-strategy", "pin-project-lite", ] @@ -194,7 +209,7 @@ dependencies = [ "cfg-if", "event-listener 3.1.0", "futures-lite 1.13.0", - "rustix 0.38.38", + "rustix 0.38.43", "windows-sys 0.48.0", ] @@ -206,7 +221,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.86", + "syn 2.0.96", ] [[package]] @@ -215,13 +230,13 @@ version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "637e00349800c0bdf8bfc21ebbc0b6524abea702b0da4168ac00d070d0c0b9f3" dependencies = [ - "async-io 2.3.4", + "async-io 2.4.0", "async-lock 3.4.0", "atomic-waker", "cfg-if", "futures-core", "futures-io", - "rustix 0.38.38", + "rustix 0.38.43", "signal-hook-registry", "slab", "windows-sys 0.59.0", @@ -235,13 +250,13 @@ checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" [[package]] name = "async-trait" -version = "0.1.83" +version = "0.1.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" +checksum = "3f934833b4b7233644e5848f235df3f57ed8c80f1528a26c3dfa13d2147fa056" dependencies = [ "proc-macro2", "quote", - "syn 2.0.86", + "syn 2.0.96", ] [[package]] @@ -279,9 +294,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +checksum = "1be3f42a67d6d345ecd59f675f3f012d6974981560836e938c22b424b85ce1be" [[package]] name = "block-buffer" @@ -301,7 +316,7 @@ dependencies = [ "async-channel", "async-task", "futures-io", - "futures-lite 2.4.0", + "futures-lite 2.5.0", "piper", ] @@ -311,6 +326,12 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1522ac6ee801a11bf9ef3f80403f4ede6eb41291fac3dde3de09989679305f25" +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + [[package]] name = "byteorder" version = "1.5.0" @@ -319,9 +340,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "cc" -version = "1.1.33" +version = "1.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3788d6ac30243803df38a3e9991cf37e41210232916d41a8222ae378f912624" +checksum = "a012a0df96dd6d06ba9a1b29d6402d1a5d77c6befd2566afdc26e10603dc93d7" dependencies = [ "shlex", ] @@ -332,11 +353,25 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-targets 0.52.6", +] + [[package]] name = "clap" -version = "4.5.20" +version = "4.5.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8" +checksum = "a8eb5e908ef3a6efbe1ed62520fb7287959888c88485abe072543190ecc66783" dependencies = [ "clap_builder", "clap_derive", @@ -344,9 +379,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.20" +version = "4.5.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54" +checksum = "96b01801b5fc6a0a232407abc821660c9c6d25a1cafc0d4f85f29fb8d9afc121" dependencies = [ "anstream", "anstyle", @@ -356,21 +391,21 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.18" +version = "4.5.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" +checksum = "54b755194d6389280185988721fffba69495eed5ee9feeee9a599b53db80318c" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.86", + "syn 2.0.96", ] [[package]] name = "clap_lex" -version = "0.7.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" [[package]] name = "color-eyre" @@ -415,28 +450,25 @@ dependencies = [ ] [[package]] -name = "constants" -version = "0.1.0" -dependencies = [ - "lazy_static", - "serde", - "toml", -] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cpufeatures" -version = "0.2.14" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" dependencies = [ "libc", ] [[package]] name = "crossbeam-utils" -version = "0.8.20" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crypto-common" @@ -497,7 +529,7 @@ checksum = "de0d48a183585823424a4ce1aa132d174a6a81bd540895822eb4c8373a8e49e8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.86", + "syn 2.0.96", ] [[package]] @@ -519,7 +551,7 @@ version = "4.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74351c3392ea1ff6cd2628e0042d268ac2371cb613252ff383b6dfa50d22fa79" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.7.0", "libc", ] @@ -529,14 +561,24 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +[[package]] +name = "erased-serde" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24e2389d65ab4fab27dc2a5de7b191e1f6617d1f1c8855c0dc569c94a4cbb18d" +dependencies = [ + "serde", + "typeid", +] + [[package]] name = "errno" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -558,9 +600,9 @@ dependencies = [ [[package]] name = "event-listener" -version = "5.3.1" +version = "5.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" +checksum = "3492acde4c3fc54c845eaab3eed8bd00c7a7d881f78bfc801e43a93dec1331ae" dependencies = [ "concurrent-queue", "parking", @@ -569,11 +611,11 @@ dependencies = [ [[package]] name = "event-listener-strategy" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" +checksum = "3c3e4e0dd3673c1139bf041f3008816d9cf2946bbfac2945c09e523b8d7b05b2" dependencies = [ - "event-listener 5.3.1", + "event-listener 5.4.0", "pin-project-lite", ] @@ -598,9 +640,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.1.1" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "foreign-types" @@ -620,7 +662,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.86", + "syn 2.0.96", ] [[package]] @@ -658,11 +700,11 @@ dependencies = [ [[package]] name = "futures-lite" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f1fa2f9765705486b33fd2acf1577f8ec449c2ba1f318ae5447697b7c08d210" +checksum = "cef40d21ae2c515b51041df9ed313ed21e572df340ea58a922a0aefe7e8891a1" dependencies = [ - "fastrand 2.1.1", + "fastrand 2.3.0", "futures-core", "futures-io", "parking", @@ -726,9 +768,9 @@ checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" [[package]] name = "hashbrown" -version = "0.15.0" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" [[package]] name = "heck" @@ -758,20 +800,21 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" name = "hula" version = "0.5.0" dependencies = [ + "chrono", "clap", "color-eyre", - "constants", "env_logger", "epoll", - "hula-types", + "hula_types", "log", "rmp-serde", + "serde", "systemd", "zbus", ] [[package]] -name = "hula-types" +name = "hula_types" version = "0.1.0" dependencies = [ "serde", @@ -784,6 +827,29 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +[[package]] +name = "iana-time-zone" +version = "0.1.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + [[package]] name = "indenter" version = "0.3.3" @@ -792,9 +858,9 @@ checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" [[package]] name = "indexmap" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" +checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" dependencies = [ "equivalent", "hashbrown", @@ -837,6 +903,22 @@ version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +[[package]] +name = "itoa" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" + +[[package]] +name = "js-sys" +version = "0.3.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + [[package]] name = "lazy_static" version = "1.5.0" @@ -845,9 +927,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.161" +version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" [[package]] name = "libsystemd-sys" @@ -868,15 +950,18 @@ checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" [[package]] name = "linux-raw-sys" -version = "0.4.14" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "log" -version = "0.4.22" +version = "0.4.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +checksum = "3d6ea2a48c204030ee31a7d7fc72c93294c92fe87ecb1789881c9543516e1a0d" +dependencies = [ + "value-bag", +] [[package]] name = "memchr" @@ -977,9 +1062,9 @@ checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "pin-project-lite" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] name = "pin-utils" @@ -994,7 +1079,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" dependencies = [ "atomic-waker", - "fastrand 2.1.1", + "fastrand 2.3.0", "futures-io", ] @@ -1022,15 +1107,15 @@ dependencies = [ [[package]] name = "polling" -version = "3.7.3" +version = "3.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc2790cd301dec6cd3b7a025e4815cf825724a51c98dccfe6a3e55f05ffb6511" +checksum = "a604568c3202727d1507653cb121dbd627a58684eb09a820fd746bee38b4442f" dependencies = [ "cfg-if", "concurrent-queue", "hermit-abi 0.4.0", "pin-project-lite", - "rustix 0.38.38", + "rustix 0.38.43", "tracing", "windows-sys 0.59.0", ] @@ -1051,23 +1136,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" dependencies = [ "once_cell", - "toml_edit 0.19.15", + "toml_edit", ] [[package]] name = "proc-macro2" -version = "1.0.89" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" dependencies = [ "proc-macro2", ] @@ -1116,9 +1201,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", @@ -1161,9 +1246,9 @@ checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustix" -version = "0.37.27" +version = "0.37.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fea8ca367a3a01fe35e6943c400addf443c0f57670e6ec51196f71a4b8762dd2" +checksum = "519165d378b97752ca44bbe15047d5d3409e875f39327546b42ac81d7e18c1b6" dependencies = [ "bitflags 1.3.2", "errno", @@ -1175,55 +1260,61 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.38" +version = "0.38.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa260229e6538e52293eeb577aabd09945a09d6d9cc0fc550ed7529056c2e32a" +checksum = "a78891ee6bf2340288408954ac787aa063d8e8817e9f53abb37c695c6d834ef6" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.7.0", "errno", "libc", - "linux-raw-sys 0.4.14", - "windows-sys 0.52.0", + "linux-raw-sys 0.4.15", + "windows-sys 0.59.0", ] +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + [[package]] name = "serde" -version = "1.0.214" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5" +checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.214" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" +checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.86", + "syn 2.0.96", ] [[package]] -name = "serde_repr" -version = "0.1.19" +name = "serde_fmt" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" +checksum = "e1d4ddca14104cd60529e8c7f7ba71a2c8acd8f7f5cfcdc2faf97eeb7c3010a4" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.86", + "serde", ] [[package]] -name = "serde_spanned" -version = "0.6.8" +name = "serde_repr" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ - "serde", + "proc-macro2", + "quote", + "syn 2.0.96", ] [[package]] @@ -1292,6 +1383,84 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[package]] +name = "sval" +version = "2.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6dc0f9830c49db20e73273ffae9b5240f63c42e515af1da1fceefb69fceafd8" + +[[package]] +name = "sval_buffer" +version = "2.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "429922f7ad43c0ef8fd7309e14d750e38899e32eb7e8da656ea169dd28ee212f" +dependencies = [ + "sval", + "sval_ref", +] + +[[package]] +name = "sval_dynamic" +version = "2.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f16ff5d839396c11a30019b659b0976348f3803db0626f736764c473b50ff4" +dependencies = [ + "sval", +] + +[[package]] +name = "sval_fmt" +version = "2.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c01c27a80b6151b0557f9ccbe89c11db571dc5f68113690c1e028d7e974bae94" +dependencies = [ + "itoa", + "ryu", + "sval", +] + +[[package]] +name = "sval_json" +version = "2.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0deef63c70da622b2a8069d8600cf4b05396459e665862e7bdb290fd6cf3f155" +dependencies = [ + "itoa", + "ryu", + "sval", +] + +[[package]] +name = "sval_nested" +version = "2.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a39ce5976ae1feb814c35d290cf7cf8cd4f045782fe1548d6bc32e21f6156e9f" +dependencies = [ + "sval", + "sval_buffer", + "sval_ref", +] + +[[package]] +name = "sval_ref" +version = "2.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb7c6ee3751795a728bc9316a092023529ffea1783499afbc5c66f5fabebb1fa" +dependencies = [ + "sval", +] + +[[package]] +name = "sval_serde" +version = "2.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a5572d0321b68109a343634e3a5d576bf131b82180c6c442dee06349dfc652a" +dependencies = [ + "serde", + "sval", + "sval_nested", +] + [[package]] name = "syn" version = "1.0.109" @@ -1305,9 +1474,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.86" +version = "2.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89275301d38033efb81a6e60e3497e734dfcc62571f2854bf4b16690398824c" +checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" dependencies = [ "proc-macro2", "quote", @@ -1331,14 +1500,15 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.13.0" +version = "3.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" +checksum = "9a8a559c81686f576e8cd0290cd2a24a2a9ad80c98b3478856500fcbd7acd704" dependencies = [ "cfg-if", - "fastrand 2.1.1", + "fastrand 2.3.0", + "getrandom", "once_cell", - "rustix 0.38.38", + "rustix 0.38.43", "windows-sys 0.59.0", ] @@ -1361,26 +1531,11 @@ dependencies = [ "once_cell", ] -[[package]] -name = "toml" -version = "0.8.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit 0.22.22", -] - [[package]] name = "toml_datetime" version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" -dependencies = [ - "serde", -] [[package]] name = "toml_edit" @@ -1390,27 +1545,14 @@ checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ "indexmap", "toml_datetime", - "winnow 0.5.40", -] - -[[package]] -name = "toml_edit" -version = "0.22.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" -dependencies = [ - "indexmap", - "serde", - "serde_spanned", - "toml_datetime", - "winnow 0.6.20", + "winnow", ] [[package]] name = "tracing" -version = "0.1.40" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "pin-project-lite", "tracing-attributes", @@ -1419,20 +1561,20 @@ 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.86", + "syn 2.0.96", ] [[package]] name = "tracing-core" -version = "0.1.32" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" dependencies = [ "once_cell", "valuable", @@ -1440,9 +1582,9 @@ dependencies = [ [[package]] name = "tracing-error" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d686ec1c0f384b1277f097b2f279a2ecc11afe8c133c1aabf036a27cb4cd206e" +checksum = "8b1581020d7a273442f5b45074a6a57d5757ad0a47dac0e9f0bd57b81936f3db" dependencies = [ "tracing", "tracing-subscriber", @@ -1450,15 +1592,21 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.18" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" dependencies = [ "sharded-slab", "thread_local", "tracing-core", ] +[[package]] +name = "typeid" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e13db2e0ccd5e14a544e8a246ba2312cd25223f616442d7f2cb0e3db614236e" + [[package]] name = "typenum" version = "1.17.0" @@ -1478,9 +1626,9 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" [[package]] name = "utf8-cstr" @@ -1500,6 +1648,42 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +[[package]] +name = "value-bag" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ef4c4aa54d5d05a279399bfa921ec387b7aba77caf7a682ae8d86785b8fdad2" +dependencies = [ + "value-bag-serde1", + "value-bag-sval2", +] + +[[package]] +name = "value-bag-serde1" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bb773bd36fd59c7ca6e336c94454d9c66386416734817927ac93d81cb3c5b0b" +dependencies = [ + "erased-serde", + "serde", + "serde_fmt", +] + +[[package]] +name = "value-bag-sval2" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53a916a702cac43a88694c97657d449775667bcd14b70419441d05b7fea4a83a" +dependencies = [ + "sval", + "sval_buffer", + "sval_dynamic", + "sval_fmt", + "sval_json", + "sval_ref", + "sval_serde", +] + [[package]] name = "version_check" version = "0.9.5" @@ -1518,6 +1702,60 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasm-bindgen" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" +dependencies = [ + "cfg-if", + "once_cell", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn 2.0.96", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" + [[package]] name = "winapi" version = "0.3.9" @@ -1549,6 +1787,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.6", +] + [[package]] name = "windows-sys" version = "0.48.0" @@ -1706,15 +1953,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "winnow" -version = "0.6.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" -dependencies = [ - "memchr", -] - [[package]] name = "xdg-home" version = "1.3.0" @@ -1809,7 +2047,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.86", + "syn 2.0.96", ] [[package]] diff --git a/tools/hula/Cargo.toml b/services/hula/Cargo.toml similarity index 64% rename from tools/hula/Cargo.toml rename to services/hula/Cargo.toml index 5a90d9c1e8..5872a19e05 100644 --- a/tools/hula/Cargo.toml +++ b/services/hula/Cargo.toml @@ -1,23 +1,20 @@ -[workspace] -resolver = "2" -members = [ - "proxy", - "types", -] - -[workspace.package] +[package] +name = "hula" +version = "0.5.0" edition = "2021" license = "GPL-3.0-only" homepage = "https://github.com/hulks/hulk" -[workspace.dependencies] +[package.metadata.pepsi] +cross-compile = true + +[dependencies] chrono = "0.4.23" clap = { version = "4.0.29", features = ["derive"] } color-eyre = "0.6.2" -constants = { path = "../../crates/constants" } env_logger = "0.10.0" epoll = "4.3.1" -hula-types = { path = "./types" } +hula_types = { path = "../../crates/hula_types/" } log = "0.4.17" rmp-serde = "1.1.1" serde = { version = "1.0.149", features = ["derive"] } diff --git a/tools/hula/proxy/src/dbus.rs b/services/hula/src/dbus.rs similarity index 93% rename from tools/hula/proxy/src/dbus.rs rename to services/hula/src/dbus.rs index 9244acfb26..50accef164 100644 --- a/tools/hula/proxy/src/dbus.rs +++ b/services/hula/src/dbus.rs @@ -8,7 +8,9 @@ use zbus::{ }; use crate::SharedState; -use constants::{HULA_DBUS_PATH, HULA_DBUS_SERVICE}; + +const HULA_DBUS_SERVICE: &str = "org.hulks.hula"; +const HULA_DBUS_PATH: &str = "/org/hulks/HuLA"; struct RobotInfo { shared_state: Arc>, diff --git a/tools/hula/proxy/src/idle.rs b/services/hula/src/idle.rs similarity index 100% rename from tools/hula/proxy/src/idle.rs rename to services/hula/src/idle.rs diff --git a/tools/hula/proxy/src/main.rs b/services/hula/src/main.rs similarity index 100% rename from tools/hula/proxy/src/main.rs rename to services/hula/src/main.rs diff --git a/tools/hula/proxy/src/proxy.rs b/services/hula/src/proxy.rs similarity index 99% rename from tools/hula/proxy/src/proxy.rs rename to services/hula/src/proxy.rs index f35e6cf90b..3d72433fe5 100644 --- a/tools/hula/proxy/src/proxy.rs +++ b/services/hula/src/proxy.rs @@ -25,8 +25,8 @@ use crate::{ idle::{charging_skull, send_idle}, SharedState, }; -use constants::HULA_SOCKET_PATH; +const HULA_SOCKET_PATH: &str = "/tmp/hula"; const LOLA_SOCKET_PATH: &str = "/tmp/robocup"; const LOLA_SOCKET_RETRY_COUNT: usize = 60; const LOLA_SOCKET_RETRY_INTERVAL: Duration = Duration::from_secs(1); diff --git a/tools/hula/proxy/Cargo.toml b/tools/hula/proxy/Cargo.toml deleted file mode 100644 index f3827c9585..0000000000 --- a/tools/hula/proxy/Cargo.toml +++ /dev/null @@ -1,18 +0,0 @@ -[package] -name = "hula" -version = "0.5.0" -edition.workspace = true -license.workspace = true -homepage.workspace = true - -[dependencies] -clap = { workspace = true } -color-eyre = { workspace = true } -constants = { workspace = true } -env_logger = { workspace = true } -epoll = { workspace = true } -hula-types = { workspace = true } -log = { workspace = true } -rmp-serde = { workspace = true } -systemd = { workspace = true } -zbus = { workspace = true } diff --git a/crates/parameter_tester/Cargo.toml b/tools/parameter_tester/Cargo.toml similarity index 100% rename from crates/parameter_tester/Cargo.toml rename to tools/parameter_tester/Cargo.toml diff --git a/crates/parameter_tester/src/main.rs b/tools/parameter_tester/src/main.rs similarity index 100% rename from crates/parameter_tester/src/main.rs rename to tools/parameter_tester/src/main.rs diff --git a/tools/pepsi/Cargo.toml b/tools/pepsi/Cargo.toml index 8c5aeea021..89e80966d3 100644 --- a/tools/pepsi/Cargo.toml +++ b/tools/pepsi/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pepsi" -version = "3.17.0" +version = "4.0.0" edition.workspace = true license.workspace = true homepage.workspace = true @@ -12,18 +12,22 @@ bat = { workspace = true } clap = { workspace = true } clap_complete = { workspace = true } color-eyre = { workspace = true } -constants = { workspace = true } futures-util = { workspace = true } +glob = { workspace = true } indicatif = { workspace = true } lazy_static = { workspace = true } nao = { workspace = true } opn = { workspace = true } +pathdiff = { workspace = true } regex = { workspace = true } repository = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } source_analyzer = { workspace = true } spl_network_messages = { workspace = true } +tempfile = { workspace = true } thiserror = { workspace = true } tokio = { workspace = true } toml = { workspace = true } +tracing = { workspace = true } +tracing-subscriber = { workspace = true } diff --git a/tools/pepsi/src/aliveness.rs b/tools/pepsi/src/aliveness.rs index f0b2a9f7dc..76513956ba 100644 --- a/tools/pepsi/src/aliveness.rs +++ b/tools/pepsi/src/aliveness.rs @@ -1,7 +1,11 @@ -use std::{collections::BTreeMap, net::IpAddr, num::ParseIntError, time::Duration}; +use std::{collections::BTreeMap, net::IpAddr, num::ParseIntError, path::Path, time::Duration}; use clap::{arg, Args}; -use color_eyre::owo_colors::{OwoColorize, Style}; +use color_eyre::{ + eyre::Context, + owo_colors::{OwoColorize, Style}, + Result, +}; use aliveness::{ query_aliveness, @@ -9,15 +13,16 @@ use aliveness::{ AlivenessError, AlivenessState, Battery, JointsArray, }; use argument_parsers::NaoAddress; -use constants::OS_VERSION; +use repository::configuration::read_os_version; +use tracing::error; #[derive(Args)] pub struct Arguments { /// Output verbose version of the aliveness information - #[arg(long, short = 'v')] + #[arg(long, short = 'v', conflicts_with = "json")] verbose: bool, /// Output aliveness information as json - #[arg(long, short = 'j')] + #[arg(long, short = 'j', conflicts_with = "verbose")] json: bool, /// Timeout in ms for waiting for responses #[arg(long, short = 't', value_parser = parse_duration, default_value = "200")] @@ -33,27 +38,34 @@ fn parse_duration(arg: &str) -> Result { type AlivenessList = BTreeMap; -#[derive(Debug, thiserror::Error)] -pub enum Error { - #[error("failed to query aliveness")] - QueryFailed(AlivenessError), - #[error("failed to serialize data")] - SerializeFailed(serde_json::Error), -} - -pub async fn aliveness(arguments: Arguments) -> Result<(), Error> { +pub async fn aliveness( + arguments: Arguments, + repository_root: Result>, +) -> Result<()> { let states = query_aliveness_list(&arguments) .await - .map_err(Error::QueryFailed)?; + .wrap_err("failed to query aliveness")?; if arguments.json { - println!( - "{}", - serde_json::to_string(&states).map_err(Error::SerializeFailed)? - ); + let json = serde_json::to_string(&states).wrap_err("failed to serialize aliveness")?; + println!("{json}"); } else if arguments.verbose { print_verbose(&states); } else { - print_summary(&states); + let expected_os_version = match repository_root { + Ok(repository_root) => match read_os_version(repository_root).await { + Ok(version) => Some(version), + Err(error) => { + error!("{error:#?}"); + None + } + }, + Err(error) => { + error!("{error:#?}"); + None + } + }; + + print_summary(&states, expected_os_version); } Ok(()) } @@ -136,8 +148,8 @@ impl SummaryElements { } } - fn append_os_version(&mut self, version: &str) { - if version != OS_VERSION { + fn append_os_version(&mut self, version: &str, expected_os_version: &str) { + if version != expected_os_version { self.append(OS_ICON, version, Style::new()); } } @@ -153,7 +165,7 @@ impl SummaryElements { } } -fn print_summary(states: &AlivenessList) { +fn print_summary(states: &AlivenessList, expected_os_version: Option) { for (ip, state) in states.iter() { let id = match ip { IpAddr::V4(ip) => ip.octets()[3], @@ -164,7 +176,9 @@ fn print_summary(states: &AlivenessList) { output.append_battery(&state.battery); output.append_temperature(&state.temperature); - output.append_os_version(&state.hulks_os_version); + if let Some(expected_os_version) = &expected_os_version { + output.append_os_version(&state.hulks_os_version, expected_os_version); + } let SystemServices { hal, hula, diff --git a/tools/pepsi/src/analyze.rs b/tools/pepsi/src/analyze.rs index d85cc06b15..bdaa37351d 100644 --- a/tools/pepsi/src/analyze.rs +++ b/tools/pepsi/src/analyze.rs @@ -1,12 +1,41 @@ -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use bat::{PagingMode, PrettyPrinter}; use clap::Subcommand; -use color_eyre::{eyre::WrapErr, Result}; +use color_eyre::{ + eyre::{eyre, WrapErr}, + Result, +}; -use repository::Repository; use source_analyzer::{contexts::Contexts, node::parse_rust_file, pretty::to_string_pretty}; +fn find_latest_file(path_pattern: impl AsRef) -> Result { + let matching_paths: Vec<_> = glob::glob( + path_pattern + .as_ref() + .to_str() + .ok_or_else(|| eyre!("failed to interpret path as Unicode"))?, + ) + .wrap_err("failed to execute glob() over target directory")? + .map(|entry| { + let path = entry.wrap_err("failed to get glob() entry")?; + let metadata = path + .metadata() + .wrap_err_with(|| format!("failed to get metadata of path {path:?}"))?; + let modified_time = metadata.modified().wrap_err_with(|| { + format!("failed to get modified time from metadata of path {path:?}") + })?; + Ok((path, modified_time)) + }) + .collect::>() + .wrap_err("failed to get matching paths")?; + let (path_with_maximal_modified_time, _modified_time) = matching_paths + .iter() + .max_by_key(|(_path, modified_time)| modified_time) + .ok_or_else(|| eyre!("failed to find any matching path"))?; + Ok(path_with_maximal_modified_time.to_path_buf()) +} + #[derive(Subcommand)] #[allow(clippy::enum_variant_names)] pub enum Arguments { @@ -20,7 +49,10 @@ pub enum Arguments { }, } -pub async fn analyze(arguments: Arguments, repository: &Repository) -> Result<()> { +pub async fn analyze( + arguments: Arguments, + repository_root: Result>, +) -> Result<()> { match arguments { Arguments::DumpContexts { file_path } => { let file = parse_rust_file(file_path).wrap_err("failed to parse rust file")?; @@ -30,10 +62,10 @@ pub async fn analyze(arguments: Arguments, repository: &Repository) -> Result<() print!("{string}"); } Arguments::DumpLatest { file_name } => { + let repository_root = repository_root?; let glob = format!("target/**/{file_name}"); println!("{glob}"); - let file_path = repository - .find_latest_file(&glob) + let file_path = find_latest_file(repository_root.as_ref().join(glob)) .wrap_err("failed find latest generated file")?; println!("{}", file_path.display()); PrettyPrinter::new() diff --git a/tools/pepsi/src/cargo.rs b/tools/pepsi/src/cargo.rs index ffeda64d7a..b1fa3d81ed 100644 --- a/tools/pepsi/src/cargo.rs +++ b/tools/pepsi/src/cargo.rs @@ -1,151 +1,216 @@ +use std::{ + env::current_dir, + ffi::{OsStr, OsString}, + path::{absolute, Path, PathBuf}, +}; + use clap::Args; use color_eyre::{ - eyre::{bail, WrapErr}, + eyre::{bail, Context, ContextCompat}, Result, }; -use tokio::process::Command as TokioCommand; +use environment::{Environment, EnvironmentArguments}; +use pathdiff::diff_paths; +use repository::cargo::Cargo; +use tokio::fs::read_to_string; +use toml::Table; +use tracing::debug; -use constants::OS_IS_NOT_LINUX; -use repository::Repository; +pub mod build; +pub mod check; +pub mod clippy; +pub mod common; +pub mod environment; +pub mod install; +pub mod run; +pub mod test; +mod heading { + pub const PACKAGE_SELECTION: &str = "Package Selection"; + pub const TARGET_SELECTION: &str = "Target Selection"; + pub const FEATURE_SELECTION: &str = "Feature Selection"; + pub const COMPILATION_OPTIONS: &str = "Compilation Options"; + pub const MANIFEST_OPTIONS: &str = "Manifest Options"; +} #[derive(Args)] -pub struct Arguments { - /// Apply to entire workspace (only valid for build/check/clippy) - #[arg(long)] - pub workspace: bool, - #[arg(long, default_value = "incremental")] - pub profile: String, - #[arg(long, default_value = "webots")] - pub target: String, - #[arg(long)] - pub no_sdk_installation: bool, - #[arg(long, default_value = None, num_args = 1..)] - pub features: Option>, - /// Pass through arguments to cargo ... -- PASSTHROUGH_ARGUMENTS - #[arg(last = true, value_parser)] - pub passthrough_arguments: Vec, - /// Use a remote machine for compilation, see ./scripts/remote for details - #[arg(long)] - pub remote: bool, +#[group(skip)] +pub struct Arguments { + pub manifest: Option, + #[command(flatten)] + pub environment: EnvironmentArguments, + #[command(flatten)] + pub cargo: CargoArguments, } -#[derive(Debug)] -pub enum Command { - Build, - Check, - Clippy, - Run, +pub trait CargoCommand { + const SUB_COMMAND: &'static str; + + fn apply(&self, cmd: &mut Cargo); + fn profile(&self) -> &str; } -pub async fn cargo(arguments: Arguments, repository: &Repository, command: Command) -> Result<()> { - if arguments.remote { - return remote(arguments, command).await; - } +pub async fn cargo( + arguments: Arguments, + repository_root: impl AsRef, + compiler_artifacts: &[impl AsRef], +) -> Result<()> { + // Map with async closures would be nice here (not yet stabilized) + let manifest_path = match arguments.manifest { + Some(manifest) => { + let absolute_manifest = resolve_manifest_path(&manifest, &repository_root) + .await + .wrap_err("failed to resolve manifest path")?; + let relative_manifest = diff_paths( + absolute_manifest, + ¤t_dir().wrap_err("failed to get current directory")?, + ) + .wrap_err("failed to express manifest relative to repository root")?; + + Some(relative_manifest) + } + None => None, + }; - if !arguments.no_sdk_installation && arguments.target == "nao" { - let installation_directory = repository - .link_sdk_home(None) + let environment = match arguments.environment.env { + Some(environment) => environment, + None => read_requested_environment(&manifest_path) .await - .wrap_err("failed to link SDK home")?; + .wrap_err("failed to read requested environment")?, + } + .resolve(&repository_root) + .await + .wrap_err("failed to resolve environment")?; - let use_docker = OS_IS_NOT_LINUX; - if !use_docker { - repository - .install_sdk(None, installation_directory) - .await - .wrap_err("failed to install SDK")?; + eprintln!("Using cargo from {environment}"); + + let mut cargo = if arguments.environment.remote { + Cargo::remote(environment) + } else { + Cargo::local(environment) + }; + + cargo + .setup(&repository_root) + .await + .wrap_err("failed to set up cargo environment")?; + + cargo.arg(CargoArguments::SUB_COMMAND); + + if let Some(manifest_path) = manifest_path { + if CargoArguments::SUB_COMMAND == "install" { + cargo.arg("--path"); + cargo.arg( + manifest_path + .parent() + .wrap_err("failed to retrieve package path from manifest path")?, + ); + } else { + cargo.arg("--manifest-path"); + cargo.arg(manifest_path); } } - match command { - Command::Build => repository - .build( - arguments.workspace, - &arguments.profile, - &arguments.target, - arguments.features, - &arguments.passthrough_arguments, - ) - .await - .wrap_err("failed to build")?, - Command::Check => repository - .check(arguments.workspace, &arguments.profile, &arguments.target) - .await - .wrap_err("failed to check")?, - Command::Clippy => repository - .clippy(arguments.workspace, &arguments.profile, &arguments.target) - .await - .wrap_err("failed to run clippy")?, - Command::Run => { - if arguments.workspace { - println!("INFO: Found --workspace with run subcommand, ignoring...") - } - repository - .run( - &arguments.profile, - &arguments.target, - arguments.features, - &arguments.passthrough_arguments, - ) - .await - .wrap_err("failed to run")? - } + arguments.cargo.apply(&mut cargo); + + let mut cargo_command = cargo + .command(&repository_root, compiler_artifacts) + .wrap_err("failed to create cargo command")?; + + debug!("Running `{cargo_command:?}`"); + + let status = cargo_command + .status() + .await + .wrap_err("failed to run cargo")?; + + if !status.success() { + bail!("cargo failed with {status}"); } Ok(()) } -pub async fn remote(arguments: Arguments, command: Command) -> Result<()> { - match command { - Command::Build => { - let mut command = TokioCommand::new("./scripts/remoteWorkspace"); - - let profile_name = match arguments.profile.as_str() { - "dev" => "debug", - other => other, - }; - let toolchain_name = match arguments.target.as_str() { - "nao" => "x86_64-aldebaran-linux-gnu/", - _ => "", - }; - command.args([ - "--return-file", - &format!( - "target/{toolchain_name}{profile_name}/hulk_{}", - arguments.target - ), - ]); - - command - .arg("./pepsi") - .arg("build") - .arg("--profile") - .arg(arguments.profile) - .arg("--target") - .arg(arguments.target); - - if arguments.workspace { - command.arg("--workspace"); - } - if arguments.no_sdk_installation { - command.arg("--no-sdk-installation"); - } - command.arg("--"); - command.args(arguments.passthrough_arguments); +async fn read_requested_environment(manifest_path: &Option) -> Result { + let Some(manifest_path) = manifest_path else { + return Ok(Environment::Native); + }; - let status = command - .status() - .await - .wrap_err("failed to execute remote script")?; + let manifest = read_to_string(manifest_path).await.wrap_err_with(|| { + format!( + "failed to read manifest at {path}", + path = manifest_path.display() + ) + })?; + let manifest: Table = toml::from_str(&manifest).wrap_err("failed to parse manifest")?; + let Some(package_metadata) = package_metadata(&manifest) else { + return Ok(Environment::Native); + }; - if !status.success() { - bail!("remote script exited with code {}", status.code().unwrap()) - } + let is_cross_compile_requested = package_metadata + .get("cross-compile") + .and_then(|value| value.as_bool()) + .unwrap_or(false); + + if !is_cross_compile_requested { + return Ok(Environment::Native); + } + + if cfg!(target_os = "linux") { + Ok(Environment::Sdk { version: None }) + } else { + Ok(Environment::Docker { image: None }) + } +} + +async fn resolve_manifest_path( + manifest: impl AsRef, + repository_root: impl AsRef, +) -> Result { + let manifest = manifest.as_ref(); + let repository_root = repository_root.as_ref(); + + Ok(match manifest.to_str() { + Some("imagine") => repository_root.join("crates/hulk_imagine/Cargo.toml"), + Some("nao") => repository_root.join("crates/hulk_nao/Cargo.toml"), + Some("replayer") => repository_root.join("crates/hulk_replayer/Cargo.toml"), + Some("webots") => repository_root.join("crates/hulk_webots/Cargo.toml"), - Ok(()) + Some("aliveness") => repository_root.join("services/aliveness/Cargo.toml"), + Some("breeze") => repository_root.join("services/breeze/Cargo.toml"), + Some("hula") => repository_root.join("services/hula/Cargo.toml"), + + Some("annotato") => repository_root.join("tools/annotato/Cargo.toml"), + Some("camera_matrix_extractor") => { + repository_root.join("tools/camera_matrix_extractor/Cargo.toml") } - Command::Check | Command::Clippy | Command::Run => { - unimplemented!("remote option is not compatible with cargo command: {command:?}") + Some("depp") => repository_root.join("tools/depp/Cargo.toml"), + Some("fanta") => repository_root.join("tools/fanta/Cargo.toml"), + Some("parameter_tester") => repository_root.join("tools/parameter_tester/Cargo.toml"), + Some("pepsi") => repository_root.join("tools/pepsi/Cargo.toml"), + Some("twix") => repository_root.join("tools/twix/Cargo.toml"), + Some("widget_gallery") => repository_root.join("tools/widget_gallery/Cargo.toml"), + + _ => { + let manifest_path = + absolute(manifest).wrap_err("failed to get absolute path of manifest")?; + + if tokio::fs::metadata(&manifest_path) + .await + .wrap_err("failed to retrieve metadata")? + .is_dir() + { + manifest_path.join("Cargo.toml") + } else { + manifest_path + } } - } + }) +} + +fn package_metadata(table: &Table) -> Option<&Table> { + table + .get("package")? + .get("metadata")? + .get("pepsi")? + .as_table() } diff --git a/tools/pepsi/src/cargo/build.rs b/tools/pepsi/src/cargo/build.rs new file mode 100644 index 0000000000..3751559ab4 --- /dev/null +++ b/tools/pepsi/src/cargo/build.rs @@ -0,0 +1,201 @@ +use std::path::PathBuf; + +use clap::{ArgAction, Parser}; +use repository::cargo::Cargo; + +use super::CargoCommand; +use super::{common::CommonOptions, heading}; + +#[derive(Clone, Debug, Default, Parser)] +#[command(display_order = 1)] +pub struct Arguments { + #[command(flatten)] + common: CommonOptions, + + /// Build artifacts in release mode, with optimizations + #[arg(short = 'r', long, help_heading = heading::COMPILATION_OPTIONS)] + release: bool, + + /// Ignore `rust-version` specification in packages + #[arg(long)] + ignore_rust_version: bool, + + /// Output build graph in JSON (unstable) + #[arg(long, help_heading = heading::COMPILATION_OPTIONS)] + unit_graph: bool, + + /// Package to build (see `cargo help pkgid`) + #[arg( + short = 'p', + long = "package", + value_name = "SPEC", + action = ArgAction::Append, + num_args=0..=1, + help_heading = heading::PACKAGE_SELECTION, + )] + packages: Vec, + + /// Build all packages in the workspace + #[arg(long, help_heading = heading::PACKAGE_SELECTION)] + workspace: bool, + + /// Exclude packages from the build + #[arg( + long, + value_name = "SPEC", + action = ArgAction::Append, + help_heading = heading::PACKAGE_SELECTION, + )] + exclude: Vec, + + /// Alias for workspace (deprecated) + #[arg(long, help_heading = heading::PACKAGE_SELECTION)] + all: bool, + + /// Build only this package's library + #[arg(long, help_heading = heading::TARGET_SELECTION)] + lib: bool, + + /// Build only the specified binary + #[arg( + long, + value_name = "NAME", + action = ArgAction::Append, + num_args=0..=1, + help_heading = heading::TARGET_SELECTION, + )] + bin: Vec, + + /// Build all binaries + #[arg(long, help_heading = heading::TARGET_SELECTION)] + bins: bool, + + /// Build only the specified example + #[arg( + long, + value_name = "NAME", + action = ArgAction::Append, + num_args=0..=1, + help_heading = heading::TARGET_SELECTION, + )] + example: Vec, + + /// Build all examples + #[arg(long, help_heading = heading::TARGET_SELECTION)] + examples: bool, + + /// Build only the specified test target + #[arg( + long, + value_name = "NAME", + action = ArgAction::Append, + help_heading = heading::TARGET_SELECTION, + )] + test: Vec, + + /// Build all tests + #[arg(long, help_heading = heading::TARGET_SELECTION)] + tests: bool, + + /// Build only the specified bench target + #[arg( + long, + value_name = "NAME", + action = ArgAction::Append, + help_heading = heading::TARGET_SELECTION, + )] + bench: Vec, + + /// Build all benches + #[arg(long, help_heading = heading::TARGET_SELECTION)] + benches: bool, + + /// Build all targets + #[arg(long, help_heading = heading::TARGET_SELECTION)] + all_targets: bool, + + /// Copy final artifacts to this directory (unstable) + #[arg(long, alias = "out-dir", value_name = "PATH", help_heading = heading::COMPILATION_OPTIONS)] + artifact_dir: Option, + + /// Output the build plan in JSON (unstable) + #[arg(long, help_heading = heading::COMPILATION_OPTIONS)] + build_plan: bool, + + /// Outputs a future incompatibility report at the end of the build (unstable) + #[arg(long)] + future_incompat_report: bool, +} + +impl CargoCommand for Arguments { + const SUB_COMMAND: &'static str = "build"; + + fn apply(&self, cargo: &mut Cargo) { + self.common.apply(cargo); + + if self.release { + cargo.arg("--release"); + } + if self.ignore_rust_version { + cargo.arg("--ignore-rust-version"); + } + if self.unit_graph { + cargo.arg("--unit-graph"); + } + for pkg in &self.packages { + cargo.arg("--package").arg(pkg); + } + if self.workspace { + cargo.arg("--workspace"); + } + for item in &self.exclude { + cargo.arg("--exclude").arg(item); + } + if self.all { + cargo.arg("--all"); + } + if self.lib { + cargo.arg("--lib"); + } + for bin in &self.bin { + cargo.arg("--bin").arg(bin); + } + if self.bins { + cargo.arg("--bins"); + } + for example in &self.example { + cargo.arg("--example").arg(example); + } + if self.examples { + cargo.arg("--examples"); + } + for test in &self.test { + cargo.arg("--test").arg(test); + } + if self.tests { + cargo.arg("--tests"); + } + for bench in &self.bench { + cargo.arg("--bench").arg(bench); + } + if self.benches { + cargo.arg("--benches"); + } + if self.all_targets { + cargo.arg("--all-targets"); + } + if let Some(dir) = self.artifact_dir.as_ref() { + cargo.arg("--artifact-dir").arg(dir); + } + if self.build_plan { + cargo.arg("--build-plan"); + } + if self.future_incompat_report { + cargo.arg("--future-incompat-report"); + } + } + + fn profile(&self) -> &str { + self.common.profile.as_deref().unwrap_or("dev") + } +} diff --git a/tools/pepsi/src/cargo/check.rs b/tools/pepsi/src/cargo/check.rs new file mode 100644 index 0000000000..93024f0596 --- /dev/null +++ b/tools/pepsi/src/cargo/check.rs @@ -0,0 +1,199 @@ +use clap::{ArgAction, Parser}; +use repository::cargo::Cargo; + +use super::{common::CommonOptions, heading, CargoCommand}; + +/// `cargo check` options which are also a subset of `cargo clippy` +#[derive(Clone, Debug, Default, Parser)] +pub struct CheckOptions { + /// Package to build (see `cargo help pkgid`) + #[arg( + short = 'p', + long = "package", + value_name = "SPEC", + action = ArgAction::Append, + num_args=0..=1, + help_heading = heading::PACKAGE_SELECTION, + )] + pub packages: Vec, + + /// Check all packages in the workspace + #[arg(long, help_heading = heading::PACKAGE_SELECTION)] + pub workspace: bool, + + /// Exclude packages from the build + #[arg( + long, + value_name = "SPEC", + action = ArgAction::Append, + help_heading = heading::PACKAGE_SELECTION, + )] + pub exclude: Vec, + + /// Alias for workspace (deprecated) + #[arg(long, help_heading = heading::PACKAGE_SELECTION,)] + pub all: bool, + + /// Check only this package's library + #[arg(long, help_heading = heading::TARGET_SELECTION)] + pub lib: bool, + + /// Check only the specified binary + #[arg( + long, + value_name = "NAME", + action = ArgAction::Append, + num_args=0..=1, + help_heading = heading::TARGET_SELECTION, + )] + pub bin: Vec, + + /// Check all binaries + #[arg(long, help_heading = heading::TARGET_SELECTION)] + pub bins: bool, + + /// Check only the specified example + #[arg( + long, + value_name = "NAME", + action = ArgAction::Append, + num_args=0..=1, + help_heading = heading::TARGET_SELECTION, + )] + pub example: Vec, + + /// Check all examples + #[arg(long, help_heading = heading::TARGET_SELECTION)] + pub examples: bool, + + /// Check only the specified test target + #[arg( + long, + value_name = "NAME", + action = ArgAction::Append, + help_heading = heading::TARGET_SELECTION, + )] + pub test: Vec, + + /// Check all tests + #[arg(long, help_heading = heading::TARGET_SELECTION)] + pub tests: bool, + + /// Check only the specified bench target + #[arg( + long, + value_name = "NAME", + action = ArgAction::Append, + help_heading = heading::TARGET_SELECTION, + )] + pub bench: Vec, + + /// Check all benches + #[arg(long, help_heading = heading::TARGET_SELECTION)] + pub benches: bool, + + /// Check all targets + #[arg(long, help_heading = heading::TARGET_SELECTION)] + pub all_targets: bool, + + /// Outputs a future incompatibility report at the end of the build (unstable) + #[arg(long)] + pub future_incompat_report: bool, +} + +impl CheckOptions { + pub fn apply(&self, cargo: &mut Cargo) { + for pkg in &self.packages { + cargo.arg("--package").arg(pkg); + } + if self.workspace { + cargo.arg("--workspace"); + } + for item in &self.exclude { + cargo.arg("--exclude").arg(item); + } + if self.all { + cargo.arg("--all"); + } + if self.lib { + cargo.arg("--lib"); + } + for bin in &self.bin { + cargo.arg("--bin").arg(bin); + } + if self.bins { + cargo.arg("--bins"); + } + for example in &self.example { + cargo.arg("--example").arg(example); + } + if self.examples { + cargo.arg("--examples"); + } + for test in &self.test { + cargo.arg("--test").arg(test); + } + if self.tests { + cargo.arg("--tests"); + } + for bench in &self.bench { + cargo.arg("--bench").arg(bench); + } + if self.benches { + cargo.arg("--benches"); + } + if self.all_targets { + cargo.arg("--all-targets"); + } + if self.future_incompat_report { + cargo.arg("--future-incompat-report"); + } + } +} + +/// Check a local package and all of its dependencies for errors +#[derive(Clone, Debug, Default, Parser)] +#[command(display_order = 1)] +#[group(skip)] +pub struct Arguments { + #[command(flatten)] + pub common: CommonOptions, + + #[command(flatten)] + pub check: CheckOptions, + + /// Build artifacts in release mode, with optimizations + #[arg(short = 'r', long, help_heading = heading::COMPILATION_OPTIONS)] + pub release: bool, + + /// Ignore `rust-version` specification in packages + #[arg(long)] + pub ignore_rust_version: bool, + + /// Output build graph in JSON (unstable) + #[arg(long, help_heading = heading::COMPILATION_OPTIONS)] + pub unit_graph: bool, +} + +impl CargoCommand for Arguments { + const SUB_COMMAND: &'static str = "check"; + + fn apply(&self, cmd: &mut Cargo) { + self.common.apply(cmd); + self.check.apply(cmd); + + if self.release { + cmd.arg("--release"); + } + if self.ignore_rust_version { + cmd.arg("--ignore-rust-version"); + } + if self.unit_graph { + cmd.arg("--unit-graph"); + } + } + + fn profile(&self) -> &str { + self.common.profile.as_deref().unwrap_or("dev") + } +} diff --git a/tools/pepsi/src/cargo/clippy.rs b/tools/pepsi/src/cargo/clippy.rs new file mode 100644 index 0000000000..93374cf286 --- /dev/null +++ b/tools/pepsi/src/cargo/clippy.rs @@ -0,0 +1,73 @@ +use clap::Parser; +use repository::cargo::Cargo; + +use super::{check::CheckOptions, common::CommonOptions, heading, CargoCommand}; + +/// Checks a package to catch common mistakes and improve your Rust code +#[derive(Clone, Debug, Default, Parser)] +#[command(display_order = 1)] +#[group(skip)] +pub struct Arguments { + #[command(flatten)] + pub common: CommonOptions, + + #[command(flatten)] + pub check: CheckOptions, + + /// Build artifacts in release mode, with optimizations + #[arg(short = 'r', long, help_heading = heading::COMPILATION_OPTIONS)] + pub release: bool, + + /// Ignore `rust-version` specification in packages + #[arg(long)] + pub ignore_rust_version: bool, + + /// Output build graph in JSON (unstable) + #[arg(long, help_heading = heading::COMPILATION_OPTIONS)] + pub unit_graph: bool, + + /// Ignore dependencies, run only on crate + #[arg(long)] + pub no_deps: bool, + + /// Automatically apply lint suggestions (see `cargo help clippy`) + #[arg(long)] + pub fix: bool, + + /// Arguments passed to rustc. + #[arg(value_name = "args", trailing_var_arg = true, num_args = 0..)] + pub args: Vec, +} + +impl CargoCommand for Arguments { + const SUB_COMMAND: &'static str = "clippy"; + + fn apply(&self, cargo: &mut Cargo) { + self.common.apply(cargo); + self.check.apply(cargo); + + if self.release { + cargo.arg("--release"); + } + if self.ignore_rust_version { + cargo.arg("--ignore-rust-version"); + } + if self.unit_graph { + cargo.arg("--unit-graph"); + } + if self.no_deps { + cargo.arg("--no-deps"); + } + if self.fix { + cargo.arg("--fix"); + } + if !self.args.is_empty() { + cargo.arg("--"); + cargo.args(&self.args); + } + } + + fn profile(&self) -> &str { + self.common.profile.as_deref().unwrap_or("dev") + } +} diff --git a/tools/pepsi/src/cargo/common.rs b/tools/pepsi/src/cargo/common.rs new file mode 100644 index 0000000000..3171b4faa0 --- /dev/null +++ b/tools/pepsi/src/cargo/common.rs @@ -0,0 +1,185 @@ +use std::path::PathBuf; + +use clap::{ArgAction, Parser}; +use repository::cargo::Cargo; + +use super::heading; + +#[derive(Clone, Debug, Default, Parser)] +pub struct CommonOptions { + /// Do not print cargo log messages + #[arg(short = 'q', long)] + pub quiet: bool, + + /// Number of parallel jobs, defaults to # of CPUs + #[arg( + short = 'j', + long, + value_name = "N", + help_heading = heading::COMPILATION_OPTIONS, + )] + pub jobs: Option, + + /// Do not abort the build as soon as there is an error (unstable) + #[arg(long, help_heading = heading::COMPILATION_OPTIONS)] + pub keep_going: bool, + + /// Build artifacts with the specified Cargo profile + #[arg( + long, + value_name = "PROFILE-NAME", + help_heading = heading::COMPILATION_OPTIONS, + )] + pub profile: Option, + + /// Space or comma separated list of features to activate + #[arg( + short = 'F', + long, + action = ArgAction::Append, + help_heading = heading::FEATURE_SELECTION, + )] + pub features: Vec, + + /// Activate all available features + #[arg(long, help_heading = heading::FEATURE_SELECTION)] + pub all_features: bool, + + /// Do not activate the `default` feature + #[arg(long, help_heading = heading::FEATURE_SELECTION)] + pub no_default_features: bool, + + /// Build for the target triple + #[arg( + long, + value_name = "TRIPLE", + env = "CARGO_BUILD_TARGET", + action = ArgAction::Append, + help_heading = heading::COMPILATION_OPTIONS, + )] + pub target: Vec, + + /// Directory for all generated artifacts + #[arg( + long, + value_name = "DIRECTORY", + help_heading = heading::COMPILATION_OPTIONS, + )] + pub target_dir: Option, + + /// Error format + #[arg(long, value_name = "FMT", action = ArgAction::Append)] + pub message_format: Vec, + + /// Use verbose output (-vv very verbose/build.rs output) + #[arg(short = 'v', long, action = ArgAction::Count)] + pub verbose: u8, + + /// Coloring: auto, always, never + #[arg(long, value_name = "WHEN")] + pub color: Option, + + /// Require Cargo.lock and cache are up to date + #[arg(long, help_heading = heading::MANIFEST_OPTIONS)] + pub frozen: bool, + + /// Require Cargo.lock is up to date + #[arg(long, help_heading = heading::MANIFEST_OPTIONS)] + pub locked: bool, + + /// Run without accessing the network + #[arg(long, help_heading = heading::MANIFEST_OPTIONS)] + pub offline: bool, + + /// Override a configuration value (unstable) + #[arg(long, value_name = "KEY=VALUE", action = ArgAction::Append)] + pub config: Vec, + + /// Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details + #[arg(short = 'Z', value_name = "FLAG", action = ArgAction::Append)] + pub unstable_flags: Vec, + + /// Timing output formats (unstable) (comma separated): html, json + #[arg( + long, + value_name = "FMTS", + num_args = 0.., + value_delimiter = ',', + require_equals = true, + help_heading = heading::COMPILATION_OPTIONS, + )] + pub timings: Option>, +} + +impl CommonOptions { + pub fn apply(&self, cargo: &mut Cargo) { + if self.quiet { + cargo.arg("--quiet"); + } + if let Some(jobs) = self.jobs { + cargo.arg("--jobs").arg(jobs.to_string()); + } + if self.keep_going { + cargo.arg("--keep-going"); + } + if let Some(profile) = self.profile.as_ref() { + cargo.arg("--profile").arg(profile); + } + for feature in &self.features { + cargo.arg("--features").arg(feature); + } + if self.all_features { + cargo.arg("--all-features"); + } + if self.no_default_features { + cargo.arg("--no-default-features"); + } + + // Support . syntax + // For example: x86_64-unknown-linux-gnu.2.17 + let rust_targets = self + .target + .iter() + .map(|target| target.split_once('.').map(|(t, _)| t).unwrap_or(target)) + .collect::>(); + rust_targets.iter().for_each(|target| { + cargo.arg("--target").arg(target); + }); + + if let Some(dir) = self.target_dir.as_ref() { + cargo.arg("--target-dir").arg(dir); + } + for fmt in &self.message_format { + cargo.arg("--message-format").arg(fmt); + } + if self.verbose > 0 { + cargo.arg(format!("-{}", "v".repeat(self.verbose.into()))); + } + if let Some(color) = self.color.as_ref() { + cargo.arg("--color").arg(color); + } + if self.frozen { + cargo.arg("--frozen"); + } + if self.locked { + cargo.arg("--locked"); + } + if self.offline { + cargo.arg("--offline"); + } + for config in &self.config { + cargo.arg("--config").arg(config); + } + for flag in &self.unstable_flags { + cargo.arg("-Z").arg(flag); + } + if let Some(timings) = &self.timings { + if timings.is_empty() { + cargo.arg("--timings"); + } else { + let timings: Vec<_> = timings.iter().map(|x| x.as_str()).collect(); + cargo.arg(format!("--timings={}", timings.join(","))); + } + } + } +} diff --git a/tools/pepsi/src/cargo/environment.rs b/tools/pepsi/src/cargo/environment.rs new file mode 100644 index 0000000000..a01efeaf81 --- /dev/null +++ b/tools/pepsi/src/cargo/environment.rs @@ -0,0 +1,61 @@ +use std::{path::Path, str::FromStr}; + +use clap::Args; +use color_eyre::eyre::{bail, Context, Error, Result}; +use repository::{cargo::Environment as RepositoryEnvironment, configuration::read_sdk_version}; + +#[derive(Args, Debug)] +pub struct EnvironmentArguments { + /// The execution environment (default: native) + #[arg(long)] + pub env: Option, + /// Use a remote machine for execution, see ./scripts/remote for details + #[arg(long)] + pub remote: bool, +} + +#[derive(Debug, Clone)] +pub enum Environment { + Native, + Sdk { version: Option }, + Docker { image: Option }, +} + +impl FromStr for Environment { + type Err = Error; + + fn from_str(string: &str) -> Result { + let (left, right) = string + .split_once(":") + .map_or((string, None), |(left, right)| (left, Some(right))); + + Ok(match left { + "native" => Self::Native, + "sdk" => Self::Sdk { + version: right.map(str::to_owned), + }, + "docker" => Self::Docker { + image: right.map(str::to_owned), + }, + _ => bail!("unknown option {left}"), + }) + } +} + +impl Environment { + pub async fn resolve(self, repository_root: impl AsRef) -> Result { + let sdk_version = read_sdk_version(&repository_root) + .await + .wrap_err("failed to get HULK OS version")?; + + Ok(match self { + Environment::Native => RepositoryEnvironment::Native, + Environment::Sdk { version } => RepositoryEnvironment::Sdk { + version: version.unwrap_or(sdk_version), + }, + Environment::Docker { image } => RepositoryEnvironment::Docker { + image: image.unwrap_or(format!("ghcr.io/hulks/naosdk:{sdk_version}")), + }, + }) + } +} diff --git a/tools/pepsi/src/cargo/install.rs b/tools/pepsi/src/cargo/install.rs new file mode 100644 index 0000000000..0308c12b04 --- /dev/null +++ b/tools/pepsi/src/cargo/install.rs @@ -0,0 +1,170 @@ +use std::path::PathBuf; + +use clap::{ArgAction, Parser}; +use repository::cargo::Cargo; + +use super::CargoCommand; +use super::{common::CommonOptions, heading}; + +/// Install a Rust binary. Default location is $HOME/.cargo/bin +#[derive(Clone, Debug, Default, Parser)] +#[command( + display_order = 1, + after_help = "Run `cargo help install` for more detailed information." +)] +#[group(skip)] +pub struct Arguments { + #[command(flatten)] + pub common: CommonOptions, + + /// Specify a version to install + #[arg(long, value_name = "VERSION", alias = "vers", requires = "crates")] + pub version: Option, + + /// Git URL to install the specified crate from + #[arg(long, value_name = "URL", conflicts_with_all = ["index", "registry"])] + pub git: Option, + + /// Branch to use when installing from git + #[arg(long, value_name = "BRANCH", requires = "git")] + pub branch: Option, + + /// Tag to use when installing from git + #[arg(long, value_name = "TAG", requires = "git")] + pub tag: Option, + + /// Specific commit to use when installing from git + #[arg(long, value_name = "SHA", requires = "git")] + pub rev: Option, + + /// list all installed packages and their versions + #[arg(long)] + pub list: bool, + + /// Force overwriting existing crates or binaries + #[arg(short, long)] + pub force: bool, + + /// Do not save tracking information + #[arg(long)] + pub no_track: bool, + + /// Build in debug mode (with the 'dev' profile) instead of release mode + #[arg(long)] + pub debug: bool, + + /// Directory to install packages into + #[arg(long, value_name = "DIR")] + pub root: Option, + + /// Registry index to install from + #[arg( + long, + value_name = "INDEX", + conflicts_with_all = ["git", "registry"], + requires = "crates", + )] + pub index: Option, + + /// Registry to use + #[arg( + long, + value_name = "REGISTRY", + conflicts_with_all = ["git", "index"], + requires = "crates", + )] + pub registry: Option, + + /// Install only the specified binary + #[arg( + long, + value_name = "NAME", + action = ArgAction::Append, + num_args=0..=1, + help_heading = heading::TARGET_SELECTION, + )] + pub bin: Vec, + + /// Install all binaries + #[arg(long, help_heading = heading::TARGET_SELECTION)] + pub bins: bool, + + /// Install only the specified example + #[arg( + long, + value_name = "NAME", + action = ArgAction::Append, + num_args=0..=1, + help_heading = heading::TARGET_SELECTION, + )] + pub example: Vec, + + /// Install all examples + #[arg(long, help_heading = heading::TARGET_SELECTION)] + pub examples: bool, + + #[arg(value_name = "crate", action = ArgAction::Append, num_args = 0..)] + pub crates: Vec, +} + +impl CargoCommand for Arguments { + const SUB_COMMAND: &'static str = "install"; + + fn apply(&self, cargo: &mut Cargo) { + self.common.apply(cargo); + + if let Some(version) = self.version.as_ref() { + cargo.arg("--version").arg(version); + } + if let Some(git) = self.git.as_ref() { + cargo.arg("--git").arg(git); + } + if let Some(branch) = self.branch.as_ref() { + cargo.arg("--branch").arg(branch); + } + if let Some(tag) = self.tag.as_ref() { + cargo.arg("--tag").arg(tag); + } + if let Some(rev) = self.rev.as_ref() { + cargo.arg("--rev").arg(rev); + } + if self.list { + cargo.arg("--list"); + } + if self.force { + cargo.arg("--force"); + } + if self.no_track { + cargo.arg("--no-track"); + } + if self.debug { + cargo.arg("--debug"); + } + if let Some(root) = self.root.as_ref() { + cargo.arg("--root").arg(root); + } + if let Some(index) = self.index.as_ref() { + cargo.arg("--index").arg(index); + } + if let Some(registry) = self.registry.as_ref() { + cargo.arg("--registry").arg(registry); + } + for bin in &self.bin { + cargo.arg("--bin").arg(bin); + } + if self.bins { + cargo.arg("--bins"); + } + for example in &self.example { + cargo.arg("--example").arg(example); + } + if self.examples { + cargo.arg("--examples"); + } + cargo.args(&self.crates); + } + + fn profile(&self) -> &str { + self.common.profile.as_deref().unwrap_or("release") + } +} diff --git a/tools/pepsi/src/cargo/run.rs b/tools/pepsi/src/cargo/run.rs new file mode 100644 index 0000000000..a82a432aa5 --- /dev/null +++ b/tools/pepsi/src/cargo/run.rs @@ -0,0 +1,94 @@ +use clap::{ArgAction, Parser}; +use repository::cargo::Cargo; + +use super::common::CommonOptions; +use super::{heading, CargoCommand}; + +#[derive(Clone, Debug, Default, Parser)] +#[command(display_order = 1)] +pub struct Arguments { + #[command(flatten)] + pub common: CommonOptions, + + /// Build artifacts in release mode, with optimizations + #[arg(short = 'r', long, help_heading = heading::COMPILATION_OPTIONS)] + pub release: bool, + + /// Ignore `rust-version` specification in packages + #[arg(long)] + pub ignore_rust_version: bool, + + /// Output build graph in JSON (unstable) + #[arg(long, help_heading = heading::COMPILATION_OPTIONS)] + pub unit_graph: bool, + + /// Package to run (see `cargo help pkgid`) + #[arg( + short = 'p', + long = "package", + value_name = "SPEC", + action = ArgAction::Append, + num_args=0..=1, + help_heading = heading::PACKAGE_SELECTION, + )] + pub packages: Vec, + + /// Run the specified binary + #[arg( + long, + value_name = "NAME", + action = ArgAction::Append, + num_args=0..=1, + help_heading = heading::TARGET_SELECTION, + )] + pub bin: Vec, + + /// Run the specified example + #[arg( + long, + value_name = "NAME", + action = ArgAction::Append, + num_args=0..=1, + help_heading = heading::TARGET_SELECTION, + )] + pub example: Vec, + + /// Arguments for the binary to run + #[arg(value_name = "args", trailing_var_arg = true, num_args = 0..)] + pub args: Vec, +} + +impl CargoCommand for Arguments { + const SUB_COMMAND: &'static str = "run"; + + fn apply(&self, cargo: &mut Cargo) { + self.common.apply(cargo); + + if self.release { + cargo.arg("--release"); + } + if self.ignore_rust_version { + cargo.arg("--ignore-rust-version"); + } + if self.unit_graph { + cargo.arg("--unit-graph"); + } + for pkg in &self.packages { + cargo.arg("--package").arg(pkg); + } + for bin in &self.bin { + cargo.arg("--bin").arg(bin); + } + for example in &self.example { + cargo.arg("--example").arg(example); + } + if !self.args.is_empty() { + cargo.arg("--"); + cargo.args(&self.args); + } + } + + fn profile(&self) -> &str { + self.common.profile.as_deref().unwrap_or("dev") + } +} diff --git a/tools/pepsi/src/cargo/test.rs b/tools/pepsi/src/cargo/test.rs new file mode 100644 index 0000000000..d94f633955 --- /dev/null +++ b/tools/pepsi/src/cargo/test.rs @@ -0,0 +1,224 @@ +use clap::{ArgAction, Parser}; +use repository::cargo::Cargo; + +use super::{common::CommonOptions, heading, CargoCommand}; + +/// Execute all unit and integration tests and build examples of a local package +#[derive(Clone, Debug, Default, Parser)] +#[command(display_order = 1)] +#[group(skip)] +pub struct Arguments { + #[command(flatten)] + pub common: CommonOptions, + + /// Build artifacts in release mode, with optimizations + #[arg(short = 'r', long, help_heading = heading::COMPILATION_OPTIONS)] + pub release: bool, + + /// Ignore `rust-version` specification in packages + #[arg(long)] + pub ignore_rust_version: bool, + + /// Output build graph in JSON (unstable) + #[arg(long, help_heading = heading::COMPILATION_OPTIONS)] + pub unit_graph: bool, + + /// Package to build (see `cargo help pkgid`) + #[arg( + short = 'p', + long = "package", + value_name = "SPEC", + action = ArgAction::Append, + num_args=0..=1, + help_heading = heading::PACKAGE_SELECTION, + )] + pub packages: Vec, + + /// Test all packages in the workspace + #[arg(long, help_heading = heading::PACKAGE_SELECTION)] + pub workspace: bool, + + /// Exclude packages from the build + #[arg( + long, + value_name = "SPEC", + action = ArgAction::Append, + help_heading = heading::PACKAGE_SELECTION, + )] + pub exclude: Vec, + + /// Alias for workspace (deprecated) + #[arg(long, help_heading = heading::PACKAGE_SELECTION)] + pub all: bool, + + /// Test only this package's library + #[arg(long, help_heading = heading::TARGET_SELECTION)] + pub lib: bool, + + /// Test only the specified binary + #[arg( + long, + value_name = "NAME", + action = ArgAction::Append, + num_args=0..=1, + help_heading = heading::TARGET_SELECTION, + )] + pub bin: Vec, + + /// Test all binaries + #[arg(long, help_heading = heading::TARGET_SELECTION)] + pub bins: bool, + + /// Test only the specified example + #[arg( + long, + value_name = "NAME", + action = ArgAction::Append, + num_args=0..=1, + help_heading = heading::TARGET_SELECTION, + )] + pub example: Vec, + + /// Test all examples + #[arg(long, help_heading = heading::TARGET_SELECTION)] + pub examples: bool, + + /// Test only the specified test target + #[arg( + long, + value_name = "NAME", + action = ArgAction::Append, + help_heading = heading::TARGET_SELECTION, + )] + pub test: Vec, + + /// Test all tests + #[arg(long, help_heading = heading::TARGET_SELECTION)] + pub tests: bool, + + /// Test only the specified bench target + #[arg( + long, + value_name = "NAME", + action = ArgAction::Append, + help_heading = heading::TARGET_SELECTION, + )] + pub bench: Vec, + + /// Test all benches + #[arg(long, help_heading = heading::TARGET_SELECTION)] + pub benches: bool, + + /// Test all targets + #[arg(long, help_heading = heading::TARGET_SELECTION)] + pub all_targets: bool, + + /// Test only this library's documentation + #[arg(long)] + pub doc: bool, + + /// Compile, but don't run tests + #[arg(long)] + pub no_run: bool, + + /// Run all tests regardless of failure + #[arg(long)] + pub no_fail_fast: bool, + + /// Outputs a future incompatibility report at the end of the build (unstable) + #[arg(long)] + pub future_incompat_report: bool, + + /// If specified, only run tests containing this string in their names + #[arg(value_name = "TESTNAME")] + pub test_name: Option, + + /// Arguments for the test binary + #[arg(value_name = "args", trailing_var_arg = true, num_args = 0..)] + pub args: Vec, +} + +impl CargoCommand for Arguments { + const SUB_COMMAND: &'static str = "build"; + + fn apply(&self, cargo: &mut Cargo) { + self.common.apply(cargo); + + if self.release { + cargo.arg("--release"); + } + if self.ignore_rust_version { + cargo.arg("--ignore-rust-version"); + } + if self.unit_graph { + cargo.arg("--unit-graph"); + } + for pkg in &self.packages { + cargo.arg("--package").arg(pkg); + } + if self.workspace { + cargo.arg("--workspace"); + } + for item in &self.exclude { + cargo.arg("--exclude").arg(item); + } + if self.all { + cargo.arg("--all"); + } + if self.lib { + cargo.arg("--lib"); + } + for bin in &self.bin { + cargo.arg("--bin").arg(bin); + } + if self.bins { + cargo.arg("--bins"); + } + for example in &self.example { + cargo.arg("--example").arg(example); + } + if self.examples { + cargo.arg("--examples"); + } + for test in &self.test { + cargo.arg("--test").arg(test); + } + if self.tests { + cargo.arg("--tests"); + } + for bench in &self.bench { + cargo.arg("--bench").arg(bench); + } + if self.benches { + cargo.arg("--benches"); + } + if self.all_targets { + cargo.arg("--all-targets"); + } + if self.doc { + cargo.arg("--doc"); + } + if self.no_run { + cargo.arg("--no-run"); + } + if self.no_fail_fast { + cargo.arg("--no-fail-fast"); + } + if self.future_incompat_report { + cargo.arg("--future-incompat-report"); + } + + if let Some(test_name) = self.test_name.as_ref() { + cargo.arg(test_name); + } + + if !self.args.is_empty() { + cargo.arg("--"); + cargo.args(&self.args); + } + } + + fn profile(&self) -> &str { + self.common.profile.as_deref().unwrap_or("test") + } +} diff --git a/tools/pepsi/src/communication.rs b/tools/pepsi/src/communication.rs index f9b40fecfb..a1a35096ac 100644 --- a/tools/pepsi/src/communication.rs +++ b/tools/pepsi/src/communication.rs @@ -1,7 +1,8 @@ +use std::path::Path; + use clap::Subcommand; use color_eyre::{eyre::WrapErr, Result}; - -use repository::Repository; +use repository::communication::configure_communication; #[derive(Subcommand)] pub enum Arguments { @@ -9,9 +10,8 @@ pub enum Arguments { Disable, } -pub async fn communication(arguments: Arguments, repository: &Repository) -> Result<()> { - repository - .set_communication(matches!(arguments, Arguments::Enable)) +pub async fn communication(arguments: Arguments, repository_root: impl AsRef) -> Result<()> { + configure_communication(matches!(arguments, Arguments::Enable), repository_root) .await .wrap_err("failed to set communication enablement") } diff --git a/tools/pepsi/src/completions.rs b/tools/pepsi/src/completions.rs index 2cc28773a5..3f7e57c3d1 100644 --- a/tools/pepsi/src/completions.rs +++ b/tools/pepsi/src/completions.rs @@ -85,9 +85,9 @@ fn dynamic_completions(shell: Shell, static_completions: String) { ("reboot", ""), ("shell", ""), ("upload", ""), - ("wireless", "list"), - ("wireless", "set"), - ("wireless", "status"), + ("wifi", "list"), + ("wifi", "set"), + ("wifi", "status"), ]; for (first, second) in COMPLETION_SUBCOMMANDS { if second.is_empty() { diff --git a/tools/pepsi/src/gammaray.rs b/tools/pepsi/src/gammaray.rs index ed0d48c499..cd19f5d324 100644 --- a/tools/pepsi/src/gammaray.rs +++ b/tools/pepsi/src/gammaray.rs @@ -1,13 +1,12 @@ -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use clap::Args; use color_eyre::{eyre::WrapErr, Result}; use argument_parsers::NaoAddress; -use constants::OS_VERSION; use nao::Nao; use opn::verify_image; -use repository::{get_image_path, Repository}; +use repository::{configuration::read_os_version, data_home::get_data_home, image::download_image}; use crate::progress_indicator::ProgressIndicator; @@ -18,23 +17,29 @@ pub struct Arguments { image_path: Option, /// Alternative HULKs-OS version e.g. 3.3 #[arg(long)] - os_version: Option, + version: Option, /// The NAOs to flash the image to, e.g. 20w or 10.1.24.22 #[arg(required = true)] naos: Vec, } -pub async fn gammaray(arguments: Arguments, repository: &Repository) -> Result<()> { - let version = arguments.os_version.as_deref().unwrap_or(OS_VERSION); +pub async fn gammaray(arguments: Arguments, repository_root: impl AsRef) -> Result<()> { + let version = match arguments.version { + Some(version) => version, + None => read_os_version(&repository_root) + .await + .wrap_err("failed to get OS version")?, + }; + let data_home = get_data_home()?; let image_path = match arguments.image_path { Some(image_path) => image_path, - None => get_image_path(version).await?, + None => download_image(&version, data_home).await?, }; let image_path = image_path.as_path(); verify_image(image_path).wrap_err("image verification failed")?; - let team_toml = &repository.parameters_root().join("team.toml"); + let team_toml = &repository_root.as_ref().join("etc/parameters/team.toml"); ProgressIndicator::map_tasks( arguments.naos, @@ -47,9 +52,9 @@ pub async fn gammaray(arguments: Arguments, repository: &Repository) -> Result<( .await .wrap_err_with(|| format!("failed to flash image to {nao_address}"))?; progress_bar.set_message("Uploading team configuration..."); - nao.rsync_with_nao(false) - .arg(team_toml.to_str().unwrap()) - .arg(format!("{}:/media/internal/", nao.host)) + nao.rsync_with_nao(false)? + .arg(team_toml) + .arg(format!("{}:/media/internal/", nao.address)) .spawn() .wrap_err("failed to upload team configuration")?; nao.reboot() diff --git a/tools/pepsi/src/location.rs b/tools/pepsi/src/location.rs index f6bf272b69..6ab048e821 100644 --- a/tools/pepsi/src/location.rs +++ b/tools/pepsi/src/location.rs @@ -1,7 +1,8 @@ +use std::path::Path; + use clap::Subcommand; use color_eyre::{eyre::WrapErr, Result}; - -use repository::Repository; +use repository::location::{list_available_locations, list_configured_locations, set_location}; #[derive(Subcommand)] pub enum Arguments { @@ -20,12 +21,11 @@ pub enum Arguments { Status, } -pub async fn location(arguments: Arguments, repository: &Repository) -> Result<()> { +pub async fn location(arguments: Arguments, repository_root: impl AsRef) -> Result<()> { match arguments { Arguments::List {} => { println!("Available Locations:"); - for location in repository - .list_available_locations() + for location in list_available_locations(repository_root) .await .wrap_err("failed to list available locations")? { @@ -33,15 +33,13 @@ pub async fn location(arguments: Arguments, repository: &Repository) -> Result<( } } Arguments::Set { target, location } => { - repository - .set_location(&target, &location) + set_location(&target, &location, repository_root) .await .wrap_err_with(|| format!("failed setting location for {target}"))?; } Arguments::Status {} => { println!("Configured Locations:"); - for (target, location) in repository - .get_configured_locations() + for (target, location) in list_configured_locations(repository_root) .await .wrap_err("failed to get configured locations")? { diff --git a/tools/pepsi/src/main.rs b/tools/pepsi/src/main.rs index e693bb8b19..355d49f9f6 100644 --- a/tools/pepsi/src/main.rs +++ b/tools/pepsi/src/main.rs @@ -1,29 +1,34 @@ -use std::path::PathBuf; +use std::{env::current_dir, path::PathBuf}; use clap::{CommandFactory, Parser, Subcommand}; -use color_eyre::{config::HookBuilder, eyre::WrapErr, Result}; +use color_eyre::{ + config::HookBuilder, + eyre::{ContextCompat, WrapErr}, + Result, +}; +use repository::{find_root::find_repository_root, inspect_version::check_for_update}; -use crate::aliveness::{aliveness, Arguments as AlivenessArguments}; -use analyze::{analyze, Arguments as AnalyzeArguments}; -use cargo::{cargo, Arguments as CargoArguments, Command as CargoCommand}; -use communication::{communication, Arguments as CommunicationArguments}; -use completions::{completions, Arguments as CompletionArguments}; -use gammaray::{gammaray, Arguments as GammarayArguments}; -use hulk::{hulk, Arguments as HulkArguments}; -use location::{location, Arguments as LocationArguments}; -use logs::{logs, Arguments as LogsArguments}; -use ping::{ping, Arguments as PingArguments}; -use player_number::{player_number, Arguments as PlayerNumberArguments}; -use post_game::{post_game, Arguments as PostGameArguments}; -use power_off::{power_off, Arguments as PoweroffArguments}; -use pre_game::{pre_game, Arguments as PreGameArguments}; -use reboot::{reboot, Arguments as RebootArguments}; -use recording::{recording, Arguments as RecordingArguments}; -use repository::{get_repository_root, Repository}; -use sdk::{sdk, Arguments as SdkArguments}; -use shell::{shell, Arguments as ShellArguments}; -use upload::{upload, Arguments as UploadArguments}; -use wireless::{wireless, Arguments as WirelessArguments}; +use aliveness::aliveness; +use analyze::analyze; +use cargo::{build, cargo, check, clippy, install, run, test}; +use communication::communication; +use completions::completions; +use gammaray::gammaray; +use hulk::hulk; +use location::location; +use logs::logs; +use ping::ping; +use player_number::player_number; +use post_game::post_game; +use power_off::power_off; +use pre_game::pre_game; +use reboot::reboot; +use recording::recording; +use sdk::sdk; +use shell::shell; +use tracing::warn; +use upload::upload; +use wifi::wifi; mod aliveness; mod analyze; @@ -45,159 +50,182 @@ mod recording; mod sdk; mod shell; mod upload; -mod wireless; +mod wifi; + +#[derive(Parser)] +#[clap(version, name = "pepsi")] +struct Arguments { + /// Alternative repository root + #[arg(long)] + repository_root: Option, + #[command(subcommand)] + command: Command, + #[arg(long)] + verbose: bool, +} + +#[derive(Subcommand)] +enum Command { + /// Analyze source code + #[clap(subcommand)] + Analyze(analyze::Arguments), + /// Get aliveness information from NAOs + Aliveness(aliveness::Arguments), + /// Compile a local package and all of its dependencies + Build(cargo::Arguments), + /// Check a local package and all of its dependencies for errors + Check(cargo::Arguments), + /// Check a package to catch common mistakes + Clippy(cargo::Arguments), + /// Enable/disable communication + #[command(subcommand)] + Communication(communication::Arguments), + /// Generate shell completion files + Completions(completions::Arguments), + /// Flash a HULKs-OS image to NAOs + Gammaray(gammaray::Arguments), + /// Control the HULK service + Hulk(hulk::Arguments), + /// Install a Rust binary + Install(cargo::Arguments), + /// Set the parameter location + #[command(subcommand)] + Location(location::Arguments), + /// Interact with logs on NAOs + #[command(subcommand)] + Logs(logs::Arguments), + /// Change player numbers of NAOs in local parameters + Playernumber(player_number::Arguments), + /// Ping NAOs + Ping(ping::Arguments), + /// Disable NAOs after a game (download logs, unset WiFi network, ...) + Postgame(post_game::Arguments), + /// Power NAOs off + Poweroff(power_off::Arguments), + /// Get NAOs ready for a game (set player numbers, upload, set WiFi network, ...) + Pregame(pre_game::Arguments), + /// Reboot NAOs + Reboot(reboot::Arguments), + /// Set cycler instances to be recorded + Recording(recording::Arguments), + /// Run a binary or example of the local package + Run(cargo::Arguments), + /// Manage the NAO SDK + #[command(subcommand)] + Sdk(sdk::Arguments), + /// Open a command line shell to a NAO + Shell(shell::Arguments), + /// Execute all unit and integration tests + Test(cargo::Arguments), + /// Upload the code to NAOs + Upload(upload::Arguments), + /// Control WiFi on NAOs + #[command(subcommand, name = "wifi")] + WiFi(wifi::Arguments), +} #[tokio::main] async fn main() -> Result<()> { - HookBuilder::new().display_env_section(false).install()?; - let arguments = Arguments::parse(); - let repository_root = match arguments.repository_root { - Some(repository_root) => Ok(repository_root), - None => get_repository_root() - .await - .wrap_err("failed to get repository root"), + let level = if arguments.verbose { + tracing::Level::TRACE + } else { + tracing::Level::WARN }; - let repository = repository_root.map(Repository::new); - if let Ok(repository) = &repository { - repository.check_new_version_available(env!("CARGO_PKG_VERSION"), "tools/pepsi")?; + tracing_subscriber::fmt() + .without_time() + .with_max_level(level) + .init(); + HookBuilder::new().display_env_section(false).install()?; + + let repository_root = arguments.repository_root.map(Ok).unwrap_or_else(|| { + let current_directory = current_dir().wrap_err("failed to get current directory")?; + find_repository_root(current_directory).wrap_err("failed to find repository root") + }); + if let Ok(repository_root) = &repository_root { + if let Err(error) = check_for_update( + env!("CARGO_PKG_VERSION"), + repository_root.join("tools/pepsi/Cargo.toml"), + ) { + warn!("{error:#?}"); + } } match arguments.command { - Command::Analyze(arguments) => analyze(arguments, &repository?) + Command::Analyze(arguments) => analyze(arguments, repository_root) .await .wrap_err("failed to execute analyze command")?, - Command::Aliveness(arguments) => aliveness(arguments) + Command::Aliveness(arguments) => aliveness(arguments, repository_root) .await .wrap_err("failed to execute aliveness command")?, - Command::Build(arguments) => cargo(arguments, &repository?, CargoCommand::Build) + Command::Build(arguments) => cargo(arguments, repository_root?, &[] as &[&str]) .await .wrap_err("failed to execute build command")?, - Command::Check(arguments) => cargo(arguments, &repository?, CargoCommand::Check) + Command::Check(arguments) => cargo(arguments, repository_root?, &[] as &[&str]) .await .wrap_err("failed to execute check command")?, - Command::Clippy(arguments) => cargo(arguments, &repository?, CargoCommand::Clippy) + Command::Clippy(arguments) => cargo(arguments, repository_root?, &[] as &[&str]) .await .wrap_err("failed to execute clippy command")?, - Command::Communication(arguments) => communication(arguments, &repository?) + Command::Communication(arguments) => communication(arguments, repository_root?) .await .wrap_err("failed to execute communication command")?, Command::Completions(arguments) => completions(arguments, Arguments::command()) .await .wrap_err("failed to execute completion command")?, - Command::Gammaray(arguments) => gammaray(arguments, &repository?) + Command::Gammaray(arguments) => gammaray(arguments, repository_root?) .await .wrap_err("failed to execute gammaray command")?, Command::Hulk(arguments) => hulk(arguments) .await .wrap_err("failed to execute hulk command")?, - Command::Location(arguments) => location(arguments, &repository?) + Command::Install(arguments) => cargo(arguments, repository_root?, &[] as &[&str]) + .await + .wrap_err("failed to execute install command")?, + Command::Location(arguments) => location(arguments, repository_root?) .await .wrap_err("failed to execute location command")?, Command::Logs(arguments) => logs(arguments) .await .wrap_err("failed to execute logs command")?, Command::Ping(arguments) => ping(arguments).await, - Command::Playernumber(arguments) => player_number(arguments, &repository?) + Command::Playernumber(arguments) => player_number(arguments, repository_root?) .await .wrap_err("failed to execute player_number command")?, Command::Postgame(arguments) => post_game(arguments) .await .wrap_err("failed to execute post_game command")?, - Command::Poweroff(arguments) => power_off(arguments) + Command::Poweroff(arguments) => power_off(arguments, repository_root) .await .wrap_err("failed to execute power_off command")?, - Command::Pregame(arguments) => pre_game(arguments, &repository?) + Command::Pregame(arguments) => pre_game(arguments, repository_root?) .await .wrap_err("failed to execute pre_game command")?, Command::Reboot(arguments) => reboot(arguments) .await .wrap_err("failed to execute reboot command")?, - Command::Recording(arguments) => recording(arguments, &repository?) + Command::Recording(arguments) => recording(arguments, repository_root?) .await .wrap_err("failed to execute recording command")?, - Command::Run(arguments) => cargo(arguments, &repository?, CargoCommand::Run) + Command::Run(arguments) => cargo(arguments, repository_root?, &[] as &[&str]) .await .wrap_err("failed to execute run command")?, - Command::Sdk(arguments) => sdk(arguments, &repository?) + Command::Sdk(arguments) => sdk(arguments, repository_root?) .await .wrap_err("failed to execute sdk command")?, Command::Shell(arguments) => shell(arguments) .await .wrap_err("failed to execute shell command")?, - Command::Upload(arguments) => upload(arguments, &repository?) + Command::Test(arguments) => cargo(arguments, repository_root?, &[] as &[&str]) + .await + .wrap_err("failed to execute test command")?, + Command::Upload(arguments) => upload(arguments, repository_root?) .await .wrap_err("failed to execute upload command")?, - Command::Wireless(arguments) => wireless(arguments) + Command::WiFi(arguments) => wifi(arguments) .await - .wrap_err("failed to execute wireless command")?, + .wrap_err("failed to execute wifi command")?, } Ok(()) } - -#[derive(Parser)] -#[clap(version, name = "pepsi")] -struct Arguments { - /// Alternative repository root (if not given the parent of .git is used) - #[arg(long)] - repository_root: Option, - #[command(subcommand)] - command: Command, -} - -#[derive(Subcommand)] -enum Command { - /// Analyze source code - #[clap(subcommand)] - Analyze(AnalyzeArguments), - /// Get aliveness information from NAOs - Aliveness(AlivenessArguments), - /// Builds the code for a target - Build(CargoArguments), - /// Checks the code with cargo check - Check(CargoArguments), - /// Checks the code with cargo clippy - Clippy(CargoArguments), - /// Enable/disable communication - #[command(subcommand)] - Communication(CommunicationArguments), - /// Generates shell completion files - Completions(CompletionArguments), - /// Flash a HULKs-OS image to NAOs - Gammaray(GammarayArguments), - /// Control the HULK service - Hulk(HulkArguments), - /// Control the configured location - #[command(subcommand)] - Location(LocationArguments), - /// Logging on the NAO - #[command(subcommand)] - Logs(LogsArguments), - /// Change player numbers of the NAOs in local parameters - Playernumber(PlayerNumberArguments), - /// Ping NAOs - Ping(PingArguments), - /// Disable NAOs after a game (downloads logs, unsets wireless network, etc.) - Postgame(PostGameArguments), - /// Power NAOs off - Poweroff(PoweroffArguments), - /// Get NAOs ready for a game (sets player numbers, uploads, sets wireless network, etc.) - Pregame(PreGameArguments), - /// Reboot NAOs - Reboot(RebootArguments), - /// Set cycler instances to be recorded - Recording(RecordingArguments), - /// Runs the code for a target - Run(CargoArguments), - /// Manage the NAO SDK - #[command(subcommand)] - Sdk(SdkArguments), - /// Opens a command line shell to a NAO - Shell(ShellArguments), - /// Upload the code to NAOs - Upload(UploadArguments), - /// Control wireless network on the NAO - #[command(subcommand)] - Wireless(WirelessArguments), -} diff --git a/tools/pepsi/src/ping.rs b/tools/pepsi/src/ping.rs index 92a114ed02..70555fd164 100644 --- a/tools/pepsi/src/ping.rs +++ b/tools/pepsi/src/ping.rs @@ -1,3 +1,5 @@ +use std::time::Duration; + use clap::Args; use argument_parsers::NaoAddress; @@ -7,9 +9,9 @@ use crate::progress_indicator::ProgressIndicator; #[derive(Args)] pub struct Arguments { - /// Time in seconds after which ping is aborted + /// Timeout in seconds after which ping is aborted #[arg(long, default_value = "2")] - pub timeout_seconds: u32, + pub timeout: u64, /// The NAOs to ping to e.g. 20w or 10.1.24.22 #[arg(required = true)] pub naos: Vec, @@ -20,9 +22,12 @@ pub async fn ping(arguments: Arguments) { arguments.naos, "Pinging NAO...", |nao_address, _progress_bar| async move { - Nao::try_new_with_ping_and_arguments(nao_address.ip, arguments.timeout_seconds) - .await - .map(|_| ()) + Nao::try_new_with_ping_and_arguments( + nao_address.ip, + Duration::from_secs(arguments.timeout), + ) + .await + .map(|_| ()) }, ) .await; diff --git a/tools/pepsi/src/player_number.rs b/tools/pepsi/src/player_number.rs index c82b48af47..76e99e5381 100644 --- a/tools/pepsi/src/player_number.rs +++ b/tools/pepsi/src/player_number.rs @@ -1,4 +1,4 @@ -use std::collections::HashSet; +use std::{collections::HashSet, path::Path}; use clap::Args; use color_eyre::{ @@ -7,7 +7,7 @@ use color_eyre::{ }; use argument_parsers::NaoNumberPlayerAssignment; -use repository::Repository; +use repository::{player_number::configure_player_number, team::read_team_configuration}; use crate::progress_indicator::ProgressIndicator; @@ -18,29 +18,17 @@ pub struct Arguments { pub assignments: Vec, } -pub async fn player_number(arguments: Arguments, repository: &Repository) -> Result<()> { - let team = repository - .get_configured_team() +pub async fn player_number(arguments: Arguments, repository_root: impl AsRef) -> Result<()> { + let repository_root = repository_root.as_ref(); + let team = read_team_configuration(repository_root) .await - .wrap_err("failed to get configured team")?; + .wrap_err("failed to get team configuration")?; - // Check if two NaoNumbers are assigned to the same PlayerNumber - // or if a NaoNumber is assigned to multiple PlayerNumbers - let mut existing_player_numbers = HashSet::new(); - let mut existing_nao_numbers = HashSet::new(); + check_for_duplication(&arguments.assignments)?; - if arguments.assignments.iter().any( - |NaoNumberPlayerAssignment { - nao_number, - player_number, - }| { - !existing_nao_numbers.insert(nao_number) - || !existing_player_numbers.insert(player_number) - }, - ) { - bail!("Duplication in NAO to player number assignments") - } + // reborrows the team to avoid moving it into the closure let naos = &team.naos; + ProgressIndicator::map_tasks( arguments.assignments, "Setting player number...", @@ -50,8 +38,7 @@ pub async fn player_number(arguments: Arguments, repository: &Repository) -> Res .iter() .find(|nao| nao.number == number) .ok_or_else(|| eyre!("NAO with Hardware ID {number} does not exist"))?; - repository - .set_player_number(&nao.head_id, assignment.player_number) + configure_player_number(&nao.head_id, assignment.player_number, repository_root) .await .wrap_err_with(|| format!("failed to set player number for {assignment}")) }, @@ -60,3 +47,23 @@ pub async fn player_number(arguments: Arguments, repository: &Repository) -> Res Ok(()) } + +fn check_for_duplication(assignments: &[NaoNumberPlayerAssignment]) -> Result<()> { + // Check if two NaoNumbers are assigned to the same PlayerNumber + // or if a NaoNumber is assigned to multiple PlayerNumbers + let mut existing_player_numbers = HashSet::new(); + let mut existing_nao_numbers = HashSet::new(); + + if assignments.iter().any( + |NaoNumberPlayerAssignment { + nao_number, + player_number, + }| { + !existing_nao_numbers.insert(nao_number) + || !existing_player_numbers.insert(player_number) + }, + ) { + bail!("duplication in NAO to player number assignments") + } + Ok(()) +} diff --git a/tools/pepsi/src/post_game.rs b/tools/pepsi/src/post_game.rs index 08cc762d7a..221b57200a 100644 --- a/tools/pepsi/src/post_game.rs +++ b/tools/pepsi/src/post_game.rs @@ -1,28 +1,18 @@ use std::path::PathBuf; -use clap::{ - builder::{PossibleValuesParser, TypedValueParser}, - Args, -}; +use clap::Args; use color_eyre::{eyre::WrapErr, Result}; -use argument_parsers::{parse_network, NaoAddress, NETWORK_POSSIBLE_VALUES}; -use nao::{Network, SystemctlAction}; +use argument_parsers::NaoAddress; +use nao::{Nao, Network, SystemctlAction}; -use crate::{ - hulk::{hulk, Arguments as HulkArguments}, - logs::{logs, Arguments as LogsArguments}, - wireless::{wireless, Arguments as WirelessArguments}, -}; +use crate::progress_indicator::ProgressIndicator; #[derive(Args)] pub struct Arguments { - /// The network to connect the wireless device to (None disconnects from anything) - #[arg( - value_parser = PossibleValuesParser::new(NETWORK_POSSIBLE_VALUES) - .map(|s| parse_network(&s).unwrap())) - ] - pub network: Network, + /// Do not disconnect from the WiFi network + #[arg(long)] + pub no_disconnect: bool, /// Directory where to store the downloaded logs (will be created if not existing) pub log_directory: PathBuf, /// The NAOs to execute that command on e.g. 20w or 10.1.24.22 @@ -31,26 +21,33 @@ pub struct Arguments { } pub async fn post_game(arguments: Arguments) -> Result<()> { - hulk(HulkArguments { - action: SystemctlAction::Stop, - naos: arguments.naos.clone(), - }) - .await - .wrap_err("failed to start HULK service")?; - - logs(LogsArguments::Download { - log_directory: arguments.log_directory, - naos: arguments.naos.clone(), - }) - .await - .wrap_err("failed to download logs")?; - - wireless(WirelessArguments::Set { - network: arguments.network, - naos: arguments.naos, - }) - .await - .wrap_err("failed to set wireless network")?; + let arguments = &arguments; + ProgressIndicator::map_tasks( + &arguments.naos, + "Executing postgame tasks...", + |nao_address, progress_bar| async move { + let nao = Nao::try_new_with_ping(nao_address.ip).await?; + progress_bar.set_message("Stopping HULK service..."); + nao.execute_systemctl(SystemctlAction::Stop, "hulk") + .await + .wrap_err_with(|| format!("failed to execute systemctl hulk on {nao_address}"))?; + + progress_bar.set_message("Disconnecting from WiFi..."); + nao.set_wifi(Network::None) + .await + .wrap_err_with(|| format!("failed to set network on {nao_address}"))?; + + progress_bar.set_message("Downloading logs..."); + let log_directory = arguments.log_directory.join(nao_address.to_string()); + nao.download_logs(log_directory, |status| { + progress_bar.set_message(format!("Downloading logs: {status}")) + }) + .await + .wrap_err_with(|| format!("failed to download logs from {nao_address}"))?; + Ok(()) + }, + ) + .await; Ok(()) } diff --git a/tools/pepsi/src/power_off.rs b/tools/pepsi/src/power_off.rs index 45a1738d6c..6f5f6106fd 100644 --- a/tools/pepsi/src/power_off.rs +++ b/tools/pepsi/src/power_off.rs @@ -1,10 +1,12 @@ +use std::path::Path; + use clap::Args; use color_eyre::{eyre::WrapErr, Result}; use argument_parsers::{number_to_ip, Connection, NaoAddress}; -use constants::TEAM; use futures_util::{stream::FuturesUnordered, StreamExt}; use nao::Nao; +use repository::team::read_team_configuration; use crate::progress_indicator::ProgressIndicator; @@ -18,9 +20,16 @@ pub struct Arguments { pub naos: Vec, } -pub async fn power_off(arguments: Arguments) -> Result<()> { +pub async fn power_off( + arguments: Arguments, + repository_root: Result>, +) -> Result<()> { if arguments.all { - let addresses = TEAM + let repository_root = repository_root?; + let team = read_team_configuration(repository_root) + .await + .wrap_err("failed to get team configuration")?; + let addresses = team .naos .iter() .map(|nao| async move { diff --git a/tools/pepsi/src/pre_game.rs b/tools/pepsi/src/pre_game.rs index f6a23f1afa..b35e59d424 100644 --- a/tools/pepsi/src/pre_game.rs +++ b/tools/pepsi/src/pre_game.rs @@ -1,27 +1,48 @@ +use std::{collections::HashMap, path::Path}; + use clap::{ builder::{PossibleValuesParser, TypedValueParser}, Args, }; -use color_eyre::{eyre::WrapErr, Result}; +use color_eyre::{ + eyre::{bail, WrapErr}, + Result, +}; -use argument_parsers::{parse_network, NaoAddressPlayerAssignment, NETWORK_POSSIBLE_VALUES}; -use nao::Network; -use repository::Repository; +use argument_parsers::{ + parse_network, NaoAddress, NaoAddressPlayerAssignment, NETWORK_POSSIBLE_VALUES, +}; +use indicatif::ProgressBar; +use nao::{Nao, Network, SystemctlAction}; +use repository::{ + communication::configure_communication, + configuration::read_os_version, + location::set_location, + recording::configure_recording_intervals, + upload::{get_hulk_binary, populate_upload_directory}, +}; +use tempfile::tempdir; use crate::{ + cargo::{self, build, cargo, environment::EnvironmentArguments, CargoCommand}, player_number::{player_number, Arguments as PlayerNumberArguments}, - recording::{parse_key_value, recording, Arguments as RecordingArguments}, - upload::{upload, Arguments as UploadArguments}, - wireless::{wireless, Arguments as WirelessArguments}, + progress_indicator::ProgressIndicator, + recording::parse_key_value, }; #[derive(Args)] +#[group(skip)] pub struct Arguments { - #[arg(long, default_value = "release")] - pub profile: String, - /// Do not update nor install SDK - #[arg(long)] - pub no_sdk_installation: bool, + #[command(flatten)] + pub pre_game: PreGameArguments, + #[command(flatten)] + pub environment: EnvironmentArguments, + #[command(flatten)] + pub build: build::Arguments, +} + +#[derive(Args)] +pub struct PreGameArguments { /// Do not build before uploading #[arg(long)] pub no_build: bool, @@ -31,101 +52,190 @@ pub struct Arguments { /// Do not remove existing remote files during uploading #[arg(long)] pub no_clean: bool, - /// Enable communication - #[arg(long)] - pub with_communication: bool, /// Skip the OS version check #[arg(long)] pub skip_os_check: bool, - /// Prepare everything for the upload without performing the actual one + /// Enable communication, communication is disabled by default #[arg(long)] - pub prepare: bool, + pub with_communication: bool, /// Intervals between cycle recordings, e.g. Control=1,VisionTop=30 to record every cycle in Control /// and one out of every 30 in VisionTop. Set to 0 or don't specify to disable recording for a cycler. - #[arg(long, value_delimiter=',', value_parser = parse_key_value::, default_value = "Control=1,VisionTop=30,VisionBottom=30,SplNetwork=1")] + #[arg( + long, + value_delimiter=',', + value_parser = parse_key_value::, + default_value = "Control=1,VisionTop=30,VisionBottom=30,SplNetwork=1", + )] pub recording_intervals: Vec<(String, usize)>, + /// Prepare everything for the upload without performing the actual one + #[arg(long)] + pub prepare: bool, /// The location to use for parameters pub location: String, - /// The network to connect the wireless device to (None disconnects from anything) + /// The network to connect the wifi device to (None disconnects from anything) #[arg( value_parser = PossibleValuesParser::new(NETWORK_POSSIBLE_VALUES) .map(|s| parse_network(&s).unwrap())) ] - pub network: Network, + pub wifi: Network, /// The NAOs to upload to with player number assignments e.g. 20w:2 or 10.1.24.22:5 (player numbers start from 1) #[arg(required = true)] pub assignments: Vec, - /// Use a remote machine for compilation, see ./scripts/remote for details - #[arg(long)] - pub remote: bool, } -pub async fn pre_game(arguments: Arguments, repository: &Repository) -> Result<()> { +pub async fn pre_game(arguments: Arguments, repository_root: impl AsRef) -> Result<()> { + let repository_root = repository_root.as_ref(); + let naos: Vec<_> = arguments + .pre_game .assignments .iter() .map(|assignment| assignment.nao_address) .collect(); - recording( - RecordingArguments { - recording_intervals: arguments.recording_intervals, - }, - repository, + configure_recording_intervals( + HashMap::from_iter(arguments.pre_game.recording_intervals.clone()), + repository_root, ) .await - .wrap_err("failed to set cyclers to be recorded")?; + .wrap_err("failed to set recording settings")?; - repository - .set_location("nao", &arguments.location) + set_location("nao", &arguments.pre_game.location, repository_root) .await - .wrap_err_with(|| format!("failed setting location for nao to {}", arguments.location))?; + .wrap_err_with(|| { + format!( + "failed setting location for nao to {}", + arguments.pre_game.location + ) + })?; + + configure_communication(arguments.pre_game.with_communication, repository_root) + .await + .wrap_err("failed to set communication")?; player_number( - PlayerNumberArguments { - assignments: arguments - .assignments - .iter() - .copied() - .map(TryFrom::try_from) - .collect::, _>>() - .wrap_err("failed to convert NAO address assignments into NAO number assignments for player number setting")? - }, - repository - ) - .await - .wrap_err("failed to set player numbers")?; + PlayerNumberArguments { + assignments: arguments. + pre_game + .assignments + .iter() + .copied() + .map(TryFrom::try_from) + .collect::, _>>() + .wrap_err("failed to convert NAO address assignments into NAO number assignments for player number setting")? + }, + repository_root + ) + .await + .wrap_err("failed to set player numbers")?; - if !arguments.prepare { - wireless(WirelessArguments::Scan { naos: naos.clone() }) + let upload_directory = tempdir().wrap_err("failed to get temporary directory")?; + let hulk_binary = get_hulk_binary(arguments.build.profile()); + + let cargo_arguments = cargo::Arguments { + manifest: Some( + repository_root + .join("crates/hulk_nao/Cargo.toml") + .into_os_string(), + ), + environment: arguments.environment, + cargo: arguments.build, + }; + + if !arguments.pre_game.no_build { + cargo(cargo_arguments, &repository_root, &[&hulk_binary]) .await - .wrap_err("failed to scan for networks")?; + .wrap_err("failed to build")?; + } + if arguments.pre_game.prepare { + eprintln!("Preparation complete, skipping the rest"); + return Ok(()); + } - wireless(WirelessArguments::Set { - network: arguments.network, - naos: naos.clone(), - }) + populate_upload_directory(&upload_directory, &repository_root, hulk_binary) .await - .wrap_err("failed to set wireless network")?; - } + .wrap_err("failed to populate upload directory")?; + + let arguments = &arguments.pre_game; + let upload_directory = &upload_directory; - upload( - UploadArguments { - profile: arguments.profile, - no_sdk_installation: arguments.no_sdk_installation, - no_build: arguments.no_build, - no_restart: arguments.no_restart, - no_clean: arguments.no_clean, - no_communication: !arguments.with_communication, - prepare: arguments.prepare, - skip_os_check: arguments.skip_os_check, - naos, - remote: arguments.remote, + ProgressIndicator::map_tasks( + &naos, + "Executing pregame tasks", + |nao_address, progress_bar| async move { + setup_nao( + nao_address, + upload_directory, + arguments, + progress_bar, + repository_root, + ) + .await }, - repository, ) + .await; + + Ok(()) +} + +async fn setup_nao( + nao_address: &NaoAddress, + upload_directory: impl AsRef, + arguments: &PreGameArguments, + progress: ProgressBar, + repository_root: &Path, +) -> Result<()> { + progress.set_message("Pinging NAO..."); + let nao = Nao::try_new_with_ping(nao_address.ip).await?; + + if !arguments.skip_os_check { + progress.set_message("Checking OS version..."); + let nao_os_version = nao + .get_os_version() + .await + .wrap_err_with(|| format!("failed to get OS version of {nao_address}"))?; + let expected_os_version = read_os_version(repository_root) + .await + .wrap_err("failed to get configured OS version")?; + if nao_os_version != expected_os_version { + bail!("mismatched OS versions: Expected {expected_os_version}, found {nao_os_version}"); + } + } + + progress.set_message("Stopping HULK..."); + nao.execute_systemctl(SystemctlAction::Stop, "hulk") + .await + .wrap_err_with(|| format!("failed to stop HULK service on {nao_address}"))?; + + progress.set_message("Uploading: ..."); + nao.upload(upload_directory, "hulk", !arguments.no_clean, |status| { + progress.set_message(format!("Uploading: {}", status)) + }) .await - .wrap_err("failed to upload")?; + .wrap_err_with(|| format!("failed to upload binary to {nao_address}"))?; + + if arguments.wifi != Network::None { + progress.set_message("Scanning for WiFi..."); + nao.scan_networks() + .await + .wrap_err_with(|| format!("failed to scan for networks on {nao_address}"))?; + } + + progress.set_message("Setting WiFi..."); + nao.set_wifi(arguments.wifi) + .await + .wrap_err_with(|| format!("failed to set network on {nao_address}"))?; + + if !arguments.no_restart { + progress.set_message("Restarting HULK..."); + if let Err(error) = nao.execute_systemctl(SystemctlAction::Start, "hulk").await { + let logs = nao + .retrieve_logs() + .await + .wrap_err("failed to retrieve logs")?; + bail!("failed to restart hulk: {error:#?}\nLogs:\n{logs}") + }; + } Ok(()) } diff --git a/tools/pepsi/src/recording.rs b/tools/pepsi/src/recording.rs index d9467d1c1d..2e8cd9a0b1 100644 --- a/tools/pepsi/src/recording.rs +++ b/tools/pepsi/src/recording.rs @@ -1,9 +1,8 @@ -use std::{collections::HashMap, error::Error}; +use std::{collections::HashMap, error::Error, path::Path}; use clap::Args; use color_eyre::{eyre::WrapErr, Result}; - -use repository::Repository; +use repository::recording::configure_recording_intervals; #[derive(Args)] pub struct Arguments { @@ -13,11 +12,13 @@ pub struct Arguments { pub recording_intervals: Vec<(String, usize)>, } -pub async fn recording(arguments: Arguments, repository: &Repository) -> Result<()> { - repository - .set_recording_intervals(HashMap::from_iter(arguments.recording_intervals)) - .await - .wrap_err("failed to set recording enablement") +pub async fn recording(arguments: Arguments, repository_root: impl AsRef) -> Result<()> { + configure_recording_intervals( + HashMap::from_iter(arguments.recording_intervals), + repository_root, + ) + .await + .wrap_err("failed to set recording settings") } pub fn parse_key_value(string: &str) -> Result<(T, U), Box> diff --git a/tools/pepsi/src/sdk.rs b/tools/pepsi/src/sdk.rs index eaf239fda0..84ce4a4a14 100644 --- a/tools/pepsi/src/sdk.rs +++ b/tools/pepsi/src/sdk.rs @@ -1,41 +1,41 @@ -use std::path::PathBuf; +use std::path::Path; use clap::Subcommand; -use color_eyre::{eyre::WrapErr, Result}; +use color_eyre::{eyre::Context, Result}; -use constants::OS_IS_NOT_LINUX; -use repository::Repository; +use repository::{ + configuration::read_sdk_version, data_home::get_data_home, sdk::download_and_install, +}; #[derive(Subcommand)] pub enum Arguments { Install { - /// Alternative SDK version e.g. 3.3 + /// SDK version e.g. `3.3.1`. If not provided, version specified by `hulk.toml` is be used. #[arg(long)] - sdk_version: Option, - /// Alternative SDK installation directory e.g. ~/.naosdk/ - #[arg(long)] - installation_directory: Option, + version: Option, }, + Path, } -pub async fn sdk(arguments: Arguments, repository: &Repository) -> Result<()> { +pub async fn sdk(arguments: Arguments, repository_root: impl AsRef) -> Result<()> { match arguments { - Arguments::Install { - sdk_version, - installation_directory, - } => { - let installation_directory = repository - .link_sdk_home(installation_directory.as_deref()) - .await - .wrap_err("failed to link SDK home")?; - - let use_docker = OS_IS_NOT_LINUX; - if !use_docker { - repository - .install_sdk(sdk_version.as_deref(), installation_directory) + Arguments::Install { version } => { + let data_home = get_data_home().wrap_err("failed to get data home")?; + let version = match version { + Some(version) => version, + None => read_sdk_version(repository_root) .await - .wrap_err("failed to install SDK")?; - } + .wrap_err("failed to get OS version")?, + }; + download_and_install(&version, data_home).await?; + } + Arguments::Path => { + let sdk_version = read_sdk_version(&repository_root) + .await + .wrap_err("failed to get HULK OS version")?; + let data_home = get_data_home().wrap_err("failed to get data home")?; + let path = &data_home.join(format!("sdk/{sdk_version}/")); + println!("{}", path.display()); } } diff --git a/tools/pepsi/src/upload.rs b/tools/pepsi/src/upload.rs index 1156bcac3e..503623e57b 100644 --- a/tools/pepsi/src/upload.rs +++ b/tools/pepsi/src/upload.rs @@ -6,25 +6,32 @@ use color_eyre::{ eyre::{bail, WrapErr}, Result, }; -use constants::OS_VERSION; use futures_util::{stream::FuturesUnordered, StreamExt}; use nao::{Nao, SystemctlAction}; -use repository::Repository; +use repository::{ + configuration::read_os_version, + upload::{get_hulk_binary, populate_upload_directory}, +}; +use tempfile::tempdir; use crate::{ - cargo::{cargo, Arguments as CargoArguments, Command}, - communication::communication, - communication::Arguments as CommunicationArguments, + cargo::{self, build, cargo, environment::EnvironmentArguments, CargoCommand}, progress_indicator::{ProgressIndicator, Task}, }; #[derive(Args)] +#[group(skip)] pub struct Arguments { - #[arg(long, default_value = "incremental")] - pub profile: String, - /// Do not update nor install SDK - #[arg(long)] - pub no_sdk_installation: bool, + #[command(flatten)] + pub upload: UploadArguments, + #[command(flatten)] + pub environment: EnvironmentArguments, + #[command(flatten)] + pub build: build::Arguments, +} + +#[derive(Args)] +pub struct UploadArguments { /// Do not build before uploading #[arg(long)] pub no_build: bool, @@ -34,40 +41,35 @@ pub struct Arguments { /// Do not remove existing remote files during uploading #[arg(long)] pub no_clean: bool, - /// Do not enable communication - #[arg(long)] - pub no_communication: bool, /// Skip the OS version check #[arg(long)] pub skip_os_check: bool, - /// Prepare everything for the upload without performing the actual one - #[arg(long)] - pub prepare: bool, /// The NAOs to upload to e.g. 20w or 10.1.24.22 #[arg(required = true)] pub naos: Vec, - /// Use a remote machine for compilation, see ./scripts/remote for details - #[arg(long)] - pub remote: bool, } async fn upload_with_progress( nao_address: &NaoAddress, - hulk_directory: impl AsRef, - arguments: &Arguments, + upload_directory: impl AsRef, + arguments: &UploadArguments, progress: &Task, + repository_root: impl AsRef, ) -> Result<()> { progress.set_message("Pinging NAO..."); let nao = Nao::try_new_with_ping(nao_address.ip).await?; if !arguments.skip_os_check { progress.set_message("Checking OS version..."); - let os_version = nao + let nao_os_version = nao .get_os_version() .await .wrap_err_with(|| format!("failed to get OS version of {nao_address}"))?; - if os_version != OS_VERSION { - bail!("mismatched OS versions: Expected {OS_VERSION}, found {os_version}"); + let expected_os_version = read_os_version(repository_root) + .await + .wrap_err("failed to get configured OS version")?; + if nao_os_version != expected_os_version { + bail!("mismatched OS versions: Expected {expected_os_version}, found {nao_os_version}"); } } @@ -77,7 +79,7 @@ async fn upload_with_progress( .wrap_err_with(|| format!("failed to stop HULK service on {nao_address}"))?; progress.set_message("Uploading: ..."); - nao.upload(hulk_directory, !arguments.no_clean, |status| { + nao.upload(upload_directory, "hulk", !arguments.no_clean, |status| { progress.set_message(format!("Uploading: {}", status)) }) .await @@ -96,65 +98,59 @@ async fn upload_with_progress( Ok(()) } -pub async fn upload(arguments: Arguments, repository: &Repository) -> Result<()> { - if !arguments.no_build { - cargo( - CargoArguments { - workspace: false, - profile: arguments.profile.clone(), - target: "nao".to_string(), - no_sdk_installation: arguments.no_sdk_installation, - features: None, - passthrough_arguments: Vec::new(), - remote: arguments.remote, - }, - repository, - Command::Build, - ) - .await - .wrap_err("failed to build the code")?; +pub async fn upload(arguments: Arguments, repository_root: impl AsRef) -> Result<()> { + let upload_directory = tempdir().wrap_err("failed to get temporary directory")?; + let hulk_binary = get_hulk_binary(arguments.build.profile()); + + let cargo_arguments = cargo::Arguments { + manifest: Some( + repository_root + .as_ref() + .join("crates/hulk_nao/Cargo.toml") + .into_os_string(), + ), + environment: arguments.environment, + cargo: arguments.build, + }; + + if !arguments.upload.no_build { + cargo(cargo_arguments, &repository_root, &[&hulk_binary]) + .await + .wrap_err("failed to build")?; } - let (_temporary_directory, hulk_directory) = repository - .create_upload_directory(arguments.profile.as_str()) + populate_upload_directory(&upload_directory, &repository_root, hulk_binary) .await - .wrap_err("failed to create upload directory")?; - - communication( - match arguments.no_communication { - true => CommunicationArguments::Disable, - false => CommunicationArguments::Enable, - }, - repository, - ) - .await - .wrap_err("failed to set communication")?; + .wrap_err("failed to populate upload directory")?; - let multi_progress = ProgressIndicator::new(); + let upload_arguments = &arguments.upload; + let repository_root = &repository_root; + let upload_directory = &upload_directory; - if arguments.prepare { - eprintln!("WARNING: This upload was only prepared, no actual upload was performed!") - } else { - arguments - .naos - .iter() - .map(|nao_address| (nao_address, multi_progress.task(nao_address.to_string()))) - .map(|(nao_address, progress)| { - let arguments = &arguments; - let hulk_directory = hulk_directory.clone(); - - progress.enable_steady_tick(); - async move { - progress.finish_with( - upload_with_progress(nao_address, hulk_directory, arguments, &progress) - .await, + let multi_progress = ProgressIndicator::new(); + arguments + .upload + .naos + .iter() + .map(|nao_address| { + let progress = multi_progress.task(nao_address.to_string()); + progress.enable_steady_tick(); + async move { + progress.finish_with( + upload_with_progress( + nao_address, + upload_directory, + upload_arguments, + &progress, + repository_root, ) - } - }) - .collect::>() - .collect::>() - .await; - } + .await, + ) + } + }) + .collect::>() + .collect::>() + .await; Ok(()) } diff --git a/tools/pepsi/src/wireless.rs b/tools/pepsi/src/wifi.rs similarity index 97% rename from tools/pepsi/src/wireless.rs rename to tools/pepsi/src/wifi.rs index b5a82ca044..83a2b9f550 100644 --- a/tools/pepsi/src/wireless.rs +++ b/tools/pepsi/src/wifi.rs @@ -43,7 +43,7 @@ pub enum Arguments { }, } -pub async fn wireless(arguments: Arguments) -> Result<()> { +pub async fn wifi(arguments: Arguments) -> Result<()> { match arguments { Arguments::Status { naos } => status(naos).await, Arguments::Scan { naos } => scan(naos).await, @@ -102,7 +102,7 @@ async fn set(naos: Vec, network: Network) { "Setting network...", |nao_address, _progress_bar| async move { let nao = Nao::try_new_with_ping(nao_address.ip).await?; - nao.set_network(network) + nao.set_wifi(network) .await .wrap_err_with(|| format!("failed to set network on {nao_address}")) }, diff --git a/tools/twix/src/main.rs b/tools/twix/src/main.rs index cc96004925..9bfbc8598e 100644 --- a/tools/twix/src/main.rs +++ b/tools/twix/src/main.rs @@ -1,9 +1,12 @@ -use std::{iter::once, net::Ipv4Addr, str::FromStr, sync::Arc, time::SystemTime}; +use std::{ + env::current_dir, iter::once, net::Ipv4Addr, path::PathBuf, str::FromStr, sync::Arc, + time::SystemTime, +}; use argument_parsers::NaoAddress; use clap::Parser; use color_eyre::{ - eyre::{bail, eyre}, + eyre::{bail, eyre, Context as _, ContextCompat}, Result, }; @@ -24,7 +27,7 @@ use fern::{colors::ColoredLevelConfig, Dispatch, InitError}; use hulk_widgets::CompletionEdit; use itertools::chain; -use log::error; +use log::{error, warn}; use nao::Nao; use panel::Panel; use panels::{ @@ -34,9 +37,8 @@ use panels::{ }; use reachable_naos::ReachableNaos; -use repository::{get_repository_root, Repository}; +use repository::{find_root::find_repository_root, inspect_version::check_for_update}; use serde_json::{from_str, to_string, Value}; -use tokio::runtime::Runtime; use visuals::Visuals; mod change_buffer; @@ -57,7 +59,9 @@ mod zoom_and_pan; struct Arguments { /// Nao address to connect to (overrides the address saved in the configuration file) pub address: Option, - + /// Alternative repository root + #[arg(long)] + repository_root: Option, /// Delete the current panel setup #[arg(long)] pub clear: bool, @@ -81,13 +85,28 @@ fn setup_logger() -> Result<(), InitError> { fn main() -> Result<(), eframe::Error> { setup_logger().unwrap(); - let arguments = Arguments::parse(); - let runtime = Runtime::new().unwrap(); - if let Ok(repository_root) = runtime.block_on(get_repository_root()) { - Repository::new(repository_root) - .check_new_version_available(env!("CARGO_PKG_VERSION"), "tools/twix") - .unwrap(); + let arguments = Arguments::parse(); + let repository_root = arguments + .repository_root + .clone() + .map(Ok) + .unwrap_or_else(|| { + let current_directory = current_dir().wrap_err("failed to get current directory")?; + find_repository_root(current_directory).wrap_err("failed to find repository root") + }); + match &repository_root { + Ok(repository_root) => { + if let Err(error) = check_for_update( + env!("CARGO_PKG_VERSION"), + repository_root.join("tools/twix/Cargo.toml"), + ) { + error!("{error:#?}"); + } + } + Err(error) => { + warn!("{error:#?}"); + } } let configuration = Configuration::load() @@ -102,6 +121,7 @@ fn main() -> Result<(), eframe::Error> { creation_context, arguments, configuration, + repository_root.ok(), ))) }), ) @@ -141,6 +161,7 @@ impl TwixApp { creation_context: &CreationContext, arguments: Arguments, configuration: Configuration, + repository_root: Option, ) -> Self { let nao_range = configuration.naos.lowest..=configuration.naos.highest; let possible_addresses: Vec<_> = chain!( @@ -159,14 +180,17 @@ impl TwixApp { .or_else(|| creation_context.storage?.get_string("address")) .unwrap_or(Ipv4Addr::LOCALHOST.to_string()); - let nao = Arc::new(Nao::new(match address.split_once(":") { - None | Some((_, "")) => { - format!("ws://{address}:1337") - } - Some((ip, port)) => { - format!("ws://{ip}:{port}") - } - })); + let nao = Arc::new(Nao::new( + match address.split_once(":") { + None | Some((_, "")) => { + format!("ws://{address}:1337") + } + Some((ip, port)) => { + format!("ws://{ip}:{port}") + } + }, + repository_root, + )); let connection_intent = creation_context .storage diff --git a/tools/twix/src/nao.rs b/tools/twix/src/nao.rs index 84eb7cb243..4df1fd3db8 100644 --- a/tools/twix/src/nao.rs +++ b/tools/twix/src/nao.rs @@ -12,9 +12,8 @@ use communication::{ client::{Client, ClientHandle, PathsEvent, Status}, messages::{Path, TextOrBinary}, }; -use log::{error, warn}; +use log::error; use parameters::{directory::Scope, json::nest_value_at_path}; -use repository::{get_repository_root, Repository}; use serde_json::Value; use tokio::{ runtime::{Builder, Runtime}, @@ -30,28 +29,20 @@ use crate::{ pub struct Nao { runtime: Runtime, client: ClientHandle, - repository: Option, + repository_root: Option, } impl Nao { - pub fn new(address: String) -> Self { + pub fn new(address: String, repository_root: Option) -> Self { let runtime = Builder::new_multi_thread().enable_all().build().unwrap(); let (client, handle) = Client::new(address); runtime.spawn(client.run()); - let repository = match runtime.block_on(get_repository_root()) { - Ok(root) => Some(Repository::new(root)), - Err(error) => { - warn!("{error:#}"); - None - } - }; - Self { runtime, client: handle, - repository, + repository_root, } } @@ -190,13 +181,14 @@ impl Nao { pub fn store_parameters(&self, path: &str, value: Value, scope: Scope) -> Result<()> { let client = self.client.clone(); - let root = self - .repository + let parameters_root = self + .repository_root .as_ref() .ok_or_eyre("repository not available, cannot store parameters")? - .parameters_root(); + .join("etc/parameters/"); self.runtime.block_on(async { - if let Err(error) = store_parameters(&client, path, value, scope, root).await { + if let Err(error) = store_parameters(&client, path, value, scope, parameters_root).await + { error!("{error:#}") } }); @@ -209,12 +201,12 @@ async fn store_parameters( path: &str, value: Value, scope: Scope, - root: PathBuf, + parameters_root: impl AsRef, ) -> Result<()> { let (_, bytes) = client.read_binary("hardware_ids").await?; let ids: Ids = bincode::deserialize(&bytes).wrap_err("bincode deserialization failed")?; let parameters = nest_value_at_path(path, value); - parameters::directory::serialize(¶meters, scope, path, root, &ids) + parameters::directory::serialize(¶meters, scope, path, parameters_root, &ids) .wrap_err("serialization failed")?; Ok(()) } diff --git a/twix b/twix index 080194adbe..2269af75d2 100755 --- a/twix +++ b/twix @@ -2,4 +2,4 @@ THIS_DIRECTORY="$(dirname $(readlink -f $0))" -cargo run --profile incremental --manifest-path="${THIS_DIRECTORY}/tools/twix/Cargo.toml" -- "$@" +cargo run --manifest-path="${THIS_DIRECTORY}/tools/twix/Cargo.toml" -- "$@" From c5170777b8571acc33c3ba1bc9744fa7d8402c16 Mon Sep 17 00:00:00 2001 From: Julian Schuler Date: Sun, 12 Jan 2025 10:31:25 +0100 Subject: [PATCH 2/3] Tar and upload source distribution --- crates/repository/src/upload.rs | 35 ++++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/crates/repository/src/upload.rs b/crates/repository/src/upload.rs index 876c826ba0..5fa0ac9d94 100644 --- a/crates/repository/src/upload.rs +++ b/crates/repository/src/upload.rs @@ -1,7 +1,13 @@ -use std::path::Path; +use std::{ffi::OsString, path::Path}; -use color_eyre::{eyre::Context, Result}; -use tokio::fs::{create_dir_all, symlink}; +use color_eyre::{ + eyre::{bail, Context}, + Result, +}; +use tokio::{ + fs::{create_dir_all, symlink}, + process::Command, +}; pub async fn populate_upload_directory( upload_directory: impl AsRef, @@ -25,6 +31,29 @@ pub async fn populate_upload_directory( .await .wrap_err("failed to link executable")?; + create_dir_all(upload_directory.join("logs")) + .await + .wrap_err("failed to create directory for logs")?; + + let source_file = upload_directory.join("logs/source.tar.gz"); + + let mut archive_command = OsString::from("cd "); + archive_command.push(repository_root); + archive_command.push(" && git ls-files --cached --others --exclude-standard | tar Tczf - "); + archive_command.push(source_file); + + let mut command = Command::new("sh"); + command.arg("-c").arg(archive_command); + + let status = command + .status() + .await + .wrap_err("failed to run archive command")?; + + if !status.success() { + bail!("archive command failed with {status}") + } + Ok(()) } From c8b843afaa282e94abe0e295708022213dae62e4 Mon Sep 17 00:00:00 2001 From: Julian Schuler Date: Sun, 12 Jan 2025 18:30:18 +0100 Subject: [PATCH 3/3] Upload source distribution directly using rsync --- crates/nao/src/lib.rs | 5 ++++- crates/repository/src/upload.rs | 35 +++++---------------------------- tools/pepsi/src/upload.rs | 2 +- 3 files changed, 10 insertions(+), 32 deletions(-) diff --git a/crates/nao/src/lib.rs b/crates/nao/src/lib.rs index 4aeb876c5d..9b443fe10a 100644 --- a/crates/nao/src/lib.rs +++ b/crates/nao/src/lib.rs @@ -312,9 +312,12 @@ impl Nao { ) -> Result<()> { let mut command = self.rsync_with_nao(true)?; command - .arg("--keep-dirlinks") + .arg("--copy-dirlinks") .arg("--copy-links") .arg("--info=progress2") + .arg("--exclude=.git") + .arg("--exclude=webots") + .arg("--filter=dir-merge,- .gitignore") .arg(format!("{}/", local_directory.as_ref().display())) .arg(format!( "{}:{}/", diff --git a/crates/repository/src/upload.rs b/crates/repository/src/upload.rs index 5fa0ac9d94..04591bd1f8 100644 --- a/crates/repository/src/upload.rs +++ b/crates/repository/src/upload.rs @@ -1,13 +1,7 @@ -use std::{ffi::OsString, path::Path}; +use std::path::Path; -use color_eyre::{ - eyre::{bail, Context}, - Result, -}; -use tokio::{ - fs::{create_dir_all, symlink}, - process::Command, -}; +use color_eyre::{eyre::Context, Result}; +use tokio::fs::{create_dir_all, symlink}; pub async fn populate_upload_directory( upload_directory: impl AsRef, @@ -31,28 +25,9 @@ pub async fn populate_upload_directory( .await .wrap_err("failed to link executable")?; - create_dir_all(upload_directory.join("logs")) + symlink(repository_root, upload_directory.join("source")) .await - .wrap_err("failed to create directory for logs")?; - - let source_file = upload_directory.join("logs/source.tar.gz"); - - let mut archive_command = OsString::from("cd "); - archive_command.push(repository_root); - archive_command.push(" && git ls-files --cached --others --exclude-standard | tar Tczf - "); - archive_command.push(source_file); - - let mut command = Command::new("sh"); - command.arg("-c").arg(archive_command); - - let status = command - .status() - .await - .wrap_err("failed to run archive command")?; - - if !status.success() { - bail!("archive command failed with {status}") - } + .wrap_err("failed to link source directory")?; Ok(()) } diff --git a/tools/pepsi/src/upload.rs b/tools/pepsi/src/upload.rs index 503623e57b..8906f5906e 100644 --- a/tools/pepsi/src/upload.rs +++ b/tools/pepsi/src/upload.rs @@ -65,7 +65,7 @@ async fn upload_with_progress( .get_os_version() .await .wrap_err_with(|| format!("failed to get OS version of {nao_address}"))?; - let expected_os_version = read_os_version(repository_root) + let expected_os_version = read_os_version(&repository_root) .await .wrap_err("failed to get configured OS version")?; if nao_os_version != expected_os_version {