diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..aa2c90e --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,13 @@ +[target.aarch64-unknown-linux-gnu] +linker = "aarch64-linux-gnu-gcc" + +[target.riscv64gc-unknown-linux-gnu] +linker = "riscv64-linux-gnu-gcc" + +[alias] +ci-fmt = "fmt --all -- --check" +ci-fmt-fix = "fmt --all" +ci-clippy = "lints clippy --all-targets --all-features" +ci-test-compile = "test --no-run --workspace --all-features --no-default-features" +ci-test = "nextest run --all-features --release --workspace --profile ci" +ci-check = "check --workspace --release --all-features --all-targets --locked" diff --git a/.config/nextest.toml b/.config/nextest.toml new file mode 100644 index 0000000..eb3720b --- /dev/null +++ b/.config/nextest.toml @@ -0,0 +1,6 @@ +[profile.ci] +slow-timeout = { period = "60s", terminate-after = 4 } + +[profile.ci.junit] # this can be some other profile, too +path = "junit.xml" + diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..c403e78 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,36 @@ +--- +name: Bug report +about: Create a report to help us improve +title: "[TITLE]" +labels: 'bug-report' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + - OS & Version: [e.g. iOS 10.2.1] + - Browser & Version [e.g. chrome v71.0.12345] + +**Smartphone (please complete the following information):** + - Device: [e.g. iPhone6] + - OS: [e.g. iOS8.1] + - Browser & Version [e.g. stock browser v0.1.2] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..da9d2ae --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,18 @@ +Description +--- + +Motivation and Context +--- + +How Has This Been Tested? +--- + +What process can a PR reviewer use to test or verify this change? +--- + +Breaking Changes +--- + +- [x] None +- [ ] Requires CLI data directory to be deleted +- [ ] Other - Please specify \ No newline at end of file diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..567c3fd --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,8 @@ +--- +version: 2 +updates: + # Maintain dependencies for GitHub Actions + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" diff --git a/.github/workflows/audit.yml b/.github/workflows/audit.yml new file mode 100644 index 0000000..c85c20c --- /dev/null +++ b/.github/workflows/audit.yml @@ -0,0 +1,27 @@ +--- +name: Security audit + +'on': + # push: + # paths: + # # Run if workflow changes + # - '.github/workflows/audit.yml' + # # Run on changed dependencies + # - '**/Cargo.toml' + # - '**/Cargo.lock' + # # Run if the configuration file changes + # - '**/audit.toml' + # Rerun periodicly to pick up new advisories + schedule: + - cron: '43 05 * * *' + # Run manually + workflow_dispatch: + +jobs: + security_audit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: rustsec/audit-check@v2.0.0 + with: + token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/build_binaries.json b/.github/workflows/build_binaries.json new file mode 100644 index 0000000..493ca14 --- /dev/null +++ b/.github/workflows/build_binaries.json @@ -0,0 +1,68 @@ +[ + { + "name": "linux-x86_64", + "runs-on": "ubuntu-22.04", + "rust": "1.77", + "target": "x86_64-unknown-linux-gnu", + "cross": false, + "features": "" + }, + { + "name": "linux-arm64", + "runs-on": "ubuntu-20.04", + "rust": "1.77", + "target": "aarch64-unknown-linux-gnu", + "cross": true, + "flags": "--workspace --exclude tari_integration_tests", + "build_enabled": true, + "best_effort": true + }, + { + "name": "linux-riscv64", + "runs-on": "ubuntu-latest", + "rust": "stable", + "target": "riscv64gc-unknown-linux-gnu", + "cross": true, + "flags": "--workspace --exclude tari_integration_tests", + "build_enabled": true, + "best_effort": true + }, + + { + "name": "macos-x86_64", + "runs-on": "macos-12", + "rust": "1.77", + "target": "x86_64-apple-darwin", + "cross": false, + "features": "" + }, + { + "name": "macos-arm64", + "runs-on": "macos-14", + "rust": "1.77", + "target": "aarch64-apple-darwin", + "cross": false, + "features": "", + "build_enabled": true, + "best_effort": true + }, + { + "name": "windows-x64", + "runs-on": "windows-2019", + "rust": "1.77", + "target": "x86_64-pc-windows-msvc", + "cross": false, + "features": "" + }, + { + "name": "windows-arm64", + "runs-on": "windows-latest", + "rust": "1.77", + "target": "aarch64-pc-windows-msvc", + "cross": false, + "target_bins": "tari_dan_wallet_cli, tari_dan_wallet_daemon, tari_indexer, tari_validator_node, tari_signaling_server", + "features": "", + "build_enabled": true, + "best_effort": true + } +] diff --git a/.github/workflows/build_binaries.yml b/.github/workflows/build_binaries.yml new file mode 100644 index 0000000..0be6eb9 --- /dev/null +++ b/.github/workflows/build_binaries.yml @@ -0,0 +1,825 @@ +--- +name: Build Matrix of Binaries + +on: + push: + tags: + - "v[0-9]+.[0-9]+.[0-9]+" + branches: + - "build-all-*" + - "build-bins-*" + schedule: + - cron: "05 00 * * *" + workflow_dispatch: + inputs: + customTag: + description: "Development Tag" + required: true + default: "development-tag" + +env: + TS_FILENAME: "tari_network_suite" + TS_BUNDLE_ID_BASE: "com.tari.network.pkg" + TS_SIG_FN: "sha256-unsigned.txt" + ## Must be a JSon string + TS_FILES: '["tari_dan_wallet_cli","tari_dan_wallet_daemon","tari_indexer","tari_validator_node","tari_signaling_server","tari_generate"]' + TS_FEATURES: "default" + TS_LIBRARIES: "" + TARI_TARGET_NETWORK: igor + TARI_NETWORK: igor + toolchain: 1.77 + matrix-json-file: ".github/workflows/build_binaries.json" + CARGO_HTTP_MULTIPLEXING: false + CARGO_UNSTABLE_SPARSE_REGISTRY: true + CARGO: cargo + CARGO_OPTIONS: "--release" + CARGO_CACHE: true + +concurrency: + # https://docs.github.com/en/actions/examples/using-concurrency-expressions-and-a-test-matrix + group: '${{ github.workflow }} @ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}' + cancel-in-progress: ${{ !startsWith(github.ref, 'refs/tags/v') || github.ref != 'refs/heads/development' || github.ref != 'refs/heads/nextnet' || github.ref != 'refs/heads/stagenet' }} + +permissions: {} + +jobs: + matrix-prep: + runs-on: ubuntu-latest + outputs: + matrix: ${{ steps.set-matrix.outputs.matrix }} + steps: + - uses: actions/checkout@v4 + with: + submodules: false + + - name: Set Matrix + id: set-matrix + run: | + # + # build all targets images + # matrix=$( jq -s -c .[] .github/workflows/build_binaries.json ) + # + # build only single target image + # matrix_selection=$( jq -c '.[] | select( ."name" == "windows-x64" )' ${{ env.matrix-json-file }} ) + # matrix_selection=$( jq -c '.[] | select( ."name" | contains("macos") )' ${{ env.matrix-json-file }} ) + # + # build select target images - build_enabled + matrix_selection=$( jq -c '.[] | select( ."build_enabled" != false )' ${{ env.matrix-json-file }} ) + # + # Setup the json build matrix + matrix=$(echo ${matrix_selection} | jq -s -c '{"builds": .}') + echo $matrix + echo $matrix | jq . + echo "matrix=${matrix}" >> $GITHUB_OUTPUT + + matrix-check: + # Debug matrix + if: ${{ false }} + runs-on: ubuntu-latest + needs: matrix-prep + steps: + - name: Install json2yaml + run: | + sudo npm install -g json2yaml + + - name: Check matrix definition + run: | + matrix='${{ needs.matrix-prep.outputs.matrix }}' + echo $matrix + echo $matrix | jq . + echo $matrix | json2yaml + + builds: + name: Building ${{ matrix.builds.name }} on ${{ matrix.builds.runs-on }} + needs: matrix-prep + continue-on-error: ${{ matrix.builds.best_effort || false }} + outputs: + TARI_NETWORK_DIR: ${{ steps.set-tari-network.outputs.TARI_NETWORK_DIR }} + TARI_VERSION: ${{ steps.set-tari-vars.outputs.TARI_VERSION }} + strategy: + fail-fast: false + matrix: ${{ fromJson(needs.matrix-prep.outputs.matrix) }} + + runs-on: ${{ matrix.builds.runs-on }} + + steps: + - name: Checkout source code + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Declare TestNet for tags + id: set-tari-network + # Don't forget to comment out the below if, when force testing with GHA_NETWORK + if: ${{ startsWith(github.ref, 'refs/tags/v') }} + env: + GHA_NETWORK: ${{ github.ref_name }} + # GHA_NETWORK: "v1.0.0-rc.4" + shell: bash + run: | + source buildtools/multinet_envs.sh ${{ env.GHA_NETWORK }} + echo ${TARI_NETWORK} + echo ${TARI_TARGET_NETWORK} + echo ${TARI_NETWORK_DIR} + echo "TARI_NETWORK=${TARI_NETWORK}" >> $GITHUB_ENV + echo "TARI_TARGET_NETWORK=${TARI_TARGET_NETWORK}" >> $GITHUB_ENV + echo "TARI_NETWORK_DIR=${TARI_NETWORK_DIR}" >> $GITHUB_ENV + echo "TARI_NETWORK_DIR=${TARI_NETWORK_DIR}" >> $GITHUB_OUTPUT + + - name: Declare Global Variables 4 GHA ${{ github.event_name }} + id: set-tari-vars + shell: bash + run: | + echo "VBRANCH=${{ github.ref_name }}" >> $GITHUB_ENV + echo "VSHA_SHORT=$(git rev-parse --short HEAD)" >> $GITHUB_ENV + TARI_VERSION=$(awk -F ' = ' '$1 ~ /^version/ \ + { gsub(/["]/, "", $2); printf("%s",$2) }' \ + "$GITHUB_WORKSPACE/applications/tari_validator_node/Cargo.toml") + echo "TARI_VERSION=${TARI_VERSION}" >> $GITHUB_ENV + echo "TARI_VERSION=${TARI_VERSION}" >> $GITHUB_OUTPUT + if [[ "${{ matrix.builds.features }}" == "" ]]; then + echo "BUILD_FEATURES=${{ env.TS_FEATURES }}" >> $GITHUB_ENV + else + echo "BUILD_FEATURES=${{ matrix.builds.features }}" >> $GITHUB_ENV + fi + TARGET_BINS="" + if [[ "${{ matrix.builds.target_bins }}" == "" ]]; then + ARRAY_BINS=( $(echo ${TS_FILES} | jq --raw-output '.[]' | awk '{ print $1 }') ) + else + ARRAY_BINS=( $(echo "${{ matrix.builds.target_bins }}" | tr ', ' '\n') ) + fi + for BIN_FILE in "${ARRAY_BINS[@]}"; do + echo "Adding ${BIN_FILE} to Builds" + TARGET_BINS+="--bin ${BIN_FILE} " + done + echo "TARGET_BINS=${TARGET_BINS}" >> $GITHUB_ENV + TARGET_LIBS="" + if [[ "${{ matrix.builds.target_libs }}" == "" ]]; then + ARRAY_LIBS=( $(echo ${TS_LIBRARIES} | tr ', ' '\n') ) + else + ARRAY_LIBS=( $(echo "${{ matrix.builds.target_libs }}" | tr ', ' '\n') ) + fi + for LIB_FILE in "${ARRAY_LIBS[@]}"; do + echo "Adding ${LIB_FILE} to library Builds" + TARGET_LIBS+="--package ${LIB_FILE} " + done + echo "TARGET_LIBS=${TARGET_LIBS}" >> $GITHUB_ENV + TARI_BUILD_ISA_CPU=${{ matrix.builds.target }} + # Strip unknown part + TARI_BUILD_ISA_CPU=${TARI_BUILD_ISA_CPU//-unknown-linux-gnu} + # Strip gc used by rust + TARI_BUILD_ISA_CPU=${TARI_BUILD_ISA_CPU//gc} + echo "TARI_BUILD_ISA_CPU=${TARI_BUILD_ISA_CPU}" >> $GITHUB_ENV + + - name: Scheduled Destination Folder Override + if: ${{ github.event_name == 'schedule' && github.event.schedule == '05 00 * * *' }} + shell: bash + run: | + echo "S3_DEST_OVERRIDE=daily/" >> $GITHUB_ENV + + - name: Setup Rust toolchain + uses: dtolnay/rust-toolchain@master + with: + components: rustfmt, clippy + toolchain: ${{ matrix.builds.rust }} + targets: ${{ matrix.builds.target }} + + - uses: rui314/setup-mold@v1 + + - name: wasm target install + run: rustup target add wasm32-unknown-unknown + + - name: Install Linux dependencies - Ubuntu + if: ${{ startsWith(runner.os,'Linux') && ( ! matrix.builds.cross ) }} + run: | + sudo apt-get update + sudo bash scripts/install_ubuntu_dependencies.sh + + - name: Install Linux dependencies - Ubuntu - cross-compiled ${{ env.TARI_BUILD_ISA_CPU }} on x86-64 + if: ${{ startsWith(runner.os,'Linux') && ( ! matrix.builds.cross ) && matrix.builds.name != 'linux-x86_64' }} + run: | + sudo apt-get update + sudo bash scripts/install_ubuntu_dependencies-cross_compile.sh ${{ env.TARI_BUILD_ISA_CPU }} + rustup target add ${{ matrix.builds.target }} + echo "PKG_CONFIG_SYSROOT_DIR=/usr/${{ env.TARI_BUILD_ISA_CPU }}-linux-gnu/" >> $GITHUB_ENV + + - name: Install macOS dependencies + if: startsWith(runner.os,'macOS') + run: | + brew install openssl cmake zip coreutils automake autoconf protobuf + rustup target add ${{ matrix.builds.target }} + + - name: Install Windows dependencies + if: startsWith(runner.os,'Windows') + run: | + vcpkg.exe install sqlite3:x64-windows zlib:x64-windows + # Bug in choco - need to install each package individually + choco upgrade llvm -y + # psutils is out of date + # choco upgrade psutils -y + choco upgrade openssl -y + # Should already be installed + # choco upgrade strawberryperl -y + choco upgrade protoc -y + rustup target add ${{ matrix.builds.target }} + + - name: Set environment variables - Nix + if: ${{ ! startsWith(runner.os,'Windows') }} + shell: bash + run: | + echo "SHARUN=shasum --algorithm 256" >> $GITHUB_ENV + echo "CC=gcc" >> $GITHUB_ENV + echo "TS_EXT=" >> $GITHUB_ENV + echo "LIB_PRE=lib" >> $GITHUB_ENV + echo "SHELL_EXT=.sh" >> $GITHUB_ENV + echo "PLATFORM_SPECIFIC_DIR=linux" >> $GITHUB_ENV + echo "TS_DIST=/dist" >> $GITHUB_ENV + + - name: Set environment variables - macOS + if: startsWith(runner.os,'macOS') + shell: bash + run: | + echo "PLATFORM_SPECIFIC_DIR=osx" >> $GITHUB_ENV + echo "LIB_EXT=.dylib" >> $GITHUB_ENV + + # Hardcoded sdk for MacOSX on ARM64 + - name: Set environment variables - macOS - ARM64 (pin/sdk) + # Debug + if: ${{ false }} + # if: ${{ startsWith(runner.os,'macOS') && matrix.builds.name == 'macos-arm64' }} + run: | + xcrun --show-sdk-path + ls -alhtR "/Library/Developer/CommandLineTools/SDKs/" + echo "RANDOMX_RS_CMAKE_OSX_SYSROOT=/Library/Developer/CommandLineTools/SDKs/MacOSX12.1.sdk" >> $GITHUB_ENV + + - name: Set environment variables - Ubuntu + if: startsWith(runner.os,'Linux') + shell: bash + run: | + echo "LIB_EXT=.so" >> $GITHUB_ENV + + - name: Set environment variables - Windows + if: startsWith(runner.os,'Windows') + shell: bash + run: | + # echo "SHARUN=pwsh C:\ProgramData\chocolatey\lib\psutils\tools\psutils-master\shasum.ps1 --algorithm 256" >> $GITHUB_ENV + mkdir -p "$GITHUB_WORKSPACE\psutils" + curl -v -o "$GITHUB_WORKSPACE\psutils\getopt.ps1" "https://raw.githubusercontent.com/lukesampson/psutils/master/getopt.ps1" + curl -v -o "$GITHUB_WORKSPACE\psutils\shasum.ps1" "https://raw.githubusercontent.com/lukesampson/psutils/master/shasum.ps1" + echo "SHARUN=pwsh $GITHUB_WORKSPACE\psutils\shasum.ps1 --algorithm 256" >> $GITHUB_ENV + echo "TS_EXT=.exe" >> $GITHUB_ENV + echo "LIB_EXT=.dll" >> $GITHUB_ENV + echo "LIB_PRE=" >> $GITHUB_ENV + echo "SHELL_EXT=.bat" >> $GITHUB_ENV + echo "TS_DIST=\dist" >> $GITHUB_ENV + echo "PLATFORM_SPECIFIC_DIR=windows" >> $GITHUB_ENV + echo "SQLITE3_LIB_DIR=C:\vcpkg\installed\x64-windows\lib" >> $GITHUB_ENV + echo "OPENSSL_DIR=C:\Program Files\OpenSSL-Win64" >> $GITHUB_ENV + echo "LIBCLANG_PATH=C:\Program Files\LLVM\bin" >> $GITHUB_ENV + echo "C:\Strawberry\perl\bin" >> $GITHUB_PATH + + - name: Cache cargo files and outputs + if: ${{ ( ! startsWith(github.ref, 'refs/tags/v') ) && ( ! matrix.builds.cross ) && ( env.CARGO_CACHE ) }} + uses: Swatinem/rust-cache@v2 + with: + key: ${{ matrix.builds.target }} + + - name: Install and setup cargo cross + if: ${{ matrix.builds.cross }} + shell: bash + run: | + #cargo install cross + cargo install cross --git https://github.com/cross-rs/cross + echo "CARGO=cross" >> $GITHUB_ENV + + - name: Install and setup cargo-auditable + if: ${{ false }} + # if: ${{ startsWith(github.ref, 'refs/tags/v') }} + shell: bash + run: | + cargo install cargo-auditable + echo "CARGO=${{ env.CARGO }} auditable" >> $GITHUB_ENV + echo "CARGO_OPTIONS=${{ env.CARGO_OPTIONS }} --release" >> $GITHUB_ENV + + - name: Show command used for Cargo + shell: bash + run: | + echo "cargo command is: ${{ env.CARGO }}" + echo "cargo options is: ${{ env.CARGO_OPTIONS }}" + echo "cross flag: ${{ matrix.builds.cross }}" + + - name: Build release binaries + timeout-minutes: 30 + shell: bash + run: | + ${{ env.CARGO }} build ${{ env.CARGO_OPTIONS }} \ + --target ${{ matrix.builds.target }} \ + --features "${{ env.BUILD_FEATURES }}" \ + ${{ env.TARGET_BINS }} \ + ${{ matrix.builds.flags }} --locked + + - name: Build release libraries + if: ${{ env.TS_LIBRARIES != '' }} + shell: bash + run: | + ${{ env.CARGO }} build ${{ env.CARGO_OPTIONS }} \ + --target ${{ matrix.builds.target }} \ + --lib ${{ env.TARGET_LIBS }} \ + ${{ matrix.builds.flags }} --locked + + - name: Copy binaries to folder for archiving + shell: bash + run: | + # set -xo pipefail + mkdir -p "$GITHUB_WORKSPACE${TS_DIST}" + cd "$GITHUB_WORKSPACE${TS_DIST}" + BINFILE="${TS_FILENAME}-${TARI_VERSION}-${VSHA_SHORT}-${{ matrix.builds.name }}${TS_EXT}" + echo "BINFILE=${BINFILE}" >> $GITHUB_ENV + echo "Copying files for ${BINFILE} to $(pwd)" + echo "MTS_SOURCE=$(pwd)" >> $GITHUB_ENV + ls -alht "$GITHUB_WORKSPACE/target/${{ matrix.builds.target }}/release/" + ARRAY_FILES=( $(echo ${TS_FILES} | jq --raw-output '.[]' | awk '{ print $1 }') ) + for FILE in "${ARRAY_FILES[@]}"; do + echo "checking for file - ${FILE}${TS_EXT}" + if [ -f "${GITHUB_WORKSPACE}/target/${{ matrix.builds.target }}/release/${FILE}${TS_EXT}" ]; then + cp -vf "${GITHUB_WORKSPACE}/target/${{ matrix.builds.target }}/release/${FILE}${TS_EXT}" . + fi + done + if [[ "${{ matrix.builds.target_libs }}" == "" ]]; then + ARRAY_LIBS=( $(echo ${TS_LIBRARIES} | tr ', ' '\n') ) + else + ARRAY_LIBS=( $(echo "${{ matrix.builds.target_libs }}" | tr ', ' '\n') ) + fi + for FILE in "${ARRAY_LIBS[@]}"; do + echo "checking for file - ${FILE}${TS_EXT}" + # Check on Nix for libs + if [ -f "${GITHUB_WORKSPACE}/target/${{ matrix.builds.target }}/release/lib${FILE}${LIB_EXT}" ]; then + cp -vf "${GITHUB_WORKSPACE}/target/${{ matrix.builds.target }}/release/lib${FILE}${LIB_EXT}" . + fi + # Check on Windows libs + if [ -f "${GITHUB_WORKSPACE}/target/${{ matrix.builds.target }}/release/${FILE}${LIB_EXT}" ]; then + cp -vf "${GITHUB_WORKSPACE}/target/${{ matrix.builds.target }}/release/${FILE}${LIB_EXT}" . + fi + done + if [ -f "${GITHUB_WORKSPACE}/applications/minotari_node/${PLATFORM_SPECIFIC_DIR}/runtime/start_tor${SHELL_EXT}" ]; then + cp -vf "${GITHUB_WORKSPACE}/applications/minotari_node/${PLATFORM_SPECIFIC_DIR}/runtime/start_tor${SHELL_EXT}" . + fi + ls -alhtR ${{ env.MTS_SOURCE }} + + - name: Pre/unsigned OSX Artifact upload for Archive + # Disabled + if: ${{ false }} + # if: startsWith(runner.os,'macOS') + continue-on-error: true + uses: actions/upload-artifact@v4 + with: + name: ${{ env.TS_FILENAME }}_unsigned-archive-${{ matrix.builds.name }} + path: "${{ env.MTS_SOURCE }}/*" + + - name: Build the macOS pkg + # Disabled + if: ${{ false }} + # if: startsWith(runner.os,'macOS') + continue-on-error: true + env: + MACOS_KEYCHAIN_PASS: ${{ secrets.MACOS_KEYCHAIN_PASS }} + MACOS_APPLICATION_ID: ${{ secrets.MACOS_APPLICATION_ID }} + MACOS_APPLICATION_CERT: ${{ secrets.MACOS_APPLICATION_CERT }} + MACOS_APPLICATION_PASS: ${{ secrets.MACOS_APPLICATION_PASS }} + MACOS_INSTALLER_ID: ${{ secrets.MACOS_INSTALLER_ID }} + MACOS_INSTALLER_CERT: ${{ secrets.MACOS_INSTALLER_CERT }} + MACOS_INSTALLER_PASS: ${{ secrets.MACOS_INSTALLER_PASS }} + MACOS_NOTARIZE_USERNAME: ${{ secrets.MACOS_NOTARIZE_USERNAME }} + MACOS_NOTARIZE_PASSWORD: ${{ secrets.MACOS_NOTARIZE_PASSWORD }} + MACOS_ASC_PROVIDER: ${{ secrets.MACOS_ASC_PROVIDER }} + run: | + echo $MACOS_APPLICATION_CERT | base64 --decode > application.p12 + echo $MACOS_INSTALLER_CERT | base64 --decode > installer.p12 + security create-keychain -p $MACOS_KEYCHAIN_PASS build.keychain + security default-keychain -s build.keychain + security unlock-keychain -p $MACOS_KEYCHAIN_PASS build.keychain + security import application.p12 -k build.keychain -P $MACOS_APPLICATION_PASS -T /usr/bin/codesign + security import installer.p12 -k build.keychain -P $MACOS_INSTALLER_PASS -T /usr/bin/pkgbuild + security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k $MACOS_KEYCHAIN_PASS build.keychain + if [[ "${{ matrix.builds.name }}" == "macos-arm64" ]]; then + echo "Add codesign extra args for ${{ matrix.builds.name }}" + OSX_CODESIGN_EXTRAS="--entitlements ${GITHUB_WORKSPACE}/applications/minotari_node/osx-pkg/entitlements.xml" + fi + cd buildtools + export target_release="target/${{ matrix.builds.target }}/release" + mkdir -p "${{ runner.temp }}/osxpkg" + export tarball_parent="${{ runner.temp }}/osxpkg" + export tarball_source="${{ env.TARI_NETWORK_DIR }}" + ./create_osx_install_zip.sh unused nozip + ARRAY_FILES=( $(echo ${TS_FILES} | jq --raw-output '.[]' | awk '{ print $1 }') ) + find "${GITHUB_WORKSPACE}/${target_release}" \ + -name "randomx-*" -type f -perm -+x \ + -exec cp -vf {} "${{ runner.temp }}/osxpkg/${{ env.TARI_NETWORK_DIR }}/runtime/" \; + FILES_DIAG_UTILS=( \ + $(find "${GITHUB_WORKSPACE}/${target_release}" \ + -name "randomx-*" -type f -perm -+x \ + -exec sh -c 'echo "$(basename "{}")"' \; \ + ) \ + ) + ARRAY_FILES+=(${FILES_DIAG_UTILS[@]}) + for FILE in "${ARRAY_FILES[@]}"; do + codesign --options runtime --force --verify --verbose --timestamp ${OSX_CODESIGN_EXTRAS} \ + --prefix "${{ env.TS_BUNDLE_ID_BASE }}.${{ env.TS_FILENAME }}." \ + --sign "Developer ID Application: $MACOS_APPLICATION_ID" \ + "${{ runner.temp }}/osxpkg/${{ env.TARI_NETWORK_DIR }}/runtime/$FILE" + codesign --verify --deep --display --verbose=4 \ + "${{ runner.temp }}/osxpkg/${{ env.TARI_NETWORK_DIR }}/runtime/$FILE" + cp -vf "${{ runner.temp }}/osxpkg/${{ env.TARI_NETWORK_DIR }}/runtime/$FILE" \ + "${{ env.MTS_SOURCE }}" + done + distDirPKG=$(mktemp -d -t ${{ env.TS_FILENAME }}) + echo "${distDirPKG}" + echo "distDirPKG=${distDirPKG}" >> $GITHUB_ENV + TS_Temp=${{ env.TS_FILENAME }} + TS_BUNDLE_ID_VALID_NAME=$(echo "${TS_Temp//_/-}") + # Strip apple-darwin + TS_ARCH=$(echo "${${{ matrix.builds.target }}//-apple-darwin/}") + pkgbuild --root "${{ runner.temp }}/osxpkg/${{ env.TARI_NETWORK_DIR }}" \ + --identifier "${{ env.TS_BUNDLE_ID_BASE }}.pkg.${TS_BUNDLE_ID_VALID_NAME}" \ + --version "${TARI_VERSION}" \ + --install-location "/tmp/tari" \ + --scripts "${{ runner.temp }}/osxpkg/${{ env.TARI_NETWORK_DIR }}/scripts" \ + --sign "Developer ID Installer: ${MACOS_INSTALLER_ID}" \ + "${distDirPKG}/${{ env.TS_FILENAME }}-${{ matrix.builds.name }}-${{ env.TARI_VERSION }}.pkg" + echo -e "Submitting to Apple...\n\n" + xcrun notarytool submit \ + "${distDirPKG}/${{ env.TS_FILENAME }}-${{ matrix.builds.name }}-${{ env.TARI_VERSION }}.pkg" \ + --apple-id "${MACOS_NOTARIZE_USERNAME}" \ + --password ${MACOS_NOTARIZE_PASSWORD} \ + --team-id ${MACOS_ASC_PROVIDER} \ + --verbose --wait 2>&1 | tee -a notarisation.result + # Maybe use line from with "Processing complete"? + requestUUID=$(tail -n5 notarisation.result | grep "id:" | cut -d" " -f 4) + requestSTATUS=$(tail -n5 notarisation.result | grep "\ \ status:" | cut -d" " -f 4) + if [[ ${requestUUID} == "" ]] || [[ ${requestSTATUS} != "Accepted" ]]; then + echo "## status: ${requestSTATUS} - could not notarize - ${requestUUID} - ${{ env.TS_FILENAME }}-${{ matrix.builds.name }}-${{ env.TARI_VERSION }}.pkg" + exit 1 + else + echo "Notarization RequestUUID: ${requestUUID}" + echo -e "\nStapling package...\ + ${{ env.TS_FILENAME }}-${{ matrix.builds.name }}-${{ env.TARI_VERSION }}.pkg\n" + xcrun stapler staple -v \ + "${distDirPKG}/${{ env.TS_FILENAME }}-${{ matrix.builds.name }}-${{ env.TARI_VERSION }}.pkg" + fi + cd ${distDirPKG} + echo "Compute pkg shasum" + ${SHARUN} "${{ env.TS_FILENAME }}-${{ matrix.builds.name }}-${{ env.TARI_VERSION }}.pkg" \ + >> "${{ env.TS_FILENAME }}-${{ matrix.builds.name }}-${{ env.TARI_VERSION }}.pkg.sha256" + cat "${{ env.TS_FILENAME }}-${{ matrix.builds.name }}-${{ env.TARI_VERSION }}.pkg.sha256" + echo "Checksum verification for pkg is " + ${SHARUN} --check "${{ env.TS_FILENAME }}-${{ matrix.builds.name }}-${{ env.TARI_VERSION }}.pkg.sha256" + + - name: Artifact upload for macOS pkg + # Disabled + if: ${{ false }} + # if: startsWith(runner.os,'macOS') + continue-on-error: true + uses: actions/upload-artifact@v4 + with: + name: ${{ env.TS_FILENAME }}-${{ matrix.builds.name }}-${{ env.TARI_VERSION }}.pkg + path: "${{ env.distDirPKG }}/${{ env.TS_FILENAME }}-${{ matrix.builds.name }}-${{ env.TARI_VERSION }}*.pkg*" + + - name: Build the Windows installer + # Disabled + if: ${{ false }} + # if: startsWith(runner.os,'Windows') + shell: cmd + run: | + cd buildtools + "%programfiles(x86)%\Inno Setup 6\iscc.exe" "/DMyAppVersion=${{ env.TARI_VERSION }}-${{ env.VSHA_SHORT }}-${{ matrix.builds.name }}-installer" "/DMinotariSuite=${{ env.TS_FILENAME }}" "/DTariSuitePath=${{ github.workspace }}${{ env.TS_DIST }}" "windows_inno_installer.iss" + cd Output + echo "Compute archive shasum" + ${{ env.SHARUN }} "${{ env.TS_FILENAME }}-${{ env.TARI_VERSION }}-${{ env.VSHA_SHORT }}-${{ matrix.builds.name }}-installer.exe" >> "${{ env.TS_FILENAME }}-${{ env.TARI_VERSION }}-${{ env.VSHA_SHORT }}-${{ matrix.builds.name }}-installer.exe.sha256" + echo "Show the shasum" + cat "${{ env.TS_FILENAME }}-${{ env.TARI_VERSION }}-${{ env.VSHA_SHORT }}-${{ matrix.builds.name }}-installer.exe.sha256" + echo "Checksum verification archive is " + ${{ env.SHARUN }} --check "${{ env.TS_FILENAME }}-${{ env.TARI_VERSION }}-${{ env.VSHA_SHORT }}-${{ matrix.builds.name }}-installer.exe.sha256" + + - name: Artifact upload for Windows installer + # Disabled + if: ${{ false }} + # if: startsWith(runner.os,'Windows') + uses: actions/upload-artifact@v4 + with: + name: "${{ env.TS_FILENAME }}_windows_installer" + path: "${{ github.workspace }}/buildtools/Output/*" + + - name: Archive and Checksum Binaries + shell: bash + run: | + echo "Archive ${{ env.BINFILE }} too ${{ env.BINFILE }}.zip" + cd "${{ env.MTS_SOURCE }}" + echo "Compute files shasum" + ${SHARUN} * >> "${{ env.BINFILE }}.sha256" + echo "Show the shasum" + cat "${{ env.BINFILE }}.sha256" + echo "Checksum verification for files is " + ${SHARUN} --check "${{ env.BINFILE }}.sha256" + 7z a "${{ env.BINFILE }}.zip" * + echo "Compute archive shasum" + ${SHARUN} "${{ env.BINFILE }}.zip" >> "${{ env.BINFILE }}.zip.sha256" + echo "Show the shasum" + cat "${{ env.BINFILE }}.zip.sha256" + echo "Checksum verification archive is " + ${SHARUN} --check "${{ env.BINFILE }}.zip.sha256" + + - name: Artifact upload for Archive + uses: actions/upload-artifact@v4 + with: + name: ${{ env.TS_FILENAME }}_archive-${{ matrix.builds.name }} + path: "${{ github.workspace }}${{ env.TS_DIST }}/${{ env.BINFILE }}.zip*" + + macOS-universal-assemble: + name: macOS universal assemble + needs: builds + + env: + TARI_VERSION: ${{ needs.builds.outputs.TARI_VERSION }} + VSHA_SHORT: ${{ needs.builds.outputs.VSHA_SHORT }} + SHARUN: "shasum --algorithm 256" + + continue-on-error: true + + runs-on: macos-14 + + steps: + - name: Checkout source code + uses: actions/checkout@v4 + + - name: Download macOS binaries + uses: actions/download-artifact@v4 + with: + path: osxuni + # macos - x86_64 / arm64 + pattern: ${{ env.TS_FILENAME }}_archive-macos-* + merge-multiple: true + + - name: Set environment variables for macOS universal + shell: bash + run: | + BINFN="${TS_FILENAME}-${TARI_VERSION}-${VSHA_SHORT}" + echo "BINFN=${BINFN}" >> $GITHUB_ENV + + - name: Install macOS dependencies + shell: bash + run: | + brew install coreutils + + - name: Verify checksums and extract + shell: bash + working-directory: osxuni + run: | + ls -alhtR + ${SHARUN} --ignore-missing --check \ + "${{ env.BINFN }}-macos-x86_64.zip.sha256" + ${SHARUN} --ignore-missing --check \ + "${{ env.BINFN }}-macos-arm64.zip.sha256" + ls -alhtR + mkdir macos-universal macos-x86_64 macos-arm64 + cd macos-x86_64 + 7z e "../${{ env.BINFN }}-macos-x86_64.zip" + cd ../macos-arm64 + 7z e "../${{ env.BINFN }}-macos-arm64.zip" + + - name: Assemble macOS universal binaries + shell: bash + working-directory: osxuni + run: | + ls -alhtR + ARRAY_FILES=( $(echo ${TS_FILES} | jq --raw-output '.[]' | awk '{ print $1 }') ) + for FILE in "${ARRAY_FILES[@]}"; do + echo "processing binary file - ${FILE}" + lipo -create -output macos-universal/${FILE} \ + macos-x86_64/${FILE} \ + macos-arm64/${FILE} + done + ARRAY_LIBS=( $(echo ${TS_LIBRARIES} | tr ', ' '\n') ) + for FILE in "${ARRAY_LIBS[@]}"; do + echo "processing library file - lib${FILE}.dylib" + lipo -create -output macos-universal/lib${FILE}.dylib \ + macos-x86_64/lib${FILE}.dylib \ + macos-arm64/lib${FILE}.dylib + done + ls -alhtR macos-universal + + - name: Build the macOS universal pkg + # Disabled + if: ${{ false }} + continue-on-error: true + env: + MACOS_KEYCHAIN_PASS: ${{ secrets.MACOS_KEYCHAIN_PASS }} + MACOS_APPLICATION_ID: ${{ secrets.MACOS_APPLICATION_ID }} + MACOS_APPLICATION_CERT: ${{ secrets.MACOS_APPLICATION_CERT }} + MACOS_APPLICATION_PASS: ${{ secrets.MACOS_APPLICATION_PASS }} + MACOS_INSTALLER_ID: ${{ secrets.MACOS_INSTALLER_ID }} + MACOS_INSTALLER_CERT: ${{ secrets.MACOS_INSTALLER_CERT }} + MACOS_INSTALLER_PASS: ${{ secrets.MACOS_INSTALLER_PASS }} + MACOS_NOTARIZE_USERNAME: ${{ secrets.MACOS_NOTARIZE_USERNAME }} + MACOS_NOTARIZE_PASSWORD: ${{ secrets.MACOS_NOTARIZE_PASSWORD }} + MACOS_ASC_PROVIDER: ${{ secrets.MACOS_ASC_PROVIDER }} + run: | + echo $MACOS_APPLICATION_CERT | base64 --decode > application.p12 + echo $MACOS_INSTALLER_CERT | base64 --decode > installer.p12 + security create-keychain -p $MACOS_KEYCHAIN_PASS build.keychain + security default-keychain -s build.keychain + security unlock-keychain -p $MACOS_KEYCHAIN_PASS build.keychain + security import application.p12 -k build.keychain -P $MACOS_APPLICATION_PASS -T /usr/bin/codesign + security import installer.p12 -k build.keychain -P $MACOS_INSTALLER_PASS -T /usr/bin/pkgbuild + security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k $MACOS_KEYCHAIN_PASS build.keychain + OSX_CODESIGN_EXTRAS="--entitlements ${GITHUB_WORKSPACE}/applications/minotari_node/osx-pkg/entitlements.xml" + cd buildtools + # export target_release="target/${{ matrix.builds.target }}/release" + # matrix.builds.target=macos-universal + # matrix.builds.name=macos-universal + export target_release="osxuni/macos-universal" + mkdir -p "${{ runner.temp }}/osxpkg" + export tarball_parent="${{ runner.temp }}/osxpkg" + export tarball_source="${{ env.TARI_NETWORK_DIR }}" + ./create_osx_install_zip.sh unused nozip + ARRAY_FILES=( $(echo ${TS_FILES} | jq --raw-output '.[]' | awk '{ print $1 }') ) + for FILE in "${ARRAY_FILES[@]}"; do + codesign --options runtime --force --verify --verbose --timestamp ${OSX_CODESIGN_EXTRAS} \ + --prefix "${{ env.TS_BUNDLE_ID_BASE }}.${{ env.TS_FILENAME }}." \ + --sign "Developer ID Application: $MACOS_APPLICATION_ID" \ + "${{ runner.temp }}/osxpkg/${{ env.TARI_NETWORK_DIR }}/runtime/$FILE" + codesign --verify --deep --display --verbose=4 \ + "${{ runner.temp }}/osxpkg/${{ env.TARI_NETWORK_DIR }}/runtime/$FILE" + cp -vf "${{ runner.temp }}/osxpkg/${{ env.TARI_NETWORK_DIR }}/runtime/$FILE" \ + "${{ github.workspace }}/osxuni/macos-universal/" + done + distDirPKG=$(mktemp -d -t ${{ env.TS_FILENAME }}) + echo "${distDirPKG}" + echo "distDirPKG=${distDirPKG}" >> $GITHUB_ENV + TS_Temp=${{ env.TS_FILENAME }} + TS_BUNDLE_ID_VALID_NAME=$(echo "${TS_Temp//_/-}") + TS_ARCH=universal + pkgbuild --root "${{ runner.temp }}/osxpkg/${{ env.TARI_NETWORK_DIR }}" \ + --identifier "${{ env.TS_BUNDLE_ID_BASE }}.pkg.${TS_BUNDLE_ID_VALID_NAME}" \ + --version "${TARI_VERSION}" \ + --install-location "/tmp/tari" \ + --scripts "${{ runner.temp }}/osxpkg/${{ env.TARI_NETWORK_DIR }}/scripts" \ + --sign "Developer ID Installer: ${MACOS_INSTALLER_ID}" \ + "${distDirPKG}/${{ env.TS_FILENAME }}-macos-universal-${{ env.TARI_VERSION }}.pkg" + echo -e "Submitting to Apple...\n\n" + xcrun notarytool submit \ + "${distDirPKG}/${{ env.TS_FILENAME }}-macos-universal-${{ env.TARI_VERSION }}.pkg" \ + --apple-id "${MACOS_NOTARIZE_USERNAME}" \ + --password ${MACOS_NOTARIZE_PASSWORD} \ + --team-id ${MACOS_ASC_PROVIDER} \ + --verbose --wait 2>&1 | tee -a notarisation.result + # Maybe use line from with "Processing complete"? + requestUUID=$(tail -n5 notarisation.result | grep "id:" | cut -d" " -f 4) + requestSTATUS=$(tail -n5 notarisation.result | grep "\ \ status:" | cut -d" " -f 4) + if [[ ${requestUUID} == "" ]] || [[ ${requestSTATUS} != "Accepted" ]]; then + echo "## status: ${requestSTATUS} - could not notarize - ${requestUUID} - ${{ env.TS_FILENAME }}-macos-universal-${{ env.TARI_VERSION }}.pkg" + exit 1 + else + echo "Notarization RequestUUID: ${requestUUID}" + echo -e "\nStapling package...\ + ${{ env.TS_FILENAME }}-macos-universal-${{ env.TARI_VERSION }}.pkg\n" + xcrun stapler staple -v \ + "${distDirPKG}/${{ env.TS_FILENAME }}-macos-universal-${{ env.TARI_VERSION }}.pkg" + fi + cd ${distDirPKG} + echo "Compute pkg shasum" + ${SHARUN} "${{ env.TS_FILENAME }}-macos-universal-${{ env.TARI_VERSION }}.pkg" \ + >> "${{ env.TS_FILENAME }}-macos-universal-${{ env.TARI_VERSION }}.pkg.sha256" + cat "${{ env.TS_FILENAME }}-macos-universal-${{ env.TARI_VERSION }}.pkg.sha256" + echo "Checksum verification for pkg is " + ${SHARUN} --check "${{ env.TS_FILENAME }}-macos-universal-${{ env.TARI_VERSION }}.pkg.sha256" + + - name: Artifact upload for macOS universal pkg + # Disabled + if: ${{ false }} + # if: startsWith(runner.os,'macOS') + continue-on-error: true + uses: actions/upload-artifact@v4 + with: + name: ${{ env.TS_FILENAME }}-macos-universal-${{ env.TARI_VERSION }}.pkg + path: "${{ env.distDirPKG }}/${{ env.TS_FILENAME }}-macos-universal-${{ env.TARI_VERSION }}*.pkg*" + + - name: Archive and Checksum macOS universal Binaries + shell: bash + working-directory: osxuni/macos-universal + run: | + # set -xo pipefail + BINFILE="${BINFN}-macos-universal" + echo "BINFILE=${BINFILE}" >> $GITHUB_ENV + echo "Archive ${BINFILE} into ${BINFILE}.zip" + echo "Compute files shasum into ${BINFILE}.sha256" + ${SHARUN} * >> "${BINFILE}.sha256" + echo "Show the shasum" + cat "${BINFILE}.sha256" + echo "Checksum verification for files is " + ${SHARUN} --check "${BINFILE}.sha256" + 7z a "${BINFILE}.zip" * + echo "Compute archive shasum into ${BINFILE}.zip.sha256" + ${SHARUN} "${BINFILE}.zip" >> "${BINFILE}.zip.sha256" + echo "Show the shasum from ${BINFILE}.zip.sha256" + cat "${BINFILE}.zip.sha256" + echo "Checksum verification archive is " + ${SHARUN} --check "${BINFILE}.zip.sha256" + + - name: Artifact upload for Archive + uses: actions/upload-artifact@v4 + with: + name: ${{ env.TS_FILENAME }}_archive-macos-universal + path: "${{ github.workspace }}/osxuni/macos-universal/${{ env.BINFILE }}.zip*" + + create-release: + if: ${{ startsWith(github.ref, 'refs/tags/v') }} + + runs-on: ubuntu-latest + needs: builds + + env: + TARI_NETWORK_DIR: ${{ needs.builds.outputs.TARI_NETWORK_DIR }} + TARI_VERSION: ${{ needs.builds.outputs.TARI_VERSION }} + + permissions: + contents: write + + steps: + - name: Download binaries + uses: actions/download-artifact@v4 + with: + path: ${{ env.TS_FILENAME }} + pattern: "${{ env.TS_FILENAME }}*" + merge-multiple: true + + - name: Verify checksums and Prep Uploads + shell: bash + working-directory: ${{ env.TS_FILENAME }} + run: | + # set -xo pipefail + sudo apt-get update + sudo apt-get --no-install-recommends --assume-yes install dos2unix + ls -alhtR + if [ -f "${{ env.TS_FILENAME }}-${{ env.TARI_VERSION }}.${{ env.TS_SIG_FN }}" ] ; then + rm -fv "${{ env.TS_FILENAME }}-${{ env.TARI_VERSION }}.${{ env.TS_SIG_FN }}" + fi + # Merge all sha256 files into one + find . -name "*.sha256" -type f -print | xargs cat >> "${{ env.TS_FILENAME }}-${{ env.TARI_VERSION }}.${{ env.TS_SIG_FN }}" + dos2unix --quiet "${{ env.TS_FILENAME }}-${{ env.TARI_VERSION }}.${{ env.TS_SIG_FN }}" + cat "${{ env.TS_FILENAME }}-${{ env.TARI_VERSION }}.${{ env.TS_SIG_FN }}" + sha256sum --ignore-missing --check "${{ env.TS_FILENAME }}-${{ env.TARI_VERSION }}.${{ env.TS_SIG_FN }}" + ls -alhtR + + - name: Create release + uses: ncipollo/release-action@v1 + with: + artifacts: "${{ env.TS_FILENAME }}*/**/*" + token: ${{ secrets.GITHUB_TOKEN }} + prerelease: true + draft: true + allowUpdates: true + updateOnlyUnreleased: true + replacesArtifacts: true + + - name: Sync assets to S3 + continue-on-error: true + if: ${{ env.AWS_SECRET_ACCESS_KEY != '' && matrix.builds.runs-on != 'self-hosted' }} + env: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + S3CMD: "cp" + S3OPTIONS: '--recursive --exclude "*" --include "*.sha256*" --include "*.zip*" --include "*.pkg*" --include "*installer.exe*"' + shell: bash + working-directory: ${{ env.TS_FILENAME }} + run: | + echo "Upload processing ..." + ls -alhtR + echo "Clean up" + # Bash check if file with wildcards, does not work as expected + # if [ -f ${{ env.TS_FILENAME }}*diag-utils* ] ; then + if ls ${{ env.TS_FILENAME }}*diag-utils* > /dev/null 2>&1 ; then + rm -fv ${{ env.TS_FILENAME }}*diag-utils* + fi + echo "Folder setup" + if ls ${{ env.TS_FILENAME }}*linux* > /dev/null 2>&1 ; then + mkdir -p "linux/${{ env.TARI_NETWORK_DIR }}/" + mv -v ${{ env.TS_FILENAME }}*linux* "linux/${{ env.TARI_NETWORK_DIR }}/" + fi + if ls ${{ env.TS_FILENAME }}*macos* > /dev/null 2>&1 ; then + mkdir -p "osx/${{ env.TARI_NETWORK_DIR }}/" + mv -v ${{ env.TS_FILENAME }}*macos* "osx/${{ env.TARI_NETWORK_DIR }}/" + fi + if ls ${{ env.TS_FILENAME }}*windows* > /dev/null 2>&1 ; then + mkdir -p "windows/${{ env.TARI_NETWORK_DIR }}/" + mv -v ${{ env.TS_FILENAME }}*windows* "windows/${{ env.TARI_NETWORK_DIR }}/" + fi + ls -alhtR + aws --version + echo "ls current" + aws s3 ls --region ${{ secrets.AWS_REGION }} \ + s3://${{ secrets.AWS_S3_BUCKET }}/current/ + echo "Upload current" + aws s3 ${{ env.S3CMD }} --region ${{ secrets.AWS_REGION }} \ + . \ + s3://${{ secrets.AWS_S3_BUCKET }}/current/ \ + ${{ env.S3OPTIONS }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..7b79853 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,260 @@ +--- +name: CI + +'on': + workflow_dispatch: + push: + branches: + - development + - main + - ci-* + pull_request: + types: + - opened + - reopened + - synchronize + merge_group: + +concurrency: + # https://docs.github.com/en/actions/examples/using-concurrency-expressions-and-a-test-matrix + group: '${{ github.workflow }} @ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}' + cancel-in-progress: ${{ !startsWith(github.ref, 'refs/tags/v') || github.ref != 'refs/heads/development' }} + +env: + nightly_toolchain: nightly-2024-10-17 + stable_toolchain: 1.82 + CARGO_HTTP_MULTIPLEXING: false + CARGO_TERM_COLOR: always + TARI_TARGET_NETWORK: localnet + TARI_NETWORK: localnet + PROTOC: protoc + TERM: unknown + +jobs: + fmt: + name: fmt + runs-on: [ self-hosted, ubuntu-high-cpu ] + + steps: + - name: checkout + uses: actions/checkout@v4 + + - name: toolchain + uses: dtolnay/rust-toolchain@nightly + with: + toolchain: ${{ env.nightly_toolchain }} + components: rustfmt + + - name: ubuntu dependencies + run: | + sudo apt-get update + sudo bash scripts/install_ubuntu_dependencies.sh + + - name: wasm target install + run: rustup target add wasm32-unknown-unknown + + - name: cargo format + run: cargo +${{ env.nightly_toolchain }} fmt --all -- --check + + clippy: + name: clippy + runs-on: [ self-hosted, ubuntu-high-cpu ] + + steps: + - name: checkout + uses: actions/checkout@v4 + + - name: toolchain + uses: dtolnay/rust-toolchain@stable + with: + toolchain: ${{ env.stable_toolchain }} + components: clippy + + - name: ubuntu dependencies + run: | + sudo apt-get update + sudo bash scripts/install_ubuntu_dependencies.sh + + - uses: rui314/setup-mold@v1 + + - name: wasm target install + run: rustup target add wasm32-unknown-unknown + + - name: Install cargo-lints + run: cargo install cargo-lints + + - name: Clippy check (with lints) + run: cargo lints clippy --all-targets --all-features + + machete: + # Checks for unused dependencies. + name: machete + runs-on: [ ubuntu-latest ] + + steps: + - name: checkout + uses: actions/checkout@v4 + + - name: toolchain + uses: dtolnay/rust-toolchain@stable + with: + toolchain: ${{ env.stable_toolchain }} + components: clippy, rustfmt + + - name: ubuntu dependencies + run: | + sudo apt-get update + sudo bash scripts/install_ubuntu_dependencies.sh + + - name: cargo machete + run: | + cargo install cargo-machete + cargo machete + + build: + name: check nightly + runs-on: [ self-hosted, ubuntu-high-cpu ] + + steps: + - name: checkout + uses: actions/checkout@v4 + + - name: toolchain + uses: dtolnay/rust-toolchain@nightly + with: + toolchain: ${{ env.nightly_toolchain }} + + - name: ubuntu dependencies + run: | + sudo apt-get update + sudo bash scripts/install_ubuntu_dependencies.sh + + - uses: rui314/setup-mold@v1 + + - name: wasm target install + run: rustup target add wasm32-unknown-unknown + + - name: cargo check + run: cargo check --release --all-features --all-targets --locked + + build-stable: + name: check stable + runs-on: [ self-hosted, ubuntu-high-cpu ] + env: + RUSTUP_PERMIT_COPY_RENAME: true + + steps: + - name: checkout + uses: actions/checkout@v4 + + - name: toolchain + uses: dtolnay/rust-toolchain@stable + with: + toolchain: stable + + - uses: Swatinem/rust-cache@v2 + + - name: ubuntu dependencies + run: | + sudo apt-get update + sudo bash scripts/install_ubuntu_dependencies.sh + + - uses: rui314/setup-mold@v1 + + - name: wasm target install + run: rustup target add wasm32-unknown-unknown + + - name: rustup show + run: | + rustup show + + - name: cargo check + run: cargo check --release --all-targets --locked + + licenses: + name: file licenses + runs-on: [ ubuntu-latest ] + + steps: + - name: checkout + uses: actions/checkout@v4 + + - name: install ripgrep + # https://github.com/BurntSushi/ripgrep/releases + env: + ripgrep_version: 14.1.0 + run: | + #wget https://github.com/BurntSushi/ripgrep/releases/download/14.1.0/ripgrep_14.1.0-1_amd64.deb.sha256 + wget https://github.com/BurntSushi/ripgrep/releases/download/${{ env.ripgrep_version }}/ripgrep_${{ env.ripgrep_version }}-1_amd64.deb + sudo dpkg -i ripgrep_${{ env.ripgrep_version }}-1_amd64.deb + rg --version || exit 1 + + - name: run the license check + run: ./scripts/file_license_check.sh + + test: + name: test + runs-on: [ self-hosted, ubuntu-high-cpu ] + + steps: + - name: checkout + uses: actions/checkout@v4 + + - name: toolchain + uses: dtolnay/rust-toolchain@stable + with: + toolchain: ${{ env.stable_toolchain }} + + - name: ubuntu dependencies + run: | + sudo apt-get update + sudo bash scripts/install_ubuntu_dependencies.sh + + - uses: rui314/setup-mold@v1 + + - name: wasm target install + run: rustup target add wasm32-unknown-unknown + + - name: Install cargo-nextest + run: cargo install cargo-nextest --locked + + - name: cargo test compile + run: cargo test --no-run --locked --all-features --release + + - name: cargo test + run: cargo nextest run --all-features --release -E "not package(integration_tests)" --profile ci + + - name: upload artifact + uses: actions/upload-artifact@v4 # upload test results as artifact + if: success() || failure() + with: + name: test-results + path: ${{ github.workspace }}/target/nextest/ci/junit.xml + + - name: cargo test cucumber + run: cargo test --release --package integration_tests --test cucumber -- --tags "not @ignore and not @flaky" + + - name: upload test result artifact + uses: actions/upload-artifact@v4 # upload test results as artifact + if: success() || failure() + with: + name: cucumber-test-results + path: ${{ github.workspace }}/integration_tests/cucumber-output-junit.xml + + - name: Upload cucumber log artifacts + uses: actions/upload-artifact@v4 + if: success() || failure() + with: + name: cucumber-log-artifacts + path: ${{ github.workspace }}/integration_tests/tests/temp/cucumber_*/*.log + + # needed for test results + event_file: + runs-on: [ ubuntu-latest ] + + steps: + - name: Upload + uses: actions/upload-artifact@v4 + with: + name: Event File + path: ${{ github.event_path }} diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml new file mode 100644 index 0000000..c8bbd30 --- /dev/null +++ b/.github/workflows/coverage.yml @@ -0,0 +1,64 @@ +--- +name: Source Coverage + +'on': + workflow_dispatch: + +env: + toolchain: nightly-2022-05-01 + +jobs: + coverage: + name: test and generate cov + runs-on: [ self-hosted, ubuntu18.04-high-mem ] + + steps: + - name: checkout + uses: actions/checkout@v4 + + - name: ubuntu dependencies + run: | + sudo apt-get update + sudo bash scripts/install_ubuntu_dependencies.sh + + - name: toolchain + uses: dtolnay/rust-toolchain@stable + with: + toolchain: nightly + components: llvm-tools-preview + + - name: cargo test compile + env: + RUSTFLAGS: "-C instrument-coverage" + RUSTDOCFLAGS: "-C instrument-coverage" + LLVM_PROFILE_FILE: "coverage_data-%p-%m.profraw" + run: cargo test --all-features --no-fail-fast + + - id: coverage + name: Prepare coverage data + env: + COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }} + run: | + cargo install grcov + grcov . -s . --binary-path ./target/debug -t coveralls --branch --ignore-not-existing \ + -o ./target/coveralls_coverage.json \ + --token $COVERALLS_REPO_TOKEN \ + --ignore target/**/*.rs \ + --ignore **/.cargo/**/*.rs \ + --vcs-branch $GITHUB_REF_NAME \ + --service-name github \ + --service-job-id ${GITHUB_RUN_ID} + + - name: archive-coverage + id: archive-coverage + uses: actions/upload-artifact@v4 + with: + path: target/coveralls_coverage.json + name: coveralls-coverage + + - name: Coveralls upload + uses: toshke/github-action@master + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + path-to-lcov: ./target/coveralls_coverage.json + file-format: coveralls diff --git a/.github/workflows/pr_signed_commits_check.yml b/.github/workflows/pr_signed_commits_check.yml new file mode 100644 index 0000000..2024a9f --- /dev/null +++ b/.github/workflows/pr_signed_commits_check.yml @@ -0,0 +1,19 @@ +--- +# Checks if the comments are signed or not +name: PR - Signed commits check + +'on': + pull_request_target + +permissions: {} + +jobs: + check-signed-commits: + name: Check signed commits in PR + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write + steps: + - name: Check signed commits in PR + uses: 1Password/check-signed-commits-action@v1 diff --git a/.github/workflows/pr_title.yml b/.github/workflows/pr_title.yml new file mode 100644 index 0000000..5801de8 --- /dev/null +++ b/.github/workflows/pr_title.yml @@ -0,0 +1,27 @@ +--- +# Checks that PR titles conform to Conventional Commits +# See https://www.conventionalcommits.org/en/v1.0.0/ for more information +name: PR + +on: + pull_request: + types: + - opened + - reopened + - edited + - synchronize + +jobs: + check-title: + runs-on: ubuntu-latest + steps: + - name: install + run: | + npm install -g @commitlint/cli @commitlint/config-conventional + echo "module.exports = {extends: ['@commitlint/config-conventional']}" > commitlint.config.js + + - name: lint + run: | + echo "$PR_TITLE" | commitlint + env: + PR_TITLE: ${{github.event.pull_request.title}} diff --git a/.github/workflows/test_results.yml b/.github/workflows/test_results.yml new file mode 100644 index 0000000..61a9d01 --- /dev/null +++ b/.github/workflows/test_results.yml @@ -0,0 +1,50 @@ +--- +name: Test Results for CI + +on: + workflow_run: + workflows: ["CI", "PR"] + types: + - completed + +permissions: {} + +jobs: + test-results: + name: Test Results + runs-on: ubuntu-latest + if: github.event.workflow_run.conclusion != 'skipped' + + permissions: + checks: write + + # needed unless run with comment_mode: off + pull-requests: write + + # required by download step to access artifacts API + actions: read + + steps: + - name: Download and Extract Artifacts + env: + GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} + run: | + mkdir -p artifacts && cd artifacts + + artifacts_url=${{ github.event.workflow_run.artifacts_url }} + + gh api --paginate "$artifacts_url" -q '.artifacts[] | [.name, .archive_download_url] | @tsv' | while read artifact + do + IFS=$'\t' read name url <<< "$artifact" + gh api $url > "$name.zip" + unzip -d "$name" "$name.zip" + done + + - name: Publish Test Results + uses: EnricoMi/publish-unit-test-result-action@v2 + with: + commit: ${{ github.event.workflow_run.head_sha }} + event_file: artifacts/Event File/event.json + check_name: "Test Results (${{ github.event.workflow_run.name || github.event_name }})" + event_name: ${{ github.event.workflow_run.event }} + files: "artifacts/**/*.xml" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..89c9e9e --- /dev/null +++ b/.gitignore @@ -0,0 +1,60 @@ +target +.env +# Non-root Cargo.locks +**/Cargo.lock +!/Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk + +# Ignore IDE workspace files +.idea +*.iml +.vs/ +*.userprefs +.vscode/ +*.code-workspace +tags + +# Ignore OS files +.DS_Store + +# Custom formatting files +.rustfmt.toml +.clippy.toml + +# RFC autogenerated documentation +book/ + +# Ignore Code Coverage Report files +report + +# On development branch only. This should be removed for point releases +*.log + +# Ignore DataStore, Database and Log files +*.mdb +/data/ +*.sqlite3 +keys.json +node_modules +temp/ + +# ignore output files from windows ISS +buildtools/Output/ + +# some folders called assets are kind of important now +# any specific "assets" folders should be gitignored +# closer to where they live +#assets/ + +# Ignore coverage profiling artifacts +*.profraw +/report/ + +# Ignore local history in Visual Studio Code +.lh + +clients/validator_node_grpc_client/package-lock.json +/dan_layer/wallet/storage_sqlite/temp.sqlite +/integration_tests/cucumber-output-junit.xml diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..3aec256 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,2857 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "const-random", + "getrandom", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "aligned" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "377e4c0ba83e4431b10df45c1d4666f178ea9c552cac93e60c3a88bf32785923" +dependencies = [ + "as-slice", +] + +[[package]] +name = "anstream" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" + +[[package]] +name = "anstyle-parse" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" +dependencies = [ + "anstyle", + "windows-sys 0.59.0", +] + +[[package]] +name = "anyhow" +version = "1.0.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775" + +[[package]] +name = "anymap2" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d301b3b94cb4b2f23d7917810addbbaff90738e0ca2be692bd027e70d7e0330c" + +[[package]] +name = "as-slice" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "516b6b4f0e40d50dcda9365d53964ec74560ad4284da2e7fc97122cd83174516" +dependencies = [ + "stable_deref_trait", +] + +[[package]] +name = "auth-git2" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3810b5af212b013fe7302b12d86616c6c39a48e18f2e4b812a5a9e5710213791" +dependencies = [ + "dirs", + "git2", + "terminal-prompt", +] + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "backtrace" +version = "0.3.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets 0.52.6", +] + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bstr" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a68f1f47cdf0ec8ee4b941b2eee2a80cb796db73118c0dd09ac63fbe405be22" +dependencies = [ + "memchr", + "regex-automata", + "serde", +] + +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" + +[[package]] +name = "cargo-generate" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a921a6f140ee9981c471b4ffdfc53372b8bc3fe417f83e87c124c6018ab6929" +dependencies = [ + "anstyle", + "anyhow", + "auth-git2", + "clap", + "console", + "dialoguer", + "env_logger", + "fs-err", + "git2", + "gix-config", + "heck 0.5.0", + "home", + "ignore", + "indexmap", + "indicatif", + "liquid", + "liquid-core", + "liquid-derive", + "liquid-lib", + "log", + "names", + "paste", + "path-absolutize", + "regex", + "remove_dir_all", + "rhai", + "sanitize-filename", + "semver", + "serde", + "tempfile", + "thiserror 1.0.69", + "time", + "toml", + "walkdir", +] + +[[package]] +name = "cargo_toml" +version = "0.20.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88da5a13c620b4ca0078845707ea9c3faf11edbc3ffd8497d11d686211cd1ac0" +dependencies = [ + "serde", + "toml", +] + +[[package]] +name = "cc" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd9de9f2205d5ef3fd67e685b0df337994ddd4495e2a28d185500d0e1edfea47" +dependencies = [ + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "clap" +version = "4.5.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb3b4b9e5a7c7514dfa52869339ee98b3156b0bfb4e8a77c4ff4babb64b1604f" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b17a95aa67cc7b5ebd32aa5370189aa0d79069ef1c64ce893bd30fb24bff20ec" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", + "terminal_size", +] + +[[package]] +name = "clap_derive" +version = "4.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.89", +] + +[[package]] +name = "clap_lex" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afb84c814227b90d6895e01398aee0d8033c00e7466aca416fb6a8e0eb19d8a7" + +[[package]] +name = "colorchoice" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" + +[[package]] +name = "console" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" +dependencies = [ + "encode_unicode", + "lazy_static", + "libc", + "unicode-width 0.1.14", + "windows-sys 0.52.0", +] + +[[package]] +name = "const-random" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" +dependencies = [ + "const-random-macro", +] + +[[package]] +name = "const-random-macro" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" +dependencies = [ + "getrandom", + "once_cell", + "tiny-keccak", +] + +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "coolor" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "691defa50318376447a73ced869862baecfab35f6aabaa91a4cd726b315bfe1a" +dependencies = [ + "crossterm", +] + +[[package]] +name = "cpufeatures" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ca741a962e1b0bff6d724a1a0958b686406e853bb14061f218562e1896f95e6" +dependencies = [ + "libc", +] + +[[package]] +name = "crokey" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520e83558f4c008ac06fa6a86e5c1d4357be6f994cce7434463ebcdaadf47bb1" +dependencies = [ + "crokey-proc_macros", + "crossterm", + "once_cell", + "serde", + "strict", +] + +[[package]] +name = "crokey-proc_macros" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "370956e708a1ce65fe4ac5bb7185791e0ece7485087f17736d54a23a0895049f" +dependencies = [ + "crossterm", + "proc-macro2", + "quote", + "strict", + "syn 1.0.109", +] + +[[package]] +name = "crossbeam" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-epoch", + "crossbeam-queue", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-queue" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + +[[package]] +name = "crossterm" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" +dependencies = [ + "bitflags", + "crossterm_winapi", + "mio", + "parking_lot", + "rustix", + "signal-hook", + "signal-hook-mio", + "winapi", +] + +[[package]] +name = "crossterm_winapi" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" +dependencies = [ + "winapi", +] + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "cvt" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2ae9bf77fbf2d39ef573205d554d87e86c12f1994e9ea335b0651b9b278bcf1" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "dialoguer" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "658bce805d770f407bc62102fca7c2c64ceef2fbcb2b8bd19d2765ce093980de" +dependencies = [ + "console", + "fuzzy-matcher", + "shell-words", + "tempfile", + "thiserror 1.0.69", + "zeroize", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "dirs" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", +] + +[[package]] +name = "doc-comment" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "encode_unicode" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" + +[[package]] +name = "env_filter" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "humantime", + "log", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "faster-hex" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2a2b11eda1d40935b26cf18f6833c526845ae8c41e58d09af6adeb6f0269183" + +[[package]] +name = "fastrand" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "fs-err" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88a41f105fe1d5b6b34b2055e3dc59bb79b46b48b2040b9e6c7b4b5de097aa41" +dependencies = [ + "autocfg", +] + +[[package]] +name = "fs_at" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14af6c9694ea25db25baa2a1788703b9e7c6648dcaeeebeb98f7561b5384c036" +dependencies = [ + "aligned", + "cfg-if", + "cvt", + "libc", + "nix", + "windows-sys 0.52.0", +] + +[[package]] +name = "fuzzy-matcher" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54614a3312934d066701a80f20f15fa3b56d67ac7722b39eea5b4c9dd1d66c94" +dependencies = [ + "thread_local", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + +[[package]] +name = "git2" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b903b73e45dc0c6c596f2d37eccece7c1c8bb6e4407b001096387c63d0d93724" +dependencies = [ + "bitflags", + "libc", + "libgit2-sys", + "log", + "openssl-probe", + "openssl-sys", + "url", +] + +[[package]] +name = "gix-actor" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc19e312cd45c4a66cd003f909163dc2f8e1623e30a0c0c6df3776e89b308665" +dependencies = [ + "bstr", + "gix-date", + "gix-utils", + "itoa", + "thiserror 1.0.69", + "winnow", +] + +[[package]] +name = "gix-config" +version = "0.40.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78e797487e6ca3552491de1131b4f72202f282fb33f198b1c34406d765b42bb0" +dependencies = [ + "bstr", + "gix-config-value", + "gix-features", + "gix-glob", + "gix-path", + "gix-ref", + "gix-sec", + "memchr", + "once_cell", + "smallvec", + "thiserror 1.0.69", + "unicode-bom", + "winnow", +] + +[[package]] +name = "gix-config-value" +version = "0.14.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3de3fdca9c75fa4b83a76583d265fa49b1de6b088ebcd210749c24ceeb74660" +dependencies = [ + "bitflags", + "bstr", + "gix-path", + "libc", + "thiserror 1.0.69", +] + +[[package]] +name = "gix-date" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d10d543ac13c97292a15e8e8b7889cd006faf739777437ed95362504b8fe81a0" +dependencies = [ + "bstr", + "itoa", + "jiff", + "thiserror 1.0.69", +] + +[[package]] +name = "gix-features" +version = "0.38.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac7045ac9fe5f9c727f38799d002a7ed3583cd777e3322a7c4b43e3cf437dc69" +dependencies = [ + "gix-hash", + "gix-trace", + "gix-utils", + "libc", + "prodash", + "sha1_smol", + "walkdir", +] + +[[package]] +name = "gix-fs" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2bfe6249cfea6d0c0e0990d5226a4cb36f030444ba9e35e0639275db8f98575" +dependencies = [ + "fastrand", + "gix-features", + "gix-utils", +] + +[[package]] +name = "gix-glob" +version = "0.16.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74908b4bbc0a0a40852737e5d7889f676f081e340d5451a16e5b4c50d592f111" +dependencies = [ + "bitflags", + "bstr", + "gix-features", + "gix-path", +] + +[[package]] +name = "gix-hash" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93d7df7366121b5018f947a04d37f034717e113dcf9ccd85c34b58e57a74d5e" +dependencies = [ + "faster-hex", + "thiserror 1.0.69", +] + +[[package]] +name = "gix-lock" +version = "14.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bc7fe297f1f4614774989c00ec8b1add59571dc9b024b4c00acb7dedd4e19d" +dependencies = [ + "gix-tempfile", + "gix-utils", + "thiserror 1.0.69", +] + +[[package]] +name = "gix-object" +version = "0.44.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f5b801834f1de7640731820c2df6ba88d95480dc4ab166a5882f8ff12b88efa" +dependencies = [ + "bstr", + "gix-actor", + "gix-date", + "gix-features", + "gix-hash", + "gix-utils", + "gix-validate", + "itoa", + "smallvec", + "thiserror 1.0.69", + "winnow", +] + +[[package]] +name = "gix-path" +version = "0.10.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c04e5a94fdb56b1e91eb7df2658ad16832428b8eeda24ff1a0f0288de2bce554" +dependencies = [ + "bstr", + "gix-trace", + "home", + "once_cell", + "thiserror 1.0.69", +] + +[[package]] +name = "gix-ref" +version = "0.47.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae0d8406ebf9aaa91f55a57f053c5a1ad1a39f60fdf0303142b7be7ea44311e5" +dependencies = [ + "gix-actor", + "gix-features", + "gix-fs", + "gix-hash", + "gix-lock", + "gix-object", + "gix-path", + "gix-tempfile", + "gix-utils", + "gix-validate", + "memmap2", + "thiserror 1.0.69", + "winnow", +] + +[[package]] +name = "gix-sec" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2007538eda296445c07949cf04f4a767307d887184d6b3e83e2d636533ddc6e" +dependencies = [ + "bitflags", + "gix-path", + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "gix-tempfile" +version = "14.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "046b4927969fa816a150a0cda2e62c80016fe11fb3c3184e4dddf4e542f108aa" +dependencies = [ + "gix-fs", + "libc", + "once_cell", + "parking_lot", + "tempfile", +] + +[[package]] +name = "gix-trace" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04bdde120c29f1fc23a24d3e115aeeea3d60d8e65bab92cc5f9d90d9302eb952" + +[[package]] +name = "gix-utils" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba427e3e9599508ed98a6ddf8ed05493db114564e338e41f6a996d2e4790335f" +dependencies = [ + "fastrand", + "unicode-normalization", +] + +[[package]] +name = "gix-validate" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e187b263461bc36cea17650141567753bc6207d036cedd1de6e81a52f277ff68" +dependencies = [ + "bstr", + "thiserror 1.0.69", +] + +[[package]] +name = "globset" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15f1ce686646e7f1e19bf7d5533fe443a45dbfb990e00629110797578b42fb19" +dependencies = [ + "aho-corasick", + "bstr", + "log", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "hashbrown" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", +] + +[[package]] +name = "idna" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "ignore" +version = "0.4.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d89fd380afde86567dfba715db065673989d6253f42b88179abd3eae47bda4b" +dependencies = [ + "crossbeam-deque", + "globset", + "log", + "memchr", + "regex-automata", + "same-file", + "walkdir", + "winapi-util", +] + +[[package]] +name = "indexmap" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" +dependencies = [ + "equivalent", + "hashbrown", + "serde", +] + +[[package]] +name = "indicatif" +version = "0.17.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbf675b85ed934d3c67b5c5469701eec7db22689d0a2139d856e0925fa28b281" +dependencies = [ + "console", + "number_prefix", + "portable-atomic", + "unicode-width 0.2.0", + "web-time", +] + +[[package]] +name = "instant" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "540654e97a3f4470a492cd30ff187bc95d89557a903a2bbf112e2fae98104ef2" + +[[package]] +name = "jiff" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d9d414fc817d3e3d62b2598616733f76c4cc74fbac96069674739b881295c8" +dependencies = [ + "jiff-tzdb-platform", + "windows-sys 0.59.0", +] + +[[package]] +name = "jiff-tzdb" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91335e575850c5c4c673b9bd467b0e025f164ca59d0564f69d0c2ee0ffad4653" + +[[package]] +name = "jiff-tzdb-platform" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9835f0060a626fe59f160437bc725491a6af23133ea906500027d1bd2f8f4329" +dependencies = [ + "jiff-tzdb", +] + +[[package]] +name = "jobserver" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" +dependencies = [ + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "kstring" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "558bf9508a558512042d3095138b1f7b8fe90c5467d94f9f1da28b3731c5dbd1" +dependencies = [ + "serde", + "static_assertions", +] + +[[package]] +name = "lazy-regex" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d8e41c97e6bc7ecb552016274b99fbb5d035e8de288c582d9b933af6677bfda" +dependencies = [ + "lazy-regex-proc_macros", + "once_cell", + "regex", +] + +[[package]] +name = "lazy-regex-proc_macros" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76e1d8b05d672c53cb9c7b920bbba8783845ae4f0b076e02a3db1d02c81b4163" +dependencies = [ + "proc-macro2", + "quote", + "regex", + "syn 2.0.89", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.164" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "433bfe06b8c75da9b2e3fbea6e5329ff87748f0b144ef75306e674c3f6f7c13f" + +[[package]] +name = "libgit2-sys" +version = "0.17.0+1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10472326a8a6477c3c20a64547b0059e4b0d086869eee31e6d7da728a8eb7224" +dependencies = [ + "cc", + "libc", + "libssh2-sys", + "libz-sys", + "openssl-sys", + "pkg-config", +] + +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags", + "libc", +] + +[[package]] +name = "libssh2-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dc8a030b787e2119a731f1951d6a773e2280c660f8ec4b0f5e1505a386e71ee" +dependencies = [ + "cc", + "libc", + "libz-sys", + "openssl-sys", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "libz-sys" +version = "1.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2d16453e800a8cf6dd2fc3eb4bc99b786a9b90c663b8559a5b1a041bf89e472" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "liquid" +version = "0.26.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cdcc72b82748f47c2933c172313f5a9aea5b2c4eb3fa4c66b4ea55bb60bb4b1" +dependencies = [ + "doc-comment", + "liquid-core", + "liquid-derive", + "liquid-lib", + "serde", +] + +[[package]] +name = "liquid-core" +version = "0.26.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2752e978ffc53670f3f2e8b3ef09f348d6f7b5474a3be3f8a5befe5382e4effb" +dependencies = [ + "anymap2", + "itertools", + "kstring", + "liquid-derive", + "num-traits", + "pest", + "pest_derive", + "regex", + "serde", + "time", +] + +[[package]] +name = "liquid-derive" +version = "0.26.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b51f1d220e3fa869e24cfd75915efe3164bd09bb11b3165db3f37f57bf673e3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", +] + +[[package]] +name = "liquid-lib" +version = "0.26.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59b1a298d3d2287ee5b1e43840d885b8fdfc37d3f4e90d82aacfd04d021618da" +dependencies = [ + "itertools", + "liquid-core", + "once_cell", + "percent-encoding", + "regex", + "time", + "unicode-segmentation", +] + +[[package]] +name = "litemap" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "maplit" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "memmap2" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f" +dependencies = [ + "libc", +] + +[[package]] +name = "minimad" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9c5d708226d186590a7b6d4a9780e2bdda5f689e0d58cd17012a298efd745d2" +dependencies = [ + "once_cell", +] + +[[package]] +name = "miniz_oxide" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +dependencies = [ + "adler2", +] + +[[package]] +name = "mio" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +dependencies = [ + "hermit-abi", + "libc", + "log", + "wasi", + "windows-sys 0.52.0", +] + +[[package]] +name = "names" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bddcd3bf5144b6392de80e04c347cd7fab2508f6df16a85fc496ecd5cec39bc" +dependencies = [ + "rand", +] + +[[package]] +name = "nix" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +dependencies = [ + "bitflags", + "cfg-if", + "cfg_aliases", + "libc", +] + +[[package]] +name = "normpath" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8911957c4b1549ac0dc74e30db9c8b0e66ddcd6d7acc33098f4c63a64a6d7ed" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "number_prefix" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" + +[[package]] +name = "object" +version = "0.36.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.52.6", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "path-absolutize" +version = "3.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4af381fe79fa195b4909485d99f73a80792331df0625188e707854f0b3383f5" +dependencies = [ + "path-dedot", +] + +[[package]] +name = "path-dedot" +version = "3.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07ba0ad7e047712414213ff67533e6dd477af0a4e1d14fb52343e53d30ea9397" +dependencies = [ + "once_cell", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pest" +version = "2.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879952a81a83930934cbf1786752d6dedc3b1f29e8f8fb2ad1d0a36f377cf442" +dependencies = [ + "memchr", + "thiserror 1.0.69", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d214365f632b123a47fd913301e14c946c61d1c183ee245fa76eb752e59a02dd" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb55586734301717aea2ac313f50b2eb8f60d2fc3dc01d190eefa2e625f60c4e" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn 2.0.89", +] + +[[package]] +name = "pest_meta" +version = "2.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b75da2a70cf4d9cb76833c990ac9cd3923c9a8905a8929789ce347c84564d03d" +dependencies = [ + "once_cell", + "pest", + "sha2", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" + +[[package]] +name = "pkg-config" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" + +[[package]] +name = "portable-atomic" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2" + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro2" +version = "1.0.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "307e3004becf10f5a6e0d59d20f3cd28231b0e0827a96cd3e0ce6d14bc1e4bb3" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "prodash" +version = "28.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "744a264d26b88a6a7e37cbad97953fa233b94d585236310bcbc88474b4092d79" + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "redox_syscall" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" +dependencies = [ + "bitflags", +] + +[[package]] +name = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom", + "libredox", + "thiserror 1.0.69", +] + +[[package]] +name = "regex" +version = "1.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "remove_dir_all" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a694f9e0eb3104451127f6cc1e5de55f59d3b1fc8c5ddfaeb6f1e716479ceb4a" +dependencies = [ + "cfg-if", + "cvt", + "fs_at", + "libc", + "normpath", + "windows-sys 0.59.0", +] + +[[package]] +name = "rhai" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61797318be89b1a268a018a92a7657096d83f3ecb31418b9e9c16dcbb043b702" +dependencies = [ + "ahash", + "bitflags", + "instant", + "num-traits", + "once_cell", + "rhai_codegen", + "smallvec", + "smartstring", + "thin-vec", +] + +[[package]] +name = "rhai_codegen" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5a11a05ee1ce44058fa3d5961d05194fdbe3ad6b40f904af764d81b86450e6b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustix" +version = "0.38.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7f649912bc1495e167a6edee79151c84b1bad49748cb4f1f1167f459f6224f6" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustversion" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "sanitize-filename" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ed72fbaf78e6f2d41744923916966c4fbe3d7c74e3037a8ee482f1115572603" +dependencies = [ + "lazy_static", + "regex", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" +dependencies = [ + "serde", +] + +[[package]] +name = "serde" +version = "1.0.215" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.215" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", +] + +[[package]] +name = "serde_spanned" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +dependencies = [ + "serde", +] + +[[package]] +name = "sha1_smol" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d" + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shell-words" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-mio" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd" +dependencies = [ + "libc", + "mio", + "signal-hook", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "smartstring" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb72c633efbaa2dd666986505016c32c3044395ceaf881518399d2f4127ee29" +dependencies = [ + "autocfg", + "static_assertions", + "version_check", +] + +[[package]] +name = "socket2" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "spinners" +version = "4.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0ef947f358b9c238923f764c72a4a9d42f2d637c46e059dbd319d6e7cfb4f82" +dependencies = [ + "lazy_static", + "maplit", + "strum", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strict" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f42444fea5b87a39db4218d9422087e66a85d0e7a0963a439b07bcdf91804006" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "strum" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" +dependencies = [ + "heck 0.4.1", + "proc-macro2", + "quote", + "rustversion", + "syn 1.0.109", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d46482f1c1c87acd84dea20c1bf5ebff4c757009ed6bf19cfd36fb10e92c4e" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", +] + +[[package]] +name = "tari_cli" +version = "0.1.0" +dependencies = [ + "anyhow", + "cargo-generate", + "cargo_toml", + "clap", + "convert_case", + "dialoguer", + "dirs-next", + "git2", + "serde", + "spinners", + "termimad", + "thiserror 2.0.3", + "tokio", + "toml", +] + +[[package]] +name = "tempfile" +version = "3.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +dependencies = [ + "cfg-if", + "fastrand", + "rustix", + "windows-sys 0.52.0", +] + +[[package]] +name = "termimad" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea6a5d4cf55d9f1cb04fcda48f725772d0733ae34e030dfc4dd36e738a5965f4" +dependencies = [ + "coolor", + "crokey", + "crossbeam", + "lazy-regex", + "minimad", + "serde", + "thiserror 1.0.69", + "unicode-width 0.1.14", +] + +[[package]] +name = "terminal-prompt" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "572818b3472910acbd5dff46a3413715c18e934b071ab2ba464a7b2c2af16376" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "terminal_size" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f599bd7ca042cfdf8f4512b277c02ba102247820f9d9d4a9f521f496751a6ef" +dependencies = [ + "rustix", + "windows-sys 0.59.0", +] + +[[package]] +name = "thin-vec" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a38c90d48152c236a3ab59271da4f4ae63d678c5d7ad6b7714d7cb9760be5e4b" + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c006c85c7651b3cf2ada4584faa36773bd07bac24acfb39f3c431b36d7e667aa" +dependencies = [ + "thiserror-impl 2.0.3", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f077553d607adc1caf65430528a576c757a71ed73944b66ebb58ef2bbd243568" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", +] + +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "time" +version = "0.3.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tinyvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.41.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cfb5bee7a6a52939ca9224d6ac897bb669134078daa8735560897f69de4d33" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.52.0", +] + +[[package]] +name = "tokio-macros" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", +] + +[[package]] +name = "toml" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[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" +version = "0.22.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "ucd-trie" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" + +[[package]] +name = "unicode-bom" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7eec5d1121208364f6793f7d2e222bf75a915c19557537745b195b253dd64217" + +[[package]] +name = "unicode-ident" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" + +[[package]] +name = "unicode-normalization" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + +[[package]] +name = "unicode-width" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" + +[[package]] +name = "url" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d157f1b96d14500ffdc1f10ba712e780825526c03d9a49b4d0324b0d9113ada" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +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.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" +dependencies = [ + "cfg-if", + "once_cell", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.89", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +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.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[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.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" +dependencies = [ + "memchr", +] + +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + +[[package]] +name = "yoke" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", +] + +[[package]] +name = "zerofrom" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" + +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..97e5808 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "tari_cli" +version = "0.1.0" +edition = "2021" +authors = ["The Tari Development Community"] +repository = "https://github.com/tari-project/tari-cli" +license = "BSD-3-Clause" + +[[bin]] +name = "tari" +path = "src/main.rs" + +[dependencies] +anyhow = "1.0.93" +clap = { version = "4.5.20", features = ["derive", "string"] } +dirs-next = "2.0.0" +tokio = { version = "1.41.1", features = ["full"] } +toml = "0.8.19" +serde = { version = "1.0.215", features = ["derive"] } +termimad = "0.31.0" +cargo-generate = "^0.22.0" +git2 = { version = "^0.19", features = ["default"] } +thiserror = "2.0.3" +spinners = "4.1.1" +convert_case = "0.6.0" +dialoguer = { version = "0.11.0", features = ["default", "fuzzy-select"] } +cargo_toml = "0.20.5" diff --git a/README.md b/README.md index b526a18..cc28184 100644 --- a/README.md +++ b/README.md @@ -1 +1,3 @@ # tari-cli + +This CLI tool is the starting point for the development of Tari templates (smart contracts in other blockchains). \ No newline at end of file diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000..292fe49 --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "stable" diff --git a/src/cli/arguments.rs b/src/cli/arguments.rs new file mode 100644 index 0000000..5525f15 --- /dev/null +++ b/src/cli/arguments.rs @@ -0,0 +1,284 @@ +// Copyright 2024 The Tari Project +// SPDX-License-Identifier: BSD-3-Clause + +use std::{env, path::PathBuf}; + +use anyhow::anyhow; +use clap::{ + builder::{styling::AnsiColor, Styles}, + Parser, Subcommand, +}; +use convert_case::{Case, Casing}; + +use crate::{ + cli::{ + commands::{create, new}, + config::{Config, TemplateRepository}, + util, + }, + git::repository::GitRepository, + loading, +}; + +const DEFAULT_DATA_FOLDER_NAME: &str = "tari_cli"; +const TEMPLATE_REPOS_FOLDER_NAME: &str = "template_repositories"; +const DEFAULT_CONFIG_FILE_NAME: &str = "tari.config.toml"; + +pub fn cli_styles() -> Styles { + Styles::styled() + .header(AnsiColor::BrightMagenta.on_default()) + .usage(AnsiColor::BrightMagenta.on_default()) + .literal(AnsiColor::BrightGreen.on_default()) + .placeholder(AnsiColor::BrightMagenta.on_default()) + .error(AnsiColor::BrightRed.on_default()) + .invalid(AnsiColor::BrightRed.on_default()) + .valid(AnsiColor::BrightGreen.on_default()) +} + +fn default_base_dir() -> PathBuf { + dirs_next::data_dir() + .unwrap_or_else(|| env::current_dir().unwrap()) + .join(DEFAULT_DATA_FOLDER_NAME) +} + +fn default_target_dir() -> PathBuf { + env::current_dir().unwrap() +} + +fn default_config_file() -> PathBuf { + dirs_next::config_dir() + .unwrap_or_else(|| env::current_dir().unwrap()) + .join(DEFAULT_DATA_FOLDER_NAME) + .join(DEFAULT_CONFIG_FILE_NAME) +} + +pub fn config_override_parser(config_override: &str) -> Result { + if config_override.is_empty() { + return Err(String::from("Override cannot be empty!")); + } + + let splitted: Vec<&str> = config_override.split("=").collect(); + if splitted.len() != 2 { + return Err(String::from("Invalid override!")); + } + let (key, value) = (splitted.first().unwrap(), splitted.get(1).unwrap()); + + if !Config::is_override_key_valid(key) { + return Err(format!("Override key invalid: {}", key)); + } + + Ok(ConfigOverride { + key: key.to_string(), + value: value.to_string(), + }) +} + +pub fn project_name_parser(project_name: &str) -> Result { + Ok(project_name.to_case(Case::Snake)) +} + +#[derive(Clone, Debug)] +pub struct ConfigOverride { + pub key: String, + pub value: String, +} + +#[derive(Clone, Parser, Debug)] +pub struct CommonArguments { + /// Base directory, where all the CLI data will be saved + #[arg(short = 'b', long, value_name = "PATH", default_value = default_base_dir().into_os_string() + )] + base_dir: PathBuf, + + /// Config file location + #[arg(short = 'c', long, value_name = "PATH", default_value = default_config_file().into_os_string() + )] + config_file_path: PathBuf, + + /// Config file overrides + #[arg(short = 'e', long, value_name = "KEY=VALUE", value_parser = config_override_parser + )] + config_overrides: Vec, +} + +#[derive(Clone, Parser)] +#[command(styles = cli_styles())] +#[command( + version, + about = "🚀 Tari DAN CLI 🚀", + long_about = "🚀 Tari DAN CLI 🚀\nHelps you manage the flow of developing Tari templates." +)] +pub struct Cli { + #[clap(flatten)] + args: CommonArguments, + + #[command(subcommand)] + command: Commands, +} + +#[derive(Clone, Subcommand)] +pub enum Commands { + /// Creates a new Tari templates project + Create { + /// Name of the project + #[arg(value_parser = project_name_parser)] + name: String, + + /// (Optional) Selected project template (ID). + /// It will be prompted if not set. + #[arg(short = 't', long)] + template: Option, + + /// Target folder where the new project will be generated + #[arg(long, value_name = "PATH", default_value = default_target_dir().into_os_string() + )] + target: PathBuf, + }, + /// Creates a new Tari wasm template project + New { + /// Name of the project + #[arg(value_parser = project_name_parser)] + name: String, + + /// (Optional) Selected wasm template (ID). + /// It will be prompted if not set. + #[arg(short = 't', long)] + template: Option, + + /// Target folder where the new project will be generated. + #[arg(long, value_name = "PATH", default_value = default_target_dir().into_os_string() + )] + target: PathBuf, + }, +} + +impl Cli { + async fn init_base_dir_and_config(&self) -> anyhow::Result { + // make sure we have all the directories set up + util::create_dir(&self.args.base_dir).await?; + + // create config file dir if not exists + util::create_dir( + &self + .args + .config_file_path + .parent() + .ok_or(anyhow!("Can't find folder of configuration file!"))? + .to_path_buf(), + ) + .await?; + + // loading/creating config + let mut config = if !util::file_exists(&self.args.config_file_path).await? { + let cfg = Config::default(); + cfg.write_to_file(&self.args.config_file_path).await?; + cfg + } else { + match Config::open(&self.args.config_file_path).await { + Ok(cfg) => cfg, + Err(error) => { + println!("Failed to open config file: {error:?}, creating default..."); + let cfg = Config::default(); + cfg.write_to_file(&self.args.config_file_path).await?; + cfg + } + } + }; + + // apply config overrides + for config_override in &self.args.config_overrides { + config.override_data(config_override.key.as_str(), config_override.value.as_str())?; + } + + Ok(config) + } + + async fn refresh_template_repository( + &self, + template_repo: &TemplateRepository, + ) -> anyhow::Result { + util::create_dir(&self.args.base_dir.join(TEMPLATE_REPOS_FOLDER_NAME)).await?; + let repo_url_splitted: Vec<&str> = template_repo.url.split("/").collect(); + let repo_name = repo_url_splitted + .last() + .ok_or(anyhow!("Failed to get repository name from URL!"))?; + let repo_user = repo_url_splitted + .get(repo_url_splitted.len() - 2) + .ok_or(anyhow!("Failed to get repository owner from URL!"))?; + let repo_folder_path = self + .args + .base_dir + .join(TEMPLATE_REPOS_FOLDER_NAME) + .join(repo_user) + .join(repo_name); + let mut repo = GitRepository::new(repo_folder_path.clone()); + + match util::dir_exists(&repo_folder_path).await? { + true => { + repo.load()?; + let current_branch = repo.current_branch_name()?; + if current_branch != template_repo.branch { + repo.pull_changes(Some(template_repo.branch.clone()))?; + } else { + repo.pull_changes(None)?; + } + } + false => { + repo.clone_and_checkout(template_repo.url.as_str(), template_repo.branch.as_str())?; + } + } + + Ok(repo) + } + + pub async fn handle_command(&self) -> anyhow::Result<()> { + // init config and dirs + let config = loading!( + "Init configuration and directories", + self.init_base_dir_and_config().await + )?; + + // refresh templates from provided repositories + let project_template_repo = loading!( + "Refresh project templates repository", + self.refresh_template_repository(&config.project_template_repository) + .await + )?; + let wasm_template_repo = loading!( + "Refresh wasm templates repository", + self.refresh_template_repository(&config.wasm_template_repository) + .await + )?; + + match &self.command { + Commands::Create { + name, + template, + target, + } => { + create::handle( + config, + project_template_repo, + name.as_str(), + template.as_ref(), + target.clone(), + ) + .await + } + Commands::New { + name, + template, + target, + } => { + new::handle( + config, + wasm_template_repo, + name.as_ref(), + template.as_ref(), + target.clone(), + ) + .await + } + } + } +} diff --git a/src/cli/commands/create.rs b/src/cli/commands/create.rs new file mode 100644 index 0000000..13ddce8 --- /dev/null +++ b/src/cli/commands/create.rs @@ -0,0 +1,94 @@ +// Copyright 2024 The Tari Project +// SPDX-License-Identifier: BSD-3-Clause + +use std::path::PathBuf; + +use anyhow::anyhow; +use cargo_generate::{GenerateArgs, TemplatePath}; +use thiserror::Error; + +use crate::{ + cli::{config::Config, util}, + git::repository::GitRepository, + loading, + templates::Collector, +}; + +const PROJECT_TEMPLATE_EXTRA_TEMPLATES_FIELD_NAME: &str = "templates_dir"; + +#[derive(Error, Debug)] +pub enum CreateHandlerError { + #[error("Template not found by name: {0}. Possible values: {1:?}")] + TemplateNotFound(String, Vec), +} + +/// Handle `create` command. +/// It creates a new Tari template development project. +pub async fn handle( + config: Config, + project_template_repo: GitRepository, + name: &str, + project_template: Option<&String>, + target: PathBuf, +) -> anyhow::Result<()> { + // selecting project template + let templates = loading!( + "Collecting available project templates", + Collector::new( + project_template_repo + .local_folder() + .join(config.project_template_repository.folder) + ) + .collect() + .await + )?; + + let template = match project_template { + Some(template_id) => templates + .iter() + .filter(|template| template.id().to_lowercase() == template_id.to_lowercase()) + .last() + .ok_or(CreateHandlerError::TemplateNotFound( + template_id.to_string(), + templates + .iter() + .map(|template| template.id().to_string()) + .collect(), + ))?, + None => &util::cli_select("🔎 Select project template", templates.as_slice())?, + }; + + let template_path = template + .path() + .to_str() + .ok_or(anyhow!("Invalid template path!"))? + .to_string(); + + // generate new project + let generate_args = GenerateArgs { + name: Some(name.to_string()), + destination: Some(target.clone()), + template_path: TemplatePath { + path: Some(template_path), + ..TemplatePath::default() + }, + ..GenerateArgs::default() + }; + loading!( + "Generate new project", + cargo_generate::generate(generate_args) + )?; + + if let Some(templates_dir) = template + .extra() + .get(PROJECT_TEMPLATE_EXTRA_TEMPLATES_FIELD_NAME) + { + util::create_dir(&target.join(&name).join(templates_dir)).await?; + } + + // git init + let mut new_repo = GitRepository::new(target.join(name)); + new_repo.init()?; + + Ok(()) +} diff --git a/src/cli/commands/mod.rs b/src/cli/commands/mod.rs new file mode 100644 index 0000000..e836b63 --- /dev/null +++ b/src/cli/commands/mod.rs @@ -0,0 +1,5 @@ +// Copyright 2024 The Tari Project +// SPDX-License-Identifier: BSD-3-Clause + +pub mod create; +pub mod new; diff --git a/src/cli/commands/new.rs b/src/cli/commands/new.rs new file mode 100644 index 0000000..a05cb49 --- /dev/null +++ b/src/cli/commands/new.rs @@ -0,0 +1,138 @@ +// Copyright 2024 The Tari Project +// SPDX-License-Identifier: BSD-3-Clause + +use std::path::PathBuf; + +use anyhow::anyhow; +use cargo_generate::{GenerateArgs, TemplatePath}; +use cargo_toml::{Manifest, Resolver, Workspace}; +use tokio::fs; + +use crate::{ + cli::{commands::create::CreateHandlerError, config::Config, util}, + git::repository::GitRepository, + loading, + templates::Collector, +}; + +const DEFAULT_TEMPLATES_DIR: &str = "templates"; + +/// Handle `new` command. +/// It creates a new Tari WASM template development project. +pub async fn handle( + config: Config, + wasm_template_repo: GitRepository, + name: &str, + wasm_template: Option<&String>, + target: PathBuf, +) -> anyhow::Result<()> { + // selecting wasm template + let templates = loading!( + "Collecting available WASM templates", + Collector::new( + wasm_template_repo + .local_folder() + .join(config.wasm_template_repository.folder) + ) + .collect() + .await + )?; + + let template = match wasm_template { + Some(template_id) => templates + .iter() + .filter(|template| template.name().to_lowercase() == template_id.to_lowercase()) + .last() + .ok_or(CreateHandlerError::TemplateNotFound( + template_id.to_string(), + templates + .iter() + .map(|template| template.id().to_string()) + .collect(), + ))?, + None => &util::cli_select("🔎 Select WASM template", templates.as_slice())?, + }; + + let template_path = template + .path() + .to_str() + .ok_or(anyhow!("Invalid template path!"))? + .to_string(); + + let cargo_toml_file = target.join("Cargo.toml"); + let is_cargo_project = util::file_exists(&cargo_toml_file).await?; + + // use '/templates' directory if exists + let has_templates_sub_dir = util::dir_exists(&target.join(DEFAULT_TEMPLATES_DIR)).await?; + let target = if has_templates_sub_dir { + target.join(DEFAULT_TEMPLATES_DIR) + } else { + target + }; + + // generate new project + let generate_args = GenerateArgs { + name: Some(name.to_string()), + destination: Some(target.clone()), + define: vec![format!("in_cargo_workspace={}", is_cargo_project)], + template_path: TemplatePath { + path: Some(template_path), + ..TemplatePath::default() + }, + ..GenerateArgs::default() + }; + loading!( + "Generate new project", + cargo_generate::generate(generate_args) + )?; + + // check if target is a cargo project and update Cargo.toml if exists + if is_cargo_project { + let project_name = if has_templates_sub_dir { + format!("{}/{}", DEFAULT_TEMPLATES_DIR, name) + } else { + name.to_string() + }; + loading!( + "Update Cargo.toml", + update_cargo_toml(&cargo_toml_file, project_name).await + )?; + } else { + // git init as new project is a separate one + if let Err(error) = GitRepository::new(target.join(name)).init() { + println!("⚠️ Git repository already initialized: {error:?}"); + } + } + + Ok(()) +} + +/// Updates Cargo.toml to make sure we have the new project in workspace members. +async fn update_cargo_toml(cargo_toml_file: &PathBuf, project_name: String) -> anyhow::Result<()> { + let mut cargo_toml = Manifest::from_path(cargo_toml_file)?; + cargo_toml.workspace = match cargo_toml.workspace { + Some(mut workspace) => { + if workspace.members.contains(&project_name) { + return Err(anyhow!( + "New project generated, but Cargo.toml already contains a workspace member with the same name: {}", + project_name + )); + } else { + workspace.members.push(project_name); + } + Some(workspace) + } + None => Some(Workspace { + members: vec![project_name], + default_members: vec![], + package: None, + exclude: vec![], + metadata: None, + resolver: Some(Resolver::V2), + dependencies: Default::default(), + lints: None, + }), + }; + fs::write(&cargo_toml_file, toml::to_string(&cargo_toml)?).await?; + Ok(()) +} diff --git a/src/cli/config.rs b/src/cli/config.rs new file mode 100644 index 0000000..e6797b7 --- /dev/null +++ b/src/cli/config.rs @@ -0,0 +1,103 @@ +// Copyright 2024 The Tari Project +// SPDX-License-Identifier: BSD-3-Clause + +use std::{path::PathBuf, string::ToString}; + +use anyhow::anyhow; +use serde::{Deserialize, Serialize}; +use tokio::{fs, io::AsyncWriteExt}; + +pub const VALID_OVERRIDE_KEYS: &[&str] = &[ + "project_template_repository.url", + "project_template_repository.branch", + "project_template_repository.folder", + "wasm_template_repository.url", + "wasm_template_repository.branch", + "wasm_template_repository.folder", +]; + +/// CLI configuration. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct Config { + pub project_template_repository: TemplateRepository, + pub wasm_template_repository: TemplateRepository, +} + +/// Repository that holds templates to generate project and Tari templates. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct TemplateRepository { + pub url: String, + pub branch: String, + pub folder: String, +} + +impl Default for Config { + fn default() -> Self { + Self { + project_template_repository: TemplateRepository { + url: "https://github.com/tari-project/wasm-template".to_string(), + branch: "main".to_string(), + folder: "project_templates".to_string(), + }, + wasm_template_repository: TemplateRepository { + url: "https://github.com/tari-project/wasm-template".to_string(), + branch: "main".to_string(), + folder: "wasm_templates".to_string(), + }, + } + } +} + +impl Config { + pub async fn open(path: &PathBuf) -> anyhow::Result { + let content = fs::read_to_string(path).await?; + Ok(toml::from_str::(content.as_str())?) + } + + pub async fn write_to_file(&self, path: &PathBuf) -> anyhow::Result<()> { + let mut file = fs::OpenOptions::new() + .write(true) + .truncate(true) + .create(true) + .open(path) + .await?; + let content = toml::to_string(self)?; + let _ = file.write(content.as_bytes()).await?; + file.flush().await?; + Ok(()) + } + + pub fn is_override_key_valid(key: &str) -> bool { + VALID_OVERRIDE_KEYS.contains(&key) + } + + pub fn override_data(&mut self, key: &str, value: &str) -> anyhow::Result<&mut Self> { + if !Self::is_override_key_valid(key) { + return Err(anyhow!("Invalid key: {}", key)); + } + + match key { + "project_template_repository.url" => { + self.project_template_repository.url = value.to_string(); + } + "project_template_repository.branch" => { + self.project_template_repository.branch = value.to_string(); + } + "project_template_repository.folder" => { + self.project_template_repository.folder = value.to_string(); + } + "wasm_template_repository.url" => { + self.wasm_template_repository.url = value.to_string(); + } + "wasm_template_repository.branch" => { + self.wasm_template_repository.branch = value.to_string(); + } + "wasm_template_repository.folder" => { + self.wasm_template_repository.folder = value.to_string(); + } + _ => {} + } + + Ok(self) + } +} diff --git a/src/cli/macros.rs b/src/cli/macros.rs new file mode 100644 index 0000000..47791bc --- /dev/null +++ b/src/cli/macros.rs @@ -0,0 +1,20 @@ +// Copyright 2024 The Tari Project +// SPDX-License-Identifier: BSD-3-Clause + +#[macro_export] +macro_rules! loading { + ( $text:expr, $call:expr ) => {{ + let mut loader = spinners::Spinner::new(spinners::Spinners::Dots, $text.into()); + let result = match $call { + Ok(res) => { + loader.stop_with_symbol("✅ "); + Ok(res) + } + Err(error) => { + loader.stop_with_symbol("❌ "); + Err(error) + } + }; + result + }}; +} diff --git a/src/cli/mod.rs b/src/cli/mod.rs new file mode 100644 index 0000000..91a32c3 --- /dev/null +++ b/src/cli/mod.rs @@ -0,0 +1,8 @@ +// Copyright 2024 The Tari Project +// SPDX-License-Identifier: BSD-3-Clause + +pub mod arguments; +pub mod commands; +pub mod config; +pub mod macros; +pub mod util; diff --git a/src/cli/util.rs b/src/cli/util.rs new file mode 100644 index 0000000..bef6837 --- /dev/null +++ b/src/cli/util.rs @@ -0,0 +1,33 @@ +// Copyright 2024 The Tari Project +// SPDX-License-Identifier: BSD-3-Clause + +use std::{fs::Metadata, io, path::PathBuf}; + +use dialoguer::FuzzySelect; +use tokio::fs; + +pub async fn create_dir(dir: &PathBuf) -> io::Result<()> { + fs::create_dir_all(dir).await +} + +pub async fn file_exists(file: &PathBuf) -> io::Result { + Ok(fs::try_exists(file).await? && path_metadata(file).await?.is_file()) +} + +pub async fn dir_exists(dir: &PathBuf) -> io::Result { + Ok(fs::try_exists(dir).await? && path_metadata(dir).await?.is_dir()) +} + +pub async fn path_metadata(path: &PathBuf) -> io::Result { + fs::metadata(path).await +} + +pub fn cli_select(prompt: &str, items: &[T]) -> anyhow::Result { + let selection = FuzzySelect::new() + .with_prompt(prompt) + .highlight_matches(true) + .items(items) + .interact()?; + + Ok(items[selection].clone()) +} diff --git a/src/git/mod.rs b/src/git/mod.rs new file mode 100644 index 0000000..5f86f08 --- /dev/null +++ b/src/git/mod.rs @@ -0,0 +1,4 @@ +// Copyright 2024 The Tari Project +// SPDX-License-Identifier: BSD-3-Clause + +pub mod repository; diff --git a/src/git/repository.rs b/src/git/repository.rs new file mode 100644 index 0000000..f7c5019 --- /dev/null +++ b/src/git/repository.rs @@ -0,0 +1,126 @@ +// Copyright 2024 The Tari Project +// SPDX-License-Identifier: BSD-3-Clause + +use std::path::PathBuf; + +use git2::{build::RepoBuilder, Repository}; +use thiserror::Error; + +pub struct GitRepository { + repository: Option, + local_folder: PathBuf, +} + +#[derive(Error, Debug)] +pub enum Error { + #[error("Git2 error: {0}")] + Git2(#[from] git2::Error), + #[error("Git repository is not initialized!")] + RepositoryNotInitialized, + #[error("Invalid branch name!")] + InvalidBranchName, + #[error("Current reference is not a branch!")] + RefIsNotBranch, +} + +pub type GitRepositoryResult = Result; + +impl GitRepository { + pub fn new(local_folder: PathBuf) -> Self { + Self { + repository: None, + local_folder, + } + } + + /// Initializes a git repository in [`local_folder`]. + pub fn init(&mut self) -> GitRepositoryResult<()> { + self.repository = Some(Repository::init(&self.local_folder)?); + Ok(()) + } + + /// Loads an existing git repository from [`local_folder`]. + pub fn load(&mut self) -> GitRepositoryResult<()> { + self.repository = Some(Repository::open(&self.local_folder).map_err(Error::Git2)?); + + Ok(()) + } + + /// Does a clone and checkout operation in [`local_folder`] based on the given repository [`url`] and [`branch`]. + pub fn clone_and_checkout(&mut self, url: &str, branch: &str) -> GitRepositoryResult<()> { + self.repository = Some( + RepoBuilder::new() + .branch(branch) + .clone(url, &self.local_folder) + .map_err(Error::Git2)?, + ); + + Ok(()) + } + + /// Pulling latest changes on an optional branch (default is the current one). + /// Note: this method always force checkout to latest head. + pub fn pull_changes(&self, branch: Option) -> GitRepositoryResult<()> { + let repo = self.repository()?; + let current_branch_name = if let Some(branch) = branch { + branch + } else { + self.current_branch_name()? + }; + let mut remote = repo.find_remote("origin")?; + + // fetch + let mut fetch_opts = git2::FetchOptions::new(); + fetch_opts.download_tags(git2::AutotagOption::All); + remote.fetch(&[current_branch_name.as_str()], Some(&mut fetch_opts), None)?; + let fetch_head = repo.find_reference("FETCH_HEAD")?; + let fetch_commit = repo.reference_to_annotated_commit(&fetch_head)?; + + // pull changes + let refname = format!("refs/heads/{}", current_branch_name); + repo.reference( + &refname, + fetch_commit.id(), + true, + &format!("Setting {} to {}", current_branch_name, fetch_commit.id()), + )?; + repo.set_head(&refname)?; + repo.checkout_head(Some( + git2::build::CheckoutBuilder::default() + .allow_conflicts(false) + .conflict_style_merge(true) + .force(), + ))?; + + Ok(()) + } + + /// Gives back the actual repository if initialized using any of the methods + /// ([`Self::init`], [`Self::load`] or [`Self::clone_and_checkout`]). + fn repository(&self) -> GitRepositoryResult<&Repository> { + if self.repository.is_none() { + return Err(Error::RepositoryNotInitialized); + } + + Ok(self.repository.as_ref().unwrap()) + } + + /// Returns current branch name. + pub fn current_branch_name(&self) -> GitRepositoryResult { + let repo = self.repository()?; + let head = repo.head()?; + if head.is_branch() { + if let Some(name) = head.name() { + Ok(name.to_string().replace("refs/heads/", "")) + } else { + Err(Error::InvalidBranchName) + } + } else { + Err(Error::RefIsNotBranch) + } + } + + pub fn local_folder(&self) -> &PathBuf { + &self.local_folder + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..e25c42c --- /dev/null +++ b/src/main.rs @@ -0,0 +1,15 @@ +// Copyright 2024 The Tari Project +// SPDX-License-Identifier: BSD-3-Clause + +use clap::Parser; + +use crate::cli::arguments::Cli; + +mod cli; +mod git; +mod templates; + +#[tokio::main] +async fn main() -> anyhow::Result<()> { + Cli::parse().handle_command().await +} diff --git a/src/templates/collector.rs b/src/templates/collector.rs new file mode 100644 index 0000000..de8fd4f --- /dev/null +++ b/src/templates/collector.rs @@ -0,0 +1,89 @@ +// Copyright 2024 The Tari Project +// SPDX-License-Identifier: BSD-3-Clause + +use std::path::PathBuf; + +use convert_case::{Case, Casing}; +use thiserror::Error; +use tokio::{fs, io}; + +use crate::templates::{Template, TemplateFile}; + +const TEMPLATE_DESCRIPTOR_FILE_NAME: &str = "template.toml"; + +#[derive(Error, Debug)] +pub enum Error { + #[error("Git2 error: {0}")] + IO(#[from] io::Error), + #[error("Failed to deserialize TOML: {0}")] + TomlDeserialize(#[from] toml::de::Error), +} + +pub type CollectorResult = Result; + +pub struct Collector { + local_folder: PathBuf, +} + +impl Collector { + pub fn new(local_folder: PathBuf) -> Self { + Self { local_folder } + } + + /// Collect and return all templates from [`Collector::local_folder`]. + pub async fn collect(&self) -> CollectorResult> { + let mut result = vec![]; + Self::collect_templates(&self.local_folder, &mut result).await?; + + Ok(result) + } + + /// Collecting recursively all the templates from a starting folder `dir`. + /// All the results will be pushed into `result`. + async fn collect_templates(dir: &PathBuf, result: &mut Vec