diff --git a/.cargo/config b/.cargo/config new file mode 100644 index 00000000..3eec48fd --- /dev/null +++ b/.cargo/config @@ -0,0 +1,11 @@ +[target.armv7-unknown-linux-gnueabihf] +linker = "arm-linux-gnueabihf-gcc" + +[target.armv7-unknown-linux-musleabihf] +linker = "arm-linux-musleabihf-gcc" + +[target.aarch64-unknown-linux-gnu] +linker = "aarch64-linux-gnu-gcc" + +[target.aarch64-unknown-linux-musl] +linker = "aarch64-linux-musl-gcc" diff --git a/.config/nextest.toml b/.config/nextest.toml deleted file mode 100644 index a5c8a4cf..00000000 --- a/.config/nextest.toml +++ /dev/null @@ -1,6 +0,0 @@ -[test-groups] -rate-limited = { max-threads = 1 } - -[[profile.default.overrides]] -filter = 'test(rate_limited::)' -test-group = 'rate-limited' diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index 377bc158..00000000 --- a/.editorconfig +++ /dev/null @@ -1,15 +0,0 @@ -root = true - -[*] -indent_style = space -indent_size = 4 -end_of_line = lf -charset = utf-8 -trim_trailing_whitespace = true -insert_final_newline = true - -[tests/snapshots/*] -trim_trailing_whitespace = false - -[*.{cff,yml}] -indent_size = 2 diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml deleted file mode 100644 index 632f801f..00000000 --- a/.github/FUNDING.yml +++ /dev/null @@ -1 +0,0 @@ -github: [NobodyXu] diff --git a/.github/actions/just-setup/action.yml b/.github/actions/just-setup/action.yml deleted file mode 100644 index f57a949f..00000000 --- a/.github/actions/just-setup/action.yml +++ /dev/null @@ -1,156 +0,0 @@ -name: Setup tools and cache -inputs: - tools: - description: Extra tools - required: false - default: "" - indexcache: - description: Enable index cache - required: true - default: true - type: boolean - buildcache: - description: Enable build cache - required: true - default: true - type: boolean - -runs: - using: composite - steps: - - name: Enable macOS developer mode for better - if: runner.os == 'macOS' - run: sudo spctl developer-mode enable-terminal - shell: bash - - - name: Enable transparent huge page - if: runner.os == 'Linux' - run: echo madvise | sudo tee /sys/kernel/mm/transparent_hugepage/enabled - shell: bash - - - name: Configure jemalloc (used by rustc) to use transparent huge page - if: runner.os == 'Linux' - run: echo "MALLOC_CONF=thp:always,metadata_thp:always" >> "$GITHUB_ENV" - shell: bash - - - name: Exclude workspace and cargo/rustup home from windows defender - if: runner.os == 'Windows' - run: | - Add-MpPreference -ExclusionPath '${{ github.workspace }}' - shell: pwsh - - - name: Add just to tools to install - run: echo "tools=just" >>"$GITHUB_ENV" - shell: bash - - - name: Add inputs.tools to tools to install - if: inputs.tools != '' - env: - inputs_tools: ${{ inputs.tools }} - run: echo "tools=$tools,$inputs_tools" >>"$GITHUB_ENV" - shell: bash - - - name: Determine native target - run: | - if [ "$RUNNER_OS" = "Linux" ]; then RUNNER_TARGET=x86_64-unknown-linux-gnu; fi - if [ "$RUNNER_OS" = "macOS" ]; then RUNNER_TARGET=aarch64-apple-darwin; fi - if [ "$RUNNER_OS" = "Windows" ]; then RUNNER_TARGET=x86_64-pc-windows-msvc; fi - echo "RUNNER_TARGET=$RUNNER_TARGET" >>"$GITHUB_ENV" - shell: bash - - - name: Install tools - uses: taiki-e/install-action@v2 - with: - tool: ${{ env.tools }} - env: - CARGO_BUILD_TARGET: ${{ env.RUNNER_TARGET }} - - - name: Install rust toolchains - run: just toolchain - shell: bash - - - name: rustc version - run: rustc -vV - shell: bash - - - name: Retrieve RUSTFLAGS for caching - if: inputs.indexcache || inputs.buildcache - id: retrieve-rustflags - run: | - if [ -n "${{ inputs.buildcache }}" ]; then - echo RUSTFLAGS="$(just print-rustflags)" >> "$GITHUB_OUTPUT" - else - echo RUSTFLAGS= >> "$GITHUB_OUTPUT" - fi - shell: bash - - - run: just ci-install-deps - shell: bash - - - if: inputs.indexcache || inputs.buildcache - uses: Swatinem/rust-cache@v2 - with: - env-vars: "CARGO CC CFLAGS CXX CMAKE RUST JUST" - env: - RUSTFLAGS: ${{ steps.retrieve-rustflags.outputs.RUSTFLAGS }} - - - name: Find zig location and create symlink to it in ~/.local/bin - if: env.JUST_USE_CARGO_ZIGBUILD - run: | - python_package_path=$(python3 -m site --user-site) - ln -s "${python_package_path}/ziglang/zig" "$HOME/.local/bin/zig" - shell: bash - - - name: Calculate zig cache key - if: env.JUST_USE_CARGO_ZIGBUILD - run: | - ZIG_VERSION=$(zig version) - SYS_CRATE_HASHSUM=$(cargo tree --all-features --prefix none --no-dedupe --target "$CARGO_BUILD_TARGET" | grep -e '-sys' -e '^ring' | sort -u | sha1sum | sed 's/[ -]*//g') - PREFIX=v0-${JOB_ID}-zig-${ZIG_VERSION}-${CARGO_BUILD_TARGET}- - echo "ZIG_CACHE_KEY=${PREFIX}${SYS_CRATE_HASHSUM}" >> "$GITHUB_ENV" - echo -e "ZIG_CACHE_RESTORE_KEY=$PREFIX" >> "$GITHUB_ENV" - shell: bash - env: - RUSTFLAGS: ${{ steps.retrieve-rustflags.outputs.RUSTFLAGS }} - JOB_ID: ${{ github.job }} - - - name: Get zig global cache dir - if: env.JUST_USE_CARGO_ZIGBUILD - id: zig_global_cache_dir_path - run: | - cache_dir=$(zig env | jq -r '.global_cache_dir') - echo "cache_dir=$cache_dir" >> "$GITHUB_OUTPUT" - shell: bash - - - name: Cache zig compilation - if: env.JUST_USE_CARGO_ZIGBUILD - uses: actions/cache@v4 - with: - path: ${{ steps.zig_global_cache_dir_path.outputs.cache_dir }} - key: ${{ env.ZIG_CACHE_KEY }} - restore-keys: | - ${{ env.ZIG_CACHE_RESTORE_KEY }} - - - name: Cache make compiled - if: runner.os == 'macOS' - id: cache-make - uses: actions/cache@v4 - with: - path: /usr/local/bin/make - key: ${{ runner.os }}-make-4.4.1 - - - name: Build and use make 4.4.1 on macOS, since cc requires make >=4.3 - if: runner.os == 'macOS' && steps.cache-make.outputs.cache-hit != 'true' - run: | - curl "https://ftp.gnu.org/gnu/make/make-${MAKE_VERSION}.tar.gz" | tar xz - pushd "make-${MAKE_VERSION}" - ./configure - make -j 4 - popd - cp -p "make-${MAKE_VERSION}/make" /usr/local/bin - env: - MAKE_VERSION: 4.4.1 - shell: bash - - - run: make -v - shell: bash diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 8239740f..5e4980b5 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -2,20 +2,8 @@ version: 2 updates: - - package-ecosystem: "github-actions" - # Workflow files stored in the - # default location of `.github/workflows` - directory: "/" - schedule: - interval: "daily" - package-ecosystem: "cargo" directory: "/" schedule: - # Only run dependabot after all compatible upgrades and transitive deps - # are done to reduce amount of PRs opened. interval: "weekly" - day: "saturday" - groups: - deps: - patterns: - - "*" + diff --git a/.github/scripts/ephemeral-crate.sh b/.github/scripts/ephemeral-crate.sh deleted file mode 100755 index 3636ca71..00000000 --- a/.github/scripts/ephemeral-crate.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env bash - -set -euxo pipefail - -cat >> crates/bin/Cargo.toml <> age.key <<< "$AGE_KEY_SECRET" - -set -x - -rage --decrypt --identity age.key --output minisign.key minisign.key.age - -ts=$(node -e 'console.log((new Date).toISOString())') -git=$(git rev-parse HEAD) -comment="gh=$GITHUB_REPOSITORY git=$git ts=$ts run=$GITHUB_RUN_ID" - -for file in "$@"; do - rsign sign -W -s minisign.key -x "$file.sig" -t "$comment" "$file" -done - -rm age.key minisign.key diff --git a/.github/scripts/release-pr-template.ejs b/.github/scripts/release-pr-template.ejs deleted file mode 100644 index d94c008b..00000000 --- a/.github/scripts/release-pr-template.ejs +++ /dev/null @@ -1,41 +0,0 @@ -<% if (pr.metaComment) { %> - -<% } %> - -This is a release PR for **<%= crate.name %>** version **<%= version.actual %>**<% - if (version.actual != version.desired) { -%> (performing a <%= version.desired %> bump).<% - } else { -%>.<% - } -%> - -**Use squash merge.** - -<% if (crate.name == "cargo-binstall") { %> -Upon merging, this will automatically create the tag `v<%= version.actual %>`, build the CLI, -create a GitHub release with the release notes below -<% } else { %> -Upon merging, this will create the tag `<%= crate.name %>-v<%= version.actual %>` -<% } %>, and CI will publish to crates.io on merge of this PR. - -**To trigger builds initially, close and then immediately re-open this PR once.** - -<% if (pr.releaseNotes) { %> ---- - -_Edit release notes into the section below:_ - - -### Release notes - -_Binstall is a tool to fetch and install Rust-based executables as binaries. It aims to be a drop-in replacement for `cargo install` in most cases. Install it today with `cargo install cargo-binstall`, from the binaries below, or if you already have it, upgrade with `cargo binstall cargo-binstall`._ - -#### In this release: - -- - -#### Other changes: - -- -<% } %> diff --git a/.github/scripts/test-detect-targets-musl.sh b/.github/scripts/test-detect-targets-musl.sh deleted file mode 100755 index e8f404b1..00000000 --- a/.github/scripts/test-detect-targets-musl.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash - -set -exuo pipefail - -TARGET=${1?} - -[ "$(detect-targets)" = "$TARGET" ] - -apk update -apk add gcompat - -ls -lsha /lib - -GNU_TARGET=${TARGET//musl/gnu} - -[ "$(detect-targets)" = "$(printf '%s\n%s' "$GNU_TARGET" "$TARGET")" ] - -echo diff --git a/.github/workflows/cache-cleanup.yml b/.github/workflows/cache-cleanup.yml deleted file mode 100644 index 92f430b8..00000000 --- a/.github/workflows/cache-cleanup.yml +++ /dev/null @@ -1,40 +0,0 @@ -name: Cleanup caches for closed PRs - -on: - # Run twice every day to remove the cache so that the caches from the closed prs - # are removed. - schedule: - - cron: "0 17 * * *" - - cron: "30 18 * * *" - workflow_dispatch: - -jobs: - cleanup: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Cleanup - run: | - set -euxo pipefail - - gh extension install actions/gh-actions-cache - - export REPO="${{ github.repository }}" - - # Setting this to not fail the workflow while deleting cache keys. - set +e - - # Remove pull requests cache, since they cannot be reused - gh pr list --state closed -L 20 --json number --jq '.[]|.number' | ( - while IFS='$\n' read -r closed_pr; do - BRANCH="refs/pull/${closed_pr}/merge" ./cleanup-cache.sh - done - ) - # Remove merge queue cache, since they cannot be reused - gh actions-cache list -L 100 | cut -f 3 | (grep 'gh-readonly-queue' || true) | sort -u | ( - while IFS='$\n' read -r branch; do - BRANCH="$branch" ./cleanup-cache.sh - done - ) - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index 42774319..00000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,398 +0,0 @@ -name: CI - -on: - workflow_dispatch: - workflow_call: - inputs: - additional_key: - required: true - type: string - default: "" - merge_group: - pull_request: - types: - - opened - - reopened - - synchronize - push: - branches: - - main - paths: - - 'Cargo.lock' - - 'Cargo.toml' - - '**/Cargo.toml' - - -concurrency: - group: ${{ github.workflow }}-${{ github.ref || github.event.pull_request.number || github.sha }}-${{ inputs.additional_key }} - cancel-in-progress: true - -env: - CARGO_TERM_COLOR: always - CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse - JUST_ENABLE_H3: true - CARGO_PROFILE_RELEASE_CODEGEN_UNITS: 4 - CARGO_PROFILE_DEV_CODEGEN_UNITS: 4 - CARGO_PROFILE_CHECK_ONLY_CODEGEN_UNITS: 4 - -jobs: - changed-files: - runs-on: ubuntu-latest - name: Test changed-files - permissions: - pull-requests: read - - outputs: - crates_changed: ${{ steps.list-changed-files.outputs.crates_changed }} - has_detect_target_changed: ${{ steps.list-changed-files.outputs.has_detect_target_changed }} - - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Get changed files - id: changed-files - uses: tj-actions/changed-files@6f67ee9ac810f0192ea7b3d2086406f97847bcf9 - with: - dir_names: true - dir_names_exclude_current_dir: true - dir_names_max_depth: 2 - - - name: List all changed files - id: list-changed-files - env: - ALL_CHANGED_FILES: ${{ steps.changed-files.outputs.all_changed_files }} - run: | - set -euxo pipefail - crates_changed="$(for file in $ALL_CHANGED_FILES; do echo $file; done | grep crates | cut -d / -f 2 | sed 's/^bin$/cargo-binstall/' || echo)" - has_detect_target_changed="$(echo "$crates_changed" | grep -q detect-targets && echo true || echo false)" - echo "crates_changed=${crates_changed//$'\n'/ }" | tee -a "$GITHUB_OUTPUT" - echo "has_detect_target_changed=$has_detect_target_changed" | tee -a "$GITHUB_OUTPUT" - - unit-tests: - needs: changed-files - runs-on: ubuntu-latest - env: - CARGO_BUILD_TARGET: x86_64-unknown-linux-gnu - - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/just-setup - env: - # just-setup use binstall to install sccache, - # which works better when we provide it with GITHUB_TOKEN. - GITHUB_TOKEN: ${{ secrets.CI_RELEASE_TEST_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - with: - tools: cargo-nextest - - - name: Decide crates to test - shell: bash - env: - CRATES_CHANGED: ${{ needs.changed-files.outputs.crates_changed }} - run: | - ARGS="" - for crate in $CRATES_CHANGED; do - ARGS="$ARGS -p $crate" - done - echo "CARGO_NEXTEST_ADDITIONAL_ARGS=$ARGS" | tee -a "$GITHUB_ENV" - - - run: just unit-tests - if: env.CARGO_NEXTEST_ADDITIONAL_ARGS != '' - env: - GITHUB_TOKEN: ${{ secrets.CI_TEST_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - CI_UNIT_TEST_GITHUB_TOKEN: ${{ secrets.CI_UNIT_TEST_GITHUB_TOKEN }} - - e2e-tests: - if: github.event_name != 'pull_request' - strategy: - fail-fast: false - matrix: - include: - - target: aarch64-apple-darwin - os: macos-latest - - target: x86_64-unknown-linux-gnu - os: ubuntu-latest - - target: x86_64-pc-windows-msvc - os: windows-latest - - runs-on: ${{ matrix.os }} - env: - CARGO_BUILD_TARGET: ${{ matrix.target }} - - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/just-setup - env: - # just-setup use binstall to install sccache, - # which works better when we provide it with GITHUB_TOKEN. - GITHUB_TOKEN: ${{ secrets.CI_RELEASE_TEST_GITHUB_TOKEN }} - - - run: just build - - run: just e2e-tests - env: - GITHUB_TOKEN: ${{ secrets.CI_TEST_GITHUB_TOKEN }} - - cross-check: - strategy: - fail-fast: false - matrix: - include: - - target: armv7-unknown-linux-musleabihf - os: ubuntu-latest - - target: armv7-unknown-linux-gnueabihf - os: ubuntu-latest - - target: aarch64-unknown-linux-musl - os: ubuntu-latest - - target: aarch64-unknown-linux-gnu - os: ubuntu-latest - - target: x86_64-unknown-linux-musl - os: ubuntu-latest - - target: x86_64-apple-darwin - os: macos-latest - - target: aarch64-pc-windows-msvc - os: windows-latest - runs-on: ${{ matrix.os }} - env: - CARGO_BUILD_TARGET: ${{ matrix.target }} - steps: - - uses: actions/checkout@v4 - - - name: Enable cargo-zigbuild - if: matrix.os == 'ubuntu-latest' - run: echo JUST_USE_CARGO_ZIGBUILD=true >> "$GITHUB_ENV" - - - uses: ./.github/actions/just-setup - with: - tools: cargo-hack@0.6.10 - env: - # just-setup use binstall to install sccache, - # which works better when we provide it with GITHUB_TOKEN. - GITHUB_TOKEN: ${{ secrets.CI_RELEASE_TEST_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - - - run: just avoid-dev-deps - - run: just check - - lint: - strategy: - fail-fast: false - matrix: - include: - - target: x86_64-apple-darwin - os: macos-latest - - target: x86_64-unknown-linux-gnu - os: ubuntu-latest - - target: x86_64-pc-windows-msvc - os: windows-latest - - runs-on: ${{ matrix.os }} - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/just-setup - env: - # just-setup use binstall to install sccache, - # which works better when we provide it with GITHUB_TOKEN. - GITHUB_TOKEN: ${{ secrets.CI_RELEASE_TEST_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - - - run: just toolchain rustfmt,clippy - - run: just avoid-dev-deps - - run: just lint - - pr-info: - outputs: - is-release: ${{ steps.meta.outputs.is-release }} - crate: ${{ steps.meta.outputs.crates-names }} - - runs-on: ubuntu-latest - steps: - - id: meta - if: github.event_name == 'pull_request' - uses: cargo-bins/release-meta@v1 - with: - event-data: ${{ toJSON(github.event) }} - extract-notes-under: "### Release notes" - - release-dry-run: - needs: pr-info - uses: ./.github/workflows/release-cli.yml - if: github.event_name != 'pull_request' - secrets: inherit - with: - info: | - { - "is-release": false, - "crate": "${{ needs.pr-info.outputs.crate }}", - "version": "0.0.0", - "notes": "" - } - CARGO_PROFILE_RELEASE_LTO: no - CARGO_PROFILE_RELEASE_CODEGEN_UNITS: 4 - - detect-targets-build: - needs: changed-files - if: needs.changed-files.outputs.has_detect_target_changed == 'true' - runs-on: ubuntu-latest - env: - CARGO_BUILD_TARGET: x86_64-unknown-linux-musl - steps: - - uses: actions/checkout@v4 - - name: Install ${{ env.CARGO_BUILD_TARGET }} target - run: | - rustup target add $CARGO_BUILD_TARGET - pip3 install -r zigbuild-requirements.txt - - uses: Swatinem/rust-cache@v2 - with: - cache-all-crates: true - - name: Build detect-targets - run: | - cargo zigbuild --features cli-logging --target $CARGO_BUILD_TARGET - # Set working directory here, otherwise `cargo-zigbuild` would download - # and build quite a few unused dependencies. - working-directory: crates/detect-targets - - uses: actions/upload-artifact@v4 - with: - name: detect-targets - path: target/${{ env.CARGO_BUILD_TARGET }}/debug/detect-targets - - detect-targets-alpine-test: - runs-on: ubuntu-latest - needs: - - detect-targets-build - - changed-files - if: needs.changed-files.outputs.has_detect_target_changed == 'true' - steps: - - uses: actions/checkout@v4 - - - uses: actions/download-artifact@v4 - with: - name: detect-targets - - run: chmod +x detect-targets - - - name: Run test in alpine - run: | - docker run --rm \ - --mount src="$PWD/detect-targets",dst=/usr/local/bin/detect-targets,type=bind \ - --mount src="$PWD/.github/scripts/test-detect-targets-musl.sh",dst=/usr/local/bin/test.sh,type=bind \ - alpine /bin/ash -c "apk update && apk add bash && test.sh x86_64-unknown-linux-musl" - - detect-targets-ubuntu-test: - needs: - - detect-targets-build - - changed-files - if: needs.changed-files.outputs.has_detect_target_changed == 'true' - strategy: - fail-fast: false - matrix: - os: - - ubuntu-20.04 - - ubuntu-latest - runs-on: ${{ matrix.os }} - steps: - - uses: actions/download-artifact@v4 - with: - name: detect-targets - - run: chmod +x detect-targets - - - name: Run test in ubuntu - run: | - set -exuo pipefail - [ "$(./detect-targets)" = "$(printf 'x86_64-unknown-linux-gnu\nx86_64-unknown-linux-musl')" ] - - detect-targets-more-glibc-test: - needs: - - detect-targets-build - - changed-files - if: needs.changed-files.outputs.has_detect_target_changed == 'true' - strategy: - fail-fast: false - matrix: - container: - - archlinux - - fedora:37 - - fedora:38 - - fedora:39 - - fedora - runs-on: ubuntu-latest - steps: - - uses: actions/download-artifact@v4 - with: - name: detect-targets - - run: chmod +x detect-targets - - - name: Run test - run: | - set -exuo pipefail - [ "$(docker run --rm \ - --mount src="$PWD/detect-targets",dst=/usr/local/bin/detect-targets,type=bind \ - ${{ matrix.container }} detect-targets )" = "$(printf 'x86_64-unknown-linux-gnu\nx86_64-unknown-linux-musl')" ] - - detect-targets-nix-test: - needs: - - detect-targets-build - - changed-files - if: needs.changed-files.outputs.has_detect_target_changed == 'true' - runs-on: ubuntu-latest - steps: - - uses: actions/download-artifact@v4 - with: - name: detect-targets - - run: chmod +x detect-targets - - - name: Run test - run: | - set -exuo pipefail - [ "$(docker run --rm \ - --mount src="$PWD/detect-targets",dst=/detect-targets,type=bind \ - nixos/nix /detect-targets )" = x86_64-unknown-linux-musl ] - - detect-targets-android-check: - needs: changed-files - if: needs.changed-files.outputs.has_detect_target_changed == 'true' - strategy: - fail-fast: false - matrix: - include: - - target: aarch64-linux-android - - runs-on: ubuntu-latest - env: - CARGO_BUILD_TARGET: ${{ matrix.target }} - - steps: - - uses: actions/checkout@v4 - - - name: Add ${{ matrix.target }} - run: rustup target add ${{ matrix.target }} - - - uses: Swatinem/rust-cache@v2 - with: - cache-all-crates: true - - name: Build detect-targets - run: | - cargo check --target ${{ matrix.target }} - # Set working directory here, otherwise `cargo-check` would download - # and build quite a few unused dependencies. - working-directory: crates/detect-targets - - # Dummy job to have a stable name for the "all tests pass" requirement - tests-pass: - name: Tests pass - needs: - - unit-tests - - e2e-tests - - cross-check - - lint - - release-dry-run - - detect-targets-build - - detect-targets-alpine-test - - detect-targets-ubuntu-test - - detect-targets-more-glibc-test - - detect-targets-nix-test - - detect-targets-android-check - if: always() # always run even if dependencies fail - runs-on: ubuntu-latest - steps: - # fail if ANY dependency has failed or cancelled - - if: "contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled')" - run: exit 1 - - run: exit 0 diff --git a/.github/workflows/gh-action.yml b/.github/workflows/gh-action.yml deleted file mode 100644 index 5d2280a2..00000000 --- a/.github/workflows/gh-action.yml +++ /dev/null @@ -1,40 +0,0 @@ -name: Test GitHub Action installer -on: - merge_group: - pull_request: - paths: - - install-from-binstall-release.ps1 - - install-from-binstall-release.sh - - action.yml - push: - branches: - - main - paths: - - install-from-binstall-release.ps1 - - install-from-binstall-release.sh - - action.yml - -jobs: - test-gha-installer: - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [macos-latest, ubuntu-latest, windows-latest] - steps: - - uses: actions/checkout@v4 - - - name: Install cargo-binstall - uses: ./ # uses action.yml from root of the repo - env: - GITHUB_TOKEN: ${{ secrets.CI_RELEASE_TEST_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - - - name: Verify successful installation - display cargo-binstall's help - run: cargo binstall --help - - - name: Verify successful installation - install example binary using cargo-binstall - run: cargo binstall -y ripgrep - env: - GITHUB_TOKEN: ${{ secrets.CI_RELEASE_TEST_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - - - name: Verify successful installation - display help of installed binary - run: rg --help diff --git a/.github/workflows/install-script.yml b/.github/workflows/install-script.yml deleted file mode 100644 index b6ac7095..00000000 --- a/.github/workflows/install-script.yml +++ /dev/null @@ -1,154 +0,0 @@ -name: Test install-script - -on: - merge_group: - pull_request: - types: - - opened - - reopened - - synchronize - paths: - - install-from-binstall-release.ps1 - - install-from-binstall-release.sh - - .github/workflows/install-script.yml - push: - branches: - - main - paths: - - install-from-binstall-release.ps1 - - install-from-binstall-release.sh - - .github/workflows/install-script.yml - -concurrency: - group: ${{ github.workflow }}-${{ github.ref || github.event.pull_request.number || github.sha }} - cancel-in-progress: true - -env: - CARGO_TERM_COLOR: always - -jobs: - unix: - strategy: - fail-fast: false - matrix: - os: [macos-latest, ubuntu-latest] - set_cargo_home: [t, f] - set_binstall_version: ['no', 'with-v', 'without-v'] - - runs-on: ${{ matrix.os }} - - steps: - - uses: actions/checkout@v4 - - - name: Set `CARGO_HOME` - if: matrix.set_cargo_home == 't' - run: | - CARGO_HOME="$(mktemp -d 2>/dev/null || mktemp -d -t 'cargo-home')" - mkdir -p "${CARGO_HOME}/bin" - echo "CARGO_HOME=$CARGO_HOME" >> "$GITHUB_ENV" - - - name: Set `BINSTALL_VERSION` - if: matrix.set_binstall_version != 'no' - env: - STRIP_V: ${{ matrix.set_binstall_version }} - GH_TOKEN: ${{ github.token }} - run: | - # fetch most recent release tag. - BINSTALL_VERSION="$(gh release list --json name --jq '[.[] | select(.name | startswith("v")) | .name] | first')" - if [[ $STRIP_V == 'without-v' ]]; then BINSTALL_VERSION="${BINSTALL_VERSION#v*}"; fi - echo "Setting BINSTALL_VERSION=$BINSTALL_VERSION" - echo "BINSTALL_VERSION=$BINSTALL_VERSION" >> "$GITHUB_ENV" - - - name: Install `cargo-binstall` using scripts - run: ./install-from-binstall-release.sh - env: - GITHUB_TOKEN: ${{ secrets.CI_RELEASE_TEST_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - - - name: Verify `cargo-binstall` installation - run: | - which cargo-binstall - cargo binstall -vV - - windows: - strategy: - fail-fast: false - matrix: - set_cargo_home: [t, f] - set_binstall_version: ['no', 'with-v', 'without-v'] - - runs-on: windows-latest - - steps: - - uses: actions/checkout@v4 - - - name: Set `CARGO_HOME` - if: matrix.set_cargo_home == 't' - shell: bash - run: | - CARGO_HOME="$(mktemp -d 2>/dev/null || mktemp -d -t 'cargo-home')" - mkdir -p "${CARGO_HOME}/bin" - echo "CARGO_HOME=$CARGO_HOME" >> "$GITHUB_ENV" - - - name: Set `BINSTALL_VERSION` - if: matrix.set_binstall_version != 'no' - shell: bash - env: - GH_TOKEN: ${{ github.token }} - STRIP_V: ${{ matrix.set_binstall_version }} - run: | - # fetch most recent release name. - BINSTALL_VERSION="$(gh release list --json name --jq '[.[] | select(.name | startswith("v")) | .name] | first')" - if [[ $STRIP_V == 'without-v' ]]; then BINSTALL_VERSION="${BINSTALL_VERSION#v*}"; fi - echo "Setting BINSTALL_VERSION=$BINSTALL_VERSION" - echo "BINSTALL_VERSION=$BINSTALL_VERSION" >> "$GITHUB_ENV" - - - name: Install `cargo-binstall` using scripts - run: ./install-from-binstall-release.ps1 - env: - GITHUB_TOKEN: ${{ secrets.CI_RELEASE_TEST_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - - - name: Verify `cargo-binstall` installation - run: cargo binstall -vV - - windows-bash: - strategy: - fail-fast: false - matrix: - set_cargo_home: [t, f] - set_binstall_version: ['no', 'with-v', 'without-v'] - - runs-on: windows-latest - - steps: - - uses: actions/checkout@v4 - - - name: Set `CARGO_HOME` - if: matrix.set_cargo_home == 't' - shell: bash - run: | - CARGO_HOME="$(mktemp -d 2>/dev/null || mktemp -d -t 'cargo-home')" - mkdir -p "${CARGO_HOME}/bin" - echo "CARGO_HOME=$CARGO_HOME" >> "$GITHUB_ENV" - - - name: Set `BINSTALL_VERSION` - if: matrix.set_binstall_version != 'no' - shell: bash - env: - GH_TOKEN: ${{ github.token }} - STRIP_V: ${{ matrix.set_binstall_version }} - run: | - # fetch most recent release name. - BINSTALL_VERSION="$(gh release list --json name --jq '[.[] | select(.name | startswith("v")) | .name] | first')" - if [[ $STRIP_V == 'without-v' ]]; then BINSTALL_VERSION="${BINSTALL_VERSION#v*}"; fi - echo "Setting BINSTALL_VERSION=$BINSTALL_VERSION" - echo "BINSTALL_VERSION=$BINSTALL_VERSION" >> "$GITHUB_ENV" - - - name: Install `cargo-binstall` using scripts - shell: bash - run: ./install-from-binstall-release.sh - env: - GITHUB_TOKEN: ${{ secrets.CI_RELEASE_TEST_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - - - name: Verify `cargo-binstall` installation - shell: bash - run: cargo binstall -vV diff --git a/.github/workflows/release-cli.yml b/.github/workflows/release-cli.yml deleted file mode 100644 index 39be23c1..00000000 --- a/.github/workflows/release-cli.yml +++ /dev/null @@ -1,135 +0,0 @@ -name: Release CLI -on: - workflow_call: - inputs: - info: - description: "The release metadata JSON" - required: true - type: string - CARGO_PROFILE_RELEASE_LTO: - description: "Used to speed up CI" - required: false - type: string - CARGO_PROFILE_RELEASE_CODEGEN_UNITS: - description: "Used to speed up CI" - required: false - type: string - -jobs: - tag: - permissions: - contents: write - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - if: fromJSON(inputs.info).is-release == 'true' - name: Push cli release tag - uses: mathieudutour/github-tag-action@v6.2 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - custom_tag: ${{ fromJSON(inputs.info).version }} - tag_prefix: v - - keygen: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - uses: cargo-bins/cargo-binstall@main - env: - GITHUB_TOKEN: ${{ secrets.CI_RELEASE_TEST_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - - name: Install binaries required - run: cargo binstall -y --force rsign2 rage - env: - GITHUB_TOKEN: ${{ secrets.CI_RELEASE_TEST_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - - - name: Create ephemeral keypair - id: keypair - env: - AGE_KEY_PUBLIC: ${{ vars.AGE_KEY_PUBLIC }} - run: .github/scripts/ephemeral-gen.sh - - uses: actions/upload-artifact@v4 - with: - name: minisign.pub - path: minisign.pub - - uses: actions/upload-artifact@v4 - with: - name: minisign.key.age - path: minisign.key.age - retention-days: 1 - - name: Check that key can be decrypted - env: - AGE_KEY_SECRET: ${{ secrets.AGE_KEY_SECRET }} - shell: bash - run: .github/scripts/ephemeral-sign.sh minisign.pub - - package: - needs: - - tag - - keygen - uses: ./.github/workflows/release-packages.yml - secrets: inherit - with: - publish: ${{ inputs.info }} - CARGO_PROFILE_RELEASE_LTO: ${{ inputs.CARGO_PROFILE_RELEASE_LTO }} - CARGO_PROFILE_RELEASE_CODEGEN_UNITS: ${{ inputs.CARGO_PROFILE_RELEASE_CODEGEN_UNITS }} - - publish: - needs: package - permissions: - contents: write - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/download-artifact@v4 - with: - name: minisign.pub - - - run: rustup toolchain install stable --no-self-update --profile minimal - - - run: .github/scripts/ephemeral-crate.sh - - - if: fromJSON(inputs.info).is-release != 'true' && fromJSON(inputs.info).crate != '' - name: DRY-RUN Publish to crates.io - env: - crate: ${{ fromJSON(inputs.info).crate }} - CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} - run: cargo publish --dry-run -p "$crate" --allow-dirty --no-default-features - - if: fromJSON(inputs.info).is-release != 'true' && fromJSON(inputs.info).crate != '' - name: Upload crate package as artifact - uses: actions/upload-artifact@v4 - with: - name: crate-package - path: target/package/*.crate - - - if: fromJSON(inputs.info).is-release == 'true' - name: Publish to crates.io - env: - crate: ${{ fromJSON(inputs.info).crate }} - CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} - run: cargo publish -p "$crate" --allow-dirty --no-default-features - - - if: fromJSON(inputs.info).is-release == 'true' - name: Upload minisign.pub - uses: svenstaro/upload-release-action@v2 - with: - repo_token: ${{ secrets.GITHUB_TOKEN }} - release_name: v${{ fromJSON(inputs.info).version }} - tag: v${{ fromJSON(inputs.info).version }} - body: ${{ fromJSON(inputs.info).notes }} - promote: true - file: minisign.pub - - - if: fromJSON(inputs.info).is-release == 'true' - name: Make release latest - run: gh release edit v${{ fromJSON(inputs.info).version }} --latest --draft=false - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - if: fromJSON(inputs.info).is-release == 'true' - name: Delete signing key artifact - uses: geekyeggo/delete-artifact@v5 - with: - name: minisign.key.age - failOnError: false - diff --git a/.github/workflows/release-packages.yml b/.github/workflows/release-packages.yml deleted file mode 100644 index 4291de23..00000000 --- a/.github/workflows/release-packages.yml +++ /dev/null @@ -1,202 +0,0 @@ -name: Build packages for release - -on: - workflow_call: - inputs: - publish: - description: "The release metadata JSON" - required: true - type: string - CARGO_PROFILE_RELEASE_LTO: - description: "Used to speed up CI" - required: false - type: string - CARGO_PROFILE_RELEASE_CODEGEN_UNITS: - description: "Used to speed up CI" - required: false - type: string - -env: - CARGO_TERM_COLOR: always - CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse - JUST_TIMINGS: true - -jobs: - build: - strategy: - fail-fast: false - matrix: - include: - - { o: macos-latest, t: x86_64-apple-darwin } - - { o: macos-latest, t: x86_64h-apple-darwin } - - { o: macos-latest, t: aarch64-apple-darwin, r: true } - - { - o: ubuntu-latest, - t: x86_64-unknown-linux-gnu, - g: 2.17, - r: true, - c: true, - } - - { - o: ubuntu-latest, - t: armv7-unknown-linux-gnueabihf, - g: 2.17, - c: true, - } - - { o: ubuntu-latest, t: aarch64-unknown-linux-gnu, g: 2.17, c: true } - - { o: ubuntu-latest, t: x86_64-unknown-linux-musl, r: true, c: true } - - { o: ubuntu-latest, t: armv7-unknown-linux-musleabihf, c: true } - - { o: ubuntu-latest, t: aarch64-unknown-linux-musl, c: true } - - { o: windows-latest, t: x86_64-pc-windows-msvc, r: true } - - { o: windows-latest, t: aarch64-pc-windows-msvc } - - name: ${{ matrix.t }} - runs-on: ${{ matrix.o }} - permissions: - contents: write - env: - CARGO_BUILD_TARGET: ${{ matrix.t }} - GLIBC_VERSION: ${{ matrix.g }} - JUST_USE_CARGO_ZIGBUILD: ${{ matrix.c }} - JUST_FOR_RELEASE: true - JUST_USE_AUDITABLE: true - JUST_ENABLE_H3: true - - steps: - - uses: actions/checkout@v4 - - - name: Override release profile lto settings - if: inputs.CARGO_PROFILE_RELEASE_LTO - run: echo "CARGO_PROFILE_RELEASE_LTO=${{ inputs.CARGO_PROFILE_RELEASE_LTO }}" >> "$GITHUB_ENV" - shell: bash - - - name: Override release profile codegen-units settings - if: inputs.CARGO_PROFILE_RELEASE_CODEGEN_UNITS - run: echo "CARGO_PROFILE_RELEASE_CODEGEN_UNITS=${{ inputs.CARGO_PROFILE_RELEASE_CODEGEN_UNITS }}" >> "$GITHUB_ENV" - shell: bash - - - uses: ./.github/actions/just-setup - with: - tools: cargo-auditable,rsign2,rage - env: - # just-setup use binstall to install sccache, - # which works better when we provide it with GITHUB_TOKEN. - GITHUB_TOKEN: ${{ secrets.CI_RELEASE_TEST_GITHUB_TOKEN }} - - - run: just toolchain rust-src - - - uses: actions/download-artifact@v4 - with: - name: minisign.pub - - run: just package - - if: runner.os == 'Windows' - run: Get-ChildItem packages/ - - if: runner.os != 'Windows' - run: ls -shal packages/ - - - name: Ensure release binary is runnable - if: "matrix.r" - run: just e2e-tests - env: - GITHUB_TOKEN: ${{ secrets.CI_RELEASE_TEST_GITHUB_TOKEN }} - - - uses: actions/download-artifact@v4 - with: - name: minisign.key.age - - name: Sign package - env: - AGE_KEY_SECRET: ${{ secrets.AGE_KEY_SECRET }} - shell: bash - run: .github/scripts/ephemeral-sign.sh packages/cargo-binstall-* - - - if: fromJSON(inputs.publish).is-release == 'true' - name: Upload to release - uses: svenstaro/upload-release-action@v2 - with: - repo_token: ${{ secrets.GITHUB_TOKEN }} - release_name: v${{ fromJSON(inputs.publish).version }} - tag: v${{ fromJSON(inputs.publish).version }} - body: ${{ fromJSON(inputs.publish).notes }} - file: packages/cargo-binstall-* - file_glob: true - prerelease: true - - if: "fromJSON(inputs.publish).is-release != 'true' || runner.os == 'macOS'" - name: Upload artifact - uses: actions/upload-artifact@v4 - with: - name: ${{ matrix.t }} - path: packages/cargo-binstall-* - retention-days: 1 - - - name: Upload timings - uses: actions/upload-artifact@v4 - with: - name: ${{ matrix.t }}-cargo-timings - path: target/cargo-timings - retention-days: 1 - - lipo: - needs: build - name: universal-apple-darwin - permissions: - contents: write - runs-on: macos-latest - env: - JUST_FOR_RELEASE: true - - steps: - - uses: actions/checkout@v4 - - - uses: taiki-e/install-action@v2 - with: - tool: just,rsign2,rage - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - uses: actions/download-artifact@v4 - with: - name: x86_64h-apple-darwin - path: packages/ - - uses: actions/download-artifact@v4 - with: - name: x86_64-apple-darwin - path: packages/ - - uses: actions/download-artifact@v4 - with: - name: aarch64-apple-darwin - path: packages/ - - - uses: actions/download-artifact@v4 - with: - name: minisign.pub - - run: ls -shalr packages/ - - run: just repackage-lipo - - run: ls -shal packages/ - - - uses: actions/download-artifact@v4 - with: - name: minisign.key.age - - env: - AGE_KEY_SECRET: ${{ secrets.AGE_KEY_SECRET }} - shell: bash - run: .github/scripts/ephemeral-sign.sh packages/cargo-binstall-universal-* - - - if: fromJSON(inputs.publish).is-release == 'true' - name: Upload to release - uses: svenstaro/upload-release-action@v2 - with: - repo_token: ${{ secrets.GITHUB_TOKEN }} - tag: v${{ fromJSON(inputs.publish).version }} - release_name: v${{ fromJSON(inputs.publish).version }} - body: ${{ fromJSON(inputs.publish).notes }} - file: packages/cargo-binstall-universal-* - file_glob: true - overwrite: true - prerelease: true - - if: fromJSON(inputs.publish).is-release != 'true' - name: Upload artifact - uses: actions/upload-artifact@v4 - with: - name: universal-apple-darwin - path: packages/cargo-binstall-universal-* - retention-days: 1 diff --git a/.github/workflows/release-plz.yml b/.github/workflows/release-plz.yml deleted file mode 100644 index db2b1e0a..00000000 --- a/.github/workflows/release-plz.yml +++ /dev/null @@ -1,27 +0,0 @@ -name: Release-plz - -permissions: - pull-requests: write - contents: write - -on: - push: - branches: - - main - -jobs: - release-plz: - name: Release-plz - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - name: Install Rust toolchain - run: rustup toolchain install stable --no-self-update --profile minimal - - name: Run release-plz - uses: MarcoIeni/release-plz-action@v0.5 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} diff --git a/.github/workflows/release-pr.yml b/.github/workflows/release-pr.yml deleted file mode 100644 index 0a1943ef..00000000 --- a/.github/workflows/release-pr.yml +++ /dev/null @@ -1,47 +0,0 @@ -name: Open cargo-binstall release PR -on: - workflow_dispatch: - inputs: - version: - description: Version to release - required: true - type: string - default: patch - -permissions: - pull-requests: write - -jobs: - make-release-pr: - permissions: - id-token: write # Enable OIDC - pull-requests: write - contents: write - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Configure toolchain - run: | - rustup toolchain install --profile minimal --no-self-update nightly - rustup default nightly - - uses: chainguard-dev/actions/setup-gitsign@main - - name: Install cargo-release - uses: taiki-e/install-action@v2 - with: - tool: cargo-release,cargo-semver-checks - env: - GITHUB_TOKEN: ${{ secrets.CI_RELEASE_TEST_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - - - run: rustup toolchain install stable --no-self-update --profile minimal - - uses: cargo-bins/release-pr@v2.1.3 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - version: ${{ inputs.version }} - crate-path: crates/bin - pr-label: release - pr-release-notes: true - pr-template-file: .github/scripts/release-pr-template.ejs - check-semver: false - check-package: true - env: - RUSTFLAGS: --cfg reqwest_unstable diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index c8131abb..00000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,54 +0,0 @@ -name: On release -on: - pull_request: - types: closed - branches: [main] # target branch of release PRs - -jobs: - info: - if: github.event.pull_request.merged - - outputs: - is-release: ${{ steps.meta.outputs.is-release }} - crate: ${{ steps.meta.outputs.crates-names }} - version: ${{ steps.meta.outputs.version-actual }} - notes: ${{ steps.meta.outputs.notes }} - - runs-on: ubuntu-latest - steps: - - id: meta - uses: cargo-bins/release-meta@v1 - with: - event-data: ${{ toJSON(github.event) }} - extract-notes-under: '### Release notes' - - release-lib: - if: needs.info.outputs.is-release == 'true' && needs.info.outputs.crate != 'cargo-binstall' - needs: info - permissions: - contents: write - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - run: rustup toolchain install stable --no-self-update --profile minimal - - name: Push lib release tag - if: needs.info.outputs.crate != 'cargo-binstall' - uses: mathieudutour/github-tag-action@v6.2 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - custom_tag: ${{ needs.info.outputs.version }} - tag_prefix: ${{ needs.info.outputs.crate }}-v - - name: Publish to crates.io - run: | - cargo publish -p '${{ needs.info.outputs.crate }}' - env: - CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} - - release-cli: - if: needs.info.outputs.crate == 'cargo-binstall' - needs: info - uses: ./.github/workflows/release-cli.yml - secrets: inherit - with: - info: ${{ toJSON(needs.info.outputs) }} - diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml new file mode 100644 index 00000000..e9188a6b --- /dev/null +++ b/.github/workflows/rust.yml @@ -0,0 +1,156 @@ +name: Rust + +on: + push: + branches: [ main ] + tags: [ 'v*' ] + pull_request: + branches: [ main ] + +env: + CARGO_TERM_COLOR: always + +jobs: + build: + name: Build + runs-on: ${{ matrix.os }} + + strategy: + fail-fast: false + matrix: + include: + - target: x86_64-unknown-linux-gnu + os: ubuntu-latest + output: cargo-binstall + archive: tgz + - target: x86_64-apple-darwin + os: macos-latest + output: cargo-binstall + archive: zip + - target: armv7-unknown-linux-gnueabihf + os: ubuntu-20.04 + output: cargo-binstall + archive: tgz + - target: x86_64-pc-windows-msvc + os: windows-latest + output: cargo-binstall.exe + archive: zip + + steps: + - uses: actions/checkout@v2 + - uses: FranzDiebold/github-env-vars-action@v1.2.1 + + - name: Configure toolchain + uses: actions-rs/toolchain@v1 + with: + toolchain: nightly + target: ${{ matrix.target }} + override: true + + - name: Install openssl (apt armv7) + if: ${{ matrix.target == 'armv7-unknown-linux-gnueabihf' }} + uses: ryankurte/action-apt@v0.3.0 + with: + arch: armhf + packages: libssl-dev:armhf libssl1.1:armhf zlib1g-dev:armhf zlib1g:armhf libc-dev:armhf + + - name: Configure caching + uses: actions/cache@v2 + # Caching disabled on macos due to https://github.com/actions/cache/issues/403 + if: ${{ matrix.os != 'macos-latest' }} + with: + key: ${{ matrix.os }}-${{ matrix.target }} + path: | + ${{ env.HOME }}/.cargo" + target + + - name: Install cross toolchain (armv7) + if: ${{ matrix.target == 'armv7-unknown-linux-gnueabihf' }} + run: sudo apt install gcc-arm-linux-gnueabihf + + - name: Enable cross compilation (armv7) + if: ${{ matrix.target == 'armv7-unknown-linux-gnueabihf' }} + run: | + echo "PKG_CONFIG_ALLOW_CROSS=1" >> $GITHUB_ENV + echo "LZMA_API_STATIC=1" >> $GITHUB_ENV + + - name: Build release + uses: actions-rs/cargo@v1 + with: + command: build + args: --target ${{ matrix.target }} --release + + - name: Copy and rename utility + run: cp target/${{ matrix.target }}/release/${{ matrix.output }} ${{ matrix.output }} + + - name: Create archive (tgz, linux) + if: ${{ matrix.os != 'macos-latest' && matrix.os != 'windows-latest' }} + run: tar -czvf cargo-binstall-${{ matrix.target }}.tgz ${{ matrix.output }} + + - name: Create archive (zip, windows) + if: ${{ matrix.os == 'windows-latest' }} + run: tar.exe -a -c -f cargo-binstall-${{ matrix.target }}.zip ${{ matrix.output }} + + - name: Create archive (zip, macos) + if: ${{ matrix.os == 'macos-latest' }} + run: zip cargo-binstall-${{ matrix.target }}.zip ${{ matrix.output }} + + - name: Upload artifacts + uses: actions/upload-artifact@v1 + with: + name: cargo-binstall-${{ matrix.target }}.${{ matrix.archive }} + path: cargo-binstall-${{ matrix.target }}.${{ matrix.archive }} + + - name: Upload binary to release + if: ${{ startsWith(github.ref, 'refs/tags/v') }} + uses: svenstaro/upload-release-action@v2 + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: cargo-binstall-${{ matrix.target }}.${{ matrix.archive }} + asset_name: cargo-binstall-${{ matrix.target }}.${{ matrix.archive }} + tag: ${{ github.ref }} + overwrite: true + + test: + name: Test + runs-on: ${{ matrix.os }} + needs: build + strategy: + fail-fast: false + matrix: + include: + - target: x86_64-unknown-linux-gnu + os: ubuntu-latest + output: cargo-binstall + archive: tgz + - target: x86_64-apple-darwin + os: macos-latest + output: cargo-binstall + archive: zip + - target: x86_64-pc-windows-msvc + os: windows-latest + output: cargo-binstall.exe + archive: zip + + steps: + - uses: actions/checkout@v2 + - uses: FranzDiebold/github-env-vars-action@v1.2.1 + + - uses: actions/download-artifact@v2 + with: + name: cargo-binstall-${{ matrix.target }}.${{ matrix.archive }} + + - name: "Extract build artifact (tgz, linux)" + if: ${{ matrix.os != 'windows-latest' && matrix.os != 'macos-latest' }} + run: tar -xvf cargo-binstall-${{ matrix.target }}.tgz + + - name: "Extract build artifact (zip, windows)" + if: ${{ matrix.os == 'windows-latest' }} + run: tar.exe -xvf cargo-binstall-${{ matrix.target }}.zip + + - name: "Extract build artifact (zip, macos)" + if: ${{ matrix.os == 'macos-latest' }} + run: unzip cargo-binstall-${{ matrix.target }}.zip + + - name: "Run binstall" + run: ./${{ matrix.output }} cargo-binstall --manifest-path . --no-confirm diff --git a/.github/workflows/shellcheck.yml b/.github/workflows/shellcheck.yml deleted file mode 100644 index b7145e64..00000000 --- a/.github/workflows/shellcheck.yml +++ /dev/null @@ -1,32 +0,0 @@ -name: Shellcheck - -on: - merge_group: - pull_request: - types: - - opened - - reopened - - synchronize - paths: - - '**.sh' - push: - branches: - - main - paths: - - '**.sh' - -concurrency: - group: ${{ github.workflow }}-${{ github.ref || github.event.pull_request.number || github.sha }} - cancel-in-progress: true - -jobs: - shellcheck: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v4 - - uses: taiki-e/install-action@v2 - with: - tool: fd-find - - name: shellcheck - run: fd -e sh -t f -X shellcheck diff --git a/.github/workflows/upgrade-transitive-deps.yml b/.github/workflows/upgrade-transitive-deps.yml deleted file mode 100644 index 8d9aad41..00000000 --- a/.github/workflows/upgrade-transitive-deps.yml +++ /dev/null @@ -1,48 +0,0 @@ -name: Upgrade transitive dependencies - -on: - workflow_dispatch: # Allow running on-demand - schedule: - - cron: "0 3 * * 5" - -jobs: - upgrade: - name: Upgrade & Open Pull Request - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - persist-credentials: true - - - name: Generate branch name - run: | - git checkout -b deps/transitive/${{ github.run_id }} - - - name: Install rust - run: | - rustup toolchain install stable --no-self-update --profile minimal - - - name: Upgrade transitive dependencies - run: cargo update --aggressive - - - name: Detect changes - id: changes - run: - # This output boolean tells us if the dependencies have actually changed - echo "count=$(git status --porcelain=v1 | wc -l)" >> $GITHUB_OUTPUT - - - name: Commit and push changes - # Only push if changes exist - if: steps.changes.outputs.count > 0 - run: | - git config user.name github-actions - git config user.email github-actions@github.com - git commit -am "dep: Upgrade transitive dependencies" - git push origin HEAD - - - name: Open pull request if needed - if: steps.changes.outputs.count > 0 - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - gh pr create --base main --label 'PR: dependencies' --title 'dep: Upgrade transitive dependencies' --body 'Update dependencies' --head $(git branch --show-current) diff --git a/.gitignore b/.gitignore index b8aaf2b2..ea8c4bf7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1 @@ /target -.DS_Store -/packages -/e2e-tests/cargo-binstall* diff --git a/Cargo.lock b/Cargo.lock index c19a45dd..da291b6c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,652 +3,208 @@ version = 3 [[package]] -name = "addr2line" -version = "0.24.2" +name = "adler32" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - -[[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", - "once_cell", - "version_check", - "zerocopy 0.7.35", -] +checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" [[package]] name = "aho-corasick" -version = "1.1.3" +version = "0.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5" dependencies = [ "memchr", ] [[package]] -name = "alloc-no-stdlib" -version = "2.0.4" +name = "ansi_term" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" - -[[package]] -name = "alloc-stdlib" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" +checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" dependencies = [ - "alloc-no-stdlib", -] - -[[package]] -name = "allocator-api2" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" - -[[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.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" -dependencies = [ - "anstyle", - "once_cell", - "windows-sys 0.59.0", + "winapi", ] [[package]] name = "anyhow" -version = "1.0.98" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" +checksum = "28b2cd92db5cbd74e8e5028f7e27dd7aa3090e89e4f2a197cc7c8dfb69c7063b" [[package]] -name = "arc-swap" -version = "1.7.1" +name = "arrayref" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" +checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" [[package]] name = "arrayvec" -version = "0.7.6" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" [[package]] -name = "async-compression" -version = "0.4.19" +name = "atty" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06575e6a9673580f52661c92107baabffbf41e2141373441cbcdc47cb733003c" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ - "brotli", - "bzip2", - "flate2", - "futures-core", - "memchr", - "pin-project-lite", - "tokio", - "xz2", - "zstd", - "zstd-safe", + "hermit-abi", + "libc", + "winapi", ] -[[package]] -name = "async-recursion" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "async-trait" -version = "0.1.88" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "atomic-file-install" -version = "1.0.11" -dependencies = [ - "reflink-copy", - "tempfile", - "tracing", - "windows 0.61.1", -] - -[[package]] -name = "atomic-waker" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" - [[package]] name = "autocfg" -version = "1.4.0" +version = "1.0.1" 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 0.8.8", - "object", - "rustc-demangle", - "windows-targets 0.52.6", -] - -[[package]] -name = "backtrace-ext" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "537beee3be4a18fb023b570f80e3ae28003db9167a751266b259926e25539d50" -dependencies = [ - "backtrace", -] - -[[package]] -name = "base16" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d27c3610c36aee21ce8ac510e6224498de4228ad772a171ed65643a24693a5a8" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" [[package]] name = "base64" -version = "0.22.1" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" - -[[package]] -name = "beef" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1" -dependencies = [ - "serde", -] - -[[package]] -name = "binstalk" -version = "0.28.31" -dependencies = [ - "binstalk-bins", - "binstalk-downloader", - "binstalk-fetchers", - "binstalk-git-repo-api", - "binstalk-registry", - "binstalk-types", - "cargo-toml-workspace", - "command-group", - "compact_str", - "detect-targets", - "either", - "itertools", - "jobslot", - "leon", - "maybe-owned", - "miette", - "semver", - "simple-git", - "strum", - "target-lexicon", - "tempfile", - "thiserror 2.0.12", - "tokio", - "tracing", - "url", - "zeroize", -] - -[[package]] -name = "binstalk-bins" -version = "0.6.13" -dependencies = [ - "atomic-file-install", - "binstalk-types", - "compact_str", - "leon", - "miette", - "normalize-path", - "thiserror 2.0.12", - "tracing", -] - -[[package]] -name = "binstalk-downloader" -version = "0.13.17" -dependencies = [ - "async-compression", - "async-trait", - "binstalk-types", - "binstall-tar", - "bytes", - "bzip2", - "cfg-if", - "compact_str", - "default-net", - "flate2", - "futures-io", - "futures-util", - "hickory-resolver", - "httpdate", - "ipconfig", - "native-tls", - "once_cell", - "rc-zip-sync", - "reqwest", - "serde", - "serde_json", - "tempfile", - "thiserror 2.0.12", - "tokio", - "tokio-tar", - "tokio-util", - "tracing", - "url", - "xz2", - "zstd", -] - -[[package]] -name = "binstalk-fetchers" -version = "0.10.18" -dependencies = [ - "async-trait", - "binstalk-downloader", - "binstalk-git-repo-api", - "binstalk-types", - "bytes", - "compact_str", - "either", - "itertools", - "leon", - "leon-macros", - "miette", - "minisign-verify", - "once_cell", - "strum", - "thiserror 2.0.12", - "tokio", - "tracing", - "url", -] - -[[package]] -name = "binstalk-git-repo-api" -version = "0.5.19" -dependencies = [ - "binstalk-downloader", - "compact_str", - "once_cell", - "percent-encoding", - "serde", - "serde-tuple-vec-map", - "serde_json", - "thiserror 2.0.12", - "tokio", - "tracing", - "tracing-subscriber", - "url", - "zeroize", -] - -[[package]] -name = "binstalk-manifests" -version = "0.15.28" -dependencies = [ - "beef", - "binstalk-types", - "compact_str", - "detect-targets", - "fs-lock", - "home", - "miette", - "semver", - "serde", - "serde-tuple-vec-map", - "serde_json", - "tempfile", - "thiserror 2.0.12", - "toml_edit", - "url", -] - -[[package]] -name = "binstalk-registry" -version = "0.11.18" -dependencies = [ - "async-trait", - "base16", - "binstalk-downloader", - "binstalk-types", - "cargo-toml-workspace", - "compact_str", - "leon", - "miette", - "normalize-path", - "once_cell", - "semver", - "serde", - "serde_json", - "sha2", - "simple-git", - "tempfile", - "thiserror 2.0.12", - "tokio", - "toml_edit", - "tracing", - "url", -] - -[[package]] -name = "binstalk-types" -version = "0.9.4" -dependencies = [ - "compact_str", - "maybe-owned", - "once_cell", - "semver", - "serde", - "serde_json", - "strum", - "strum_macros", - "url", -] - -[[package]] -name = "binstall-tar" -version = "0.4.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3620d72763b5d8df3384f3b2ec47dc5885441c2abbd94dd32197167d08b014a" -dependencies = [ - "filetime", - "libc", - "xattr", -] +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" [[package]] name = "bitflags" -version = "1.3.2" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" [[package]] -name = "bitflags" -version = "2.9.0" +name = "blake2b_simd" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" - -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +checksum = "afa748e348ad3be8263be728124b24a24f268266f6f5d58af9d75f6a40b5c587" dependencies = [ - "generic-array", -] - -[[package]] -name = "brotli" -version = "7.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc97b8f16f944bba54f0433f07e30be199b6dc2bd25937444bbad560bcea29bd" -dependencies = [ - "alloc-no-stdlib", - "alloc-stdlib", - "brotli-decompressor", -] - -[[package]] -name = "brotli-decompressor" -version = "4.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74fa05ad7d803d413eb8380983b092cbbaf9a85f151b871360e7b00cd7060b37" -dependencies = [ - "alloc-no-stdlib", - "alloc-stdlib", -] - -[[package]] -name = "bstr" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4" -dependencies = [ - "memchr", - "regex-automata 0.4.9", - "serde", + "arrayref", + "arrayvec", + "constant_time_eq", ] [[package]] name = "bumpalo" -version = "3.17.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" +checksum = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820" [[package]] name = "byteorder" -version = "1.5.0" +version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" -version = "1.10.1" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38" [[package]] -name = "bytesize" -version = "1.3.3" +name = "bytes" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e93abca9e28e0a1b9877922aacb20576e05d4679ffa78c3d6dc22a26a216659" - -[[package]] -name = "bytesize" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3c8f83209414aacf0eeae3cf730b18d6981697fba62f200fcfb92b9f082acba" +checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040" [[package]] name = "bzip2" -version = "0.5.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49ecfb22d906f800d4fe833b6282cf4dc1c298f5057ca0b5445e5c209735ca47" +checksum = "42b7c3cbf0fa9c1b82308d57191728ca0256cb821220f4e2fd410a72ade26e3b" dependencies = [ "bzip2-sys", - "libbz2-rs-sys", + "libc", ] [[package]] name = "bzip2-sys" -version = "0.1.13+1.0.8" +version = "0.1.10+1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225bff33b2141874fe80d71e07d6eec4f85c5c216453dd96388240f96e1acc14" +checksum = "17fa3d1ac1ca21c5c4e36a97f3c3eb25084576f6fc47bf0139c1123434216c6c" dependencies = [ "cc", + "libc", "pkg-config", ] -[[package]] -name = "camino" -version = "1.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3" -dependencies = [ - "serde", -] - [[package]] name = "cargo-binstall" -version = "1.12.3" +version = "0.4.1" dependencies = [ - "atomic-file-install", - "binstalk", - "binstalk-manifests", - "clap", - "clap-cargo", - "compact_str", + "anyhow", + "cargo_metadata", + "cargo_toml", + "crates-index", + "crates_io_api", "dirs", - "embed-resource", - "file-format", - "home", + "env_logger", + "flate2", "log", - "miette", - "mimalloc", - "once_cell", + "reqwest", "semver", + "serde", + "serde_derive", + "simplelog", + "structopt", "strum", "strum_macros", - "supports-color", - "tempfile", + "tar", + "tempdir", + "tinytemplate", "tokio", - "tracing", - "tracing-core", - "tracing-log", - "tracing-subscriber", - "vergen", - "zeroize", + "xz2", + "zip", ] [[package]] name = "cargo-platform" -version = "0.1.9" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea" +checksum = "0226944a63d1bf35a3b5f948dd7c59e263db83695c9e8bffc4037de02e30f1d7" dependencies = [ "serde", ] -[[package]] -name = "cargo-toml-workspace" -version = "7.0.6" -dependencies = [ - "cargo_toml", - "compact_str", - "glob", - "normalize-path", - "serde", - "tempfile", - "thiserror 2.0.12", - "tracing", -] - [[package]] name = "cargo_metadata" -version = "0.18.1" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" +checksum = "11a47b6286279a9998588ef7050d1ebc2500c69892a557c90fe5d071c64415dc" dependencies = [ - "camino", "cargo-platform", "semver", + "semver-parser", "serde", "serde_json", - "thiserror 1.0.69", ] [[package]] name = "cargo_toml" -version = "0.22.1" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02260d489095346e5cafd04dea8e8cb54d1d74fcd759022a9b72986ebe9a1257" +checksum = "513d17226888c7b8283ac02a1c1b0d8a9d4cbf6db65dfadb79f598f5d7966fe9" dependencies = [ "serde", + "serde_derive", "toml", ] [[package]] -name = "castaway" -version = "0.2.3" +name = "cc" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0abae9be0aaf9ea96a3b1b8b1b55c602ca751eba1b1500220cea4ecbafe7c0d5" +checksum = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48" dependencies = [ - "rustversion", + "jobserver", ] [[package]] -name = "cc" -version = "1.2.19" +name = "cfg-if" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e3a13707ac958681c13b39b458c073d0d9bc8a22cb1b2f4c8e55eb72c13f362" -dependencies = [ - "jobserver", - "libc", - "shlex", -] +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" [[package]] name = "cfg-if" @@ -656,146 +212,46 @@ 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 = "chardetng" -version = "0.1.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14b8f0b65b7b08ae3c8187e8d77174de20cb6777864c6b832d8ad365999cf1ea" -dependencies = [ - "cfg-if", - "encoding_rs", - "memchr", -] - [[package]] name = "chrono" -version = "0.4.40" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" dependencies = [ + "libc", + "num-integer", "num-traits", -] - -[[package]] -name = "clap" -version = "4.5.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2df961d8c8a0d08aa9945718ccf584145eee3f3aa06cddbeac12933781102e04" -dependencies = [ - "clap_builder", - "clap_derive", -] - -[[package]] -name = "clap-cargo" -version = "0.15.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d546f0e84ff2bfa4da1ce9b54be42285767ba39c688572ca32412a09a73851e5" -dependencies = [ - "anstyle", - "clap", -] - -[[package]] -name = "clap_builder" -version = "4.5.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "132dbda40fb6753878316a489d5a1242a8ef2f0d9e47ba01c951ea8aa7d013a5" -dependencies = [ - "anstream", - "anstyle", - "clap_lex", - "strsim", - "terminal_size", -] - -[[package]] -name = "clap_derive" -version = "4.5.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "clap_lex" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" - -[[package]] -name = "clru" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbd0f76e066e64fdc5631e3bb46381254deab9ef1158292f27c8c57e3bf3fe59" - -[[package]] -name = "cmake" -version = "0.1.54" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0" -dependencies = [ - "cc", -] - -[[package]] -name = "colorchoice" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" - -[[package]] -name = "command-group" -version = "5.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a68fa787550392a9d58f44c21a3022cfb3ea3e2458b7f85d3b399d0ceeccf409" -dependencies = [ - "async-trait", - "nix", - "tokio", + "serde", + "time", "winapi", ] [[package]] -name = "compact_str" -version = "0.9.0" +name = "clap" +version = "2.33.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb1325a1cece981e8a296ab8f0f9b63ae357bd0784a9faaf548cc7b480707a" +checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" dependencies = [ - "castaway", - "cfg-if", - "itoa", - "rustversion", - "ryu", - "serde", - "static_assertions", + "ansi_term", + "atty", + "bitflags", + "strsim", + "textwrap", + "unicode-width", + "vec_map", ] [[package]] -name = "core-foundation" -version = "0.9.4" +name = "constant_time_eq" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" -dependencies = [ - "core-foundation-sys", - "libc", -] +checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" [[package]] name = "core-foundation" -version = "0.10.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63" +checksum = "0a89e2ae426ea83155dccf10c0fa6b1463ef6d5fcb44cee0b224a408fa640a62" dependencies = [ "core-foundation-sys", "libc", @@ -803,331 +259,129 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.7" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b" [[package]] -name = "cpufeatures" -version = "0.2.17" +name = "crates-index" +version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +checksum = "7f24823d553339d125040d989d2a593a01b034fe5ac17714423bcd2c3d168878" dependencies = [ - "libc", + "git2", + "glob", + "hex", + "home", + "memchr", + "semver", + "serde", + "serde_derive", + "serde_json", + "smartstring", ] [[package]] -name = "crc" -version = "3.2.1" +name = "crates_io_api" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636" +checksum = "5fb38f290ea3b9521e536be22d072cd93a04dd385552ba6c6bfb851513a3e5e9" dependencies = [ - "crc-catalog", + "chrono", + "futures", + "log", + "reqwest", + "serde", + "serde_derive", + "serde_json", + "tokio", + "url", ] -[[package]] -name = "crc-catalog" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" - [[package]] name = "crc32fast" -version = "1.4.2" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" dependencies = [ - "cfg-if", -] - -[[package]] -name = "critical-section" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" - -[[package]] -name = "crossbeam-channel" -version = "0.5.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" -dependencies = [ - "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", + "cfg-if 1.0.0", ] [[package]] name = "crossbeam-utils" -version = "0.8.21" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" - -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +checksum = "02d96d1e189ef58269ebe5b97953da3274d83a93af647c2ddd6f9dab28cedb8d" dependencies = [ - "generic-array", - "typenum", -] - -[[package]] -name = "data-encoding" -version = "2.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" - -[[package]] -name = "default-net" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c5a6569a908354d49b10db3c516d69aca1eccd97562fd31c98b13f00b73ca66" -dependencies = [ - "dlopen2", - "libc", - "memalloc", - "netlink-packet-core", - "netlink-packet-route", - "netlink-sys", - "once_cell", - "system-configuration 0.5.1", - "windows 0.48.0", -] - -[[package]] -name = "deflate64" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da692b8d1080ea3045efaab14434d40468c3d8657e42abddfffca87b428f4c1b" - -[[package]] -name = "deranged" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" -dependencies = [ - "powerfmt", -] - -[[package]] -name = "derive_destructure2" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64b697ac90ff296f0fc031ee5a61c7ac31fb9fff50e3fb32873b09223613fc0c" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "detect-targets" -version = "0.1.47" -dependencies = [ - "cfg-if", - "guess_host_triple", - "tokio", - "tracing", - "tracing-subscriber", - "windows-sys 0.59.0", -] - -[[package]] -name = "detect-wasi" -version = "1.0.28" -dependencies = [ - "tempfile", -] - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "crypto-common", + "autocfg", + "cfg-if 1.0.0", + "lazy_static", ] [[package]] name = "dirs" -version = "6.0.0" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" +checksum = "142995ed02755914747cc6ca76fc7e4583cd18578746716d0508ea6ed558b9ff" dependencies = [ "dirs-sys", ] [[package]] name = "dirs-sys" -version = "0.5.0" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" +checksum = "8e93d7f5705de3e49895a2b5e0b8855a1c27f080192ae9c32a6432d50741a57a" dependencies = [ "libc", - "option-ext", "redox_users", - "windows-sys 0.59.0", -] - -[[package]] -name = "displaydoc" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "dlopen2" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09b4f5f101177ff01b8ec4ecc81eead416a8aa42819a2869311b3420fa114ffa" -dependencies = [ - "libc", - "once_cell", "winapi", ] -[[package]] -name = "dunce" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" - -[[package]] -name = "either" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" - -[[package]] -name = "embed-resource" -version = "3.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fbc6e0d8e0c03a655b53ca813f0463d2c956bc4db8138dbc89f120b066551e3" -dependencies = [ - "cc", - "memchr", - "rustc_version", - "toml", - "vswhom", - "winreg 0.52.0", -] - [[package]] name = "encoding_rs" -version = "0.8.35" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +checksum = "801bbab217d7f79c0062f4f7205b5d4427c6d1a7bd7aafdd1475f7c59d62b283" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", ] [[package]] -name = "enum-as-inner" -version = "0.6.1" +name = "env_logger" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1e6a265c649f3f5979b601d26f1d05ada116434c87741c9493cb56218f76cbc" +checksum = "f26ecb66b4bdca6c1409b40fb255eefc2bd4f6d135dab3c3124f80ffa2a9661e" dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn", + "atty", + "humantime", + "log", + "regex", + "termcolor", ] [[package]] -name = "equivalent" -version = "1.0.2" +name = "filetime" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" - -[[package]] -name = "errno" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +checksum = "0c122a393ea57648015bf06fbd3d372378992e86b9ff5a7a497b076a28c79efe" dependencies = [ - "errno-dragonfly", + "cfg-if 1.0.0", "libc", + "redox_syscall 0.1.57", "winapi", ] -[[package]] -name = "errno" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" -dependencies = [ - "libc", - "windows-sys 0.59.0", -] - -[[package]] -name = "errno-dragonfly" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" -dependencies = [ - "cc", - "libc", -] - -[[package]] -name = "faster-hex" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2a2b11eda1d40935b26cf18f6833c526845ae8c41e58d09af6adeb6f0269183" -dependencies = [ - "serde", -] - -[[package]] -name = "fastrand" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" - -[[package]] -name = "file-format" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7ef3d5e8ae27277c8285ac43ed153158178ef0f79567f32024ca8140a0c7cd8" - -[[package]] -name = "filetime" -version = "0.2.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" -dependencies = [ - "cfg-if", - "libc", - "libredox", - "windows-sys 0.59.0", -] - [[package]] name = "flate2" -version = "1.1.1" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece" +checksum = "2cfff41391129e0a856d6d822600b8d71179d46879e310417eb9c762eb178b42" dependencies = [ + "cfg-if 0.1.10", "crc32fast", - "libz-ng-sys", - "libz-rs-sys", - "miniz_oxide 0.8.8", + "libc", + "miniz_oxide", ] [[package]] @@ -1153,36 +407,25 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "form_urlencoded" -version = "1.2.1" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +checksum = "ece68d15c92e84fa4f19d3780f1294e5ca82a78a6d515f1efaabcc144688be00" dependencies = [ + "matches", "percent-encoding", ] [[package]] -name = "fs-lock" -version = "0.1.10" -dependencies = [ - "fs4", - "tracing", -] - -[[package]] -name = "fs4" -version = "0.13.1" +name = "fuchsia-cprng" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8640e34b88f7652208ce9e88b1a37a2ae95227d84abec377ccd3c5cfeb141ed4" -dependencies = [ - "rustix 1.0.5", - "windows-sys 0.59.0", -] +checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" [[package]] name = "futures" -version = "0.3.31" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +checksum = "9b3b0c040a1fe6529d30b3c5944b280c7f0dcb2930d2c3062bca967b602583d0" dependencies = [ "futures-channel", "futures-core", @@ -1195,9 +438,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.31" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +checksum = "4b7109687aa4e177ef6fe84553af6280ef2778bdb7783ba44c9dc3399110fe64" dependencies = [ "futures-core", "futures-sink", @@ -1205,15 +448,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.31" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" +checksum = "847ce131b72ffb13b6109a221da9ad97a64cbe48feb1028356b836b47b8f1748" [[package]] name = "futures-executor" -version = "0.3.31" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +checksum = "4caa2b2b68b880003057c1dd49f1ed937e38f22fcf6c212188a121f08cf40a65" dependencies = [ "futures-core", "futures-task", @@ -1222,16 +465,17 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.31" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" +checksum = "611834ce18aaa1bd13c4b374f5d653e1027cf99b6b502584ff8c9a64413b30bb" [[package]] name = "futures-macro" -version = "0.3.31" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +checksum = "77408a692f1f97bcc61dc001d752e00643408fbc922e4d634c655df50d595556" dependencies = [ + "proc-macro-hack", "proc-macro2", "quote", "syn", @@ -1239,21 +483,24 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.31" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" +checksum = "f878195a49cee50e006b02b93cf7e0a95a38ac7b776b4c4d9cc1207cd20fcb3d" [[package]] name = "futures-task" -version = "0.3.31" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" +checksum = "7c554eb5bf48b2426c4771ab68c6b14468b6e76cc90996f528c3338d761a4d0d" +dependencies = [ + "once_cell", +] [[package]] name = "futures-util" -version = "0.3.31" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +checksum = "d304cff4a7b99cfb7986f7d43fbe93d175e72e704a8860787cc95e9ffd85cbd2" dependencies = [ "futures-channel", "futures-core", @@ -1262,872 +509,56 @@ dependencies = [ "futures-sink", "futures-task", "memchr", - "pin-project-lite", + "pin-project", "pin-utils", + "proc-macro-hack", + "proc-macro-nested", "slab", ] [[package]] -name = "generator" -version = "0.8.4" +name = "getrandom" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc6bd114ceda131d3b1d665eba35788690ad37f5916457286b32ab6fd3c438dd" +checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6" dependencies = [ - "cfg-if", + "cfg-if 0.1.10", "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "git2" +version = "0.13.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44f267c9da8a4de3c615b59e23606c75f164f84896e97f4dd6c15a4294de4359" +dependencies = [ + "bitflags", + "libc", + "libgit2-sys", "log", - "rustversion", - "windows 0.58.0", -] - -[[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", - "js-sys", - "libc", - "wasi 0.11.0+wasi-snapshot-preview1", - "wasm-bindgen", -] - -[[package]] -name = "getrandom" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "r-efi", - "wasi 0.14.2+wasi-0.2.4", - "wasm-bindgen", -] - -[[package]] -name = "gimli" -version = "0.31.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" - -[[package]] -name = "gix" -version = "0.71.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a61e71ec6817fc3c9f12f812682cfe51ee6ea0d2e27e02fc3849c35524617435" -dependencies = [ - "gix-actor", - "gix-attributes", - "gix-command", - "gix-commitgraph", - "gix-config", - "gix-credentials", - "gix-date", - "gix-diff", - "gix-discover", - "gix-features", - "gix-filter", - "gix-fs", - "gix-glob", - "gix-hash", - "gix-hashtable", - "gix-ignore", - "gix-index", - "gix-lock", - "gix-negotiate", - "gix-object", - "gix-odb", - "gix-pack", - "gix-path", - "gix-pathspec", - "gix-prompt", - "gix-protocol", - "gix-ref", - "gix-refspec", - "gix-revision", - "gix-revwalk", - "gix-sec", - "gix-shallow", - "gix-submodule", - "gix-tempfile", - "gix-trace", - "gix-transport", - "gix-traverse", - "gix-url", - "gix-utils", - "gix-validate", - "gix-worktree", - "gix-worktree-state", - "once_cell", - "smallvec", - "thiserror 2.0.12", -] - -[[package]] -name = "gix-actor" -version = "0.34.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f438c87d4028aca4b82f82ba8d8ab1569823cfb3e5bc5fa8456a71678b2a20e7" -dependencies = [ - "bstr", - "gix-date", - "gix-utils", - "itoa", - "thiserror 2.0.12", - "winnow 0.7.6", -] - -[[package]] -name = "gix-attributes" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4e25825e0430aa11096f8b65ced6780d4a96a133f81904edceebb5344c8dd7f" -dependencies = [ - "bstr", - "gix-glob", - "gix-path", - "gix-quote", - "gix-trace", - "kstring", - "smallvec", - "thiserror 2.0.12", - "unicode-bom", -] - -[[package]] -name = "gix-bitmap" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1db9765c69502650da68f0804e3dc2b5f8ccc6a2d104ca6c85bc40700d37540" -dependencies = [ - "thiserror 2.0.12", -] - -[[package]] -name = "gix-chunk" -version = "0.4.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b1f1d8764958699dc764e3f727cef280ff4d1bd92c107bbf8acd85b30c1bd6f" -dependencies = [ - "thiserror 2.0.12", -] - -[[package]] -name = "gix-command" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0378995847773a697f8e157fe2963ecf3462fe64be05b7b3da000b3b472def8" -dependencies = [ - "bstr", - "gix-path", - "gix-quote", - "gix-trace", - "shell-words", -] - -[[package]] -name = "gix-commitgraph" -version = "0.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "043cbe49b7a7505150db975f3cb7c15833335ac1e26781f615454d9d640a28fe" -dependencies = [ - "bstr", - "gix-chunk", - "gix-hash", - "memmap2", - "thiserror 2.0.12", -] - -[[package]] -name = "gix-config" -version = "0.44.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c6f830bf746604940261b49abf7f655d2c19cadc9f4142ae9379e3a316e8cfa" -dependencies = [ - "bstr", - "gix-config-value", - "gix-features", - "gix-glob", - "gix-path", - "gix-ref", - "gix-sec", - "memchr", - "once_cell", - "smallvec", - "thiserror 2.0.12", - "unicode-bom", - "winnow 0.7.6", -] - -[[package]] -name = "gix-config-value" -version = "0.14.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8dc2c844c4cf141884678cabef736fd91dd73068b9146e6f004ba1a0457944b6" -dependencies = [ - "bitflags 2.9.0", - "bstr", - "gix-path", - "libc", - "thiserror 2.0.12", -] - -[[package]] -name = "gix-credentials" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25322308aaf65789536b860d21137c3f7b69004ac4971c15c1abb08d3951c062" -dependencies = [ - "bstr", - "gix-command", - "gix-config-value", - "gix-path", - "gix-prompt", - "gix-sec", - "gix-trace", - "gix-url", - "thiserror 2.0.12", -] - -[[package]] -name = "gix-date" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daa30058ec7d3511fbc229e4f9e696a35abd07ec5b82e635eff864a2726217e4" -dependencies = [ - "bstr", - "itoa", - "jiff", - "thiserror 2.0.12", -] - -[[package]] -name = "gix-diff" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2c975dad2afc85e4e233f444d1efbe436c3cdcf3a07173984509c436d00a3f8" -dependencies = [ - "bstr", - "gix-hash", - "gix-object", - "thiserror 2.0.12", -] - -[[package]] -name = "gix-discover" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7fb8a4349b854506a3915de18d3341e5f1daa6b489c8affc9ca0d69efe86781" -dependencies = [ - "bstr", - "dunce", - "gix-fs", - "gix-hash", - "gix-path", - "gix-ref", - "gix-sec", - "thiserror 2.0.12", -] - -[[package]] -name = "gix-features" -version = "0.41.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "016d6050219458d14520fe22bdfdeb9cb71631dec9bc2724767c983f60109634" -dependencies = [ - "bytes", - "bytesize 1.3.3", - "crc32fast", - "crossbeam-channel", - "flate2", - "gix-path", - "gix-trace", - "gix-utils", - "libc", - "once_cell", - "parking_lot", - "prodash", - "thiserror 2.0.12", - "walkdir", -] - -[[package]] -name = "gix-filter" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb2b2bbffdc5cc9b2b82fc82da1b98163c9b423ac2b45348baa83a947ac9ab89" -dependencies = [ - "bstr", - "encoding_rs", - "gix-attributes", - "gix-command", - "gix-hash", - "gix-object", - "gix-packetline-blocking", - "gix-path", - "gix-quote", - "gix-trace", - "gix-utils", - "smallvec", - "thiserror 2.0.12", -] - -[[package]] -name = "gix-fs" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "951e886120dc5fa8cac053e5e5c89443f12368ca36811b2e43d1539081f9c111" -dependencies = [ - "bstr", - "fastrand", - "gix-features", - "gix-path", - "gix-utils", - "thiserror 2.0.12", -] - -[[package]] -name = "gix-glob" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20972499c03473e773a2099e5fd0c695b9b72465837797a51a43391a1635a030" -dependencies = [ - "bitflags 2.9.0", - "bstr", - "gix-features", - "gix-path", -] - -[[package]] -name = "gix-hash" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "834e79722063958b03342edaa1e17595cd2939bb2b3306b3225d0815566dcb49" -dependencies = [ - "faster-hex", - "gix-features", - "sha1-checked", - "thiserror 2.0.12", -] - -[[package]] -name = "gix-hashtable" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f06066d8702a9186dc1fdc1ed751ff2d7e924ceca21cb5d51b8f990c9c2e014a" -dependencies = [ - "gix-hash", - "hashbrown 0.14.5", - "parking_lot", -] - -[[package]] -name = "gix-ignore" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a27c8380f493a10d1457f756a3f81924d578fc08d6535e304dfcafbf0261d18" -dependencies = [ - "bstr", - "gix-glob", - "gix-path", - "gix-trace", - "unicode-bom", -] - -[[package]] -name = "gix-index" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "855bece2d4153453aa5d0a80d51deea1ce8cd6a3b4cf213da85ac344ccb908a7" -dependencies = [ - "bitflags 2.9.0", - "bstr", - "filetime", - "fnv", - "gix-bitmap", - "gix-features", - "gix-fs", - "gix-hash", - "gix-lock", - "gix-object", - "gix-traverse", - "gix-utils", - "gix-validate", - "hashbrown 0.14.5", - "itoa", - "libc", - "memmap2", - "rustix 0.38.44", - "smallvec", - "thiserror 2.0.12", -] - -[[package]] -name = "gix-lock" -version = "17.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df47b8f11c34520db5541bc5fc9fbc8e4b0bdfcec3736af89ccb1a5728a0126f" -dependencies = [ - "gix-tempfile", - "gix-utils", - "thiserror 2.0.12", -] - -[[package]] -name = "gix-negotiate" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dad912acf5a68a7defa4836014337ff4381af8c3c098f41f818a8c524285e57b" -dependencies = [ - "bitflags 2.9.0", - "gix-commitgraph", - "gix-date", - "gix-hash", - "gix-object", - "gix-revwalk", - "smallvec", - "thiserror 2.0.12", -] - -[[package]] -name = "gix-object" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4943fcdae6ffc135920c9ea71e0362ed539182924ab7a85dd9dac8d89b0dd69a" -dependencies = [ - "bstr", - "gix-actor", - "gix-date", - "gix-features", - "gix-hash", - "gix-hashtable", - "gix-path", - "gix-utils", - "gix-validate", - "itoa", - "smallvec", - "thiserror 2.0.12", - "winnow 0.7.6", -] - -[[package]] -name = "gix-odb" -version = "0.68.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50306d40dcc982eb6b7593103f066ea6289c7b094cb9db14f3cd2be0b9f5e610" -dependencies = [ - "arc-swap", - "gix-date", - "gix-features", - "gix-fs", - "gix-hash", - "gix-hashtable", - "gix-object", - "gix-pack", - "gix-path", - "gix-quote", - "parking_lot", - "tempfile", - "thiserror 2.0.12", -] - -[[package]] -name = "gix-pack" -version = "0.58.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b65fffb09393c26624ca408d32cfe8776fb94cd0a5cdf984905e1d2f39779cb" -dependencies = [ - "clru", - "gix-chunk", - "gix-features", - "gix-hash", - "gix-hashtable", - "gix-object", - "gix-path", - "gix-tempfile", - "memmap2", - "parking_lot", - "smallvec", - "thiserror 2.0.12", - "uluru", -] - -[[package]] -name = "gix-packetline" -version = "0.18.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "123844a70cf4d5352441dc06bab0da8aef61be94ec239cb631e0ba01dc6d3a04" -dependencies = [ - "bstr", - "faster-hex", - "gix-trace", - "thiserror 2.0.12", -] - -[[package]] -name = "gix-packetline-blocking" -version = "0.18.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ecf3ea2e105c7e45587bac04099824301262a6c43357fad5205da36dbb233b3" -dependencies = [ - "bstr", - "faster-hex", - "gix-trace", - "thiserror 2.0.12", -] - -[[package]] -name = "gix-path" -version = "0.10.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f910668e2f6b2a55ff35a1f04df88a1a049f7b868507f4cbeeaa220eaba7be87" -dependencies = [ - "bstr", - "gix-trace", - "home", - "once_cell", - "thiserror 2.0.12", -] - -[[package]] -name = "gix-pathspec" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fef8422c3c9066d649074b24025125963f85232bfad32d6d16aea9453b82ec14" -dependencies = [ - "bitflags 2.9.0", - "bstr", - "gix-attributes", - "gix-config-value", - "gix-glob", - "gix-path", - "thiserror 2.0.12", -] - -[[package]] -name = "gix-prompt" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbf9cbf6239fd32f2c2c9c57eeb4e9b28fa1c9b779fa0e3b7c455eb1ca49d5f0" -dependencies = [ - "gix-command", - "gix-config-value", - "parking_lot", - "rustix 0.38.44", - "thiserror 2.0.12", -] - -[[package]] -name = "gix-protocol" -version = "0.49.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5678ddae1d62880bc30e2200be1b9387af3372e0e88e21f81b4e7f8367355b5a" -dependencies = [ - "bstr", - "gix-credentials", - "gix-date", - "gix-features", - "gix-hash", - "gix-lock", - "gix-negotiate", - "gix-object", - "gix-ref", - "gix-refspec", - "gix-revwalk", - "gix-shallow", - "gix-trace", - "gix-transport", - "gix-utils", - "maybe-async", - "thiserror 2.0.12", - "winnow 0.7.6", -] - -[[package]] -name = "gix-quote" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b005c550bf84de3b24aa5e540a23e6146a1c01c7d30470e35d75a12f827f969" -dependencies = [ - "bstr", - "gix-utils", - "thiserror 2.0.12", -] - -[[package]] -name = "gix-ref" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2e1f7eb6b7ce82d2d19961f74bd637bab3ea79b1bc7bfb23dbefc67b0415d8b" -dependencies = [ - "gix-actor", - "gix-features", - "gix-fs", - "gix-hash", - "gix-lock", - "gix-object", - "gix-path", - "gix-tempfile", - "gix-utils", - "gix-validate", - "memmap2", - "thiserror 2.0.12", - "winnow 0.7.6", -] - -[[package]] -name = "gix-refspec" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d8587b21e2264a6e8938d940c5c99662779c13a10741a5737b15fc85c252ffc" -dependencies = [ - "bstr", - "gix-hash", - "gix-revision", - "gix-validate", - "smallvec", - "thiserror 2.0.12", -] - -[[package]] -name = "gix-revision" -version = "0.33.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "342caa4e158df3020cadf62f656307c3948fe4eacfdf67171d7212811860c3e9" -dependencies = [ - "bstr", - "gix-commitgraph", - "gix-date", - "gix-hash", - "gix-object", - "gix-revwalk", - "thiserror 2.0.12", -] - -[[package]] -name = "gix-revwalk" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dc7c3d7e5cdc1ab8d35130106e4af0a4f9f9eca0c81f4312b690780e92bde0d" -dependencies = [ - "gix-commitgraph", - "gix-date", - "gix-hash", - "gix-hashtable", - "gix-object", - "smallvec", - "thiserror 2.0.12", -] - -[[package]] -name = "gix-sec" -version = "0.10.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47aeb0f13de9ef2f3033f5ff218de30f44db827ac9f1286f9ef050aacddd5888" -dependencies = [ - "bitflags 2.9.0", - "gix-path", - "libc", - "windows-sys 0.52.0", -] - -[[package]] -name = "gix-shallow" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc0598aacfe1d52575a21c9492fee086edbb21e228ec36c819c42ab923f434c3" -dependencies = [ - "bstr", - "gix-hash", - "gix-lock", - "thiserror 2.0.12", -] - -[[package]] -name = "gix-submodule" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78c7390c2059505c365e9548016d4edc9f35749c6a9112b7b1214400bbc68da2" -dependencies = [ - "bstr", - "gix-config", - "gix-path", - "gix-pathspec", - "gix-refspec", - "gix-url", - "thiserror 2.0.12", -] - -[[package]] -name = "gix-tempfile" -version = "17.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6de439bbb9a5d3550c9c7fab0e16d2d637d120fcbe0dfbc538772a187f099b" -dependencies = [ - "gix-fs", - "libc", - "once_cell", - "parking_lot", - "tempfile", -] - -[[package]] -name = "gix-trace" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c396a2036920c69695f760a65e7f2677267ccf483f25046977d87e4cb2665f7" - -[[package]] -name = "gix-transport" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3f68c2870bfca8278389d2484a7f2215b67d0b0cc5277d3c72ad72acf41787e" -dependencies = [ - "base64", - "bstr", - "gix-command", - "gix-credentials", - "gix-features", - "gix-packetline", - "gix-quote", - "gix-sec", - "gix-url", - "reqwest", - "thiserror 2.0.12", -] - -[[package]] -name = "gix-traverse" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36c0b049f8bdb61b20016694102f7b507f2e1727e83e9c5e6dad4f7d84ff7384" -dependencies = [ - "bitflags 2.9.0", - "gix-commitgraph", - "gix-date", - "gix-hash", - "gix-hashtable", - "gix-object", - "gix-revwalk", - "smallvec", - "thiserror 2.0.12", -] - -[[package]] -name = "gix-url" -version = "0.30.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48dfe23f93f1ddb84977d80bb0dd7aa09d1bf5d5afc0c9b6820cccacc25ae860" -dependencies = [ - "bstr", - "gix-features", - "gix-path", - "percent-encoding", - "thiserror 2.0.12", + "openssl-probe", + "openssl-sys", "url", ] -[[package]] -name = "gix-utils" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "189f8724cf903e7fd57cfe0b7bc209db255cacdcb22c781a022f52c3a774f8d0" -dependencies = [ - "fastrand", - "unicode-normalization", -] - -[[package]] -name = "gix-validate" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34b5f1253109da6c79ed7cf6e1e38437080bb6d704c76af14c93e2f255234084" -dependencies = [ - "bstr", - "thiserror 2.0.12", -] - -[[package]] -name = "gix-worktree" -version = "0.40.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7760dbc4b79aa274fed30adc0d41dca6b917641f26e7867c4071b1fb4dc727b" -dependencies = [ - "bstr", - "gix-attributes", - "gix-features", - "gix-fs", - "gix-glob", - "gix-hash", - "gix-ignore", - "gix-index", - "gix-object", - "gix-path", - "gix-validate", -] - -[[package]] -name = "gix-worktree-state" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "490eb4d38ec2735b3466840aa3881b44ec1a4c180d6a658abfab03910380e18b" -dependencies = [ - "bstr", - "gix-features", - "gix-filter", - "gix-fs", - "gix-glob", - "gix-hash", - "gix-index", - "gix-object", - "gix-path", - "gix-worktree", - "io-close", - "thiserror 2.0.12", -] - [[package]] name = "glob" -version = "0.3.2" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" - -[[package]] -name = "guess_host_triple" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dd62763349a2c83ed2ce9ce5429158085fa43e0cdd8c60011a7843765d04e18" -dependencies = [ - "errno 0.2.8", - "libc", - "log", - "winapi", -] +checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" [[package]] name = "h2" -version = "0.4.9" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75249d144030531f8dee69fe9cea04d3edf809a017ae445e2abdff6629e86633" +checksum = "fc018e188373e2777d0ef2467ebff62a08e66c3f5857b23c8fbec3018210dc00" dependencies = [ - "atomic-waker", - "bytes", + "bytes 1.0.1", "fnv", "futures-core", "futures-sink", + "futures-util", "http", "indexmap", "slab", @@ -2136,697 +567,275 @@ dependencies = [ "tracing", ] -[[package]] -name = "h3" -version = "0.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dfb059a4f28a66f186ed16ad912d142f490676acba59353831d7cb45a96b0d3" -dependencies = [ - "bytes", - "fastrand", - "futures-util", - "http", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "h3-quinn" -version = "0.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d482318ae94198fc8e3cbb0b7ba3099c865d744e6ec7c62039ca7b6b6c66fbf" -dependencies = [ - "bytes", - "futures", - "h3", - "quinn", - "tokio", - "tokio-util", -] - [[package]] name = "hashbrown" -version = "0.14.5" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" -dependencies = [ - "ahash", - "allocator-api2", -] - -[[package]] -name = "hashbrown" -version = "0.15.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" [[package]] name = "heck" -version = "0.5.0" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - -[[package]] -name = "hickory-proto" -version = "0.25.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d844af74f7b799e41c78221be863bade11c430d46042c3b49ca8ae0c6d27287" +checksum = "87cbf45460356b7deeb5e3415b5563308c0a9b057c85e12b06ad551f98d0a6ac" dependencies = [ - "async-recursion", - "async-trait", - "bitflags 2.9.0", - "bytes", - "cfg-if", - "critical-section", - "data-encoding", - "enum-as-inner", - "futures-channel", - "futures-io", - "futures-util", - "h2", - "h3", - "h3-quinn", - "http", - "idna", - "ipnet", - "once_cell", - "pin-project-lite", - "quinn", - "rand 0.9.1", - "ring", - "rustls", - "rustls-pki-types", - "thiserror 2.0.12", - "time", - "tinyvec", - "tokio", - "tokio-rustls", - "tracing", - "url", + "unicode-segmentation", ] [[package]] -name = "hickory-resolver" -version = "0.25.1" +name = "hermit-abi" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a128410b38d6f931fcc6ca5c107a3b02cabd6c05967841269a4ad65d23c44331" +checksum = "5aca5565f760fb5b220e499d72710ed156fdb74e631659e99377d9ebfbd13ae8" dependencies = [ - "cfg-if", - "futures-util", - "hickory-proto", - "ipconfig", - "moka", - "once_cell", - "parking_lot", - "quinn", - "rand 0.9.1", - "resolv-conf", - "rustls", - "smallvec", - "thiserror 2.0.12", - "tokio", - "tokio-rustls", - "tracing", + "libc", +] + +[[package]] +name = "hex" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "644f9158b2f133fd50f5fb3242878846d9eb792e445c893805ff0e3824006e35" +dependencies = [ + "serde", ] [[package]] name = "home" -version = "0.5.11" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" +checksum = "2456aef2e6b6a9784192ae780c0f15bc57df0e918585282325e8c8ac27737654" dependencies = [ - "windows-sys 0.59.0", -] - -[[package]] -name = "hostname" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56f203cd1c76362b69e3863fd987520ac36cf70a8c92627449b2f64a8cf7d65" -dependencies = [ - "cfg-if", - "libc", - "windows-link", + "winapi", ] [[package]] name = "http" -version = "1.3.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" +checksum = "84129d298a6d57d246960ff8eb831ca4af3f96d29e2e28848dae275408658e26" dependencies = [ - "bytes", + "bytes 0.5.6", "fnv", "itoa", ] [[package]] name = "http-body" -version = "1.0.1" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +checksum = "5dfb77c123b4e2f72a2069aeae0b4b4949cc7e966df277813fc16347e7549737" dependencies = [ - "bytes", + "bytes 1.0.1", "http", -] - -[[package]] -name = "http-body-util" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" -dependencies = [ - "bytes", - "futures-core", - "http", - "http-body", "pin-project-lite", ] [[package]] name = "httparse" -version = "1.10.1" +version = "1.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" +checksum = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9" [[package]] name = "httpdate" -version = "1.0.3" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +checksum = "494b4d60369511e7dea41cf646832512a94e542f68bb9c49e54518e0f468eb47" [[package]] -name = "human_format" -version = "1.1.0" +name = "humantime" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c3b1f728c459d27b12448862017b96ad4767b1ec2ec5e6434e99f1577f085b8" +checksum = "3c1ad908cc71012b7bea4d0c53ba96a8cba9962f048fa68d143376143d863b7a" [[package]] name = "hyper" -version = "1.6.0" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" +checksum = "8bf09f61b52cfcf4c00de50df88ae423d6c02354e385a86341133b5338630ad1" dependencies = [ - "bytes", + "bytes 1.0.1", "futures-channel", + "futures-core", "futures-util", "h2", "http", "http-body", "httparse", + "httpdate", "itoa", - "pin-project-lite", - "smallvec", + "pin-project", + "socket2", "tokio", + "tower-service", + "tracing", "want", ] [[package]] name = "hyper-rustls" -version = "0.27.5" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" +checksum = "5f9f7a97316d44c0af9b0301e65010573a853a9fc97046d7331d7f6bc0fd5a64" dependencies = [ "futures-util", - "http", "hyper", - "hyper-util", + "log", "rustls", - "rustls-native-certs", - "rustls-pki-types", "tokio", "tokio-rustls", - "tower-service", - "webpki-roots", + "webpki", ] [[package]] name = "hyper-tls" -version = "0.6.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" dependencies = [ - "bytes", - "http-body-util", + "bytes 1.0.1", "hyper", - "hyper-util", "native-tls", "tokio", "tokio-native-tls", - "tower-service", -] - -[[package]] -name = "hyper-util" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497bbc33a26fdd4af9ed9c70d63f61cf56a938375fbb32df34db9b1cd6d643f2" -dependencies = [ - "bytes", - "futures-channel", - "futures-util", - "http", - "http-body", - "hyper", - "libc", - "pin-project-lite", - "socket2", - "tokio", - "tower-service", - "tracing", -] - -[[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.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d" - -[[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.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7" - -[[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.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2" - -[[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", ] [[package]] name = "idna" -version = "1.0.3" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +checksum = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" 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", + "matches", + "unicode-bidi", + "unicode-normalization", ] [[package]] name = "indexmap" -version = "2.9.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +checksum = "4fb1fa934250de4de8aef298d81c729a7d33d8c239daa3a7575e6b92bfc7313b" dependencies = [ - "equivalent", - "hashbrown 0.15.2", + "autocfg", + "hashbrown", ] [[package]] -name = "io-close" -version = "0.3.7" +name = "instant" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cadcf447f06744f8ce713d2d6239bb5bde2c357a452397a9ed90c625da390bc" +checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec" dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "ipconfig" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" -dependencies = [ - "socket2", - "widestring", - "windows-sys 0.48.0", - "winreg 0.50.0", + "cfg-if 1.0.0", ] [[package]] name = "ipnet" -version = "2.11.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" - -[[package]] -name = "is_ci" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7655c9839580ee829dfacba1d1278c2b7883e50a277ff7541299489d6bdfdc45" - -[[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.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" -dependencies = [ - "either", -] +checksum = "47be2f14c678be2fdcab04ab1171db51b2762ce6f0a8ee87c8dd4a04ed216135" [[package]] name = "itoa" -version = "1.0.15" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" - -[[package]] -name = "jiff" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ad87c89110f55e4cd4dc2893a9790820206729eaf221555f742d540b0724a0" -dependencies = [ - "jiff-static", - "jiff-tzdb-platform", - "log", - "portable-atomic", - "portable-atomic-util", - "serde", - "windows-sys 0.59.0", -] - -[[package]] -name = "jiff-static" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d076d5b64a7e2fe6f0743f02c43ca4a6725c0f904203bfe276a5b3e793103605" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "jiff-tzdb" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1283705eb0a21404d2bfd6eef2a7593d240bc42a0bdb39db0ad6fa2ec026524" - -[[package]] -name = "jiff-tzdb-platform" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "875a5a69ac2bab1a891711cf5eccbec1ce0341ea805560dcd90b7a2e925132e8" -dependencies = [ - "jiff-tzdb", -] +checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" [[package]] name = "jobserver" -version = "0.1.33" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" +checksum = "5c71313ebb9439f74b00d9d2dcec36440beaf57a6aa0623068441dd7cd81a7f2" dependencies = [ - "getrandom 0.3.2", "libc", ] -[[package]] -name = "jobslot" -version = "0.2.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c14ffb4145759d1032337501fb73b427c754a5f37d89dfe281dd89f48048e6ef" -dependencies = [ - "cfg-if", - "derive_destructure2", - "getrandom 0.3.2", - "libc", - "scopeguard", - "tokio", - "windows-sys 0.59.0", -] - [[package]] name = "js-sys" -version = "0.3.77" +version = "0.3.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +checksum = "cf3d7383929f7c9c7c2d0fa596f325832df98c3704f2c60553080f7127a58175" dependencies = [ - "once_cell", "wasm-bindgen", ] -[[package]] -name = "kstring" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "558bf9508a558512042d3095138b1f7b8fe90c5467d94f9f1da28b3731c5dbd1" -dependencies = [ - "static_assertions", -] - [[package]] name = "lazy_static" -version = "1.5.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" - -[[package]] -name = "leon" -version = "3.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42a865ffec5587961f5afc6d365bccb304f4feaa1928f4fe94c91c9d210d7310" -dependencies = [ - "miette", - "thiserror 2.0.12", -] - -[[package]] -name = "leon-macros" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0c7ff357e507638488e191166c6e396ff53f921664547d40fe63aa38e5014b0" -dependencies = [ - "leon", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "libbz2-rs-sys" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0864a00c8d019e36216b69c2c4ce50b83b7bd966add3cf5ba554ec44f8bebcf5" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.172" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" +checksum = "9385f66bf6105b241aa65a61cb923ef20efc665cb9f9bb50ac2f0c4b7f378d41" [[package]] -name = "libmimalloc-sys" -version = "0.1.42" +name = "libgit2-sys" +version = "0.12.17+1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec9d6fac27761dabcd4ee73571cdb06b7022dc99089acbe5435691edffaac0f4" +checksum = "f4ebdf65ca745126df8824688637aa0535a88900b83362d8ca63893bcf4e8841" dependencies = [ "cc", "libc", + "libssh2-sys", + "libz-sys", + "openssl-sys", + "pkg-config", ] [[package]] -name = "libredox" -version = "0.1.3" +name = "libssh2-sys" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +checksum = "df40b13fe7ea1be9b9dffa365a51273816c345fc1811478b57ed7d964fbfc4ce" dependencies = [ - "bitflags 2.9.0", + "cc", "libc", - "redox_syscall 0.5.11", + "libz-sys", + "openssl-sys", + "pkg-config", + "vcpkg", ] [[package]] -name = "libz-ng-sys" -version = "1.1.22" +name = "libz-sys" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7118c2c2a3c7b6edc279a8b19507672b9c4d716f95e671172dfa4e23f9fd824" +checksum = "602113192b08db8f38796c4e85c39e960c145965140e918018bcde1952429655" dependencies = [ - "cmake", + "cc", "libc", + "pkg-config", + "vcpkg", ] -[[package]] -name = "libz-rs-sys" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6489ca9bd760fe9642d7644e827b0c9add07df89857b0416ee15c1cc1a3b8c5a" -dependencies = [ - "zlib-rs", -] - -[[package]] -name = "linux-raw-sys" -version = "0.4.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" - -[[package]] -name = "linux-raw-sys" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" - -[[package]] -name = "litemap" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" - [[package]] name = "lock_api" -version = "0.4.12" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +checksum = "5a3c91c24eae6777794bb1997ad98bbb87daf92890acab859f7eaa4320333176" dependencies = [ - "autocfg", "scopeguard", ] [[package]] name = "log" -version = "0.4.27" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" - -[[package]] -name = "loom" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" dependencies = [ - "cfg-if", - "generator", - "scoped-tls", - "tracing", - "tracing-subscriber", -] - -[[package]] -name = "lzma-rs" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "297e814c836ae64db86b36cf2a557ba54368d03f6afcd7d947c266692f71115e" -dependencies = [ - "byteorder", - "crc", + "cfg-if 1.0.0", ] [[package]] name = "lzma-sys" -version = "0.1.20" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fda04ab3764e6cde78b9974eec4f779acaba7c4e84b36eca3cf77c581b85d27" +checksum = "bdb4b7c3eddad11d3af9e86c487607d2d2442d185d848575365c4856ba96d619" dependencies = [ "cc", "libc", @@ -2834,498 +843,214 @@ dependencies = [ ] [[package]] -name = "matchers" -version = "0.1.0" +name = "matches" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" -dependencies = [ - "regex-automata 0.1.10", -] - -[[package]] -name = "maybe-async" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cf92c10c7e361d6b99666ec1c6f9805b0bea2c3bd8c78dc6fe98ac5bd78db11" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "maybe-owned" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4facc753ae494aeb6e3c22f839b158aebd4f9270f55cd3c79906c45476c47ab4" -dependencies = [ - "serde", -] - -[[package]] -name = "memalloc" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df39d232f5c40b0891c10216992c2f250c054105cb1e56f0fc9032db6203ecc1" +checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" [[package]] name = "memchr" -version = "2.7.4" +version = "2.3.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 = "miette" -version = "7.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a955165f87b37fd1862df2a59547ac542c77ef6d17c666f619d1ad22dd89484" -dependencies = [ - "backtrace", - "backtrace-ext", - "cfg-if", - "miette-derive", - "owo-colors", - "supports-color", - "supports-hyperlinks", - "supports-unicode", - "terminal_size", - "textwrap", - "thiserror 1.0.69", - "unicode-width 0.1.14", -] - -[[package]] -name = "miette-derive" -version = "7.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf45bf44ab49be92fd1227a3be6fc6f617f1a337c06af54981048574d8783147" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "mimalloc" -version = "0.1.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "995942f432bbb4822a7e9c3faa87a695185b0d09273ba85f097b54f4e458f2af" -dependencies = [ - "libmimalloc-sys", -] +checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" [[package]] name = "mime" -version = "0.3.17" +version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" - -[[package]] -name = "minisign-verify" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6367d84fb54d4242af283086402907277715b8fe46976963af5ebf173f8efba3" +checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" [[package]] name = "miniz_oxide" -version = "0.7.4" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" +checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435" dependencies = [ - "adler", -] - -[[package]] -name = "miniz_oxide" -version = "0.8.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" -dependencies = [ - "adler2", + "adler32", ] [[package]] name = "mio" -version = "1.0.3" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" +checksum = "cf80d3e903b34e0bd7282b218398aec54e082c840d9baf8339e0080a0c542956" dependencies = [ "libc", - "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.52.0", + "log", + "miow", + "ntapi", + "winapi", ] [[package]] -name = "moka" -version = "0.12.10" +name = "miow" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9321642ca94a4282428e6ea4af8cc2ca4eac48ac7a6a4ea8f33f76d0ce70926" +checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" dependencies = [ - "crossbeam-channel", - "crossbeam-epoch", - "crossbeam-utils", - "loom", - "parking_lot", - "portable-atomic", - "rustc_version", - "smallvec", - "tagptr", - "thiserror 1.0.69", - "uuid", + "winapi", ] [[package]] name = "native-tls" -version = "0.2.14" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" +checksum = "b8d96b2e1c8da3957d58100b09f102c6d9cfdfced01b7ec5a8974044bb09dbd4" dependencies = [ + "lazy_static", "libc", "log", "openssl", "openssl-probe", "openssl-sys", "schannel", - "security-framework 2.11.1", + "security-framework", "security-framework-sys", "tempfile", ] [[package]] -name = "netlink-packet-core" -version = "0.7.0" +name = "ntapi" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72724faf704479d67b388da142b186f916188505e7e0b26719019c525882eda4" +checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" dependencies = [ - "anyhow", - "byteorder", - "netlink-packet-utils", -] - -[[package]] -name = "netlink-packet-route" -version = "0.17.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053998cea5a306971f88580d0829e90f270f940befd7cf928da179d4187a5a66" -dependencies = [ - "anyhow", - "bitflags 1.3.2", - "byteorder", - "libc", - "netlink-packet-core", - "netlink-packet-utils", -] - -[[package]] -name = "netlink-packet-utils" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ede8a08c71ad5a95cdd0e4e52facd37190977039a4704eb82a283f713747d34" -dependencies = [ - "anyhow", - "byteorder", - "paste", - "thiserror 1.0.69", -] - -[[package]] -name = "netlink-sys" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16c903aa70590cb93691bf97a767c8d1d6122d2cc9070433deb3bbf36ce8bd23" -dependencies = [ - "bytes", - "libc", - "log", -] - -[[package]] -name = "nix" -version = "0.27.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" -dependencies = [ - "bitflags 2.9.0", - "cfg-if", - "libc", -] - -[[package]] -name = "normalize-path" -version = "0.2.1" - -[[package]] -name = "nu-ansi-term" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" -dependencies = [ - "overload", "winapi", ] [[package]] -name = "num-conv" -version = "0.1.0" +name = "num-integer" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +dependencies = [ + "autocfg", + "num-traits", +] [[package]] name = "num-traits" -version = "0.2.19" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" dependencies = [ "autocfg", ] [[package]] -name = "num_enum" -version = "0.7.3" +name = "num_cpus" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" -dependencies = [ - "num_enum_derive", -] - -[[package]] -name = "num_enum_derive" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "num_threads" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" dependencies = [ + "hermit-abi", "libc", ] -[[package]] -name = "object" -version = "0.36.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" -dependencies = [ - "memchr", -] - -[[package]] -name = "oem_cp" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "330138902ab4dab09a86e6b7ab7ddeffb5f8435d52fe0df1bce8b06a17b10ee4" -dependencies = [ - "phf", - "phf_codegen", - "serde", - "serde_json", -] - [[package]] name = "once_cell" -version = "1.21.3" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" -dependencies = [ - "critical-section", - "portable-atomic", -] +checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0" [[package]] name = "openssl" -version = "0.10.72" +version = "0.10.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fedfea7d58a1f73118430a55da6a286e7b044961736ce96a16a17068ea25e5da" +checksum = "038d43985d1ddca7a9900630d8cd031b56e4794eecc2e9ea39dd17aa04399a70" dependencies = [ - "bitflags 2.9.0", - "cfg-if", + "bitflags", + "cfg-if 1.0.0", "foreign-types", + "lazy_static", "libc", - "once_cell", - "openssl-macros", "openssl-sys", ] -[[package]] -name = "openssl-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "openssl-probe" -version = "0.1.6" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" - -[[package]] -name = "openssl-src" -version = "300.5.0+3.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8ce546f549326b0e6052b649198487d91320875da901e7bd11a06d1ee3f9c2f" -dependencies = [ - "cc", -] +checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" [[package]] name = "openssl-sys" -version = "0.9.107" +version = "0.9.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8288979acd84749c744a9014b4382d42b8f7b2592847b5afb2ed29e5d16ede07" +checksum = "921fc71883267538946025deffb622905ecad223c28efbfdef9bb59a0175f3e6" dependencies = [ + "autocfg", "cc", "libc", - "openssl-src", "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 = "oval" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "135cef32720c6746450d910890b0b69bcba2bbf6f85c9f4583df13fe415de828" - -[[package]] -name = "overload" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" - -[[package]] -name = "ownable" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dcba94d1536fcc470287d96fd26356c38da8215fdb9a74285b09621f35d9350" -dependencies = [ - "ownable-macro", -] - -[[package]] -name = "ownable-macro" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2c91d2781624dec1234581a1a01e63638f36546ad72ee82873ac1b84f41117b" -dependencies = [ - "proc-macro-error", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "owo-colors" -version = "4.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1036865bb9422d3300cf723f657c2851d0e9ab12567854b1f4eba3d77decf564" - [[package]] name = "parking_lot" -version = "0.12.3" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb" dependencies = [ + "instant", "lock_api", "parking_lot_core", ] [[package]] name = "parking_lot_core" -version = "0.9.10" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", + "instant", "libc", - "redox_syscall 0.5.11", + "redox_syscall 0.2.5", "smallvec", - "windows-targets 0.52.6", + "winapi", ] -[[package]] -name = "paste" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" - [[package]] name = "percent-encoding" -version = "2.3.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" [[package]] -name = "phf" -version = "0.11.3" +name = "pest" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" +checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" dependencies = [ - "phf_shared", + "ucd-trie", ] [[package]] -name = "phf_codegen" -version = "0.11.3" +name = "pin-project" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" +checksum = "9ccc2237c2c489783abd8c4c80e5450fc0e98644555b1364da68cc29aa151ca7" dependencies = [ - "phf_generator", - "phf_shared", + "pin-project-internal", ] [[package]] -name = "phf_generator" -version = "0.11.3" +name = "pin-project-internal" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" +checksum = "f8e8d2bf0b23038a4424865103a4df472855692821aab4e4f5c3312d461d9e5f" dependencies = [ - "phf_shared", - "rand 0.8.5", -] - -[[package]] -name = "phf_shared" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" -dependencies = [ - "siphasher", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "pin-project-lite" -version = "0.2.16" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" +checksum = "6b063f57ec186e6140e2b8b6921e5f1bd89c7356dda5b33acc5401203ca6131c" [[package]] name = "pin-utils" @@ -3335,59 +1060,15 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.32" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" - -[[package]] -name = "portable-atomic" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e" - -[[package]] -name = "portable-atomic-util" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" -dependencies = [ - "portable-atomic", -] - -[[package]] -name = "positioned-io" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8078ce4d22da5e8f57324d985cc9befe40c49ab0507a192d6be9e59584495c9" -dependencies = [ - "byteorder", - "libc", - "winapi", -] - -[[package]] -name = "powerfmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" +checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" [[package]] name = "ppv-lite86" -version = "0.2.21" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" -dependencies = [ - "zerocopy 0.8.24", -] - -[[package]] -name = "proc-macro-crate" -version = "3.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" -dependencies = [ - "toml_edit", -] +checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" [[package]] name = "proc-macro-error" @@ -3398,6 +1079,7 @@ dependencies = [ "proc-macro-error-attr", "proc-macro2", "quote", + "syn", "version_check", ] @@ -3412,508 +1094,286 @@ dependencies = [ "version_check", ] +[[package]] +name = "proc-macro-hack" +version = "0.5.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" + +[[package]] +name = "proc-macro-nested" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eba180dafb9038b050a4c280019bbedf9f2467b61e5d892dcad585bb57aadc5a" + [[package]] name = "proc-macro2" -version = "1.0.95" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" dependencies = [ - "unicode-ident", -] - -[[package]] -name = "prodash" -version = "29.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f04bb108f648884c23b98a0e940ebc2c93c0c3b89f04dbaf7eb8256ce617d1bc" -dependencies = [ - "bytesize 2.0.1", - "human_format", - "log", - "parking_lot", -] - -[[package]] -name = "quinn" -version = "0.11.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3bd15a6f2967aef83887dcb9fec0014580467e33720d073560cf015a5683012" -dependencies = [ - "bytes", - "cfg_aliases", - "futures-io", - "pin-project-lite", - "quinn-proto", - "quinn-udp", - "rustc-hash", - "rustls", - "socket2", - "thiserror 2.0.12", - "tokio", - "tracing", - "web-time", -] - -[[package]] -name = "quinn-proto" -version = "0.11.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b820744eb4dc9b57a3398183639c511b5a26d2ed702cedd3febaa1393caa22cc" -dependencies = [ - "bytes", - "getrandom 0.3.2", - "rand 0.9.1", - "ring", - "rustc-hash", - "rustls", - "rustls-pki-types", - "slab", - "thiserror 2.0.12", - "tinyvec", - "tracing", - "web-time", -] - -[[package]] -name = "quinn-udp" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "541d0f57c6ec747a90738a52741d3221f7960e8ac2f0ff4b1a63680e033b4ab5" -dependencies = [ - "cfg_aliases", - "libc", - "once_cell", - "socket2", - "tracing", - "windows-sys 0.59.0", + "unicode-xid", ] [[package]] name = "quote" -version = "1.0.40" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +checksum = "991431c3519a3f36861882da93630ce66b52918dcf1b8e2fd66b397fc96f28df" dependencies = [ "proc-macro2", ] -[[package]] -name = "r-efi" -version = "5.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" - [[package]] name = "rand" -version = "0.8.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" dependencies = [ - "rand_core 0.6.4", + "fuchsia-cprng", + "libc", + "rand_core 0.3.1", + "rdrand", + "winapi", ] [[package]] name = "rand" -version = "0.9.1" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" dependencies = [ + "getrandom", + "libc", "rand_chacha", - "rand_core 0.9.3", + "rand_core 0.5.1", + "rand_hc", ] [[package]] name = "rand_chacha" -version = "0.9.0" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" dependencies = [ "ppv-lite86", - "rand_core 0.9.3", + "rand_core 0.5.1", ] [[package]] name = "rand_core" -version = "0.6.4" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +dependencies = [ + "rand_core 0.4.2", +] [[package]] name = "rand_core" -version = "0.9.3" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" dependencies = [ - "getrandom 0.3.2", + "getrandom", ] [[package]] -name = "rc-zip" -version = "5.3.1" +name = "rand_hc" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebde715984a68b306e5b41884cbcb8158e0e1dbe6e2841212983333b1662c416" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" dependencies = [ - "bzip2", - "chardetng", - "chrono", - "crc32fast", - "deflate64", - "encoding_rs", - "lzma-rs", - "miniz_oxide 0.7.4", - "num_enum", - "oem_cp", - "oval", - "ownable", - "thiserror 1.0.69", - "tracing", - "winnow 0.5.40", - "zstd", + "rand_core 0.5.1", ] [[package]] -name = "rc-zip-sync" -version = "4.2.6" +name = "rdrand" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdd21d17384a7da18df6b70f49bc2daa84d15e0fa0929bc635541383b3827190" +checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" dependencies = [ - "oval", - "positioned-io", - "rc-zip", - "tracing", + "rand_core 0.3.1", ] [[package]] name = "redox_syscall" -version = "0.3.5" +version = "0.1.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" -dependencies = [ - "bitflags 1.3.2", -] +checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" [[package]] name = "redox_syscall" -version = "0.5.11" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2f103c6d277498fbceb16e84d317e2a400f160f46904d5f5410848c829511a3" +checksum = "94341e4e44e24f6b591b59e47a8a027df12e008d73fd5672dbea9cc22f4507d9" dependencies = [ - "bitflags 2.9.0", + "bitflags", ] [[package]] name = "redox_users" -version = "0.5.0" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b" +checksum = "de0737333e7a9502c789a36d7c7fa6092a49895d4faa31ca5df163857ded2e9d" dependencies = [ - "getrandom 0.2.15", - "libredox", - "thiserror 2.0.12", -] - -[[package]] -name = "reflink-copy" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78c81d000a2c524133cc00d2f92f019d399e57906c3b7119271a2495354fe895" -dependencies = [ - "cfg-if", - "libc", - "rustix 1.0.5", - "windows 0.61.1", + "getrandom", + "redox_syscall 0.1.57", + "rust-argon2", ] [[package]] name = "regex" -version = "1.11.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +checksum = "38cf2c13ed4745de91a5eb834e11c00bcc3709e773173b2ce4c56c9fbde04b9c" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.9", - "regex-syntax 0.8.5", -] - -[[package]] -name = "regex-automata" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" -dependencies = [ - "regex-syntax 0.6.29", -] - -[[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 0.8.5", + "regex-syntax", + "thread_local", ] [[package]] name = "regex-syntax" -version = "0.6.29" +version = "0.6.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" +checksum = "3b181ba2dcf07aaccad5448e8ead58db5b742cf85dfe035e2227f137a539a189" [[package]] -name = "regex-syntax" -version = "0.8.5" +name = "remove_dir_all" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] [[package]] name = "reqwest" -version = "0.12.15" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d19c46a6fdd48bc4dab94b6103fccc55d34c67cc0ad04653aad4ea2a07cd7bbb" +checksum = "bf12057f289428dbf5c591c74bf10392e4a8003f993405a902f20117019022d4" dependencies = [ - "async-compression", "base64", - "bytes", + "bytes 1.0.1", "encoding_rs", - "futures-channel", "futures-core", "futures-util", - "h2", - "h3", - "h3-quinn", "http", "http-body", - "http-body-util", "hyper", "hyper-rustls", "hyper-tls", - "hyper-util", "ipnet", "js-sys", + "lazy_static", "log", "mime", "native-tls", - "once_cell", "percent-encoding", "pin-project-lite", - "quinn", "rustls", - "rustls-native-certs", - "rustls-pemfile", - "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", - "slab", - "sync_wrapper", - "system-configuration 0.6.1", "tokio", "tokio-native-tls", "tokio-rustls", - "tokio-util", - "tower", - "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", - "wasm-streams", "web-sys", "webpki-roots", - "windows-registry", -] - -[[package]] -name = "resolv-conf" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48375394603e3dd4b2d64371f7148fd8c7baa2680e28741f2cb8d23b59e3d4c4" -dependencies = [ - "hostname", + "winreg", ] [[package]] name = "ring" -version = "0.17.14" +version = "0.16.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" dependencies = [ "cc", - "cfg-if", - "getrandom 0.2.15", "libc", + "once_cell", + "spin", "untrusted", - "windows-sys 0.52.0", + "web-sys", + "winapi", ] [[package]] -name = "rustc-demangle" -version = "0.1.24" +name = "rust-argon2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" - -[[package]] -name = "rustc-hash" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" - -[[package]] -name = "rustc_version" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +checksum = "4b18820d944b33caa75a71378964ac46f58517c92b6ae5f762636247c09e78fb" dependencies = [ - "semver", -] - -[[package]] -name = "rustix" -version = "0.38.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" -dependencies = [ - "bitflags 2.9.0", - "errno 0.3.11", - "libc", - "linux-raw-sys 0.4.15", - "windows-sys 0.59.0", -] - -[[package]] -name = "rustix" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d97817398dd4bb2e6da002002db259209759911da105da92bec29ccb12cf58bf" -dependencies = [ - "bitflags 2.9.0", - "errno 0.3.11", - "libc", - "linux-raw-sys 0.9.4", - "windows-sys 0.59.0", + "base64", + "blake2b_simd", + "constant_time_eq", + "crossbeam-utils", ] [[package]] name = "rustls" -version = "0.23.26" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df51b5869f3a441595eac5e8ff14d486ff285f7b8c0df8770e49c3b56351f0f0" +checksum = "064fd21ff87c6e87ed4506e68beb42459caa4a0e2eb144932e6776768556980b" dependencies = [ + "base64", "log", - "once_cell", "ring", - "rustls-pki-types", - "rustls-webpki", - "subtle", - "zeroize", + "sct", + "webpki", ] [[package]] -name = "rustls-native-certs" -version = "0.8.1" +name = "ryu" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcff2dd52b58a8d98a70243663a0d234c4e2b79235637849d15913394a247d3" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" + +[[package]] +name = "schannel" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" dependencies = [ - "openssl-probe", - "rustls-pki-types", - "schannel", - "security-framework 3.2.0", + "lazy_static", + "winapi", ] [[package]] -name = "rustls-pemfile" -version = "2.2.0" +name = "scopeguard" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" -dependencies = [ - "rustls-pki-types", -] +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] -name = "rustls-pki-types" -version = "1.11.0" +name = "sct" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c" -dependencies = [ - "web-time", -] - -[[package]] -name = "rustls-webpki" -version = "0.103.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fef8b8769aaccf73098557a87cd1816b4f9c7c16811c9c77142aa695c16f2c03" +checksum = "e3042af939fca8c3453b7af0f1c66e533a15a86169e39de2657310ade8f98d3c" dependencies = [ "ring", - "rustls-pki-types", "untrusted", ] -[[package]] -name = "rustversion" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" - -[[package]] -name = "ryu" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" - -[[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 = "schannel" -version = "0.1.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" -dependencies = [ - "windows-sys 0.59.0", -] - -[[package]] -name = "scoped-tls" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" - -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - [[package]] name = "security-framework" -version = "2.11.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +checksum = "c1759c2e3c8580017a484a7ac56d3abc5a6c1feadf88db2f3633f12ae4268c69" dependencies = [ - "bitflags 2.9.0", - "core-foundation 0.9.4", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271720403f46ca04f7ba6f55d438f8bd878d6b8ca0a1046e8228c4145bcbb316" -dependencies = [ - "bitflags 2.9.0", - "core-foundation 0.10.0", + "bitflags", + "core-foundation", "core-foundation-sys", "libc", "security-framework-sys", @@ -3921,9 +1381,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.14.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" +checksum = "f99b9d5e26d2a71633cc4f2ebae7cc9f874044e0c351a27e17892d76dce5678b" dependencies = [ "core-foundation-sys", "libc", @@ -3931,36 +1391,37 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.26" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" +checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" dependencies = [ + "semver-parser", "serde", ] [[package]] -name = "serde" -version = "1.0.219" +name = "semver-parser" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" +dependencies = [ + "pest", +] + +[[package]] +name = "serde" +version = "1.0.119" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bdd36f49e35b61d49efd8aa7fc068fd295961fd2286d0b2ee9a4c7a14e99cc3" dependencies = [ "serde_derive", ] -[[package]] -name = "serde-tuple-vec-map" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a04d0ebe0de77d7d445bb729a895dcb0a288854b267ca85f030ce51cdc578c82" -dependencies = [ - "serde", -] - [[package]] name = "serde_derive" -version = "1.0.219" +version = "1.0.119" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +checksum = "552954ce79a059ddd5fd68c271592374bd15cab2274970380c000118aeffe1cd" dependencies = [ "proc-macro2", "quote", @@ -3969,30 +1430,20 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.140" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +checksum = "4fceb2595057b6891a4ee808f70054bd2d12f0e97f1cbb78689b59f676df325a" dependencies = [ "itoa", - "memchr", "ryu", "serde", ] -[[package]] -name = "serde_spanned" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" -dependencies = [ - "serde", -] - [[package]] name = "serde_urlencoded" -version = "0.7.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +checksum = "edfa57a7f8d9c1d260a549e7224100f6c43d43f9103e06dd8b4095a9b2b43ce9" dependencies = [ "form_urlencoded", "itoa", @@ -4001,117 +1452,62 @@ dependencies = [ ] [[package]] -name = "sha1" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "sha1-checked" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89f599ac0c323ebb1c6082821a54962b839832b03984598375bff3975b804423" -dependencies = [ - "digest", - "sha1", -] - -[[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 = "sharded-slab" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" -dependencies = [ - "lazy_static", -] - -[[package]] -name = "shell-words" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" - -[[package]] -name = "shlex" +name = "signal-hook-registry" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - -[[package]] -name = "signal-hook-registry" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +checksum = "16f1d0fef1604ba8f7a073c7e701f213e056707210e9020af4528e0101ce11a6" dependencies = [ "libc", ] [[package]] -name = "simple-git" -version = "0.2.19" +name = "simplelog" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "532b9541e0f6fe54f7a721953d81371b58b601d618294bd8eba284df79acefff" +checksum = "4bc0ffd69814a9b251d43afcabf96dad1b29f5028378056257be9e3fecc9f720" dependencies = [ - "compact_str", - "derive_destructure2", - "gix", - "thiserror 1.0.69", - "tokio", - "tracing", + "chrono", + "log", + "termcolor", ] -[[package]] -name = "siphasher" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" - [[package]] name = "slab" -version = "0.4.9" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] +checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" [[package]] name = "smallvec" -version = "1.15.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" +checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" [[package]] -name = "socket2" -version = "0.5.9" +name = "smartstring" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef" +checksum = "1ada87540bf8ef4cf8a1789deb175626829bb59b1fefd816cf7f7f55efcdbae9" dependencies = [ - "libc", - "windows-sys 0.52.0", + "serde", + "static_assertions", ] [[package]] -name = "stable_deref_trait" -version = "1.2.0" +name = "socket2" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +checksum = "9e3dfc207c526015c632472a77be09cf1b6e46866581aecae5cc38fb4235dea2" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[package]] name = "static_assertions" @@ -4121,208 +1517,131 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "strsim" -version = "0.11.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + +[[package]] +name = "structopt" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5277acd7ee46e63e5168a80734c9f6ee81b1367a7d8772a2d765df2a3705d28c" +dependencies = [ + "clap", + "lazy_static", + "structopt-derive", +] + +[[package]] +name = "structopt-derive" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ba9cdfda491b814720b6b06e0cac513d922fc407582032e8706e9f137976f90" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] [[package]] name = "strum" -version = "0.27.1" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f64def088c51c9510a8579e3c5d67c65349dcf755e5479ad3d010aa6454e2c32" +checksum = "7318c509b5ba57f18533982607f24070a55d353e90d4cae30c467cdb2ad5ac5c" [[package]] name = "strum_macros" -version = "0.27.1" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c77a8c5abcaf0f9ce05d62342b7d298c346515365c36b673df4ebe3ced01fde8" +checksum = "ee8bc6b87a5112aeeab1f4a9f7ab634fe6cbefc4850006df31267f4cfb9e3149" dependencies = [ "heck", "proc-macro2", "quote", - "rustversion", "syn", ] -[[package]] -name = "subtle" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" - -[[package]] -name = "supports-color" -version = "3.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c64fc7232dd8d2e4ac5ce4ef302b1d81e0b80d055b9d77c7c4f51f6aa4c867d6" -dependencies = [ - "is_ci", -] - -[[package]] -name = "supports-hyperlinks" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "804f44ed3c63152de6a9f90acbea1a110441de43006ea51bcce8f436196a288b" - -[[package]] -name = "supports-unicode" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7401a30af6cb5818bb64852270bb722533397edcfc7344954a38f420819ece2" - [[package]] name = "syn" -version = "2.0.100" +version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" +checksum = "cc60a3d73ea6594cd712d830cc1f0390fd71542d8c8cd24e70cc54cdfd5e05d5" dependencies = [ "proc-macro2", "quote", - "unicode-ident", + "unicode-xid", ] [[package]] -name = "sync_wrapper" -version = "1.0.2" +name = "tar" +version = "0.4.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +checksum = "489997b7557e9a43e192c527face4feacc78bfbe6eed67fd55c4c9e381cba290" dependencies = [ - "futures-core", -] - -[[package]] -name = "synstructure" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "system-configuration" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" -dependencies = [ - "bitflags 1.3.2", - "core-foundation 0.9.4", - "system-configuration-sys 0.5.0", -] - -[[package]] -name = "system-configuration" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" -dependencies = [ - "bitflags 2.9.0", - "core-foundation 0.9.4", - "system-configuration-sys 0.6.0", -] - -[[package]] -name = "system-configuration-sys" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" -dependencies = [ - "core-foundation-sys", + "filetime", "libc", + "redox_syscall 0.1.57", + "xattr", ] [[package]] -name = "system-configuration-sys" -version = "0.6.0" +name = "tempdir" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +checksum = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" dependencies = [ - "core-foundation-sys", - "libc", + "rand 0.4.6", + "remove_dir_all", ] -[[package]] -name = "tagptr" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b2093cf4c8eb1e67749a6762251bc9cd836b6fc171623bd0a9d324d37af2417" - -[[package]] -name = "target-lexicon" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e502f78cdbb8ba4718f566c418c52bc729126ffd16baee5baa718cf25dd5a69a" - [[package]] name = "tempfile" -version = "3.19.1" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf" +checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" dependencies = [ - "fastrand", - "getrandom 0.3.2", - "once_cell", - "rustix 1.0.5", - "windows-sys 0.59.0", + "cfg-if 0.1.10", + "libc", + "rand 0.7.3", + "redox_syscall 0.1.57", + "remove_dir_all", + "winapi", ] [[package]] -name = "terminal_size" -version = "0.4.2" +name = "termcolor" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45c6481c4829e4cc63825e62c49186a34538b7b2750b73b266581ffb612fb5ed" +checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" dependencies = [ - "rustix 1.0.5", - "windows-sys 0.59.0", + "winapi-util", ] [[package]] name = "textwrap" -version = "0.16.2" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c13547615a44dc9c452a8a534638acdf07120d4b6847c8178705da06306a3057" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" dependencies = [ - "unicode-linebreak", - "unicode-width 0.2.0", + "unicode-width", ] [[package]] name = "thiserror" -version = "1.0.69" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +checksum = "e0f4a65597094d4483ddaed134f409b2cb7c1beccf25201a9f73c719254fa98e" dependencies = [ - "thiserror-impl 1.0.69", -] - -[[package]] -name = "thiserror" -version = "2.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" -dependencies = [ - "thiserror-impl 2.0.12", + "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.69" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "thiserror-impl" -version = "2.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +checksum = "7765189610d8241a44529806d6fd1f2e0a08734313a35d5b3a556f92b381f3c0" dependencies = [ "proc-macro2", "quote", @@ -4331,94 +1650,74 @@ dependencies = [ [[package]] name = "thread_local" -version = "1.1.8" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" dependencies = [ - "cfg-if", - "once_cell", + "lazy_static", ] [[package]] name = "time" -version = "0.3.41" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" dependencies = [ - "deranged", - "itoa", "libc", - "num-conv", - "num_threads", - "powerfmt", + "wasi 0.10.0+wasi-snapshot-preview1", + "winapi", +] + +[[package]] +name = "tinytemplate" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2ada8616fad06a2d0c455adc530de4ef57605a8120cc65da9653e0e9623ca74" +dependencies = [ "serde", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" - -[[package]] -name = "time-macros" -version = "0.2.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" -dependencies = [ - "num-conv", - "time-core", -] - -[[package]] -name = "tinystr" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" -dependencies = [ - "displaydoc", - "zerovec", + "serde_json", ] [[package]] name = "tinyvec" -version = "1.9.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71" +checksum = "ccf8dbc19eb42fba10e8feaaec282fb50e2c14b2726d6301dbfeed0f73306a6f" dependencies = [ "tinyvec_macros", ] [[package]] name = "tinyvec_macros" -version = "0.1.1" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "1.44.2" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6b88822cbe49de4185e3a4cbf8321dd487cf5fe0c5c65695fef6346371e9c48" +checksum = "134af885d758d645f0f0505c9a8b3f9bf8a348fd822e112ab5248138348f1722" dependencies = [ - "backtrace", - "bytes", + "autocfg", + "bytes 1.0.1", "libc", + "memchr", "mio", + "num_cpus", + "once_cell", + "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2", "tokio-macros", - "windows-sys 0.52.0", + "winapi", ] [[package]] name = "tokio-macros" -version = "2.5.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +checksum = "caf7b11a536f46a809a8a9f0bb4237020f70ecbf115b842360afb127ea2fda57" dependencies = [ "proc-macro2", "quote", @@ -4427,9 +1726,9 @@ dependencies = [ [[package]] name = "tokio-native-tls" -version = "0.3.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" dependencies = [ "native-tls", "tokio", @@ -4437,400 +1736,190 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.26.2" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" +checksum = "bc6844de72e57df1980054b38be3a9f4702aba4858be64dd700181a8a6d0e1b6" dependencies = [ "rustls", "tokio", -] - -[[package]] -name = "tokio-stream" -version = "0.1.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" -dependencies = [ - "futures-core", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "tokio-tar" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d5714c010ca3e5c27114c1cdeb9d14641ace49874aa5626d7149e47aedace75" -dependencies = [ - "filetime", - "futures-core", - "libc", - "redox_syscall 0.3.5", - "tokio", - "tokio-stream", - "xattr", + "webpki", ] [[package]] name = "tokio-util" -version = "0.7.14" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b9590b93e6fcc1739458317cccd391ad3955e2bde8913edf6f95f9e65a8f034" +checksum = "5143d049e85af7fbc36f5454d990e62c2df705b3589f123b71f441b6b59f443f" dependencies = [ - "bytes", + "bytes 1.0.1", "futures-core", "futures-sink", + "log", "pin-project-lite", "tokio", ] [[package]] name = "toml" -version = "0.8.20" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148" -dependencies = [ - "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" +checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" dependencies = [ "serde", ] -[[package]] -name = "toml_edit" -version = "0.22.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" -dependencies = [ - "indexmap", - "serde", - "serde_spanned", - "toml_datetime", - "winnow 0.7.6", -] - -[[package]] -name = "tower" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" -dependencies = [ - "futures-core", - "futures-util", - "pin-project-lite", - "sync_wrapper", - "tokio", - "tower-layer", - "tower-service", -] - -[[package]] -name = "tower-layer" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" - [[package]] name = "tower-service" -version = "0.3.3" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" +checksum = "e987b6bf443f4b5b3b6f38704195592cca41c5bb7aedd3c3693c7081f8289860" [[package]] name = "tracing" -version = "0.1.41" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +checksum = "9f47026cdc4080c07e49b37087de021820269d996f581aac150ef9e5583eefe3" dependencies = [ - "log", + "cfg-if 1.0.0", "pin-project-lite", - "tracing-attributes", "tracing-core", ] -[[package]] -name = "tracing-attributes" -version = "0.1.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "tracing-core" -version = "0.1.33" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +checksum = "f50de3927f93d202783f4513cda820ab47ef17f624b03c096e86ef00c67e6b5f" dependencies = [ - "once_cell", - "valuable", -] - -[[package]] -name = "tracing-log" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" -dependencies = [ - "log", - "once_cell", - "tracing-core", -] - -[[package]] -name = "tracing-serde" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "704b1aeb7be0d0a84fc9828cae51dab5970fee5088f83d1dd7ee6f6246fc6ff1" -dependencies = [ - "serde", - "tracing-core", -] - -[[package]] -name = "tracing-subscriber" -version = "0.3.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" -dependencies = [ - "matchers", - "nu-ansi-term", - "once_cell", - "regex", - "serde", - "serde_json", - "sharded-slab", - "smallvec", - "thread_local", - "tracing", - "tracing-core", - "tracing-log", - "tracing-serde", + "lazy_static", ] [[package]] name = "try-lock" -version = "0.2.5" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" [[package]] -name = "typenum" -version = "1.18.0" +name = "ucd-trie" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" +checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" [[package]] -name = "uluru" -version = "3.1.0" +name = "unicode-bidi" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c8a2469e56e6e5095c82ccd3afb98dad95f7af7929aab6d8ba8d6e0f73657da" +checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" dependencies = [ - "arrayvec", + "matches", ] -[[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.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" - -[[package]] -name = "unicode-linebreak" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" - [[package]] name = "unicode-normalization" -version = "0.1.24" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" +checksum = "a13e63ab62dbe32aeee58d1c5408d35c36c392bba5d9d3142287219721afe606" dependencies = [ "tinyvec", ] [[package]] -name = "unicode-width" -version = "0.1.14" +name = "unicode-segmentation" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" +checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796" [[package]] name = "unicode-width" -version = "0.2.0" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" +checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" + +[[package]] +name = "unicode-xid" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" [[package]] name = "untrusted" -version = "0.9.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" [[package]] name = "url" -version = "2.5.4" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +checksum = "5909f2b0817350449ed73e8bcd81c8c3c8d9a7a5d8acba4b27db277f1868976e" dependencies = [ "form_urlencoded", "idna", + "matches", "percent-encoding", - "serde", ] -[[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 = "uuid" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9" -dependencies = [ - "getrandom 0.3.2", -] - -[[package]] -name = "valuable" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" - [[package]] name = "vcpkg" -version = "0.2.15" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" +checksum = "b00bca6106a5e23f3eee943593759b7fcddb00554332e856d990c893966879fb" [[package]] -name = "vergen" -version = "8.3.2" +name = "vec_map" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2990d9ea5967266ea0ccf413a4aa5c42a93dbcfda9cb49a97de6931726b12566" -dependencies = [ - "anyhow", - "cargo_metadata", - "cfg-if", - "regex", - "rustc_version", - "rustversion", - "time", -] +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" [[package]] name = "version_check" -version = "0.9.5" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - -[[package]] -name = "vswhom" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be979b7f07507105799e854203b470ff7c78a1639e330a58f183b5fea574608b" -dependencies = [ - "libc", - "vswhom-sys", -] - -[[package]] -name = "vswhom-sys" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb067e4cbd1ff067d1df46c9194b5de0e98efd2810bbc95c5d5e5f25a3231150" -dependencies = [ - "cc", - "libc", -] - -[[package]] -name = "walkdir" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" -dependencies = [ - "same-file", - "winapi-util", -] +checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" [[package]] name = "want" -version = "0.3.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" dependencies = [ + "log", "try-lock", ] [[package]] name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +version = "0.9.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" [[package]] name = "wasi" -version = "0.14.2+wasi-0.2.4" +version = "0.10.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" -dependencies = [ - "wit-bindgen-rt", -] +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" [[package]] name = "wasm-bindgen" -version = "0.2.100" +version = "0.2.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +checksum = "3cd364751395ca0f68cafb17666eee36b63077fb5ecd972bbcd74c90c4bf736e" dependencies = [ - "cfg-if", - "once_cell", - "rustversion", + "cfg-if 1.0.0", + "serde", + "serde_json", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.100" +version = "0.2.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +checksum = "1114f89ab1f4106e5b55e688b828c0ab0ea593a1ea7c094b141b14cbaaec2d62" dependencies = [ "bumpalo", + "lazy_static", "log", "proc-macro2", "quote", @@ -4840,22 +1929,21 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.50" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" +checksum = "1fe9756085a84584ee9457a002b7cdfe0bfff169f45d2591d8be1345a6780e35" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "js-sys", - "once_cell", "wasm-bindgen", "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.100" +version = "0.2.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +checksum = "7a6ac8995ead1f084a8dea1e65f194d0973800c7f571f6edd70adf06ecf77084" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4863,9 +1951,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.100" +version = "0.2.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +checksum = "b5a48c72f299d80557c7c62e37e7225369ecc0c963964059509fbafe917c7549" dependencies = [ "proc-macro2", "quote", @@ -4876,61 +1964,39 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.100" +version = "0.2.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "wasm-streams" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" -dependencies = [ - "futures-util", - "js-sys", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] +checksum = "7e7811dd7f9398f14cc76efd356f98f03aa30419dea46aa810d71e819fc97158" [[package]] name = "web-sys" -version = "0.3.77" +version = "0.3.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +checksum = "222b1ef9334f92a21d3fb53dc3fd80f30836959a90f9274a626d7e06315ba3c3" dependencies = [ "js-sys", "wasm-bindgen", ] [[package]] -name = "web-time" -version = "1.1.0" +name = "webpki" +version = "0.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" dependencies = [ - "js-sys", - "wasm-bindgen", + "ring", + "untrusted", ] [[package]] name = "webpki-roots" -version = "0.26.8" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2210b291f7ea53617fbafcc4939f10914214ec15aace5ba62293a668f322c5c9" +checksum = "aabe153544e473b775453675851ecc86863d2a81d786d741f6b76778f2a48940" dependencies = [ - "rustls-pki-types", + "webpki", ] -[[package]] -name = "widestring" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd7cf3379ca1aac9eea11fba24fd7e315d621f8dfe35c8d7d2be8b793726e07d" - [[package]] name = "winapi" version = "0.3.9" @@ -4949,11 +2015,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.9" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" dependencies = [ - "windows-sys 0.59.0", + "winapi", ] [[package]] @@ -4962,633 +2028,43 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" -dependencies = [ - "windows-targets 0.48.5", -] - -[[package]] -name = "windows" -version = "0.58.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" -dependencies = [ - "windows-core 0.58.0", - "windows-targets 0.52.6", -] - -[[package]] -name = "windows" -version = "0.61.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5ee8f3d025738cb02bad7868bbb5f8a6327501e870bf51f1b455b0a2454a419" -dependencies = [ - "windows-collections", - "windows-core 0.61.0", - "windows-future", - "windows-link", - "windows-numerics", -] - -[[package]] -name = "windows-collections" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" -dependencies = [ - "windows-core 0.61.0", -] - -[[package]] -name = "windows-core" -version = "0.58.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" -dependencies = [ - "windows-implement 0.58.0", - "windows-interface 0.58.0", - "windows-result 0.2.0", - "windows-strings 0.1.0", - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-core" -version = "0.61.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980" -dependencies = [ - "windows-implement 0.60.0", - "windows-interface 0.59.1", - "windows-link", - "windows-result 0.3.2", - "windows-strings 0.4.0", -] - -[[package]] -name = "windows-future" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a1d6bbefcb7b60acd19828e1bc965da6fcf18a7e39490c5f8be71e54a19ba32" -dependencies = [ - "windows-core 0.61.0", - "windows-link", -] - -[[package]] -name = "windows-implement" -version = "0.58.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "windows-implement" -version = "0.60.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "windows-interface" -version = "0.58.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "windows-interface" -version = "0.59.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "windows-link" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" - -[[package]] -name = "windows-numerics" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" -dependencies = [ - "windows-core 0.61.0", - "windows-link", -] - -[[package]] -name = "windows-registry" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3" -dependencies = [ - "windows-result 0.3.2", - "windows-strings 0.3.1", - "windows-targets 0.53.0", -] - -[[package]] -name = "windows-result" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" -dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-result" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-strings" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" -dependencies = [ - "windows-result 0.2.0", - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-strings" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-strings" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97" -dependencies = [ - "windows-link", -] - -[[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 0.52.6", - "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-targets" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" -dependencies = [ - "windows_aarch64_gnullvm 0.53.0", - "windows_aarch64_msvc 0.53.0", - "windows_i686_gnu 0.53.0", - "windows_i686_gnullvm 0.53.0", - "windows_i686_msvc 0.53.0", - "windows_x86_64_gnu 0.53.0", - "windows_x86_64_gnullvm 0.53.0", - "windows_x86_64_msvc 0.53.0", -] - -[[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_gnullvm" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" - -[[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_aarch64_msvc" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" - -[[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_gnu" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" - -[[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_gnullvm" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" - -[[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_i686_msvc" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" - -[[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_gnu" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" - -[[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_gnullvm" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" - -[[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 = "windows_x86_64_msvc" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" - -[[package]] -name = "winnow" -version = "0.5.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" -dependencies = [ - "memchr", -] - -[[package]] -name = "winnow" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63d3fcd9bba44b03821e7d699eeee959f3126dcc4aa8e4ae18ec617c2a5cea10" -dependencies = [ - "memchr", -] - [[package]] name = "winreg" -version = "0.50.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69" dependencies = [ - "cfg-if", - "windows-sys 0.48.0", + "winapi", ] -[[package]] -name = "winreg" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5" -dependencies = [ - "cfg-if", - "windows-sys 0.48.0", -] - -[[package]] -name = "wit-bindgen-rt" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" -dependencies = [ - "bitflags 2.9.0", -] - -[[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 = "xattr" -version = "1.5.0" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d65cbf2f12c15564212d48f4e3dfb87923d25d611f2aed18f4cb23f0413d89e" +checksum = "244c3741f4240ef46274860397c7c74e50eb23624996930e484c16679633a54c" dependencies = [ "libc", - "rustix 1.0.5", ] [[package]] name = "xz2" -version = "0.1.7" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388c44dc09d76f1536602ead6d325eb532f5c122f17782bd57fb47baeeb767e2" +checksum = "c179869f34fc7c01830d3ce7ea2086bc3a07e0d35289b667d0a8bf910258926c" dependencies = [ "lzma-sys", ] [[package]] -name = "yoke" -version = "0.7.5" +name = "zip" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +checksum = "8264fcea9b7a036a4a5103d7153e988dbc2ebbafb34f68a3c2d404b6b82d74b6" dependencies = [ - "serde", - "stable_deref_trait", - "yoke-derive", - "zerofrom", -] - -[[package]] -name = "yoke-derive" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] - -[[package]] -name = "zerocopy" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" -dependencies = [ - "zerocopy-derive 0.7.35", -] - -[[package]] -name = "zerocopy" -version = "0.8.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2586fea28e186957ef732a5f8b3be2da217d65c5969d4b1e17f973ebbe876879" -dependencies = [ - "zerocopy-derive 0.8.24", -] - -[[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", -] - -[[package]] -name = "zerocopy-derive" -version = "0.8.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a996a8f63c5c4448cd959ac1bab0aaa3306ccfd060472f85943ee0750f0169be" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "zerofrom" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" -dependencies = [ - "zerofrom-derive", -] - -[[package]] -name = "zerofrom-derive" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "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", -] - -[[package]] -name = "zlib-rs" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "868b928d7949e09af2f6086dfc1e01936064cc7a819253bce650d4e2a2d63ba8" - -[[package]] -name = "zstd" -version = "0.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" -dependencies = [ - "zstd-safe", -] - -[[package]] -name = "zstd-safe" -version = "7.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d" -dependencies = [ - "zstd-sys", -] - -[[package]] -name = "zstd-sys" -version = "2.0.15+zstd.1.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb81183ddd97d0c74cedf1d50d85c8d08c1b8b68ee863bdee9e706eedba1a237" -dependencies = [ - "cc", - "pkg-config", + "byteorder", + "bzip2", + "crc32fast", + "flate2", + "thiserror", + "time", ] diff --git a/Cargo.toml b/Cargo.toml index 3ed0edbb..a47b295b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,73 +1,46 @@ -[workspace] -resolver = "2" -members = [ - "crates/atomic-file-install", - "crates/bin", - "crates/binstalk", - "crates/binstalk-bins", - "crates/binstalk-fetchers", - "crates/binstalk-registry", - "crates/binstalk-manifests", - "crates/binstalk-types", - "crates/binstalk-downloader", - "crates/cargo-toml-workspace", - "crates/detect-wasi", - "crates/fs-lock", - "crates/normalize-path", - "crates/detect-targets", - "crates/binstalk-git-repo-api", -] +[package] +name = "cargo-binstall" +description = "Rust binary package installer for CI integration" +repository = "https://github.com/ryankurte/cargo-binstall" +documentation = "https://docs.rs/cargo-binstall" +version = "0.4.1" +authors = ["ryan "] +edition = "2018" +license = "GPL-3.0" -[profile.release] -opt-level = 3 -lto = true -codegen-units = 1 -panic = "abort" -strip = "symbols" -[profile.release.build-override] -inherits = "dev.build-override" +[package.metadata.binstall] +pkg-url = "{ repo }/releases/download/v{ version }/{ name }-{ target }.{ format }" +bin-dir = "{ bin }{ format }" -[profile.release.package."tokio-tar"] -opt-level = "z" +[package.metadata.binstall.overrides.x86_64-pc-windows-msvc] +pkg-fmt = "zip" +[package.metadata.binstall.overrides.x86_64-apple-darwin] +pkg-fmt = "zip" -[profile.release.package."binstall-tar"] -opt-level = "z" +[dependencies] +crates_io_api = "0.7.0" +cargo_metadata = "0.12.2" +tinytemplate = "1.2.0" +tokio = { version = "1.4.0", features = [ "full" ] } +log = "0.4.14" +structopt = "0.3.21" +simplelog = "0.9.0" +anyhow = "1.0.40" +reqwest = { version = "0.11.2", features = [ "rustls-tls" ], default-features = false } +tempdir = "0.3.7" +flate2 = "1.0.14" +tar = "0.4.30" +cargo_toml = "0.8.1" +serde = { version = "1.0.119", features = [ "derive" ] } +strum_macros = "0.20.1" +strum = "0.20.0" +dirs = "3.0.1" +serde_derive = "1.0.118" +crates-index = "0.16.2" +semver = "0.11.0" +xz2 = "0.1.6" +zip = "0.5.11" -[profile.dev] -opt-level = 0 -debug = true -lto = false -debug-assertions = true -overflow-checks = true -codegen-units = 32 - -# Set the default for dependencies on debug. -[profile.dev.package."*"] -opt-level = 3 - -[profile.dev.package."tokio-tar"] -opt-level = "z" - -[profile.dev.package."binstall-tar"] -opt-level = "z" - -[profile.dev.build-override] -inherits = "dev" -debug = false -debug-assertions = false -overflow-checks = false -incremental = false - -[profile.check-only] -inherits = "dev" -debug = false -debug-assertions = false -overflow-checks = false -panic = "abort" - -[profile.check-only.build-override] -inherits = "check-only" - -[profile.check-only.package."*"] -inherits = "check-only" +[dev-dependencies] +env_logger = "0.8.2" diff --git a/crates/bin/LICENSE b/LICENSE.txt similarity index 100% rename from crates/bin/LICENSE rename to LICENSE.txt diff --git a/README.md b/README.md index d8a49b50..bfa5cdea 100644 --- a/README.md +++ b/README.md @@ -1,177 +1,154 @@ # Cargo B(inary)Install -Binstall provides a low-complexity mechanism for installing Rust binaries as an alternative to building from source (via `cargo install`) or manually downloading packages. -This is intended to work with existing CI artifacts and infrastructure, and with minimal overhead for package maintainers. +`cargo binstall` provides a low-complexity mechanism for installing rust binaries as an alternative to building from source (via `cargo install`) or manually downloading packages. This is intended to work with existing CI artifacts and infrastructure, and with minimal overhead for package maintainers. +To support `binstall` maintainers must add configuration values to `Cargo.toml` to allow the tool to locate the appropriate binary package for a given version and target. See [Supporting Binary Installation](#Supporting-Binary-Installation) for instructions on how to support `binstall` in your projects. -Binstall works by fetching the crate information from `crates.io` and searching the linked `repository` for matching releases and artifacts, falling back to the [quickinstall](https://github.com/alsuren/cargo-quickinstall) third-party artifact host, to alternate targets as supported, and finally to `cargo install` as a last resort. -[![CI build](https://github.com/cargo-bins/cargo-binstall/actions/workflows/ci.yml/badge.svg)](https://github.com/cargo-bins/cargo-binstall/actions) -[![GitHub tag](https://img.shields.io/github/tag/cargo-bins/cargo-binstall.svg)](https://github.com/cargo-bins/cargo-binstall) +To get started _using_ `cargo-binstall`, first install the binary (either via `cargo install cargo-binstall` or by downloading a pre-compiled [release](https://github.com/ryankurte/cargo-binstall/releases). +Supported packages can be installed using `cargo binstall NAME` where `NAME` is the crate.io package name. +Package versions and targets may be specified using the `--version` and `--target` arguments respectively, and install directory with `--install-dir` (this defaults to `$HOME/.cargo/bin`, with fall-backs to `$HOME/.bin` if unavailable). For additional options please see `cargo binstall --help`. + +``` +[garry] ➜ ~ cargo binstall radio-sx128x --version 0.14.1-alpha.5 +21:14:09 [INFO] Installing package: 'radio-sx128x' +21:14:13 [INFO] Downloading package from: 'https://github.com/rust-iot/rust-radio-sx128x/releases/download/v0.14.1-alpha.5/sx128x-util-x86_64-apple-darwin.tgz' +21:14:18 [INFO] This will install the following binaries: +21:14:18 [INFO] - sx128x-util (sx128x-util-x86_64-apple-darwin -> /Users/ryankurte/.cargo/bin/sx128x-util-v0.14.1-alpha.5) +21:14:18 [INFO] And create (or update) the following symlinks: +21:14:18 [INFO] - sx128x-util (/Users/ryankurte/.cargo/bin/sx128x-util-v0.14.1-alpha.5 -> /Users/ryankurte/.cargo/bin/sx128x-util) +21:14:18 [INFO] Do you wish to continue? yes/no +yes +21:15:30 [INFO] Installing binaries... +21:15:30 [INFO] Installation complete! +``` + + +## Status + +![Build](https://github.com/ryankurte/cargo-binstall/workflows/Rust/badge.svg) +[![GitHub tag](https://img.shields.io/github/tag/ryankurte/cargo-binstall.svg)](https://github.com/ryankurte/cargo-binstall) [![Crates.io](https://img.shields.io/crates/v/cargo-binstall.svg)](https://crates.io/crates/cargo-binstall) +[![Docs.rs](https://docs.rs/cargo-binstall/badge.svg)](https://docs.rs/cargo-binstall) -_You may want to [see this page as it was when the latest version was published](https://crates.io/crates/cargo-binstall)._ +### Features -## Usage +- Manifest discovery + - [x] Fetch crate / manifest via crates.io + - [ ] Fetch crate / manifest via git (/ github / gitlab) + - [x] Use local crate / manifest (`--manifest-path`) + - [ ] Unofficial packaging +- Package formats + - [x] Tgz + - [x] Txz + - [x] Tar + - [x] Zip + - [x] Bin +- Extraction / Transformation + - [x] Extract from subdirectory in archive (ie. support archives with platform or target subdirectories) + - [x] Extract specific files from archive (ie. support single archive with multiple platform binaries) +- Security + - [ ] Package signing + - [ ] Package verification -```console -$ cargo binstall radio-sx128x@0.14.1-alpha.5 - INFO resolve: Resolving package: 'radio-sx128x@=0.14.1-alpha.5' - WARN The package radio-sx128x v0.14.1-alpha.5 (x86_64-unknown-linux-gnu) has been downloaded from github.com - INFO This will install the following binaries: - INFO - sx128x-util (sx128x-util-x86_64-unknown-linux-gnu -> /home/.cargo/bin/sx128x-util) -Do you wish to continue? [yes]/no -? yes - INFO Installing binaries... - INFO Done in 2.838798298s + +## Supporting Binary Installation + +`binstall` works with existing CI-built binary outputs, with configuration via `[package.metadata.binstall]` keys in the relevant crate manifest. +When configuring `binstall` you can test against a local manifest with `--manifest-path=PATH` argument to use the crate and manifest at the provided `PATH`, skipping crate discovery and download. + +To get started, add a `[package.metadata.binstall]` section to your `Cargo.toml. As an example, the default configuration would be: + +```toml +[package.metadata.binstall] +pkg-url = "{ repo }/releases/download/v{ version }/{ name }-{ target }-v{ version }.{ format }" +bin-dir = "{ name }-{ target }-v{ version }/{ bin }{ format }" +pkg-fmt = "tgz" ``` -Binstall aims to be a drop-in replacement for `cargo install` in many cases, and supports similar options. +With the following configuration keys: -For unattended use (e.g. in CI), use the `--no-confirm` flag. -For additional options please see `cargo binstall --help`. +- `pkg-url` specifies the package download URL for a given target/version, templated +- `bin-path` specifies the binary path within the package, templated (with an `.exe` suffix on windows) +- `pkg-fmt` overrides the package format for download/extraction (defaults to: `tgz`) -## Installation -### If you already have it +`pkg-url` and `bin-path` are templated to support different names for different versions / architectures / etc. +Template variables use the format `{ VAR }` where `VAR` is the name of the variable, with the following variables available: +- `name` is the name of the crate / package +- `version` is the crate version (per `--version` and the crate manifest) +- `repo` is the repository linked in `Cargo.toml` +- `bin` is the name of a specific binary, inferred from the crate configuration +- `target` is the rust target name (defaults to your architecture, but can be overridden using the `--target` command line option if required(). -To upgrade cargo-binstall, use `cargo binstall cargo-binstall`! - -### Quickly - -Here are one-liners for downloading and installing a pre-compiled `cargo-binstall` binary. - -#### Linux and macOS +`pkg-url`, `pkg-fmt` and `bin-path` can be overridden on a per-target basis if required, for example, if your `x86_64-pc-windows-msvc` builds use `zip` archives this could be set via: ``` -curl -L --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.sh | bash +[package.metadata.binstall.overrides.x86_64-pc-windows-msvc] +pkg-fmt = "zip" ``` -or if you have [homebrew](https://brew.sh/) installed: +### Defaults -``` -brew install cargo-binstall +By default `binstall` is setup to work with github releases, and expects to find: + +- an archive named `{ name }-{ target }-v{ version }.{ format }` + - so that this does not overwrite different targets or versions when manually downloaded +- located at `{ repo }/releases/download/v{ version }/` + - compatible with github tags / releases +- containing a folder named `{ name }-{ target }-v{ version }` + - so that prior binary files are not overwritten when manually executing `tar -xvf ...` +- containing binary files in the form `{ bin }{ format }` (where `bin` is the cargo binary name and `format` is `.exe` on windows and empty on other platforms) + +If your package already uses this approach, you shouldn't need to set anything. + +### Examples + +For example, the default configuration (as shown above) for a crate called `radio-sx128x` (version: `v0.14.1-alpha.5` on x86_64 linux) would be interpolated to: + +- A download URL of `https://github.com/rust-iot/rust-radio-sx128x/releases/download/v0.14.1-alpha.5/rust-radio-sx128x-x86_64-unknown-linux-gnu-v0.14.1-alpha.5.tgz` +- Containing a single binary file `rust-radio-sx128x-x86_64-unknown-linux-gnu-v0.14.1-alpha.5/rust-radio-x86_64-unknown-linux-gnu` +- Installed to`$HOME/.cargo/bin/rust-radio-sx128x-v0.14.1-alpha.5` +- With a symlink from `$HOME/.cargo/bin/rust-radio-sx128x` + +#### If the package name does not match the crate name + +As is common with libraries / utilities (and the `radio-sx128x` example), this can be overridden by specifying the `pkg-url`: + +```toml +[package.metadata.binstall] +pkg-url = "{ repo }/releases/download/v{ version }/sx128x-util-{ target }-v{ version }.{ format }" ``` -#### Windows +Which provides a download URL of: `https://github.com/rust-iot/rust-radio-sx128x/releases/download/v0.14.1-alpha.5/sx128x-util-x86_64-unknown-linux-gnu-v0.14.1-alpha.5.tgz` -``` -Set-ExecutionPolicy Unrestricted -Scope Process; iex (iwr "https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.ps1").Content + +#### If the package structure differs from the default + +Were the package to contain binaries in the form `name-target[.exe]`, this could be overridden using the `bin-dir` key: + +```toml +[package.metadata.binstall] +bin-dir = "{ bin }-{ target }{ format }" ``` -### Manually - -Download the relevant package for your system below, unpack it, and move the `cargo-binstall` executable into `$HOME/.cargo/bin`: - -| OS | Arch | URL | -| ------- | ------- | ------------------------------------------------------------ | -| Linux | x86\_64 | https://github.com/cargo-bins/cargo-binstall/releases/latest/download/cargo-binstall-x86_64-unknown-linux-musl.tgz | -| Linux | armv7 | https://github.com/cargo-bins/cargo-binstall/releases/latest/download/cargo-binstall-armv7-unknown-linux-musleabihf.tgz | -| Linux | arm64 | https://github.com/cargo-bins/cargo-binstall/releases/latest/download/cargo-binstall-aarch64-unknown-linux-musl.tgz | -| Mac | Intel | https://github.com/cargo-bins/cargo-binstall/releases/latest/download/cargo-binstall-x86_64-apple-darwin.zip | -| Mac | Apple Silicon | https://github.com/cargo-bins/cargo-binstall/releases/latest/download/cargo-binstall-aarch64-apple-darwin.zip | -| Mac | Universal
(both archs) | https://github.com/cargo-bins/cargo-binstall/releases/latest/download/cargo-binstall-universal-apple-darwin.zip | -| Windows | Intel/AMD | https://github.com/cargo-bins/cargo-binstall/releases/latest/download/cargo-binstall-x86_64-pc-windows-msvc.zip | -| Windows | ARM 64 | https://github.com/cargo-bins/cargo-binstall/releases/latest/download/cargo-binstall-aarch64-pc-windows-msvc.zip | - -### From source - -With a recent [Rust](https://rustup.rs) installed: - -``` -cargo install cargo-binstall -``` - -### In GitHub Actions - -We provide a first-party, minimal action that installs the latest version of Binstall: - -```yml - - uses: cargo-bins/cargo-binstall@main -``` - -For more features, we recommend the excellent [taiki-e/install-action](https://github.com/marketplace/actions/install-development-tools), which has dedicated support for selected tools and uses Binstall for everything else. - -## Companion tools - -These are useful *third-party* tools which work well with Binstall. - -### [`cargo-update`](https://github.com/nabijaczleweli/cargo-update) - -While you can upgrade crates explicitly by running `cargo binstall` again, `cargo-update` takes care of updating all tools as needed. -It automatically uses Binstall to install the updates if it is present. - -### [`cargo-run-bin`](https://github.com/dustinblackman/cargo-run-bin) - -Binstall and `cargo install` both install tools globally by default, which is fine for system-wide tools. -When installing tooling for a project, however, you may prefer to both scope the tools to that project and control their versions in code. -That's where `cargo-run-bin` comes in, with a dedicated section in your Cargo.toml and a short cargo subcommand. -When Binstall is available, it installs from binary whenever possible... and you can even manage Binstall itself with `cargo-run-bin`! - -## Unsupported crates - -Binstall is generally smart enough to auto-detect artifacts in most situations. -However, if a package fails to install, you can manually specify the `pkg-url`, `bin-dir`, and `pkg-fmt` as needed at the command line, with values as documented in [SUPPORT.md](https://github.com/cargo-bins/cargo-binstall/blob/main/SUPPORT.md). - -```console -$ cargo-binstall \ - --pkg-url="{ repo }/releases/download/{ version }/{ name }-{ version }-{ target }.{ archive-format }" \ - --pkg-fmt="txz" \ - crate_name -``` - -Maintainers wanting to make their users' life easier can add [explicit Binstall metadata](https://github.com/cargo-bins/cargo-binstall/blob/main/SUPPORT.md) to `Cargo.toml` to locate the appropriate binary package for a given version and target. - -## Signatures - -We have initial, limited [support](https://github.com/cargo-bins/cargo-binstall/blob/main/SIGNING.md) for maintainers to specify a signing public key and where to find package signatures. -With this enabled, Binstall will download and verify signatures for that package. - -You can use `--only-signed` to refuse to install packages if they're not signed. - -If you like to live dangerously (please don't use this outside testing), you can use `--skip-signatures` to disable checking or even downloading signatures at all. +Which provides a binary path of: `sx128x-util-x86_64-unknown-linux-gnu[.exe]`. It is worth noting that binary names are inferred from the crate, so long as cargo builds them this _should_ just work. ## FAQ -### Why use this? -Because `wget`-ing releases is frustrating, `cargo install` takes a not inconsequential portion of forever on constrained devices, and often putting together actual _packages_ is overkill. - -### Why use the cargo manifest? -Crates already have these, and they already contain a significant portion of the required information. -Also, there's this great and woefully underused (IMO) `[package.metadata]` field. - -### Is this secure? -Yes and also no? - -We have [initial support](https://github.com/cargo-bins/cargo-binstall/blob/main/SIGNING.md) for verifying signatures, but not a lot of the ecosystem produces signatures at the moment. -See [#1](https://github.com/cargo-bins/cargo-binstall/issues/1) to discuss more on this. - -We always pull the metadata from crates.io over HTTPS, and verify the checksum of the crate tar. -We also enforce using HTTPS with TLS >= 1.2 for the actual download of the package files. - -Compared to something like a `curl ... | sh` script, we're not running arbitrary code, but of course the crate you're downloading a package for might itself be malicious! - -### What do the error codes mean? -You can find a full description of errors including exit codes here: - -### Are debug symbols available? -Yes! -Extra pre-built packages with a `.full` suffix are available and contain split debuginfo, documentation files, and extra binaries like the `detect-wasi` utility. - -## Telemetry collection - -Some crate installation strategies may collect anonymized usage statistics by default. -Currently, only the name of the crate to be installed, its version, the target platform triple, and the collecting user agent are sent to endpoints under the `https://warehouse-clerk-tmp.vercel.app/api/crate` URL when the `quickinstall` artifact host is used. -The maintainers of the `quickinstall` project use this data to determine which crate versions are most worthwhile to build and host. -The aggregated collected telemetry is publicly accessible at . -Should you be interested on it, the backend code for these endpoints can be found at . - -If you prefer not to participate in this data collection, you can opt out by any of the following methods: - -- Setting the `--disable-telemetry` flag in the command line interface. -- Setting the `BINSTALL_DISABLE_TELEMETRY` environment variable to `true`. -- Disabling the `quickinstall` strategy with `--disable-strategy quick-install`, or if specifying a list of strategies to use with `--strategy`, avoiding including `quickinstall` in that list. -- Adding `quick-install` to the `disabled-strategies` configuration key in the crate metadata (refer to [the related support documentation](SUPPORT.md#support-for-cargo-binstall) for more details). +- Why use this? + - Because `wget`-ing releases is frustrating, `cargo install` takes a not inconsequential portion of forever on constrained devices, + and often putting together actual _packages_ is overkill. +- Why use the cargo manifest? + - Crates already have these, and they already contain a significant portion of the required information. + Also there's this great and woefully underused (imo) `[package.metadata]` field. +- Why not use a binary repository instead? + - Then we'd need to _host_ a binary repository, and worry about publishing and all the other fun things that come with releasing software. + This way we can use existing CI infrastructure and build artifacts, and maintainers can choose how to distribute their packages. +- Is this secure? + - Yes and also no? We're not (yet? #1) doing anything to verify the CI binaries are produced by the right person / organisation. + However, we're pulling data from crates.io and the cargo manifest, both of which are _already_ trusted entities, and this is + functionally a replacement for `curl ... | bash` or `wget`-ing the same files, so, things can be improved but it's also sorta moot --- -If you have ideas/contributions or anything is not working the way you expect (in which case, please include an output with `--log-level debug`) and feel free to open an issue or PR. +If you have ideas / contributions or anything is not working the way you expect (in which case, please include an output with `--log-level debug`) and feel free to open an issue or PR. diff --git a/SIGNING.md b/SIGNING.md deleted file mode 100644 index 29803ce4..00000000 --- a/SIGNING.md +++ /dev/null @@ -1,112 +0,0 @@ -# Signature support - -Binstall supports verifying signatures of downloaded files. -At the moment, only one algorithm is supported, but this is expected to improve as time goes. - -This feature requires adding to the Cargo.toml metadata: no autodiscovery here! - -## Minimal example - -Generate a [minisign](https://jedisct1.github.io/minisign/) keypair: - -```console -minisign -G -W -p signing.pub -s signing.key - -# or with rsign2: -rsign generate -W -p signing.pub -s signing.key -``` - -In your Cargo.toml, put: - -```toml -[package.metadata.binstall.signing] -algorithm = "minisign" -pubkey = "RWRnmBcLmQbXVcEPWo2OOKMI36kki4GiI7gcBgIaPLwvxe14Wtxm9acX" -``` - -Replace the value of `pubkey` with the public key in your `signing.pub`. - -Save the `signing.key` as a secret in your CI, then use it when building packages: - -```console -tar cvf package-name.tar.zst your-files # or however - -minisign -S -W -s signing.key -x package-name.tar.zst.sig -m package-name.tar.zst - -# or with rsign2: -rsign sign -W -s signing.key -x package-name.tar.zst.sig package-name.tar.zst -``` - -Upload both your package and the matching `.sig`. - -Now when binstall downloads your packages, it will also download the `.sig` file and use the `pubkey` in the Cargo.toml to verify the signature. -If the signature has a trusted comment, it will print it at install time. - -By default, `minisign` and `rsign2` prompt for a password; above we disable this with `-W`. -While you _can_ set a password, we recommend instead using [age](https://github.com/FiloSottile/age) (or the Rust version [rage](https://github.com/str4d/rage)) to separately encrypt the key, which we find is much better for automation. - -```console -rage-keygen -o age.key -Public key: age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p - -rage -r age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p -o signing.key.age signing.key -rage -d -i age.key -o signing.key signing.key.age -``` - -For just-in-time or "keyless" schemes, securely generating and passing the ephemeral key to other jobs or workflows presents subtle issues. -`cargo-binstall` has an implementation in [its own release process][`release.yml`] that you can use as example. - -[`expect`]: https://linux.die.net/man/1/expect -[`release.yml`]: https://github.com/cargo-bins/cargo-binstall/blob/main/.github/workflows/release.yml - -## Reference - -- `algorithm`: required, see below. -- `pubkey`: required, must be the public key. -- `file`: optional, a template to specify the URL of the signature file. Defaults to `{ url }.sig` where `{ url }` is the download URL of the package. - -### Minisign - -`algorithm` must be `"minisign"`. - -The legacy signature format is not supported. - -The `pubkey` must be in the same format as minisign generates. -It may or may not include the untrusted comment; it's ignored by Binstall so we recommend not. - -## Just-in-time signing - -To reduce the risk of a key being stolen, this scheme supports just-in-time or "keyless" signing. -The idea is to generate a keypair when releasing, use it for signing the packages, save the key in the Cargo.toml before publishing to a registry, and then discard the private key when it's done. -That way, there's no key to steal nor to store securely, and every release is signed by a different key. -And because crates.io is immutable, it's impossible to overwrite the key. - -There is one caveat to keep in mind: with the scheme as described above, Binstalling with `--git` may not work: - -- If the Cargo.toml in the source contains a partially-filled `[...signing]` section, Binstall will fail. -- If the section contains a different key than the ephemeral one used to sign the packages, Binstall will refuse to install what it sees as corrupt packages. -- If the section is missing entirely, Binstall will work, but of course signatures won't be checked. - -The solution here is either: - -- Commit the Cargo.toml with the ephemeral public key to the repo when publishing. -- Omit the `[...signing]` section in the source, and write the entire section on publish instead of just filling in the `pubkey`; signatures won't be checked for `--git` installs. Binstall uses this approach. -- Instruct your users to use `--skip-signatures` if they want to install with `--git`. - -## Why not X? (Sigstore, GPG, signify, with SSH keys, ...) - -We're open to pull requests adding algorithms! -We're especially interested in Sigstore for a better implementation of "just-in-time" signing (which it calls "keyless"). -We chose minisign as the first supported algorithm as it's lightweight, fairly popular, and has zero options to choose from. - -## There's a competing project that does package signature verification differently! - -[Tell us about it](https://github.com/cargo-bins/cargo-binstall/issues/1)! -We're not looking to fracture the ecosystem here, and will gladly implement support if something exists already. - -We'll also work with others in the space to eventually formalise this beyond Binstall, for example around the [`dist-manifest.json`](https://crates.io/crates/cargo-dist-schema) metadata format. - -## What's the relationship to crate/registry signing? - -There isn't one. -Crate signing is something we're also interested in, and if/when it materialises we'll add support in Binstall for the bits that concern us, but by nature package signing is not related to (source) crate signing. diff --git a/SUPPORT.md b/SUPPORT.md deleted file mode 100644 index f18fd17f..00000000 --- a/SUPPORT.md +++ /dev/null @@ -1,181 +0,0 @@ -# Support for `cargo binstall` - -`binstall` works with existing CI-built binary outputs, with configuration via `[package.metadata.binstall]` keys in the relevant crate manifest. -When configuring `binstall` you can test against a local manifest with `--manifest-path=PATH` argument to use the crate and manifest at the provided `PATH`, skipping crate discovery and download. - -To get started, check the [default](#Defaults) first, only add a `[package.metadata.binstall]` section -to your `Cargo.toml` if the default does not work for you. - -As an example, the configuration would be like this: - -```toml -[package.metadata.binstall] -pkg-url = "{ repo }/releases/download/v{ version }/{ name }-{ target }-v{ version }{ archive-suffix }" -bin-dir = "{ name }-{ target }-v{ version }/{ bin }{ binary-ext }" -pkg-fmt = "tgz" -disabled-strategies = ["quick-install", "compile"] -``` - -With the following configuration keys: - -- `pkg-url` specifies the package download URL for a given target/version, templated -- `bin-dir` specifies the binary path within the package, templated (with an `.exe` suffix on windows) -- `pkg-fmt` overrides the package format for download/extraction (defaults to: `tgz`), check [the documentation](https://docs.rs/binstalk-types/latest/binstalk_types/cargo_toml_binstall/enum.PkgFmt.html) for all supported formats. -- `disabled-strategies` to disable specific strategies (e.g. `crate-meta-data` for trying to find pre-built on your repository, - `quick-install` for pre-built from third-party cargo-bins/cargo-quickinstall, `compile` for falling back to `cargo-install`) - for your crate (defaults to empty array). - If `--strategies` is passed on the command line, then the `disabled-strategies` in `package.metadata` will be ignored. - Otherwise, the `disabled-strategies` in `package.metadata` and `--disable-strategies` will be merged. - - -`pkg-url` and `bin-dir` are templated to support different names for different versions / architectures / etc. -Template variables use the format `{ VAR }` where `VAR` is the name of the variable, -`\{` for literal `{`, `\}` for literal `}` and `\\` for literal `\`, -with the following variables available: -- `name` is the name of the crate/package -- `version` is the crate version (per `--version` and the crate manifest) -- `repo` is the repository linked in `Cargo.toml` -- `bin` is the name of a specific binary, inferred from the crate configuration -- `target` is the rust target name (defaults to your architecture, but can be overridden using the `--target` command line option if required() -- `archive-suffix` is the filename extension of the package archive format that includes the prefix `.`, e.g. `.tgz` for tgz or `.exe`/`""` for bin. -- `archive-format` is the soft-deprecated filename extension of the package archive format that does not include the prefix `.`, e.g. `tgz` for tgz or `exe`/`""` for bin. -- `binary-ext` is the string `.exe` if the `target` is for Windows, or the empty string otherwise -- `format` is a soft-deprecated alias for `archive-format` in `pkg-url`, and alias for `binary-ext` in `bin-dir`; in the future, this may warn at install time. -- `target-family`: Operating system of the target from [`target_lexicon::OperatingSystem`] -- `target-arch`: Architecture of the target, `universal` on `{universal, universal2}-apple-darwin`, - otherwise from [`target_lexicon::Architecture`] -- `target-libc`: ABI environment of the target from [`target_lexicon::Environment`] -- `target-vendor`: Vendor of the target from [`target_lexicon::Vendor`] - -[`target_lexicon::OperatingSystem`]: https://docs.rs/target-lexicon/latest/target_lexicon/enum.OperatingSystem.html -[`target_lexicon::Architecture`]: https://docs.rs/target-lexicon/latest/target_lexicon/enum.Architecture.html -[`target_lexicon::Environment`]: https://docs.rs/target-lexicon/latest/target_lexicon/enum.Environment.html -[`target_lexicon::Vendor`]: https://docs.rs/target-lexicon/latest/target_lexicon/enum.Vendor.html - -`pkg-url`, `pkg-fmt` and `bin-dir` can be overridden on a per-target basis if required, for example, if your `x86_64-pc-windows-msvc` builds use `zip` archives this could be set via: - -``` -[package.metadata.binstall.overrides.x86_64-pc-windows-msvc] -pkg-fmt = "zip" -``` - -### Defaults - -By default, `binstall` will try all supported package formats and would do the same for `bin-dir`. - -It will first extract the archives, then iterate over the following list, finding the first dir -that exists: - - - `{ name }-{ target }-v{ version }` - - `{ name }-{ target }-{ version }` - - `{ name }-{ version }-{ target }` - - `{ name }-v{ version }-{ target }` - - `{ name }-{ target }` - - `{ name }-{ version }` - - `{ name }-v{ version }` - - `{ name }` - -Then it will concat the dir with `"{ bin }{ binary-ext }"` and use that as the final `bin-dir`. - -`name` here is name of the crate, `bin` is the cargo binary name and `binary-ext` is `.exe` -on windows and empty on other platforms). - -The default value for `pkg-url` will depend on the repository of the package. - -It is set up to work with GitHub releases, GitLab releases, bitbucket downloads -and source forge downloads. - -If your package already uses any of these URLs, you shouldn't need to set anything. - -The URLs are derived from a set of filenames and a set of paths, which are -"multiplied together": every filename appended to every path. The filenames -are: - -- `{ name }-{ target }-{ version }{ archive-suffix }` -- `{ name }-{ target }-v{ version }{ archive-suffix }` -- `{ name }-{ version }-{ target }{ archive-suffix }` -- `{ name }-v{ version }-{ target }{ archive-suffix }` -- `{ name }_{ target }_{ version }{ archive-suffix }` -- `{ name }_{ target }_v{ version }{ archive-suffix }` -- `{ name }_{ version }_{ target }{ archive-suffix }` -- `{ name }_v{ version }_{ target }{ archive-suffix }` -- `{ name }-{ target }{ archive-suffix }` ("versionless") -- `{ name }_{ target }{ archive-suffix }` ("versionless") - -The paths are: - -#### for GitHub - -- `{ repo }/releases/download/{ version }/` -- `{ repo }/releases/download/v{ version }/` - -#### for GitLab - -- `{ repo }/-/releases/{ version }/downloads/binaries/` -- `{ repo }/-/releases/v{ version }/downloads/binaries/` - -Note that this uses the [Permanent links to release assets][gitlab-permalinks] -feature of GitLab EE: it requires you to create an asset as a link with a -`filepath`, which, as of writing, can only be set using GitLab's API. - -[gitlab-permalinks]: https://docs.gitlab.com/ee/user/project/releases/index.html#permanent-links-to-latest-release-assets - -#### for BitBucket - -- `{ repo }/downloads/` - -Binaries must be uploaded to the project's "Downloads" page on BitBucket. - -Also note that as there are no per-release downloads, the "versionless" -filename is not considered here. - -#### for SourceForge - -- `{ repo }/files/binaries/{ version }` -- `{ repo }/files/binaries/v{ version }` - -The URLs also have `/download` appended as per SourceForge's schema. - -Binary must be uploaded to the "File" page of your project, under the directory -`binaries/v{ version }`. - -#### Others - -For all other situations, `binstall` does not provide a default `pkg-url` and -you need to manually specify it. - -### QuickInstall - -[QuickInstall](https://github.com/alsuren/cargo-quickinstall) is an unofficial repository of prebuilt binaries for Crates, and `binstall` has built-in support for it! If your crate is built by QuickInstall, it will already work with `binstall`. However, binaries as configured above take precedence when they exist. - -### Examples - -For example, the default configuration (as shown above) for a crate called `radio-sx128x` (version: `v0.14.1-alpha.5` on x86\_64 linux) would be interpolated to: - -- A download URL of `https://github.com/rust-iot/rust-radio-sx128x/releases/download/v0.14.1-alpha.5/rust-radio-sx128x-x86_64-unknown-linux-gnu-v0.14.1-alpha.5.tgz` -- Containing a single binary file `rust-radio-sx128x-x86_64-unknown-linux-gnu-v0.14.1-alpha.5/rust-radio-x86_64-unknown-linux-gnu` -- Installed to`$HOME/.cargo/bin/rust-radio-sx128x-v0.14.1-alpha.5` -- With a symlink from `$HOME/.cargo/bin/rust-radio-sx128x` - -#### If the package name does not match the crate name - -As is common with libraries/utilities (and the `radio-sx128x` example), this can be overridden by specifying the `pkg-url`: - -```toml -[package.metadata.binstall] -pkg-url = "{ repo }/releases/download/v{ version }/sx128x-util-{ target }-v{ version }{ archive-suffix }" -``` - -Which provides a download URL of `https://github.com/rust-iot/rust-radio-sx128x/releases/download/v0.14.1-alpha.5/sx128x-util-x86_64-unknown-linux-gnu-v0.14.1-alpha.5.tgz` - - -#### If the package structure differs from the default - -Were the package to contain binaries in the form `name-target[.exe]`, this could be overridden using the `bin-dir` key: - -```toml -[package.metadata.binstall] -bin-dir = "{ bin }-{ target }{ binary-ext }" -``` - -Which provides a binary path of: `sx128x-util-x86_64-unknown-linux-gnu[.exe]`. It is worth noting that binary names are inferred from the crate, so long as cargo builds them this _should_ just work. diff --git a/action.yml b/action.yml deleted file mode 100644 index 3c3b1663..00000000 --- a/action.yml +++ /dev/null @@ -1,14 +0,0 @@ -name: 'Install cargo-binstall' -description: 'Install the latest version of cargo-binstall tool' - -runs: - using: composite - steps: - - name: Install cargo-binstall - if: runner.os != 'Windows' - shell: sh - run: curl -L --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.sh | bash - - name: Install cargo-binstall - if: runner.os == 'Windows' - run: Set-ExecutionPolicy Unrestricted -Scope Process; iex (iwr "https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.ps1").Content - shell: powershell diff --git a/crates/detect-targets/build.rs b/build.rs similarity index 51% rename from crates/detect-targets/build.rs rename to build.rs index 15152d41..a77a878b 100644 --- a/crates/detect-targets/build.rs +++ b/build.rs @@ -1,7 +1,6 @@ -fn main() { - println!("cargo:rerun-if-changed=build.rs"); - // Fetch build target and define this for the compiler +// Fetch build target and define this for the compiler +fn main() { println!( "cargo:rustc-env=TARGET={}", std::env::var("TARGET").unwrap() diff --git a/cleanup-cache.sh b/cleanup-cache.sh deleted file mode 100755 index 4fcbf6d3..00000000 --- a/cleanup-cache.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash - -set -uxo pipefail - -REPO="${REPO?}" -BRANCH="${BRANCH?}" - -while true; do - echo "Fetching list of cache key for $BRANCH" - cacheKeysForPR="$(gh actions-cache list -R "$REPO" -B "$BRANCH" -L 100 | cut -f 1)" - - if [ -z "$cacheKeysForPR" ]; then - break - fi - - echo "Deleting caches..." - for cacheKey in $cacheKeysForPR - do - echo Removing "$cacheKey" - gh actions-cache delete "$cacheKey" -R "$REPO" -B "$BRANCH" --confirm - done -done -echo "Done cleaning up $BRANCH" diff --git a/crates/atomic-file-install/CHANGELOG.md b/crates/atomic-file-install/CHANGELOG.md deleted file mode 100644 index f49d292d..00000000 --- a/crates/atomic-file-install/CHANGELOG.md +++ /dev/null @@ -1,44 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - -## [Unreleased] - -## [1.0.11](https://github.com/cargo-bins/cargo-binstall/compare/atomic-file-install-v1.0.10...atomic-file-install-v1.0.11) - 2025-03-19 - -### Other - -- *(deps)* bump windows from 0.60.0 to 0.61.1 in the deps group across 1 directory ([#2097](https://github.com/cargo-bins/cargo-binstall/pull/2097)) - -## [1.0.10](https://github.com/cargo-bins/cargo-binstall/compare/atomic-file-install-v1.0.9...atomic-file-install-v1.0.10) - 2025-02-22 - -### Other - -- *(deps)* bump windows from 0.59.0 to 0.60.0 in the deps group across 1 directory (#2063) - -## [1.0.9](https://github.com/cargo-bins/cargo-binstall/compare/atomic-file-install-v1.0.8...atomic-file-install-v1.0.9) - 2025-01-19 - -### Other - -- update Cargo.lock dependencies - -## [1.0.8](https://github.com/cargo-bins/cargo-binstall/compare/atomic-file-install-v1.0.7...atomic-file-install-v1.0.8) - 2025-01-13 - -### Other - -- update Cargo.lock dependencies - -## [1.0.7](https://github.com/cargo-bins/cargo-binstall/compare/atomic-file-install-v1.0.6...atomic-file-install-v1.0.7) - 2025-01-11 - -### Other - -- *(deps)* bump the deps group with 3 updates (#2015) - -## [1.0.6](https://github.com/cargo-bins/cargo-binstall/compare/atomic-file-install-v1.0.5...atomic-file-install-v1.0.6) - 2024-11-18 - -### Other - -- Upgrade transitive dependencies ([#1969](https://github.com/cargo-bins/cargo-binstall/pull/1969)) diff --git a/crates/atomic-file-install/Cargo.toml b/crates/atomic-file-install/Cargo.toml deleted file mode 100644 index 2394f0eb..00000000 --- a/crates/atomic-file-install/Cargo.toml +++ /dev/null @@ -1,18 +0,0 @@ -[package] -name = "atomic-file-install" -version = "1.0.11" -edition = "2021" -description = "For atomically installing a file or a symlink." -repository = "https://github.com/cargo-bins/cargo-binstall" -documentation = "https://docs.rs/atomic-install" -authors = ["Jiahao XU "] -license = "Apache-2.0 OR MIT" -rust-version = "1.65.0" - -[dependencies] -reflink-copy = "0.1.15" -tempfile = "3.5.0" -tracing = "0.1.39" - -[target.'cfg(windows)'.dependencies] -windows = { version = "0.61.1", features = ["Win32_Storage_FileSystem", "Win32_Foundation"] } diff --git a/crates/atomic-file-install/LICENSE-APACHE b/crates/atomic-file-install/LICENSE-APACHE deleted file mode 100644 index 1b5ec8b7..00000000 --- a/crates/atomic-file-install/LICENSE-APACHE +++ /dev/null @@ -1,176 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS diff --git a/crates/atomic-file-install/LICENSE-MIT b/crates/atomic-file-install/LICENSE-MIT deleted file mode 100644 index 31aa7938..00000000 --- a/crates/atomic-file-install/LICENSE-MIT +++ /dev/null @@ -1,23 +0,0 @@ -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/crates/atomic-file-install/src/lib.rs b/crates/atomic-file-install/src/lib.rs deleted file mode 100644 index 4f444262..00000000 --- a/crates/atomic-file-install/src/lib.rs +++ /dev/null @@ -1,219 +0,0 @@ -//! Atomically install a regular file or a symlink to destination, -//! can be either noclobber (fail if destination already exists) or -//! replacing it atomically if it exists. - -use std::{fs, io, path::Path}; - -use reflink_copy::reflink_or_copy; -use tempfile::{NamedTempFile, TempPath}; -use tracing::{debug, warn}; - -#[cfg(unix)] -use std::os::unix::fs::symlink as symlink_file_inner; - -#[cfg(windows)] -use std::os::windows::fs::symlink_file as symlink_file_inner; - -fn parent(p: &Path) -> io::Result<&Path> { - p.parent().ok_or_else(|| { - io::Error::new( - io::ErrorKind::InvalidData, - format!("`{}` does not have a parent", p.display()), - ) - }) -} - -fn copy_to_tempfile(src: &Path, dst: &Path) -> io::Result { - let parent = parent(dst)?; - debug!("Creating named tempfile at '{}'", parent.display()); - let tempfile = NamedTempFile::new_in(parent)?; - - debug!( - "Copying from '{}' to '{}'", - src.display(), - tempfile.path().display() - ); - fs::remove_file(tempfile.path())?; - // src and dst is likely to be on the same filesystem. - // Uses reflink if the fs support it, or fallback to - // `fs::copy` if it doesn't support it or it is not on the - // same filesystem. - reflink_or_copy(src, tempfile.path())?; - - debug!("Retrieving permissions of '{}'", src.display()); - let permissions = src.metadata()?.permissions(); - - debug!( - "Setting permissions of '{}' to '{permissions:#?}'", - tempfile.path().display() - ); - tempfile.as_file().set_permissions(permissions)?; - - Ok(tempfile) -} - -/// Install a file, this fails if the `dst` already exists. -/// -/// This is a blocking function, must be called in `block_in_place` mode. -pub fn atomic_install_noclobber(src: &Path, dst: &Path) -> io::Result<()> { - debug!( - "Attempting to rename from '{}' to '{}'.", - src.display(), - dst.display() - ); - - let tempfile = copy_to_tempfile(src, dst)?; - - debug!( - "Persisting '{}' to '{}', fail if dst already exists", - tempfile.path().display(), - dst.display() - ); - tempfile.persist_noclobber(dst)?; - - Ok(()) -} - -/// Atomically install a file, this atomically replace `dst` if it exists. -/// -/// This is a blocking function, must be called in `block_in_place` mode. -pub fn atomic_install(src: &Path, dst: &Path) -> io::Result<()> { - debug!( - "Attempting to atomically rename from '{}' to '{}'", - src.display(), - dst.display() - ); - - if let Err(err) = fs::rename(src, dst) { - warn!("Attempting at atomic rename failed: {err}, fallback to other methods."); - - #[cfg(windows)] - { - match win::replace_file(src, dst) { - Ok(()) => { - debug!("ReplaceFileW succeeded."); - return Ok(()); - } - Err(err) => { - warn!("ReplaceFileW failed: {err}, fallback to using tempfile plus rename") - } - } - } - - // src and dst is not on the same filesystem/mountpoint. - // Fallback to creating NamedTempFile on the parent dir of - // dst. - - persist(copy_to_tempfile(src, dst)?.into_temp_path(), dst)?; - } else { - debug!("Attempting at atomically succeeded."); - } - - Ok(()) -} - -/// Create a symlink at `link` to `dest`, this fails if the `link` -/// already exists. -/// -/// This is a blocking function, must be called in `block_in_place` mode. -pub fn atomic_symlink_file_noclobber(dest: &Path, link: &Path) -> io::Result<()> { - match symlink_file_inner(dest, link) { - Ok(_) => Ok(()), - - #[cfg(windows)] - // Symlinks on Windows are disabled in some editions, so creating one is unreliable. - // Fallback to copy if it fails. - Err(_) => atomic_install_noclobber(dest, link), - - #[cfg(not(windows))] - Err(err) => Err(err), - } -} - -/// Atomically create a symlink at `link` to `dest`, this atomically replace -/// `link` if it already exists. -/// -/// This is a blocking function, must be called in `block_in_place` mode. -pub fn atomic_symlink_file(dest: &Path, link: &Path) -> io::Result<()> { - let parent = parent(link)?; - - debug!("Creating tempPath at '{}'", parent.display()); - let temp_path = NamedTempFile::new_in(parent)?.into_temp_path(); - // Remove this file so that we can create a symlink - // with the name. - fs::remove_file(&temp_path)?; - - debug!( - "Creating symlink '{}' to file '{}'", - temp_path.display(), - dest.display() - ); - - match symlink_file_inner(dest, &temp_path) { - Ok(_) => persist(temp_path, link), - - #[cfg(windows)] - // Symlinks on Windows are disabled in some editions, so creating one is unreliable. - // Fallback to copy if it fails. - Err(_) => atomic_install(dest, link), - - #[cfg(not(windows))] - Err(err) => Err(err), - } -} - -fn persist(temp_path: TempPath, to: &Path) -> io::Result<()> { - debug!("Persisting '{}' to '{}'", temp_path.display(), to.display()); - match temp_path.persist(to) { - Ok(()) => Ok(()), - #[cfg(windows)] - Err(tempfile::PathPersistError { - error, - path: temp_path, - }) => { - warn!( - "Failed to persist symlink '{}' to '{}': {error}, fallback to ReplaceFileW", - temp_path.display(), - to.display(), - ); - win::replace_file(&temp_path, to).map_err(io::Error::from) - } - #[cfg(not(windows))] - Err(err) => Err(err.into()), - } -} - -#[cfg(windows)] -mod win { - use std::{os::windows::ffi::OsStrExt, path::Path}; - - use windows::{ - core::{Error, PCWSTR}, - Win32::Storage::FileSystem::{ReplaceFileW, REPLACE_FILE_FLAGS}, - }; - - pub(super) fn replace_file(src: &Path, dst: &Path) -> Result<(), Error> { - let mut src: Vec<_> = src.as_os_str().encode_wide().collect(); - let mut dst: Vec<_> = dst.as_os_str().encode_wide().collect(); - - // Ensure it is terminated with 0 - src.push(0); - dst.push(0); - - // SAFETY: We use it according its doc - // https://learn.microsoft.com/en-nz/windows/win32/api/winbase/nf-winbase-replacefilew - // - // NOTE that this function is available since windows XP, so we don't need to - // lazily load this function. - unsafe { - ReplaceFileW( - PCWSTR::from_raw(dst.as_ptr()), // lpreplacedfilename - PCWSTR::from_raw(src.as_ptr()), // lpreplacementfilename - PCWSTR::null(), // lpbackupfilename, null for no backup file - REPLACE_FILE_FLAGS(0), // dwreplaceflags - None, // lpexclude, unused - None, // lpreserved, unused - ) - } - } -} diff --git a/crates/bin/Cargo.toml b/crates/bin/Cargo.toml deleted file mode 100644 index fd5c42b3..00000000 --- a/crates/bin/Cargo.toml +++ /dev/null @@ -1,90 +0,0 @@ -[package] -name = "cargo-binstall" -description = "Binary installation for rust projects" -repository = "https://github.com/cargo-bins/cargo-binstall" -documentation = "https://docs.rs/cargo-binstall" -version = "1.12.3" -rust-version = "1.79.0" -authors = ["ryan "] -edition = "2021" -license = "GPL-3.0-only" -readme = "../../README.md" - -# These MUST remain even if they're not needed in recent versions because -# OLD versions use them to upgrade -[package.metadata.binstall] -pkg-url = "{ repo }/releases/download/v{ version }/{ name }-{ target }.{ archive-format }" -bin-dir = "{ bin }{ binary-ext }" - -[package.metadata.binstall.overrides.x86_64-pc-windows-msvc] -pkg-fmt = "zip" -[package.metadata.binstall.overrides.x86_64-apple-darwin] -pkg-fmt = "zip" - -[dependencies] -atomic-file-install = { version = "1.0.11", path = "../atomic-file-install" } -binstalk = { path = "../binstalk", version = "0.28.31", default-features = false } -binstalk-manifests = { path = "../binstalk-manifests", version = "0.15.28" } -clap = { version = "4.5.3", features = ["derive", "env", "wrap_help"] } -clap-cargo = "0.15.2" -compact_str = "0.9.0" -dirs = "6.0.0" -file-format = { version = "0.26.0", default-features = false } -home = "0.5.9" -log = { version = "0.4.22", features = ["std"] } -miette = "7.0.0" -mimalloc = { version = "0.1.39", default-features = false, optional = true } -once_cell = "1.18.0" -semver = "1.0.17" -strum = "0.27.0" -strum_macros = "0.27.0" -supports-color = "3.0.0" -tempfile = "3.5.0" -tokio = { version = "1.44.0", features = ["rt-multi-thread", "signal"], default-features = false } -tracing = { version = "0.1.39", default-features = false } -tracing-core = "0.1.32" -tracing-log = { version = "0.2.0", default-features = false } -tracing-subscriber = { version = "0.3.17", features = ["fmt", "json", "ansi"], default-features = false } -zeroize = "1.8.1" - -[build-dependencies] -embed-resource = "3.0.1" -vergen = { version = "8.2.7", features = ["build", "cargo", "git", "gitcl", "rustc"] } - -[features] -default = ["static", "rustls", "trust-dns", "fancy-no-backtrace", "zstd-thin", "git"] - -git = ["binstalk/git"] -git-max-perf = ["binstalk/git-max-perf"] - -mimalloc = ["dep:mimalloc"] - -static = ["binstalk/static"] -pkg-config = ["binstalk/pkg-config"] - -zlib-ng = ["binstalk/zlib-ng"] -zlib-rs = ["binstalk/zlib-rs"] - -rustls = ["binstalk/rustls"] -native-tls = ["binstalk/native-tls"] - -trust-dns = ["binstalk/trust-dns"] - -# Experimental HTTP/3 client, this would require `--cfg reqwest_unstable` -# to be passed to `rustc`. -http3 = ["binstalk/http3"] - -zstd-thin = ["binstalk/zstd-thin"] -cross-lang-fat-lto = ["binstalk/cross-lang-fat-lto"] - -fancy-no-backtrace = ["miette/fancy-no-backtrace"] -fancy-with-backtrace = ["fancy-no-backtrace", "miette/fancy"] - -log_max_level_info = ["log/max_level_info", "tracing/max_level_info", "log_release_max_level_info"] -log_max_level_debug = ["log/max_level_debug", "tracing/max_level_debug", "log_release_max_level_debug"] - -log_release_max_level_info = ["log/release_max_level_info", "tracing/release_max_level_info"] -log_release_max_level_debug = ["log/release_max_level_debug", "tracing/release_max_level_debug"] - -[package.metadata.docs.rs] -rustdoc-args = ["--cfg", "docsrs"] diff --git a/crates/bin/build.rs b/crates/bin/build.rs deleted file mode 100644 index ed3b0772..00000000 --- a/crates/bin/build.rs +++ /dev/null @@ -1,46 +0,0 @@ -use std::{ - io, - path::Path, - process::{Child, Command}, - thread, -}; - -fn succeeds(res: io::Result) -> bool { - res.and_then(|mut child| child.wait()) - .map(|status| status.success()) - .unwrap_or(false) -} - -fn main() { - let handle = thread::spawn(|| { - println!("cargo:rerun-if-changed=build.rs"); - println!("cargo:rerun-if-changed=manifest.rc"); - println!("cargo:rerun-if-changed=windows.manifest"); - - embed_resource::compile("manifest.rc", embed_resource::NONE) - .manifest_required() - .unwrap(); - }); - - let git = Command::new("git").arg("--version").spawn(); - - // .git is usually a dir, but it also can be a file containing - // path to another .git if it is a submodule. - // - // If build.rs is run on a git repository, then ../../.git - // should exists. - let is_git_repo = Path::new("../../.git").exists(); - - let mut builder = vergen::EmitBuilder::builder(); - builder.all_build().all_cargo().all_rustc(); - - if is_git_repo && succeeds(git) { - builder.all_git(); - } else { - builder.disable_git(); - } - - builder.emit().unwrap(); - - handle.join().unwrap(); -} diff --git a/crates/bin/manifest.rc b/crates/bin/manifest.rc deleted file mode 100644 index d648a5c7..00000000 --- a/crates/bin/manifest.rc +++ /dev/null @@ -1,2 +0,0 @@ -#define RT_MANIFEST 24 -1 RT_MANIFEST "windows.manifest" diff --git a/crates/bin/release.toml b/crates/bin/release.toml deleted file mode 100644 index db2a5847..00000000 --- a/crates/bin/release.toml +++ /dev/null @@ -1,15 +0,0 @@ -pre-release-commit-message = "release: cargo-binstall v{{version}}" -tag-prefix = "" -tag-message = "cargo-binstall {{version}}" - -# We wait until the release CI is done before publishing, -# because publishing is irreversible, but a release can be -# reverted a lot more easily. -publish = false - -[[pre-release-replacements]] -file = "windows.manifest" -search = "^ version=\"[\\d.]+[.]0\"" -replace = " version=\"{{version}}.0\"" -prerelease = false -max = 1 diff --git a/crates/bin/src/args.rs b/crates/bin/src/args.rs deleted file mode 100644 index b993ed72..00000000 --- a/crates/bin/src/args.rs +++ /dev/null @@ -1,724 +0,0 @@ -use std::{ - env, - ffi::OsString, - fmt, mem, - num::{NonZeroU16, NonZeroU64, ParseIntError}, - path::PathBuf, - str::FromStr, -}; - -use binstalk::{ - helpers::remote, - manifests::cargo_toml_binstall::PkgFmt, - ops::resolve::{CrateName, VersionReqExt}, - registry::Registry, -}; -use binstalk_manifests::cargo_toml_binstall::{PkgOverride, Strategy}; -use clap::{builder::PossibleValue, error::ErrorKind, CommandFactory, Parser, ValueEnum}; -use compact_str::CompactString; -use log::LevelFilter; -use semver::VersionReq; -use strum::EnumCount; -use zeroize::Zeroizing; - -#[derive(Debug, Parser)] -#[clap( - version, - about = "Install a Rust binary... from binaries!", - after_long_help = - "License: GPLv3. Source available at https://github.com/cargo-bins/cargo-binstall\n\n\ - Some crate installation strategies may collect anonymized usage statistics by default. \ - If you prefer not to participate on such data collection, you can opt out by using the \ - `--disable-telemetry` flag or its associated environment variable. For more details \ - about this data collection, please refer to the mentioned flag or the project's README \ - file", - arg_required_else_help(true), - // Avoid conflict with version_req - disable_version_flag(true), - styles = clap_cargo::style::CLAP_STYLING, -)] -pub struct Args { - /// Packages to install. - /// - /// Syntax: `crate[@version]` - /// - /// Each value is either a crate name alone, or a crate name followed by @ and the version to - /// install. The version syntax is as with the --version option. - /// - /// When multiple names are provided, the --version option and override option - /// `--manifest-path` and `--git` are unavailable due to ambiguity. - /// - /// If duplicate names are provided, the last one (and their version requirement) - /// is kept. - #[clap( - help_heading = "Package selection", - value_name = "crate[@version]", - required_unless_present_any = ["version", "self_install", "help"], - )] - pub(crate) crate_names: Vec, - - /// Package version to install. - /// - /// Takes either an exact semver version or a semver version requirement expression, which will - /// be resolved to the highest matching version available. - /// - /// Cannot be used when multiple packages are installed at once, use the attached version - /// syntax in that case. - #[clap( - help_heading = "Package selection", - long = "version", - value_parser(VersionReq::parse_from_cli), - value_name = "VERSION" - )] - pub(crate) version_req: Option, - - /// Override binary target set. - /// - /// Binstall is able to look for binaries for several targets, installing the first one it finds - /// in the order the targets were given. For example, on a 64-bit glibc Linux distribution, the - /// default is to look first for a `x86_64-unknown-linux-gnu` binary, then for a - /// `x86_64-unknown-linux-musl` binary. However, on a musl system, the gnu version will not be - /// considered. - /// - /// This option takes a comma-separated list of target triples, which will be tried in order. - /// They override the default list, which is detected automatically from the current platform. - /// - /// If falling back to installing from source, the first target will be used. - #[clap( - help_heading = "Package selection", - alias = "target", - long, - value_name = "TRIPLE", - env = "CARGO_BUILD_TARGET" - )] - pub(crate) targets: Option>, - - /// Override Cargo.toml package manifest path. - /// - /// This skips searching crates.io for a manifest and uses the specified path directly, useful - /// for debugging and when adding Binstall support. This may be either the path to the folder - /// containing a Cargo.toml file, or the Cargo.toml file itself. - /// - /// This option cannot be used with `--git`. - #[clap(help_heading = "Overrides", long, value_name = "PATH")] - pub(crate) manifest_path: Option, - - #[cfg(feature = "git")] - /// Override how to fetch Cargo.toml package manifest. - /// - /// This skip searching crates.io and instead clone the repository specified and - /// runs as if `--manifest-path $cloned_repo` is passed to binstall. - /// - /// This option cannot be used with `--manifest-path`. - #[clap( - help_heading = "Overrides", - long, - conflicts_with("manifest_path"), - value_name = "URL" - )] - pub(crate) git: Option, - - /// Path template for binary files in packages - /// - /// Overrides the Cargo.toml package manifest bin-dir. - #[clap(help_heading = "Overrides", long)] - pub(crate) bin_dir: Option, - - /// Format for package downloads - /// - /// Overrides the Cargo.toml package manifest pkg-fmt. - /// - /// The available package formats are: - /// - /// - tar: download format is TAR (uncompressed) - /// - /// - tbz2: Download format is TAR + Bzip2 - /// - /// - tgz: Download format is TGZ (TAR + GZip) - /// - /// - txz: Download format is TAR + XZ - /// - /// - tzstd: Download format is TAR + Zstd - /// - /// - zip: Download format is Zip - /// - /// - bin: Download format is raw / binary - #[clap(help_heading = "Overrides", long, value_name = "PKG_FMT")] - pub(crate) pkg_fmt: Option, - - /// URL template for package downloads - /// - /// Overrides the Cargo.toml package manifest pkg-url. - #[clap(help_heading = "Overrides", long, value_name = "TEMPLATE")] - pub(crate) pkg_url: Option, - - /// Override the rate limit duration. - /// - /// By default, cargo-binstall allows one request per 10 ms. - /// - /// Example: - /// - /// - `6`: Set the duration to 6ms, allows one request per 6 ms. - /// - /// - `6/2`: Set the duration to 6ms and request_count to 2, - /// allows 2 requests per 6ms. - /// - /// Both duration and request count must not be 0. - #[clap( - help_heading = "Overrides", - long, - default_value_t = RateLimit::default(), - env = "BINSTALL_RATE_LIMIT", - value_name = "LIMIT", - )] - pub(crate) rate_limit: RateLimit, - - /// Specify the strategies to be used, - /// binstall will run the strategies specified in order. - /// - /// If this option is specified, then cargo-binstall will ignore - /// `disabled-strategies` in `package.metadata` in the cargo manifest - /// of the installed packages. - /// - /// Default value is "crate-meta-data,quick-install,compile". - #[clap( - help_heading = "Overrides", - long, - value_delimiter(','), - env = "BINSTALL_STRATEGIES" - )] - pub(crate) strategies: Vec, - - /// Disable the strategies specified. - /// If a strategy is specified in `--strategies` and `--disable-strategies`, - /// then it will be removed. - /// - /// If `--strategies` is not specified, then the strategies specified in this - /// option will be merged with the disabled-strategies` in `package.metadata` - /// in the cargo manifest of the installed packages. - #[clap( - help_heading = "Overrides", - long, - value_delimiter(','), - env = "BINSTALL_DISABLE_STRATEGIES", - value_name = "STRATEGIES" - )] - pub(crate) disable_strategies: Vec, - - /// If `--github-token` or environment variable `GITHUB_TOKEN`/`GH_TOKEN` - /// is not specified, then cargo-binstall will try to extract github token from - /// `$HOME/.git-credentials` or `$HOME/.config/gh/hosts.yml` by default. - /// - /// This option can be used to disable that behavior. - #[clap( - help_heading = "Overrides", - long, - env = "BINSTALL_NO_DISCOVER_GITHUB_TOKEN" - )] - pub(crate) no_discover_github_token: bool, - - /// Maximum time each resolution (one for each possible target and each strategy), in seconds. - #[clap( - help_heading = "Overrides", - long, - env = "BINSTALL_MAXIMUM_RESOLUTION_TIMEOUT", - default_value_t = NonZeroU16::new(15).unwrap(), - value_name = "TIMEOUT" - )] - pub(crate) maximum_resolution_timeout: NonZeroU16, - - /// This flag is now enabled by default thus a no-op. - /// - /// By default, Binstall will install a binary as-is in the install path. - #[clap(help_heading = "Options", long, default_value_t = true)] - pub(crate) no_symlinks: bool, - - /// Dry run, fetch and show changes without installing binaries. - #[clap(help_heading = "Options", long)] - pub(crate) dry_run: bool, - - /// Disable interactive mode / confirmation prompts. - #[clap( - help_heading = "Options", - short = 'y', - long, - env = "BINSTALL_NO_CONFIRM" - )] - pub(crate) no_confirm: bool, - - /// Do not cleanup temporary files. - #[clap(help_heading = "Options", long)] - pub(crate) no_cleanup: bool, - - /// Continue installing other crates even if one of the crate failed to install. - #[clap(help_heading = "Options", long)] - pub(crate) continue_on_failure: bool, - - /// By default, binstall keeps track of the installed packages with metadata files - /// stored in the installation root directory. - /// - /// This flag tells binstall not to use or create that file. - /// - /// With this flag, binstall will refuse to overwrite any existing files unless the - /// `--force` flag is used. - /// - /// This also disables binstall’s ability to protect against multiple concurrent - /// invocations of binstall installing at the same time. - /// - /// This flag will also be passed to `cargo-install` if it is invoked. - #[clap(help_heading = "Options", long)] - pub(crate) no_track: bool, - - /// Disable statistics collection on popular crates. - /// - /// Strategy quick-install (can be disabled via --disable-strategies) collects - /// statistics of popular crates by default, by sending the crate, version, target - /// and status to https://cargo-quickinstall-stats-server.fly.dev/record-install - #[clap(help_heading = "Options", long, env = "BINSTALL_DISABLE_TELEMETRY")] - pub(crate) disable_telemetry: bool, - - /// Install binaries in a custom location. - /// - /// By default, binaries are installed to the global location `$CARGO_HOME/bin`, and global - /// metadata files are updated with the package information. Specifying another path here - /// switches over to a "local" install, where binaries are installed at the path given, and the - /// global metadata files are not updated. - #[clap(help_heading = "Options", long, value_name = "PATH")] - pub(crate) install_path: Option, - - /// Install binaries with a custom cargo root. - /// - /// By default, we use `$CARGO_INSTALL_ROOT` or `$CARGO_HOME` as the - /// cargo root and global metadata files are updated with the - /// package information. - /// - /// Specifying another path here would install the binaries and update - /// the metadata files inside the path you specified. - /// - /// NOTE that `--install-path` takes precedence over this option. - #[clap(help_heading = "Options", long, alias = "roots")] - pub(crate) root: Option, - - /// The URL of the registry index to use. - /// - /// Cannot be used with `--registry`. - #[clap(help_heading = "Options", long)] - pub(crate) index: Option, - - /// Name of the registry to use. Registry names are defined in Cargo config - /// files . - /// - /// If not specified in cmdline or via environment variable, the default - /// registry is used, which is defined by the - /// `registry.default` config key in `.cargo/config.toml` which defaults - /// to crates-io. - /// - /// If it is set, then it will try to read environment variable - /// `CARGO_REGISTRIES_{registry_name}_INDEX` for index url and fallback to - /// reading from `registries..index`. - /// - /// Cannot be used with `--index`. - #[clap( - help_heading = "Options", - long, - env = "CARGO_REGISTRY_DEFAULT", - conflicts_with("index") - )] - pub(crate) registry: Option, - - /// This option will be passed through to all `cargo-install` invocations. - /// - /// It will require `Cargo.lock` to be up to date. - #[clap(help_heading = "Options", long)] - pub(crate) locked: bool, - - /// Deprecated, here for back-compat only. Secure is now on by default. - #[clap(hide(true), long)] - pub(crate) secure: bool, - - /// Force a crate to be installed even if it is already installed. - #[clap(help_heading = "Options", long)] - pub(crate) force: bool, - - /// Require a minimum TLS version from remote endpoints. - /// - /// The default is not to require any minimum TLS version, and use the negotiated highest - /// version available to both this client and the remote server. - #[clap(help_heading = "Options", long, value_enum, value_name = "VERSION")] - pub(crate) min_tls_version: Option, - - /// Specify the root certificates to use for https connnections, - /// in addition to default system-wide ones. - #[clap( - help_heading = "Options", - long, - env = "BINSTALL_HTTPS_ROOT_CERTS", - value_name = "PATH" - )] - pub(crate) root_certificates: Vec, - - /// Print logs in json format to be parsable. - #[clap(help_heading = "Options", long)] - pub json_output: bool, - - /// Provide the github token for accessing the restful API of api.github.com - /// - /// Fallback to environment variable `GITHUB_TOKEN` if this option is not - /// specified (which is also shown by clap's auto generated doc below), or - /// try environment variable `GH_TOKEN`, which is also used by `gh` cli. - /// - /// If none of them is present, then binstall will try to extract github - /// token from `$HOME/.git-credentials` or `$HOME/.config/gh/hosts.yml` - /// unless `--no-discover-github-token` is specified. - #[clap( - help_heading = "Options", - long, - env = "GITHUB_TOKEN", - value_name = "TOKEN" - )] - pub(crate) github_token: Option, - - /// Only install packages that are signed - /// - /// The default is to verify signatures if they are available, but to allow - /// unsigned packages as well. - #[clap(help_heading = "Options", long)] - pub(crate) only_signed: bool, - - /// Don't check any signatures - /// - /// The default is to verify signatures if they are available. This option - /// disables that behaviour entirely, which will also stop downloading - /// signature files in the first place. - /// - /// Note that this is insecure and not recommended outside of testing. - #[clap(help_heading = "Options", long, conflicts_with = "only_signed")] - pub(crate) skip_signatures: bool, - - /// Print version information - #[clap(help_heading = "Meta", short = 'V')] - pub version: bool, - - /// Utility log level - /// - /// Set to `trace` to print very low priority, often extremely - /// verbose information. - /// - /// Set to `debug` when submitting a bug report. - /// - /// Set to `info` to only print useful information. - /// - /// Set to `warn` to only print on hazardous situations. - /// - /// Set to `error` to only print serious errors. - /// - /// Set to `off` to disable logging completely, this will also - /// disable output from `cargo-install`. - /// - /// If `--log-level` is not specified on cmdline, then cargo-binstall - /// will try to read environment variable `BINSTALL_LOG_LEVEL` and - /// interpret it as a log-level. - #[clap(help_heading = "Meta", long, value_name = "LEVEL")] - pub log_level: Option, - - /// Implies `--log-level debug` and it can also be used with `--version` - /// to print out verbose information, - #[clap(help_heading = "Meta", short, long)] - pub verbose: bool, - - /// Equivalent to setting `log_level` to `off`. - /// - /// This would override the `log_level`. - #[clap(help_heading = "Meta", short, long, conflicts_with("verbose"))] - pub(crate) quiet: bool, - - #[clap(long, hide(true))] - pub(crate) self_install: bool, -} - -#[derive(Debug, Clone)] -pub(crate) struct GithubToken(pub(crate) Zeroizing>); - -impl From<&str> for GithubToken { - fn from(s: &str) -> Self { - Self(Zeroizing::new(s.into())) - } -} - -#[derive(Debug, Copy, Clone, ValueEnum)] -pub(crate) enum TLSVersion { - #[clap(name = "1.2")] - Tls1_2, - #[clap(name = "1.3")] - Tls1_3, -} - -impl From for remote::TLSVersion { - fn from(ver: TLSVersion) -> Self { - match ver { - TLSVersion::Tls1_2 => remote::TLSVersion::TLS_1_2, - TLSVersion::Tls1_3 => remote::TLSVersion::TLS_1_3, - } - } -} - -#[derive(Copy, Clone, Debug)] -pub(crate) struct RateLimit { - pub(crate) duration: NonZeroU16, - pub(crate) request_count: NonZeroU64, -} - -impl fmt::Display for RateLimit { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}/{}", self.duration, self.request_count) - } -} - -impl FromStr for RateLimit { - type Err = ParseIntError; - - fn from_str(s: &str) -> Result { - Ok(if let Some((first, second)) = s.split_once('/') { - Self { - duration: first.parse()?, - request_count: second.parse()?, - } - } else { - Self { - duration: s.parse()?, - ..Default::default() - } - }) - } -} - -impl Default for RateLimit { - fn default() -> Self { - Self { - duration: NonZeroU16::new(10).unwrap(), - request_count: NonZeroU64::new(1).unwrap(), - } - } -} - -/// Strategy for installing the package -#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] -pub(crate) struct StrategyWrapped(pub(crate) Strategy); - -impl StrategyWrapped { - const VARIANTS: &'static [Self; 3] = &[ - Self(Strategy::CrateMetaData), - Self(Strategy::QuickInstall), - Self(Strategy::Compile), - ]; -} - -impl ValueEnum for StrategyWrapped { - fn value_variants<'a>() -> &'a [Self] { - Self::VARIANTS - } - fn to_possible_value(&self) -> Option { - Some(PossibleValue::new(self.0.to_str())) - } -} - -pub fn parse() -> (Args, PkgOverride) { - // Filter extraneous arg when invoked by cargo - // `cargo run -- --help` gives ["target/debug/cargo-binstall", "--help"] - // `cargo binstall --help` gives ["/home/ryan/.cargo/bin/cargo-binstall", "binstall", "--help"] - let mut args: Vec = env::args_os().collect(); - let args = if args.get(1).map(|arg| arg == "binstall").unwrap_or_default() { - // Equivalent to - // - // args.remove(1); - // - // But is O(1) - args.swap(0, 1); - let mut args = args.into_iter(); - drop(args.next().unwrap()); - - args - } else { - args.into_iter() - }; - - // Load options - let mut opts = Args::parse_from(args); - - if opts.self_install { - return (opts, Default::default()); - } - - if opts.log_level.is_none() { - if let Some(log) = env::var("BINSTALL_LOG_LEVEL") - .ok() - .and_then(|s| s.parse().ok()) - { - opts.log_level = Some(log); - } else if opts.quiet { - opts.log_level = Some(LevelFilter::Off); - } else if opts.verbose { - opts.log_level = Some(LevelFilter::Debug); - } - } - - // Ensure no conflict - let mut command = Args::command(); - - if opts.crate_names.len() > 1 { - let option = if opts.version_req.is_some() { - "version" - } else if opts.manifest_path.is_some() { - "manifest-path" - } else { - #[cfg(not(feature = "git"))] - { - "" - } - - #[cfg(feature = "git")] - if opts.git.is_some() { - "git" - } else { - "" - } - }; - - if !option.is_empty() { - command - .error( - ErrorKind::ArgumentConflict, - format_args!( - r#"override option used with multi package syntax. -You cannot use --{option} and specify multiple packages at the same time. Do one or the other."# - ), - ) - .exit(); - } - } - - // Check strategies for duplicates - let mut new_dup_strategy_err = || { - command.error( - ErrorKind::TooManyValues, - "--strategies should not contain duplicate strategy", - ) - }; - - if opts.strategies.len() > Strategy::COUNT { - // If len of strategies is larger than number of variants of Strategy, - // then there must be duplicates by pigeon hole principle. - new_dup_strategy_err().exit() - } - - // Whether specific variant of Strategy is present - let mut is_variant_present = [false; Strategy::COUNT]; - - for strategy in &opts.strategies { - let index = strategy.0 as u8 as usize; - if is_variant_present[index] { - new_dup_strategy_err().exit() - } else { - is_variant_present[index] = true; - } - } - - let ignore_disabled_strategies = !opts.strategies.is_empty(); - - // Default strategies if empty - if opts.strategies.is_empty() { - opts.strategies = vec![ - StrategyWrapped(Strategy::CrateMetaData), - StrategyWrapped(Strategy::QuickInstall), - StrategyWrapped(Strategy::Compile), - ]; - } - - // Filter out all disabled strategies - if !opts.disable_strategies.is_empty() { - // Since order doesn't matter, we can sort it and remove all duplicates - // to speedup checking. - opts.disable_strategies.sort_unstable(); - opts.disable_strategies.dedup(); - - // disable_strategies.len() <= Strategy::COUNT, of which is faster - // to just use [Strategy]::contains rather than - // [Strategy]::binary_search - opts.strategies - .retain(|strategy| !opts.disable_strategies.contains(strategy)); - - if opts.strategies.is_empty() { - command - .error(ErrorKind::TooFewValues, "You have disabled all strategies") - .exit() - } - } - - // Ensure that Strategy::Compile is specified as the last strategy - if opts.strategies[..(opts.strategies.len() - 1)].contains(&StrategyWrapped(Strategy::Compile)) - { - command - .error( - ErrorKind::InvalidValue, - "Compile strategy must be the last one", - ) - .exit() - } - - if opts.github_token.is_none() { - if let Ok(github_token) = env::var("GH_TOKEN") { - opts.github_token = Some(GithubToken(Zeroizing::new(github_token.into()))); - } - } - match opts.github_token.as_ref() { - Some(token) if token.0.len() < 10 => opts.github_token = None, - _ => (), - } - - let cli_overrides = PkgOverride { - pkg_url: opts.pkg_url.take(), - pkg_fmt: opts.pkg_fmt.take(), - bin_dir: opts.bin_dir.take(), - disabled_strategies: Some( - mem::take(&mut opts.disable_strategies) - .into_iter() - .map(|strategy| strategy.0) - .collect::>() - .into_boxed_slice(), - ), - ignore_disabled_strategies, - signing: None, - }; - - (opts, cli_overrides) -} - -#[cfg(test)] -mod test { - use strum::VariantArray; - - use super::*; - - #[test] - fn verify_cli() { - Args::command().debug_assert() - } - - #[test] - fn quickinstall_url_matches() { - let long_help = Args::command() - .get_opts() - .find(|opt| opt.get_long() == Some("disable-telemetry")) - .unwrap() - .get_long_help() - .unwrap() - .to_string(); - assert!( - long_help.ends_with(binstalk::QUICKINSTALL_STATS_URL), - "{}", - long_help - ); - } - - const _: () = assert!(Strategy::VARIANTS.len() == StrategyWrapped::VARIANTS.len()); -} diff --git a/crates/bin/src/bin_util.rs b/crates/bin/src/bin_util.rs deleted file mode 100644 index 9761526f..00000000 --- a/crates/bin/src/bin_util.rs +++ /dev/null @@ -1,64 +0,0 @@ -use std::{ - process::{ExitCode, Termination}, - time::Duration, -}; - -use binstalk::errors::BinstallError; -use binstalk::helpers::tasks::AutoAbortJoinHandle; -use miette::Result; -use tokio::runtime::Runtime; -use tracing::{error, info}; - -use crate::signal::cancel_on_user_sig_term; - -pub enum MainExit { - Success(Option), - Error(BinstallError), - Report(miette::Report), -} - -impl Termination for MainExit { - fn report(self) -> ExitCode { - match self { - Self::Success(spent) => { - if let Some(spent) = spent { - info!("Done in {spent:?}"); - } - ExitCode::SUCCESS - } - Self::Error(err) => err.report(), - Self::Report(err) => { - error!("Fatal error:\n{err:?}"); - ExitCode::from(16) - } - } - } -} - -impl MainExit { - pub fn new(res: Result<()>, done: Option) -> Self { - res.map(|()| MainExit::Success(done)).unwrap_or_else(|err| { - err.downcast::() - .map(MainExit::Error) - .unwrap_or_else(MainExit::Report) - }) - } -} - -/// This function would start a tokio multithreading runtime, -/// then `block_on` the task it returns. -/// -/// It will cancel the future if user requested cancellation -/// via signal. -pub fn run_tokio_main( - f: impl FnOnce() -> Result>>>, -) -> Result<()> { - let rt = Runtime::new().map_err(BinstallError::from)?; - let _guard = rt.enter(); - - if let Some(handle) = f()? { - rt.block_on(cancel_on_user_sig_term(handle))? - } else { - Ok(()) - } -} diff --git a/crates/bin/src/entry.rs b/crates/bin/src/entry.rs deleted file mode 100644 index 43feaa3a..00000000 --- a/crates/bin/src/entry.rs +++ /dev/null @@ -1,630 +0,0 @@ -use std::{ - env, fs, - path::{Path, PathBuf}, - sync::Arc, - time::Duration, -}; - -use atomic_file_install::atomic_install; -use binstalk::{ - errors::{BinstallError, CrateContextError}, - fetchers::{Fetcher, GhCrateMeta, QuickInstall, SignaturePolicy}, - get_desired_targets, - helpers::{ - jobserver_client::LazyJobserverClient, - lazy_gh_api_client::LazyGhApiClient, - remote::{Certificate, Client}, - tasks::AutoAbortJoinHandle, - }, - ops::{ - self, - resolve::{CrateName, Resolution, ResolutionFetch, VersionReqExt}, - CargoTomlFetchOverride, Options, Resolver, - }, - TARGET, -}; -use binstalk_manifests::{ - cargo_config::Config, - cargo_toml_binstall::{PkgOverride, Strategy}, - crate_info::{CrateInfo, CrateSource}, - crates_manifests::Manifests, -}; -use compact_str::CompactString; -use file_format::FileFormat; -use home::cargo_home; -use log::LevelFilter; -use miette::{miette, Report, Result, WrapErr}; -use semver::Version; -use tokio::task::block_in_place; -use tracing::{debug, error, info, warn}; - -use crate::{args::Args, gh_token, git_credentials, install_path, ui::confirm}; - -pub fn install_crates( - args: Args, - cli_overrides: PkgOverride, - jobserver_client: LazyJobserverClient, -) -> Result>>> { - // Compute Resolvers - let mut cargo_install_fallback = false; - - let resolvers: Vec<_> = args - .strategies - .into_iter() - .filter_map(|strategy| match strategy.0 { - Strategy::CrateMetaData => Some(GhCrateMeta::new as Resolver), - Strategy::QuickInstall => Some(QuickInstall::new as Resolver), - Strategy::Compile => { - cargo_install_fallback = true; - None - } - }) - .collect(); - - // Load .cargo/config.toml - let cargo_home = cargo_home().map_err(BinstallError::from)?; - let mut config = Config::load_from_path(cargo_home.join("config.toml"))?; - - // Compute paths - let cargo_root = args.root; - let (install_path, mut manifests, temp_dir) = compute_paths_and_load_manifests( - cargo_root.clone(), - args.install_path, - args.no_track, - cargo_home, - &mut config, - )?; - - // Remove installed crates - let mut crate_names = - filter_out_installed_crates(args.crate_names, args.force, manifests.as_mut())?.peekable(); - - if crate_names.peek().is_none() { - debug!("Nothing to do"); - return Ok(None); - } - - // Launch target detection - let desired_targets = get_desired_targets(args.targets); - - // Initialize reqwest client - let rate_limit = args.rate_limit; - - let mut http = config.http.take(); - - let client = Client::new( - concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION")), - args.min_tls_version.map(|v| v.into()), - rate_limit.duration, - rate_limit.request_count, - read_root_certs( - args.root_certificates, - http.as_mut().and_then(|http| http.cainfo.take()), - ), - ) - .map_err(BinstallError::from)?; - - let gh_api_client = args - .github_token - .map(|token| token.0) - .or_else(|| { - if args.no_discover_github_token { - None - } else { - git_credentials::try_from_home() - } - }) - .map(|token| LazyGhApiClient::new(client.clone(), Some(token))) - .unwrap_or_else(|| { - if args.no_discover_github_token { - LazyGhApiClient::new(client.clone(), None) - } else { - LazyGhApiClient::with_get_gh_token_future(client.clone(), async { - match gh_token::get().await { - Ok(token) => Some(token), - Err(err) => { - debug!(?err, "Failed to retrieve token from `gh auth token`"); - debug!("Failed to read git credential file"); - None - } - } - }) - } - }); - - // Create binstall_opts - let binstall_opts = Arc::new(Options { - no_symlinks: args.no_symlinks, - dry_run: args.dry_run, - force: args.force, - quiet: args.log_level == Some(LevelFilter::Off), - locked: args.locked, - no_track: args.no_track, - - version_req: args.version_req, - #[cfg(feature = "git")] - cargo_toml_fetch_override: match (args.manifest_path, args.git) { - (Some(manifest_path), None) => Some(CargoTomlFetchOverride::Path(manifest_path)), - (None, Some(git_url)) => Some(CargoTomlFetchOverride::Git(git_url)), - (None, None) => None, - _ => unreachable!("manifest_path and git cannot be specified at the same time"), - }, - - #[cfg(not(feature = "git"))] - cargo_toml_fetch_override: args.manifest_path.map(CargoTomlFetchOverride::Path), - cli_overrides, - - desired_targets, - resolvers, - cargo_install_fallback, - - temp_dir: temp_dir.path().to_owned(), - install_path, - cargo_root, - - client, - gh_api_client, - jobserver_client, - registry: if let Some(index) = args.index { - index - } else if let Some(registry_name) = args - .registry - .or_else(|| config.registry.and_then(|registry| registry.default)) - { - let registry_name_lowercase = registry_name.to_lowercase(); - - let v = env::vars().find_map(|(k, v)| { - let name_lowercase = k - .strip_prefix("CARGO_REGISTRIES_")? - .strip_suffix("_INDEX")? - .to_lowercase(); - - (name_lowercase == registry_name_lowercase).then_some(v) - }); - - if let Some(v) = &v { - v - } else { - config - .registries - .as_ref() - .and_then(|registries| registries.get(®istry_name)) - .and_then(|registry| registry.index.as_deref()) - .ok_or_else(|| BinstallError::UnknownRegistryName(registry_name))? - } - .parse() - .map_err(BinstallError::from)? - } else { - Default::default() - }, - - signature_policy: if args.only_signed { - SignaturePolicy::Require - } else if args.skip_signatures { - SignaturePolicy::Ignore - } else { - SignaturePolicy::IfPresent - }, - disable_telemetry: args.disable_telemetry, - - maximum_resolution_timeout: Duration::from_secs( - args.maximum_resolution_timeout.get().into(), - ), - }); - - // Destruct args before any async function to reduce size of the future - let dry_run = args.dry_run; - let no_confirm = args.no_confirm; - let no_cleanup = args.no_cleanup; - - // Resolve crates - let tasks: Vec<_> = crate_names - .map(|(crate_name, current_version)| { - AutoAbortJoinHandle::spawn(ops::resolve::resolve( - binstall_opts.clone(), - crate_name, - current_version, - )) - }) - .collect(); - - Ok(Some(if args.continue_on_failure { - AutoAbortJoinHandle::spawn(async move { - // Collect results - let mut resolution_fetchs = Vec::new(); - let mut resolution_sources = Vec::new(); - let mut errors = Vec::new(); - - for task in tasks { - match task.flattened_join().await { - Ok(Resolution::AlreadyUpToDate) => {} - Ok(Resolution::Fetch(fetch)) => { - fetch.print(&binstall_opts); - resolution_fetchs.push(fetch) - } - Ok(Resolution::InstallFromSource(source)) => { - source.print(); - resolution_sources.push(source) - } - Err(BinstallError::CrateContext(err)) => errors.push(err), - Err(e) => panic!("Expected BinstallError::CrateContext(_), got {}", e), - } - } - - if resolution_fetchs.is_empty() && resolution_sources.is_empty() { - return if let Some(err) = BinstallError::crate_errors(errors) { - Err(err.into()) - } else { - debug!("Nothing to do"); - Ok(()) - }; - } - - // Confirm - if !dry_run && !no_confirm { - if let Err(abort_err) = confirm().await { - return if let Some(err) = BinstallError::crate_errors(errors) { - Err(Report::new(abort_err).wrap_err(err)) - } else { - Err(abort_err.into()) - }; - } - } - - let manifest_update_res = do_install_fetches_continue_on_failure( - resolution_fetchs, - manifests, - &binstall_opts, - dry_run, - temp_dir, - no_cleanup, - &mut errors, - ); - - let tasks: Vec<_> = resolution_sources - .into_iter() - .map(|source| AutoAbortJoinHandle::spawn(source.install(binstall_opts.clone()))) - .collect(); - - for task in tasks { - match task.flattened_join().await { - Ok(_) => (), - Err(BinstallError::CrateContext(err)) => errors.push(err), - Err(e) => panic!("Expected BinstallError::CrateContext(_), got {}", e), - } - } - - match (BinstallError::crate_errors(errors), manifest_update_res) { - (None, Ok(())) => Ok(()), - (None, Err(err)) => Err(err), - (Some(err), Ok(())) => Err(err.into()), - (Some(err), Err(manifest_update_err)) => { - Err(Report::new(err).wrap_err(manifest_update_err)) - } - } - }) - } else { - AutoAbortJoinHandle::spawn(async move { - // Collect results - let mut resolution_fetchs = Vec::new(); - let mut resolution_sources = Vec::new(); - - for task in tasks { - match task.await?? { - Resolution::AlreadyUpToDate => {} - Resolution::Fetch(fetch) => { - fetch.print(&binstall_opts); - resolution_fetchs.push(fetch) - } - Resolution::InstallFromSource(source) => { - source.print(); - resolution_sources.push(source) - } - } - } - - if resolution_fetchs.is_empty() && resolution_sources.is_empty() { - debug!("Nothing to do"); - return Ok(()); - } - - // Confirm - if !dry_run && !no_confirm { - confirm().await?; - } - - do_install_fetches( - resolution_fetchs, - manifests, - &binstall_opts, - dry_run, - temp_dir, - no_cleanup, - )?; - - let tasks: Vec<_> = resolution_sources - .into_iter() - .map(|source| AutoAbortJoinHandle::spawn(source.install(binstall_opts.clone()))) - .collect(); - - for task in tasks { - task.await??; - } - - Ok(()) - }) - })) -} - -fn do_read_root_cert(path: &Path) -> Result, BinstallError> { - use std::io::{Read, Seek}; - - let mut file = fs::File::open(path)?; - let file_format = FileFormat::from_reader(&mut file)?; - - let open_cert = match file_format { - FileFormat::PemCertificate => Certificate::from_pem, - FileFormat::DerCertificate => Certificate::from_der, - _ => { - warn!( - "Unable to load {}: Expected pem or der ceritificate but found {file_format}", - path.display() - ); - - return Ok(None); - } - }; - - // Move file back to its head - file.rewind()?; - - let mut buffer = Vec::with_capacity(200); - file.read_to_end(&mut buffer)?; - - open_cert(&buffer).map_err(From::from).map(Some) -} - -fn read_root_certs( - root_certificate_paths: Vec, - config_cainfo: Option, -) -> impl Iterator { - root_certificate_paths - .into_iter() - .chain(config_cainfo) - .filter_map(|path| match do_read_root_cert(&path) { - Ok(optional_cert) => optional_cert, - Err(err) => { - warn!( - "Failed to load root certificate at {}: {err}", - path.display() - ); - None - } - }) -} - -/// Return (install_path, manifests, temp_dir) -fn compute_paths_and_load_manifests( - roots: Option, - install_path: Option, - no_track: bool, - cargo_home: PathBuf, - config: &mut Config, -) -> Result<(PathBuf, Option, tempfile::TempDir)> { - // Compute cargo_roots - let cargo_roots = - install_path::get_cargo_roots_path(roots, cargo_home, config).ok_or_else(|| { - error!("No viable cargo roots path found of specified, try `--roots`"); - miette!("No cargo roots path found or specified") - })?; - - // Compute install directory - let (install_path, custom_install_path) = - install_path::get_install_path(install_path, Some(&cargo_roots)); - let install_path = install_path.ok_or_else(|| { - error!("No viable install path found of specified, try `--install-path`"); - miette!("No install path found or specified") - })?; - fs::create_dir_all(&install_path).map_err(BinstallError::Io)?; - debug!("Using install path: {}", install_path.display()); - - let no_manifests = no_track || custom_install_path; - - // Load manifests - let manifests = if !no_manifests { - Some(Manifests::open_exclusive(&cargo_roots)?) - } else { - None - }; - - // Create a temporary directory for downloads etc. - // - // Put all binaries to a temporary directory under `dst` first, catching - // some failure modes (e.g., out of space) before touching the existing - // binaries. This directory will get cleaned up via RAII. - let temp_dir = tempfile::Builder::new() - .prefix("cargo-binstall") - .tempdir_in(&install_path) - .map_err(BinstallError::from) - .wrap_err("Creating a temporary directory failed.")?; - - Ok((install_path, manifests, temp_dir)) -} - -/// Return vec of (crate_name, current_version) -fn filter_out_installed_crates( - crate_names: Vec, - force: bool, - manifests: Option<&mut Manifests>, -) -> Result)> + '_> { - let mut installed_crates = manifests - .map(Manifests::load_installed_crates) - .transpose()?; - - Ok(CrateName::dedup(crate_names) - .filter_map(move |crate_name| { - let name = &crate_name.name; - - let curr_version = installed_crates - .as_mut() - // Since crate_name is deduped, every entry of installed_crates - // can be visited at most once. - // - // So here we take ownership of the version stored to avoid cloning. - .and_then(|crates| crates.remove(name)); - - match ( - force, - curr_version, - &crate_name.version_req, - ) { - (false, Some(curr_version), Some(version_req)) - if version_req.is_latest_compatible(&curr_version) => - { - debug!("Bailing out early because we can assume wanted is already installed from metafile"); - info!("{name} v{curr_version} is already installed, use --force to override"); - None - } - - // The version req is "*" thus a remote upgraded version could exist - (false, Some(curr_version), None) => { - Some((crate_name, Some(curr_version))) - } - - _ => Some((crate_name, None)), - } - })) -} - -#[allow(clippy::vec_box)] -fn do_install_fetches( - resolution_fetchs: Vec>, - // Take manifests by value to drop the `FileLock`. - manifests: Option, - binstall_opts: &Options, - dry_run: bool, - temp_dir: tempfile::TempDir, - no_cleanup: bool, -) -> Result<()> { - if resolution_fetchs.is_empty() { - return Ok(()); - } - - if dry_run { - info!("Dry-run: Not proceeding to install fetched binaries"); - return Ok(()); - } - - block_in_place(|| { - let metadata_vec = resolution_fetchs - .into_iter() - .map(|fetch| fetch.install(binstall_opts)) - .collect::, BinstallError>>()?; - - if let Some(manifests) = manifests { - manifests.update(metadata_vec)?; - } - - if no_cleanup { - // Consume temp_dir without removing it from fs. - let _ = temp_dir.into_path(); - } else { - temp_dir.close().unwrap_or_else(|err| { - warn!("Failed to clean up some resources: {err}"); - }); - } - - Ok(()) - }) -} - -#[allow(clippy::vec_box)] -fn do_install_fetches_continue_on_failure( - resolution_fetchs: Vec>, - // Take manifests by value to drop the `FileLock`. - manifests: Option, - binstall_opts: &Options, - dry_run: bool, - temp_dir: tempfile::TempDir, - no_cleanup: bool, - errors: &mut Vec>, -) -> Result<()> { - if resolution_fetchs.is_empty() { - return Ok(()); - } - - if dry_run { - info!("Dry-run: Not proceeding to install fetched binaries"); - return Ok(()); - } - - block_in_place(|| { - let metadata_vec = resolution_fetchs - .into_iter() - .filter_map(|fetch| match fetch.install(binstall_opts) { - Ok(crate_info) => Some(crate_info), - Err(BinstallError::CrateContext(err)) => { - errors.push(err); - None - } - Err(e) => panic!("Expected BinstallError::CrateContext(_), got {}", e), - }) - .collect::>(); - - if let Some(manifests) = manifests { - manifests.update(metadata_vec)?; - } - - if no_cleanup { - // Consume temp_dir without removing it from fs. - let _ = temp_dir.into_path(); - } else { - temp_dir.close().unwrap_or_else(|err| { - warn!("Failed to clean up some resources: {err}"); - }); - } - - Ok(()) - }) -} - -pub fn self_install(args: Args) -> Result<()> { - // Load .cargo/config.toml - let cargo_home = cargo_home().map_err(BinstallError::from)?; - let mut config = Config::load_from_path(cargo_home.join("config.toml"))?; - - // Compute paths - let cargo_root = args.root; - let (install_path, manifests, _) = compute_paths_and_load_manifests( - cargo_root.clone(), - args.install_path, - args.no_track, - cargo_home, - &mut config, - )?; - - let mut dest = install_path.join("cargo-binstall"); - if cfg!(windows) { - assert!(dest.set_extension("exe")); - } - - atomic_install(&env::current_exe().map_err(BinstallError::from)?, &dest) - .map_err(BinstallError::from)?; - - if let Some(manifests) = manifests { - manifests.update(vec![CrateInfo { - name: CompactString::const_new("cargo-binstall"), - version_req: CompactString::const_new("*"), - current_version: Version::new( - env!("CARGO_PKG_VERSION_MAJOR").parse().unwrap(), - env!("CARGO_PKG_VERSION_MINOR").parse().unwrap(), - env!("CARGO_PKG_VERSION_PATCH").parse().unwrap(), - ), - source: CrateSource::cratesio_registry(), - target: CompactString::const_new(TARGET), - bins: vec![CompactString::const_new("cargo-binstall")], - }])?; - } - - Ok(()) -} diff --git a/crates/bin/src/gh_token.rs b/crates/bin/src/gh_token.rs deleted file mode 100644 index 9cc7adeb..00000000 --- a/crates/bin/src/gh_token.rs +++ /dev/null @@ -1,99 +0,0 @@ -use std::{ - io, - process::{Output, Stdio}, - str, -}; - -use tokio::{io::AsyncWriteExt, process::Command}; -use zeroize::{Zeroize, Zeroizing}; - -pub(super) async fn get() -> io::Result>> { - let output = Command::new("gh") - .args(["auth", "token"]) - .stdout_with_optional_input(None) - .await?; - - if !output.is_empty() { - return Ok(output); - } - - Command::new("git") - .args(["credential", "fill"]) - .stdout_with_optional_input(Some("host=github.com\nprotocol=https".as_bytes())) - .await? - .lines() - .find_map(|line| { - line.trim() - .strip_prefix("password=") - .map(|token| Zeroizing::new(token.into())) - }) - .ok_or_else(|| { - io::Error::new( - io::ErrorKind::Other, - "Password not found in `git credential fill` output", - ) - }) -} - -trait CommandExt { - // Helper function to execute a command, optionally with input - async fn stdout_with_optional_input( - &mut self, - input: Option<&[u8]>, - ) -> io::Result>>; -} - -impl CommandExt for Command { - async fn stdout_with_optional_input( - &mut self, - input: Option<&[u8]>, - ) -> io::Result>> { - self.stdout(Stdio::piped()) - .stderr(Stdio::null()) - .stdin(if input.is_some() { - Stdio::piped() - } else { - Stdio::null() - }); - - let mut child = self.spawn()?; - - if let Some(input) = input { - child.stdin.take().unwrap().write_all(input).await?; - } - - let Output { status, stdout, .. } = child.wait_with_output().await?; - - if status.success() { - let s = String::from_utf8(stdout).map_err(|err| { - let msg = format!( - "Invalid output for `{:?}`, expected utf8: {err}", - self.as_std() - ); - - zeroize_and_drop(err.into_bytes()); - - io::Error::new(io::ErrorKind::InvalidData, msg) - })?; - - let trimmed = s.trim(); - - Ok(if trimmed.len() == s.len() { - Zeroizing::new(s.into_boxed_str()) - } else { - Zeroizing::new(trimmed.into()) - }) - } else { - zeroize_and_drop(stdout); - - Err(io::Error::new( - io::ErrorKind::Other, - format!("`{:?}` process exited with `{status}`", self.as_std()), - )) - } - } -} - -fn zeroize_and_drop(mut bytes: Vec) { - bytes.zeroize(); -} diff --git a/crates/bin/src/git_credentials.rs b/crates/bin/src/git_credentials.rs deleted file mode 100644 index 9c9a35ba..00000000 --- a/crates/bin/src/git_credentials.rs +++ /dev/null @@ -1,66 +0,0 @@ -use std::{env, fs, path::PathBuf}; - -use dirs::home_dir; -use zeroize::Zeroizing; - -pub fn try_from_home() -> Option>> { - if let Some(mut home) = home_dir() { - home.push(".git-credentials"); - if let Some(cred) = from_file(home) { - return Some(cred); - } - } - - if let Some(home) = env::var_os("XDG_CONFIG_HOME") { - let mut home = PathBuf::from(home); - home.push("git/credentials"); - - if let Some(cred) = from_file(home) { - return Some(cred); - } - } - - None -} - -fn from_file(path: PathBuf) -> Option>> { - Zeroizing::new(fs::read_to_string(path).ok()?) - .lines() - .find_map(from_line) - .map(Box::::from) - .map(Zeroizing::new) -} - -fn from_line(line: &str) -> Option<&str> { - let cred = line - .trim() - .strip_prefix("https://")? - .strip_suffix("@github.com")?; - - Some(cred.split_once(':')?.1) -} - -#[cfg(test)] -mod test { - use super::*; - - const GIT_CREDENTIALS_TEST_CASES: &[(&str, Option<&str>)] = &[ - // Success - ("https://NobodyXu:gho_asdc@github.com", Some("gho_asdc")), - ( - "https://NobodyXu:gho_asdc12dz@github.com", - Some("gho_asdc12dz"), - ), - // Failure - ("http://NobodyXu:gho_asdc@github.com", None), - ("https://NobodyXu:gho_asdc@gitlab.com", None), - ("https://NobodyXugho_asdc@github.com", None), - ]; - - #[test] - fn test_extract_from_line() { - GIT_CREDENTIALS_TEST_CASES.iter().for_each(|(line, res)| { - assert_eq!(from_line(line), *res); - }) - } -} diff --git a/crates/bin/src/install_path.rs b/crates/bin/src/install_path.rs deleted file mode 100644 index b5dea041..00000000 --- a/crates/bin/src/install_path.rs +++ /dev/null @@ -1,56 +0,0 @@ -use std::{ - env::var_os, - path::{Path, PathBuf}, -}; - -use binstalk_manifests::cargo_config::Config; -use tracing::debug; - -pub fn get_cargo_roots_path( - cargo_roots: Option, - cargo_home: PathBuf, - config: &mut Config, -) -> Option { - if let Some(p) = cargo_roots { - Some(p) - } else if let Some(p) = var_os("CARGO_INSTALL_ROOT") { - // Environmental variables - let p = PathBuf::from(p); - debug!("using CARGO_INSTALL_ROOT ({})", p.display()); - Some(p) - } else if let Some(root) = config.install.take().and_then(|install| install.root) { - debug!("using `install.root` {} from cargo config", root.display()); - Some(root) - } else { - debug!("using ({}) as cargo home", cargo_home.display()); - Some(cargo_home) - } -} - -/// Fetch install path from environment -/// roughly follows -/// -/// Return (install_path, is_custom_install_path) -pub fn get_install_path( - install_path: Option, - cargo_roots: Option>, -) -> (Option, bool) { - // Command line override first first - if let Some(p) = install_path { - return (Some(p), true); - } - - // Then cargo_roots - if let Some(p) = cargo_roots { - return (Some(p.as_ref().join("bin")), false); - } - - // Local executable dir if no cargo is found - let dir = dirs::executable_dir(); - - if let Some(d) = &dir { - debug!("Fallback to {}", d.display()); - } - - (dir, true) -} diff --git a/crates/bin/src/lib.rs b/crates/bin/src/lib.rs deleted file mode 100644 index 1e25d408..00000000 --- a/crates/bin/src/lib.rs +++ /dev/null @@ -1,14 +0,0 @@ -#![cfg_attr(docsrs, feature(doc_auto_cfg))] - -mod args; -mod bin_util; -mod entry; -mod gh_token; -mod git_credentials; -mod install_path; -mod logging; -mod main_impl; -mod signal; -mod ui; - -pub use main_impl::do_main; diff --git a/crates/bin/src/logging.rs b/crates/bin/src/logging.rs deleted file mode 100644 index 320f266a..00000000 --- a/crates/bin/src/logging.rs +++ /dev/null @@ -1,248 +0,0 @@ -use std::{ - cmp::min, - io::{self, Write}, - iter::repeat, -}; - -use log::{LevelFilter, Log, STATIC_MAX_LEVEL}; -use once_cell::sync::Lazy; -use supports_color::{on as supports_color_on_stream, Stream::Stdout}; -use tracing::{ - callsite::Callsite, - dispatcher, field, - subscriber::{self, set_global_default}, - Event, Level, Metadata, -}; -use tracing_core::{identify_callsite, metadata::Kind, subscriber::Subscriber}; -use tracing_log::AsTrace; -use tracing_subscriber::{ - filter::targets::Targets, - fmt::{fmt, MakeWriter}, - layer::SubscriberExt, -}; - -// Shamelessly taken from tracing-log - -struct Fields { - message: field::Field, -} - -static FIELD_NAMES: &[&str] = &["message"]; - -impl Fields { - fn new(cs: &'static dyn Callsite) -> Self { - let fieldset = cs.metadata().fields(); - let message = fieldset.field("message").unwrap(); - Fields { message } - } -} - -macro_rules! log_cs { - ($level:expr, $cs:ident, $meta:ident, $fields:ident, $ty:ident) => { - struct $ty; - static $cs: $ty = $ty; - static $meta: Metadata<'static> = Metadata::new( - "log event", - "log", - $level, - None, - None, - None, - field::FieldSet::new(FIELD_NAMES, identify_callsite!(&$cs)), - Kind::EVENT, - ); - static $fields: Lazy = Lazy::new(|| Fields::new(&$cs)); - - impl Callsite for $ty { - fn set_interest(&self, _: subscriber::Interest) {} - fn metadata(&self) -> &'static Metadata<'static> { - &$meta - } - } - }; -} - -log_cs!( - Level::TRACE, - TRACE_CS, - TRACE_META, - TRACE_FIELDS, - TraceCallsite -); -log_cs!( - Level::DEBUG, - DEBUG_CS, - DEBUG_META, - DEBUG_FIELDS, - DebugCallsite -); -log_cs!(Level::INFO, INFO_CS, INFO_META, INFO_FIELDS, InfoCallsite); -log_cs!(Level::WARN, WARN_CS, WARN_META, WARN_FIELDS, WarnCallsite); -log_cs!( - Level::ERROR, - ERROR_CS, - ERROR_META, - ERROR_FIELDS, - ErrorCallsite -); - -fn loglevel_to_cs(level: log::Level) -> (&'static Fields, &'static Metadata<'static>) { - match level { - log::Level::Trace => (&*TRACE_FIELDS, &TRACE_META), - log::Level::Debug => (&*DEBUG_FIELDS, &DEBUG_META), - log::Level::Info => (&*INFO_FIELDS, &INFO_META), - log::Level::Warn => (&*WARN_FIELDS, &WARN_META), - log::Level::Error => (&*ERROR_FIELDS, &ERROR_META), - } -} - -struct Logger; - -impl Logger { - fn init(log_level: LevelFilter) { - log::set_max_level(log_level); - log::set_logger(&Self).unwrap(); - } -} - -impl Log for Logger { - fn enabled(&self, metadata: &log::Metadata<'_>) -> bool { - if metadata.level() > log::max_level() { - // First, check the log record against the current max level enabled. - false - } else { - // Check if the current `tracing` dispatcher cares about this. - dispatcher::get_default(|dispatch| dispatch.enabled(&metadata.as_trace())) - } - } - - fn log(&self, record: &log::Record<'_>) { - // Dispatch manually instead of using methods provided by tracing-log - // to avoid having fields "log.target = ..." in the log message, - // which makes the log really hard to read. - if self.enabled(record.metadata()) { - dispatcher::get_default(|dispatch| { - let (keys, meta) = loglevel_to_cs(record.level()); - - dispatch.event(&Event::new( - meta, - &meta - .fields() - .value_set(&[(&keys.message, Some(record.args() as &dyn field::Value))]), - )); - }); - } - } - - fn flush(&self) {} -} - -struct ErrorFreeWriter; - -fn report_err(err: io::Error) { - writeln!(io::stderr(), "Failed to write to stdout: {err}").ok(); -} - -impl io::Write for &ErrorFreeWriter { - fn write(&mut self, buf: &[u8]) -> io::Result { - io::stdout().write(buf).or_else(|err| { - report_err(err); - // Behave as if writing to /dev/null so that logging system - // would keep working. - Ok(buf.len()) - }) - } - - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - io::stdout().write_all(buf).or_else(|err| { - report_err(err); - // Behave as if writing to /dev/null so that logging system - // would keep working. - Ok(()) - }) - } - - fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result { - io::stdout().write_vectored(bufs).or_else(|err| { - report_err(err); - // Behave as if writing to /dev/null so that logging system - // would keep working. - Ok(bufs.iter().map(|io_slice| io_slice.len()).sum()) - }) - } - - fn flush(&mut self) -> io::Result<()> { - io::stdout().flush().or_else(|err| { - report_err(err); - // Behave as if writing to /dev/null so that logging system - // would keep working. - Ok(()) - }) - } -} - -impl<'a> MakeWriter<'a> for ErrorFreeWriter { - type Writer = &'a Self; - - fn make_writer(&'a self) -> Self::Writer { - self - } -} - -pub fn logging(log_level: LevelFilter, json_output: bool) { - // Calculate log_level - let log_level = min(log_level, STATIC_MAX_LEVEL); - - let allowed_targets = (log_level != LevelFilter::Trace).then_some([ - "atomic_file_install", - "binstalk", - "binstalk_bins", - "binstalk_downloader", - "binstalk_fetchers", - "binstalk_registry", - "cargo_binstall", - "cargo_toml_workspace", - "detect_targets", - "simple_git", - ]); - - // Forward log to tracing - Logger::init(log_level); - - // Build fmt subscriber - let log_level = log_level.as_trace(); - let subscriber_builder = fmt().with_max_level(log_level).with_writer(ErrorFreeWriter); - - let subscriber: Box = if json_output { - Box::new(subscriber_builder.json().finish()) - } else { - // Disable time, target, file, line_num, thread name/ids to make the - // output more readable - let subscriber_builder = subscriber_builder - .without_time() - .with_target(false) - .with_file(false) - .with_line_number(false) - .with_thread_names(false) - .with_thread_ids(false); - - // subscriber_builder defaults to write to io::stdout(), - // so tests whether it supports color. - let stdout_supports_color = supports_color_on_stream(Stdout) - .map(|color_level| color_level.has_basic) - .unwrap_or_default(); - - Box::new(subscriber_builder.with_ansi(stdout_supports_color).finish()) - }; - - // Builder layer for filtering - let filter_layer = allowed_targets.map(|allowed_targets| { - Targets::new().with_targets(allowed_targets.into_iter().zip(repeat(log_level))) - }); - - // Builder final subscriber with filtering - let subscriber = subscriber.with(filter_layer); - - // Setup global subscriber - set_global_default(subscriber).unwrap(); -} diff --git a/crates/bin/src/main.rs b/crates/bin/src/main.rs deleted file mode 100644 index 7733f5df..00000000 --- a/crates/bin/src/main.rs +++ /dev/null @@ -1,11 +0,0 @@ -use std::process::Termination; - -use cargo_binstall::do_main; - -#[cfg(feature = "mimalloc")] -#[global_allocator] -static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc; - -fn main() -> impl Termination { - do_main() -} diff --git a/crates/bin/src/main_impl.rs b/crates/bin/src/main_impl.rs deleted file mode 100644 index 51d29ccc..00000000 --- a/crates/bin/src/main_impl.rs +++ /dev/null @@ -1,66 +0,0 @@ -use std::{process::Termination, time::Instant}; - -use binstalk::{helpers::jobserver_client::LazyJobserverClient, TARGET}; -use log::LevelFilter; -use tracing::debug; - -use crate::{ - args, - bin_util::{run_tokio_main, MainExit}, - entry, - logging::logging, -}; - -pub fn do_main() -> impl Termination { - let (args, cli_overrides) = args::parse(); - - if args.version { - let cargo_binstall_version = env!("CARGO_PKG_VERSION"); - if args.verbose { - let build_date = env!("VERGEN_BUILD_DATE"); - - let features = env!("VERGEN_CARGO_FEATURES"); - - let git_sha = option_env!("VERGEN_GIT_SHA").unwrap_or("UNKNOWN"); - let git_commit_date = option_env!("VERGEN_GIT_COMMIT_DATE").unwrap_or("UNKNOWN"); - - let rustc_semver = env!("VERGEN_RUSTC_SEMVER"); - let rustc_commit_hash = env!("VERGEN_RUSTC_COMMIT_HASH"); - let rustc_llvm_version = env!("VERGEN_RUSTC_LLVM_VERSION"); - - println!( - r#"cargo-binstall: {cargo_binstall_version} -build-date: {build_date} -build-target: {TARGET} -build-features: {features} -build-commit-hash: {git_sha} -build-commit-date: {git_commit_date} -rustc-version: {rustc_semver} -rustc-commit-hash: {rustc_commit_hash} -rustc-llvm-version: {rustc_llvm_version}"# - ); - } else { - println!("{cargo_binstall_version}"); - } - MainExit::Success(None) - } else if args.self_install { - MainExit::new(entry::self_install(args), None) - } else { - logging( - args.log_level.unwrap_or(LevelFilter::Info), - args.json_output, - ); - - let start = Instant::now(); - - let jobserver_client = LazyJobserverClient::new(); - - let result = - run_tokio_main(|| entry::install_crates(args, cli_overrides, jobserver_client)); - - let done = start.elapsed(); - debug!("run time: {done:?}"); - - MainExit::new(result, Some(done)) - } -} diff --git a/crates/bin/src/signal.rs b/crates/bin/src/signal.rs deleted file mode 100644 index 907fdfe8..00000000 --- a/crates/bin/src/signal.rs +++ /dev/null @@ -1,84 +0,0 @@ -use std::io; - -use binstalk::{errors::BinstallError, helpers::tasks::AutoAbortJoinHandle}; -use tokio::signal; - -/// This function will poll the handle while listening for ctrl_c, -/// `SIGINT`, `SIGHUP`, `SIGTERM` and `SIGQUIT`. -/// -/// When signal is received, [`BinstallError::UserAbort`] will be returned. -/// -/// It would also ignore `SIGUSER1` and `SIGUSER2` on unix. -/// -/// This function uses [`tokio::signal`] and once exit, does not reset the default -/// signal handler, so be careful when using it. -pub async fn cancel_on_user_sig_term( - handle: AutoAbortJoinHandle, -) -> Result { - ignore_signals()?; - - tokio::select! { - biased; - - res = wait_on_cancellation_signal() => { - res.map_err(BinstallError::Io) - .and(Err(BinstallError::UserAbort)) - } - res = handle => res, - } -} - -fn ignore_signals() -> io::Result<()> { - #[cfg(unix)] - unix::ignore_signals_on_unix()?; - - Ok(()) -} - -/// If call to it returns `Ok(())`, then all calls to this function after -/// that also returns `Ok(())`. -async fn wait_on_cancellation_signal() -> Result<(), io::Error> { - #[cfg(unix)] - unix::wait_on_cancellation_signal_unix().await?; - - #[cfg(not(unix))] - signal::ctrl_c().await?; - - Ok(()) -} - -#[cfg(unix)] -mod unix { - use super::*; - use signal::unix::{signal, SignalKind}; - - /// Same as [`wait_on_cancellation_signal`] but is only available on unix. - pub async fn wait_on_cancellation_signal_unix() -> Result<(), io::Error> { - tokio::select! { - biased; - - res = wait_for_signal_unix(SignalKind::interrupt()) => res, - res = wait_for_signal_unix(SignalKind::hangup()) => res, - res = wait_for_signal_unix(SignalKind::terminate()) => res, - res = wait_for_signal_unix(SignalKind::quit()) => res, - } - } - - /// Wait for first arrival of signal. - pub async fn wait_for_signal_unix(kind: signal::unix::SignalKind) -> Result<(), io::Error> { - let mut sig_listener = signal::unix::signal(kind)?; - if sig_listener.recv().await.is_some() { - Ok(()) - } else { - // Use pending() here for the same reason as above. - std::future::pending().await - } - } - - pub fn ignore_signals_on_unix() -> Result<(), io::Error> { - drop(signal(SignalKind::user_defined1())?); - drop(signal(SignalKind::user_defined2())?); - - Ok(()) - } -} diff --git a/crates/bin/src/ui.rs b/crates/bin/src/ui.rs deleted file mode 100644 index bcbb67e1..00000000 --- a/crates/bin/src/ui.rs +++ /dev/null @@ -1,56 +0,0 @@ -use std::{ - io::{self, BufRead, StdinLock, Write}, - thread, -}; - -use binstalk::errors::BinstallError; -use tokio::sync::oneshot; - -fn ask_for_confirm(stdin: &mut StdinLock, input: &mut String) -> io::Result<()> { - { - let mut stdout = io::stdout().lock(); - - write!(&mut stdout, "Do you wish to continue? [yes]/no\n? ")?; - stdout.flush()?; - } - - stdin.read_line(input)?; - - Ok(()) -} - -pub async fn confirm() -> Result<(), BinstallError> { - let (tx, rx) = oneshot::channel(); - - thread::spawn(move || { - // This task should be the only one able to - // access stdin - let mut stdin = io::stdin().lock(); - let mut input = String::with_capacity(16); - - let res = loop { - if ask_for_confirm(&mut stdin, &mut input).is_err() { - break false; - } - - match input.as_str().trim() { - "yes" | "y" | "YES" | "Y" | "" => break true, - "no" | "n" | "NO" | "N" => break false, - _ => { - input.clear(); - continue; - } - } - }; - - // The main thread might be terminated by signal and thus cancelled - // the confirmation. - tx.send(res).ok(); - }); - - if rx.await.unwrap() { - Ok(()) - } else { - Err(BinstallError::UserAbort) - } -} diff --git a/crates/bin/windows.manifest b/crates/bin/windows.manifest deleted file mode 100644 index 215f50b2..00000000 --- a/crates/bin/windows.manifest +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - true - UTF-8 - SegmentHeap - - - diff --git a/crates/binstalk-bins/CHANGELOG.md b/crates/binstalk-bins/CHANGELOG.md deleted file mode 100644 index de489e3e..00000000 --- a/crates/binstalk-bins/CHANGELOG.md +++ /dev/null @@ -1,90 +0,0 @@ -# Changelog -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - -## [Unreleased] - -## [0.6.13](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-bins-v0.6.12...binstalk-bins-v0.6.13) - 2025-03-19 - -### Other - -- updated the following local packages: atomic-file-install - -## [0.6.12](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-bins-v0.6.11...binstalk-bins-v0.6.12) - 2025-03-07 - -### Other - -- *(deps)* bump the deps group with 3 updates ([#2072](https://github.com/cargo-bins/cargo-binstall/pull/2072)) - -## [0.6.11](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-bins-v0.6.10...binstalk-bins-v0.6.11) - 2025-02-22 - -### Other - -- updated the following local packages: atomic-file-install - -## [0.6.10](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-bins-v0.6.9...binstalk-bins-v0.6.10) - 2025-02-11 - -### Other - -- updated the following local packages: binstalk-types - -## [0.6.9](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-bins-v0.6.8...binstalk-bins-v0.6.9) - 2025-01-19 - -### Other - -- update Cargo.lock dependencies - -## [0.6.8](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-bins-v0.6.7...binstalk-bins-v0.6.8) - 2025-01-13 - -### Other - -- update Cargo.lock dependencies - -## [0.6.7](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-bins-v0.6.6...binstalk-bins-v0.6.7) - 2025-01-11 - -### Other - -- *(deps)* bump the deps group with 3 updates (#2015) - -## [0.6.6](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-bins-v0.6.5...binstalk-bins-v0.6.6) - 2024-12-14 - -### Other - -- *(deps)* bump the deps group with 2 updates (#1997) - -## [0.6.5](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-bins-v0.6.4...binstalk-bins-v0.6.5) - 2024-11-23 - -### Other - -- updated the following local packages: binstalk-types - -## [0.6.4](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-bins-v0.6.3...binstalk-bins-v0.6.4) - 2024-11-18 - -### Other - -- updated the following local packages: atomic-file-install - -## [0.6.3](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-bins-v0.6.2...binstalk-bins-v0.6.3) - 2024-11-09 - -### Other - -- *(deps)* bump the deps group with 3 updates ([#1966](https://github.com/cargo-bins/cargo-binstall/pull/1966)) - -## [0.6.2](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-bins-v0.6.1...binstalk-bins-v0.6.2) - 2024-11-05 - -### Other - -- *(deps)* bump the deps group with 3 updates ([#1954](https://github.com/cargo-bins/cargo-binstall/pull/1954)) - -## [0.6.1](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-bins-v0.6.0...binstalk-bins-v0.6.1) - 2024-11-02 - -### Other - -- Improve UI orompt for installation ([#1950](https://github.com/cargo-bins/cargo-binstall/pull/1950)) - -## [0.6.0](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-bins-v0.5.0...binstalk-bins-v0.6.0) - 2024-08-10 - -### Other -- updated the following local packages: binstalk-types diff --git a/crates/binstalk-bins/Cargo.toml b/crates/binstalk-bins/Cargo.toml deleted file mode 100644 index d3f8c6c4..00000000 --- a/crates/binstalk-bins/Cargo.toml +++ /dev/null @@ -1,21 +0,0 @@ -[package] -name = "binstalk-bins" -version = "0.6.13" -edition = "2021" - -description = "The binstall binaries discovery and installation crate." -repository = "https://github.com/cargo-bins/cargo-binstall" -documentation = "https://docs.rs/binstalk-bins" -rust-version = "1.65.0" -authors = ["Jiahao XU "] -license = "GPL-3.0-only" - -[dependencies] -atomic-file-install = { version = "1.0.11", path = "../atomic-file-install" } -binstalk-types = { version = "0.9.4", path = "../binstalk-types" } -compact_str = { version = "0.9.0", features = ["serde"] } -leon = "3.0.0" -miette = "7.0.0" -normalize-path = { version = "0.2.1", path = "../normalize-path" } -thiserror = "2.0.11" -tracing = "0.1.39" diff --git a/crates/binstalk-bins/LICENSE b/crates/binstalk-bins/LICENSE deleted file mode 100644 index f288702d..00000000 --- a/crates/binstalk-bins/LICENSE +++ /dev/null @@ -1,674 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. diff --git a/crates/binstalk-bins/src/lib.rs b/crates/binstalk-bins/src/lib.rs deleted file mode 100644 index 15f92a25..00000000 --- a/crates/binstalk-bins/src/lib.rs +++ /dev/null @@ -1,369 +0,0 @@ -use std::{ - borrow::Cow, - fmt, io, - path::{self, Component, Path, PathBuf}, -}; - -use atomic_file_install::{ - atomic_install, atomic_install_noclobber, atomic_symlink_file, atomic_symlink_file_noclobber, -}; -use binstalk_types::cargo_toml_binstall::{PkgFmt, PkgMeta}; -use compact_str::{format_compact, CompactString}; -use leon::Template; -use miette::Diagnostic; -use normalize_path::NormalizePath; -use thiserror::Error as ThisError; -use tracing::debug; - -#[derive(Debug, ThisError, Diagnostic)] -pub enum Error { - /// bin-dir configuration provided generates source path outside - /// of the temporary dir. - #[error( - "bin-dir configuration provided generates source path outside of the temporary dir: {}", .0.display() - )] - InvalidSourceFilePath(Box), - - /// bin-dir configuration provided generates empty source path. - #[error("bin-dir configuration provided generates empty source path")] - EmptySourceFilePath, - - /// Bin file is not found. - #[error("bin file {} not found", .0.display())] - BinFileNotFound(Box), - - #[error(transparent)] - Io(#[from] io::Error), - - #[error("Failed to render template: {0}")] - #[diagnostic(transparent)] - TemplateRender(#[from] leon::RenderError), -} - -/// Return true if the path does not look outside of current dir -/// -/// * `path` - must be normalized before passing to this function -fn is_valid_path(path: &Path) -> bool { - !matches!( - path.components().next(), - // normalized path cannot have curdir or parentdir, - // so checking prefix/rootdir is enough. - Some(Component::Prefix(..) | Component::RootDir) - ) -} - -/// Must be called after the archive is downloaded and extracted. -/// This function might uses blocking I/O. -pub fn infer_bin_dir_template( - data: &Data, - has_dir: &mut dyn FnMut(&Path) -> bool, -) -> Cow<'static, str> { - let name = data.name; - let target = data.target; - let version = data.version; - - // Make sure to update - // fetchers::gh_crate_meta::hosting::{FULL_FILENAMES, - // NOVERSION_FILENAMES} if you update this array. - let gen_possible_dirs: [for<'r> fn(&'r str, &'r str, &'r str) -> String; 8] = [ - |name, target, version| format!("{name}-{target}-v{version}"), - |name, target, version| format!("{name}-{target}-{version}"), - |name, target, version| format!("{name}-{version}-{target}"), - |name, target, version| format!("{name}-v{version}-{target}"), - |name, target, _version| format!("{name}-{target}"), - // Ignore the following when updating hosting::{FULL_FILENAMES, NOVERSION_FILENAMES} - |name, _target, version| format!("{name}-{version}"), - |name, _target, version| format!("{name}-v{version}"), - |name, _target, _version| name.to_string(), - ]; - - let default_bin_dir_template = Cow::Borrowed("{ bin }{ binary-ext }"); - - gen_possible_dirs - .into_iter() - .map(|gen_possible_dir| gen_possible_dir(name, target, version)) - .find(|dirname| has_dir(Path::new(&dirname))) - .map(|mut dir| { - dir.reserve_exact(1 + default_bin_dir_template.len()); - dir += "/"; - dir += &default_bin_dir_template; - Cow::Owned(dir) - }) - // Fallback to no dir - .unwrap_or(default_bin_dir_template) -} - -pub struct BinFile { - pub base_name: CompactString, - pub source: PathBuf, - pub archive_source_path: PathBuf, - pub dest: PathBuf, - pub link: Option, -} - -impl BinFile { - /// * `tt` - must have a template with name "bin_dir" - pub fn new( - data: &Data<'_>, - base_name: &str, - tt: &Template<'_>, - no_symlinks: bool, - ) -> Result { - let binary_ext = if data.target.contains("windows") { - ".exe" - } else { - "" - }; - - let ctx = Context { - name: data.name, - repo: data.repo, - target: data.target, - version: data.version, - bin: base_name, - binary_ext, - - target_related_info: data.target_related_info, - }; - - let (source, archive_source_path) = if data.meta.pkg_fmt == Some(PkgFmt::Bin) { - ( - data.bin_path.to_path_buf(), - data.bin_path.file_name().unwrap().into(), - ) - } else { - // Generate install paths - // Source path is the download dir + the generated binary path - let path = tt.render(&ctx)?; - - let path_normalized = Path::new(&path).normalize(); - - if path_normalized.components().next().is_none() { - return Err(Error::EmptySourceFilePath); - } - - if !is_valid_path(&path_normalized) { - return Err(Error::InvalidSourceFilePath(path_normalized.into())); - } - - (data.bin_path.join(&path_normalized), path_normalized) - }; - - // Destination at install dir + base-name{.extension} - let mut dest = data.install_path.join(ctx.bin); - if !binary_ext.is_empty() { - let binary_ext = binary_ext.strip_prefix('.').unwrap(); - - // PathBuf::set_extension returns false if Path::file_name - // is None, but we know that the file name must be Some, - // thus we assert! the return value here. - assert!(dest.set_extension(binary_ext)); - } - - let (dest, link) = if no_symlinks { - (dest, None) - } else { - // Destination path is the install dir + base-name-version{.extension} - let dest_file_path_with_ver = format!("{}-v{}{}", ctx.bin, ctx.version, ctx.binary_ext); - let dest_with_ver = data.install_path.join(dest_file_path_with_ver); - - (dest_with_ver, Some(dest)) - }; - - Ok(Self { - base_name: format_compact!("{base_name}{binary_ext}"), - source, - archive_source_path, - dest, - link, - }) - } - - pub fn preview_bin(&self) -> impl fmt::Display + '_ { - struct PreviewBin<'a> { - base_name: &'a str, - dest: path::Display<'a>, - } - - impl fmt::Display for PreviewBin<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{} => {}", self.base_name, self.dest) - } - } - - PreviewBin { - base_name: &self.base_name, - dest: self.dest.display(), - } - } - - pub fn preview_link(&self) -> impl fmt::Display + '_ { - OptionalLazyFormat(self.link.as_ref().map(|link| LazyFormat { - base_name: &self.base_name, - source: link.display(), - dest: self.link_dest().display(), - })) - } - - /// Return `Ok` if the source exists, otherwise `Err`. - pub fn check_source_exists( - &self, - has_file: &mut dyn FnMut(&Path) -> bool, - ) -> Result<(), Error> { - if has_file(&self.archive_source_path) { - Ok(()) - } else { - Err(Error::BinFileNotFound((&*self.source).into())) - } - } - - fn pre_install_bin(&self) -> Result<(), Error> { - if !self.source.try_exists()? { - return Err(Error::BinFileNotFound((&*self.source).into())); - } - - #[cfg(unix)] - std::fs::set_permissions( - &self.source, - std::os::unix::fs::PermissionsExt::from_mode(0o755), - )?; - - Ok(()) - } - - pub fn install_bin(&self) -> Result<(), Error> { - self.pre_install_bin()?; - - debug!( - "Atomically install file from '{}' to '{}'", - self.source.display(), - self.dest.display() - ); - - atomic_install(&self.source, &self.dest)?; - - Ok(()) - } - - pub fn install_bin_noclobber(&self) -> Result<(), Error> { - self.pre_install_bin()?; - - debug!( - "Installing file from '{}' to '{}' only if dst not exists", - self.source.display(), - self.dest.display() - ); - - atomic_install_noclobber(&self.source, &self.dest)?; - - Ok(()) - } - - pub fn install_link(&self) -> Result<(), Error> { - if let Some(link) = &self.link { - let dest = self.link_dest(); - debug!( - "Create link '{}' pointing to '{}'", - link.display(), - dest.display() - ); - atomic_symlink_file(dest, link)?; - } - - Ok(()) - } - - pub fn install_link_noclobber(&self) -> Result<(), Error> { - if let Some(link) = &self.link { - let dest = self.link_dest(); - debug!( - "Create link '{}' pointing to '{}' only if dst not exists", - link.display(), - dest.display() - ); - atomic_symlink_file_noclobber(dest, link)?; - } - - Ok(()) - } - - fn link_dest(&self) -> &Path { - if cfg!(target_family = "unix") { - Path::new(self.dest.file_name().unwrap()) - } else { - &self.dest - } - } -} - -/// Data required to get bin paths -pub struct Data<'a> { - pub name: &'a str, - pub target: &'a str, - pub version: &'a str, - pub repo: Option<&'a str>, - pub meta: PkgMeta, - pub bin_path: &'a Path, - pub install_path: &'a Path, - /// More target related info, it's recommend to provide the following keys: - /// - target_family, - /// - target_arch - /// - target_libc - /// - target_vendor - pub target_related_info: &'a dyn leon::Values, -} - -#[derive(Clone)] -struct Context<'c> { - name: &'c str, - repo: Option<&'c str>, - target: &'c str, - version: &'c str, - bin: &'c str, - - /// Filename extension on the binary, i.e. .exe on Windows, nothing otherwise - binary_ext: &'c str, - - target_related_info: &'c dyn leon::Values, -} - -impl leon::Values for Context<'_> { - fn get_value<'s>(&'s self, key: &str) -> Option> { - match key { - "name" => Some(Cow::Borrowed(self.name)), - "repo" => self.repo.map(Cow::Borrowed), - "target" => Some(Cow::Borrowed(self.target)), - "version" => Some(Cow::Borrowed(self.version)), - "bin" => Some(Cow::Borrowed(self.bin)), - "binary-ext" => Some(Cow::Borrowed(self.binary_ext)), - // Soft-deprecated alias for binary-ext - "format" => Some(Cow::Borrowed(self.binary_ext)), - - key => self.target_related_info.get_value(key), - } - } -} - -struct LazyFormat<'a> { - base_name: &'a str, - source: path::Display<'a>, - dest: path::Display<'a>, -} - -impl fmt::Display for LazyFormat<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{} ({} -> {})", self.base_name, self.source, self.dest) - } -} - -struct OptionalLazyFormat<'a>(Option>); - -impl fmt::Display for OptionalLazyFormat<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if let Some(lazy_format) = self.0.as_ref() { - fmt::Display::fmt(lazy_format, f) - } else { - Ok(()) - } - } -} diff --git a/crates/binstalk-downloader/CHANGELOG.md b/crates/binstalk-downloader/CHANGELOG.md deleted file mode 100644 index 3dfaa698..00000000 --- a/crates/binstalk-downloader/CHANGELOG.md +++ /dev/null @@ -1,116 +0,0 @@ -# Changelog -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - -## [Unreleased] - -## [0.13.17](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-downloader-v0.13.16...binstalk-downloader-v0.13.17) - 2025-04-05 - -### Other - -- Fix clippy lints ([#2111](https://github.com/cargo-bins/cargo-binstall/pull/2111)) - -## [0.13.16](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-downloader-v0.13.15...binstalk-downloader-v0.13.16) - 2025-03-19 - -### Other - -- Fix clippy warnings for detect-targets and binstalk-downloader ([#2098](https://github.com/cargo-bins/cargo-binstall/pull/2098)) -- Bump hickory-resolver to 0.25.1 ([#2096](https://github.com/cargo-bins/cargo-binstall/pull/2096)) - -## [0.13.15](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-downloader-v0.13.14...binstalk-downloader-v0.13.15) - 2025-03-15 - -### Other - -- *(deps)* bump the deps group with 2 updates ([#2084](https://github.com/cargo-bins/cargo-binstall/pull/2084)) -- *(deps)* bump tokio from 1.43.0 to 1.44.0 in the deps group ([#2079](https://github.com/cargo-bins/cargo-binstall/pull/2079)) - -## [0.13.14](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-downloader-v0.13.13...binstalk-downloader-v0.13.14) - 2025-03-07 - -### Other - -- Use bzip2/libbz2-rs-sys ([#2071](https://github.com/cargo-bins/cargo-binstall/pull/2071)) -- *(deps)* bump the deps group with 3 updates ([#2072](https://github.com/cargo-bins/cargo-binstall/pull/2072)) - -## [0.13.13](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-downloader-v0.13.12...binstalk-downloader-v0.13.13) - 2025-02-28 - -### Other - -- Use flate2/zlib-rs for dev/release build ([#2068](https://github.com/cargo-bins/cargo-binstall/pull/2068)) - -## [0.13.12](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-downloader-v0.13.11...binstalk-downloader-v0.13.12) - 2025-02-11 - -### Other - -- Upgrade hickory-resolver to 0.25.0-alpha.5 ([#2038](https://github.com/cargo-bins/cargo-binstall/pull/2038)) - -## [0.13.11](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-downloader-v0.13.10...binstalk-downloader-v0.13.11) - 2025-02-04 - -### Added - -- *(downloader)* allow remote::Client to be customised (#2035) - -## [0.13.10](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-downloader-v0.13.9...binstalk-downloader-v0.13.10) - 2025-01-19 - -### Other - -- update Cargo.lock dependencies - -## [0.13.9](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-downloader-v0.13.8...binstalk-downloader-v0.13.9) - 2025-01-13 - -### Other - -- update Cargo.lock dependencies - -## [0.13.8](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-downloader-v0.13.7...binstalk-downloader-v0.13.8) - 2025-01-11 - -### Other - -- *(deps)* bump the deps group with 3 updates (#2015) - -## [0.13.7](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-downloader-v0.13.6...binstalk-downloader-v0.13.7) - 2025-01-04 - -### Other - -- *(deps)* bump the deps group with 2 updates (#2010) - -## [0.13.6](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-downloader-v0.13.5...binstalk-downloader-v0.13.6) - 2024-12-14 - -### Other - -- *(deps)* bump the deps group with 2 updates (#1997) - -## [0.13.5](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-downloader-v0.13.4...binstalk-downloader-v0.13.5) - 2024-11-23 - -### Other - -- *(deps)* bump the deps group with 2 updates ([#1981](https://github.com/cargo-bins/cargo-binstall/pull/1981)) - -## [0.13.4](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-downloader-v0.13.3...binstalk-downloader-v0.13.4) - 2024-11-09 - -### Other - -- *(deps)* bump the deps group with 3 updates ([#1966](https://github.com/cargo-bins/cargo-binstall/pull/1966)) - -## [0.13.3](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-downloader-v0.13.2...binstalk-downloader-v0.13.3) - 2024-11-05 - -### Other - -- *(deps)* bump the deps group with 3 updates ([#1954](https://github.com/cargo-bins/cargo-binstall/pull/1954)) - -## [0.13.2](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-downloader-v0.13.1...binstalk-downloader-v0.13.2) - 2024-11-02 - -### Other - -- Use rc-zip-sync for zip extraction ([#1942](https://github.com/cargo-bins/cargo-binstall/pull/1942)) - -## [0.13.1](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-downloader-v0.13.0...binstalk-downloader-v0.13.1) - 2024-08-12 - -### Other -- Enable happy eyeballs when using hickory-dns ([#1877](https://github.com/cargo-bins/cargo-binstall/pull/1877)) - -## [0.13.0](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-downloader-v0.12.0...binstalk-downloader-v0.13.0) - 2024-08-10 - -### Other -- Bump hickory-resolver to 0.25.0-alpha.2 ([#1869](https://github.com/cargo-bins/cargo-binstall/pull/1869)) diff --git a/crates/binstalk-downloader/Cargo.toml b/crates/binstalk-downloader/Cargo.toml deleted file mode 100644 index c89c539e..00000000 --- a/crates/binstalk-downloader/Cargo.toml +++ /dev/null @@ -1,138 +0,0 @@ -[package] -name = "binstalk-downloader" -description = "The binstall toolkit for downloading and extracting file" -repository = "https://github.com/cargo-bins/cargo-binstall" -documentation = "https://docs.rs/binstalk-downloader" -version = "0.13.17" -rust-version = "1.79.0" -authors = ["ryan "] -edition = "2021" -license = "Apache-2.0 OR MIT" - -[dependencies] -async-trait = "0.1.88" -async-compression = { version = "0.4.4", features = [ - "gzip", - "zstd", - "xz", - "bzip2", - "tokio", -] } -binstalk-types = { version = "0.9.4", path = "../binstalk-types" } -bytes = "1.4.0" -bzip2 = { version = "0.5.2", default-features = false, features = [ - "libbz2-rs-sys", -] } -cfg-if = "1" -compact_str = "0.9.0" -flate2 = { version = "1.0.28", default-features = false } -futures-util = "0.3.30" -futures-io = "0.3.30" -httpdate = "1.0.2" -rc-zip-sync = { version = "4.2.6", features = [ - "deflate", - "bzip2", - "deflate64", - "lzma", - "zstd", -] } -reqwest = { version = "0.12.5", features = [ - "http2", - "stream", - "zstd", - "gzip", - "brotli", - "deflate", -], default-features = false } -serde = { version = "1.0.163", features = ["derive"], optional = true } -serde_json = { version = "1.0.107", optional = true } -# Use a fork here since we need PAX support, but the upstream -# does not hav the PR merged yet. -# -#tar = "0.4.38" -tar = { package = "binstall-tar", version = "0.4.39" } -tempfile = "3.5.0" -thiserror = "2.0.11" -tokio = { version = "1.44.0", features = [ - "macros", - "rt-multi-thread", - "sync", - "time", - "fs", -], default-features = false } -tokio-tar = "0.3.0" -tokio-util = { version = "0.7.8", features = ["io"] } -tracing = "0.1.39" -hickory-resolver = { version = "0.25.1", optional = true, features = [ - "dnssec-ring", -] } -once_cell = { version = "1.18.0", optional = true } -url = "2.5.4" - -xz2 = "0.1.7" - -# zstd is also depended by zip. -# Since zip 0.6.3 depends on zstd 0.11, we can use 0.12.0 here -# because it uses the same zstd-sys version. -# Otherwise there will be a link conflict. -zstd = { version = "0.13.2", default-features = false } - -[target."cfg(not(target_arch = \"wasm32\"))".dependencies.native-tls-crate] -optional = true -package = "native-tls" -# The version must be kept in sync of reqwest -version = "0.2.10" - -[features] -default = ["static", "rustls"] - -static = ["bzip2/static", "xz2/static", "native-tls-crate?/vendored"] -pkg-config = ["zstd/pkg-config"] - -zlib-ng = ["flate2/zlib-ng"] -zlib-rs = ["flate2/zlib-rs"] - -# Dummy feature, enabled if rustls or native-tls is enabled. -# Used to avoid compilation error when no feature is enabled. -__tls = [] - -rustls = [ - "__tls", - - "reqwest/rustls-tls", - "reqwest/rustls-tls-webpki-roots", - "reqwest/rustls-tls-native-roots", - - # Enable the following features only if hickory-resolver is enabled. - "hickory-resolver?/tls-ring", - # hickory-resolver currently supports https with rustls - "hickory-resolver?/https-ring", - "hickory-resolver?/quic-ring", - "hickory-resolver?/h3-ring", -] -native-tls = ["__tls", "native-tls-crate", "reqwest/native-tls"] - -# Enable hickory-resolver so that features on it will also be enabled. -hickory-dns = ["hickory-resolver", "default-net", "ipconfig", "once_cell"] - -# Deprecated alias for hickory-dns, since trust-dns is renamed to hickory-dns -trust-dns = ["hickory-dns"] - -# HTTP3 is temporarily disabled by reqwest. -# -# Experimental HTTP/3 client, this would require `--cfg reqwest_unstable` -# to be passed to `rustc`. -http3 = ["reqwest/http3"] - -zstd-thin = ["zstd/thin"] - -cross-lang-fat-lto = ["zstd/fat-lto"] - -json = ["serde", "serde_json"] - -[target."cfg(windows)".dependencies] -default-net = { version = "0.22.0", optional = true } -ipconfig = { version = "0.3.2", optional = true, default-features = false } - -[package.metadata.docs.rs] -rustdoc-args = ["--cfg", "docsrs"] diff --git a/crates/binstalk-downloader/LICENSE-APACHE b/crates/binstalk-downloader/LICENSE-APACHE deleted file mode 100644 index 1b5ec8b7..00000000 --- a/crates/binstalk-downloader/LICENSE-APACHE +++ /dev/null @@ -1,176 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS diff --git a/crates/binstalk-downloader/LICENSE-MIT b/crates/binstalk-downloader/LICENSE-MIT deleted file mode 100644 index 31aa7938..00000000 --- a/crates/binstalk-downloader/LICENSE-MIT +++ /dev/null @@ -1,23 +0,0 @@ -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/crates/binstalk-downloader/src/download.rs b/crates/binstalk-downloader/src/download.rs deleted file mode 100644 index 78943730..00000000 --- a/crates/binstalk-downloader/src/download.rs +++ /dev/null @@ -1,408 +0,0 @@ -use std::{fmt, io, path::Path}; - -use binstalk_types::cargo_toml_binstall::PkgFmtDecomposed; -use bytes::Bytes; -use futures_util::{stream::FusedStream, Stream, StreamExt}; -use thiserror::Error as ThisError; -use tracing::{debug, error, instrument}; - -pub use binstalk_types::cargo_toml_binstall::{PkgFmt, TarBasedFmt}; -pub use rc_zip_sync::rc_zip::error::Error as ZipError; - -use crate::remote::{Client, Error as RemoteError, Response, Url}; - -mod async_extracter; -use async_extracter::*; - -mod async_tar_visitor; -use async_tar_visitor::extract_tar_based_stream_and_visit; -pub use async_tar_visitor::{TarEntriesVisitor, TarEntry, TarEntryType}; - -mod extracter; - -mod extracted_files; -pub use extracted_files::{ExtractedFiles, ExtractedFilesEntry}; - -mod zip_extraction; - -#[derive(Debug, ThisError)] -#[non_exhaustive] -pub enum DownloadError { - #[error("Failed to extract zipfile: {0}")] - Unzip(#[from] ZipError), - - #[error("Failed to download from remote: {0}")] - Remote(#[from] RemoteError), - - /// A generic I/O error. - /// - /// - Code: `binstall::io` - /// - Exit: 74 - #[error("I/O Error: {0}")] - Io(io::Error), -} - -impl From for DownloadError { - fn from(err: io::Error) -> Self { - err.downcast::() - .unwrap_or_else(DownloadError::Io) - } -} - -impl From for io::Error { - fn from(e: DownloadError) -> io::Error { - match e { - DownloadError::Io(io_error) => io_error, - e => io::Error::new(io::ErrorKind::Other, e), - } - } -} - -pub trait DataVerifier: Send + Sync { - /// Digest input data. - /// - /// This method can be called repeatedly for use with streaming messages, - /// it will be called in the order of the message received. - fn update(&mut self, data: &Bytes); - - /// Finalise the data verification. - /// - /// Return false if the data is invalid. - fn validate(&mut self) -> bool; -} - -impl DataVerifier for () { - fn update(&mut self, _: &Bytes) {} - fn validate(&mut self) -> bool { - true - } -} - -#[derive(Debug)] -enum DownloadContent { - ToIssue { client: Client, url: Url }, - Response(Response), -} - -impl DownloadContent { - async fn into_response(self) -> Result { - Ok(match self { - DownloadContent::ToIssue { client, url } => client.get(url).send(true).await?, - DownloadContent::Response(response) => response, - }) - } -} - -pub struct Download<'a> { - content: DownloadContent, - data_verifier: Option<&'a mut dyn DataVerifier>, -} - -impl fmt::Debug for Download<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&self.content, f) - } -} - -impl Download<'static> { - pub fn new(client: Client, url: Url) -> Self { - Self { - content: DownloadContent::ToIssue { client, url }, - data_verifier: None, - } - } - - pub fn from_response(response: Response) -> Self { - Self { - content: DownloadContent::Response(response), - data_verifier: None, - } - } -} - -impl<'a> Download<'a> { - pub fn new_with_data_verifier( - client: Client, - url: Url, - data_verifier: &'a mut dyn DataVerifier, - ) -> Self { - Self { - content: DownloadContent::ToIssue { client, url }, - data_verifier: Some(data_verifier), - } - } - - pub fn from_response_with_data_verifier( - response: Response, - data_verifier: &'a mut dyn DataVerifier, - ) -> Self { - Self { - content: DownloadContent::Response(response), - data_verifier: Some(data_verifier), - } - } - - pub fn with_data_verifier(self, data_verifier: &mut dyn DataVerifier) -> Download<'_> { - Download { - content: self.content, - data_verifier: Some(data_verifier), - } - } - - async fn get_stream( - self, - ) -> Result< - impl FusedStream> + Send + Sync + Unpin + 'a, - DownloadError, - > { - let mut data_verifier = self.data_verifier; - Ok(self - .content - .into_response() - .await? - .bytes_stream() - .map(move |res| { - let bytes = res?; - - if let Some(data_verifier) = &mut data_verifier { - data_verifier.update(&bytes); - } - - Ok(bytes) - }) - // Call `fuse` at the end to make sure `data_verifier` is only - // called when the stream still has elements left. - .fuse()) - } -} - -/// Make sure `stream` is an alias instead of taking the value to avoid -/// exploding size of the future generated. -/// -/// Accept `FusedStream` only since the `stream` could be already consumed. -async fn consume_stream(stream: &mut S) -where - S: Stream> + FusedStream + Unpin, -{ - while let Some(res) = stream.next().await { - if let Err(err) = res { - error!(?err, "failed to consume stream"); - break; - } - } -} - -impl Download<'_> { - /// Download a file from the provided URL and process it in memory. - /// - /// This does not support verifying a checksum due to the partial extraction - /// and will ignore one if specified. - /// - /// NOTE that this API does not support gnu extension sparse file unlike - /// [`Download::and_extract`]. - #[instrument(skip(self, visitor))] - pub async fn and_visit_tar( - self, - fmt: TarBasedFmt, - visitor: &mut dyn TarEntriesVisitor, - ) -> Result<(), DownloadError> { - let has_data_verifier = self.data_verifier.is_some(); - let mut stream = self.get_stream().await?; - - debug!("Downloading and extracting then in-memory processing"); - - let res = extract_tar_based_stream_and_visit(&mut stream, fmt, visitor).await; - - if has_data_verifier { - consume_stream(&mut stream).await; - } - - if res.is_ok() { - debug!("Download, extraction and in-memory procession OK"); - } - - res - } - - /// Download a file from the provided URL and extract it to the provided path. - /// - /// NOTE that this will only extract directory and regular files. - #[instrument( - skip(self, path), - fields(path = format_args!("{}", path.as_ref().display())) - )] - pub async fn and_extract( - self, - fmt: PkgFmt, - path: impl AsRef, - ) -> Result { - async fn inner( - this: Download<'_>, - fmt: PkgFmt, - path: &Path, - ) -> Result { - let has_data_verifier = this.data_verifier.is_some(); - let mut stream = this.get_stream().await?; - - debug!("Downloading and extracting to: '{}'", path.display()); - - let res = match fmt.decompose() { - PkgFmtDecomposed::Tar(fmt) => { - extract_tar_based_stream(&mut stream, path, fmt).await - } - PkgFmtDecomposed::Bin => extract_bin(&mut stream, path).await, - PkgFmtDecomposed::Zip => extract_zip(&mut stream, path).await, - }; - - if has_data_verifier { - consume_stream(&mut stream).await; - } - - if res.is_ok() { - debug!("Download OK, extracted to: '{}'", path.display()); - } - - res - } - - inner(self, fmt, path.as_ref()).await - } - - #[instrument(skip(self))] - pub async fn into_bytes(self) -> Result { - let bytes = self.content.into_response().await?.bytes().await?; - if let Some(verifier) = self.data_verifier { - verifier.update(&bytes); - } - Ok(bytes) - } -} - -#[cfg(test)] -mod test { - use super::*; - - use std::{ - collections::{HashMap, HashSet}, - ffi::OsStr, - num::NonZeroU16, - }; - use tempfile::tempdir; - - #[tokio::test] - async fn test_and_extract() { - let client = crate::remote::Client::new( - concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION")), - None, - NonZeroU16::new(10).unwrap(), - 1.try_into().unwrap(), - [], - ) - .unwrap(); - - // cargo-binstall - let cargo_binstall_url = "https://github.com/cargo-bins/cargo-binstall/releases/download/v0.20.1/cargo-binstall-aarch64-unknown-linux-musl.tgz"; - - let extracted_files = - Download::new(client.clone(), Url::parse(cargo_binstall_url).unwrap()) - .and_extract(PkgFmt::Tgz, tempdir().unwrap()) - .await - .unwrap(); - - assert!(extracted_files.has_file(Path::new("cargo-binstall"))); - assert!(!extracted_files.has_file(Path::new("1234"))); - - let files = HashSet::from([OsStr::new("cargo-binstall").into()]); - assert_eq!(extracted_files.get_dir(Path::new(".")).unwrap(), &files); - - assert_eq!( - extracted_files.0, - HashMap::from([ - ( - Path::new("cargo-binstall").into(), - ExtractedFilesEntry::File - ), - ( - Path::new(".").into(), - ExtractedFilesEntry::Dir(Box::new(files)) - ) - ]) - ); - - // cargo-watch - let cargo_watch_url = "https://github.com/watchexec/cargo-watch/releases/download/v8.4.0/cargo-watch-v8.4.0-aarch64-unknown-linux-gnu.tar.xz"; - - let extracted_files = Download::new(client.clone(), Url::parse(cargo_watch_url).unwrap()) - .and_extract(PkgFmt::Txz, tempdir().unwrap()) - .await - .unwrap(); - - let dir = Path::new("cargo-watch-v8.4.0-aarch64-unknown-linux-gnu"); - - assert_eq!( - extracted_files.get_dir(Path::new(".")).unwrap(), - &HashSet::from([dir.as_os_str().into()]) - ); - - assert_eq!( - extracted_files.get_dir(dir).unwrap(), - &HashSet::from_iter( - [ - "README.md", - "LICENSE", - "completions", - "cargo-watch", - "cargo-watch.1" - ] - .iter() - .map(OsStr::new) - .map(Box::::from) - ), - ); - - assert_eq!( - extracted_files.get_dir(&dir.join("completions")).unwrap(), - &HashSet::from([OsStr::new("zsh").into()]), - ); - - assert!(extracted_files.has_file(&dir.join("cargo-watch"))); - assert!(extracted_files.has_file(&dir.join("cargo-watch.1"))); - assert!(extracted_files.has_file(&dir.join("LICENSE"))); - assert!(extracted_files.has_file(&dir.join("README.md"))); - - assert!(!extracted_files.has_file(&dir.join("completions"))); - assert!(!extracted_files.has_file(&dir.join("asdfcqwe"))); - - assert!(extracted_files.has_file(&dir.join("completions/zsh"))); - - // sccache, tgz and zip - let sccache_config = [ - ("https://github.com/mozilla/sccache/releases/download/v0.3.3/sccache-v0.3.3-x86_64-pc-windows-msvc.tar.gz", PkgFmt::Tgz), - ("https://github.com/mozilla/sccache/releases/download/v0.3.3/sccache-v0.3.3-x86_64-pc-windows-msvc.zip", PkgFmt::Zip), - ]; - - for (sccache_url, fmt) in sccache_config { - let extracted_files = Download::new(client.clone(), Url::parse(sccache_url).unwrap()) - .and_extract(fmt, tempdir().unwrap()) - .await - .unwrap(); - - let dir = Path::new("sccache-v0.3.3-x86_64-pc-windows-msvc"); - - assert_eq!( - extracted_files.get_dir(Path::new(".")).unwrap(), - &HashSet::from([dir.as_os_str().into()]) - ); - - assert_eq!( - extracted_files.get_dir(dir).unwrap(), - &HashSet::from_iter( - ["README.md", "LICENSE", "sccache.exe"] - .iter() - .map(OsStr::new) - .map(Box::::from) - ), - ); - } - } -} diff --git a/crates/binstalk-downloader/src/download/async_extracter.rs b/crates/binstalk-downloader/src/download/async_extracter.rs deleted file mode 100644 index ca2f0710..00000000 --- a/crates/binstalk-downloader/src/download/async_extracter.rs +++ /dev/null @@ -1,167 +0,0 @@ -use std::{ - borrow::Cow, - fs, - future::Future, - io::{self, Write}, - path::{Component, Path, PathBuf}, -}; - -use bytes::Bytes; -use futures_util::Stream; -use tempfile::tempfile as create_tmpfile; -use tokio::sync::mpsc; -use tracing::debug; - -use super::{extracter::*, DownloadError, ExtractedFiles, TarBasedFmt}; -use crate::{ - download::zip_extraction::do_extract_zip, - utils::{extract_with_blocking_task, StreamReadable}, -}; - -pub async fn extract_bin(stream: S, path: &Path) -> Result -where - S: Stream> + Send + Sync + Unpin, -{ - debug!("Writing to `{}`", path.display()); - - extract_with_blocking_decoder(stream, path, |rx, path| { - let mut extracted_files = ExtractedFiles::new(); - - extracted_files.add_file(Path::new(path.file_name().unwrap())); - - write_stream_to_file(rx, fs::File::create(path)?)?; - - Ok(extracted_files) - }) - .await -} - -pub async fn extract_zip(stream: S, path: &Path) -> Result -where - S: Stream> + Unpin + Send + Sync, -{ - debug!("Downloading from zip archive to tempfile"); - - extract_with_blocking_decoder(stream, path, |rx, path| { - debug!("Decompressing from zip archive to `{}`", path.display()); - - do_extract_zip(write_stream_to_file(rx, create_tmpfile()?)?, path).map_err(io::Error::from) - }) - .await -} - -pub async fn extract_tar_based_stream( - stream: S, - dst: &Path, - fmt: TarBasedFmt, -) -> Result -where - S: Stream> + Send + Sync + Unpin, -{ - debug!("Extracting from {fmt} archive to {}", dst.display()); - - extract_with_blocking_decoder(stream, dst, move |rx, dst| { - // Adapted from https://docs.rs/tar/latest/src/tar/archive.rs.html#189-219 - - if dst.symlink_metadata().is_err() { - fs::create_dir_all(dst)?; - } - - // Canonicalizing the dst directory will prepend the path with '\\?\' - // on windows which will allow windows APIs to treat the path as an - // extended-length path with a 32,767 character limit. Otherwise all - // unpacked paths over 260 characters will fail on creation with a - // NotFound exception. - let dst = &dst - .canonicalize() - .map(Cow::Owned) - .unwrap_or(Cow::Borrowed(dst)); - - let mut tar = create_tar_decoder(StreamReadable::new(rx), fmt)?; - let mut entries = tar.entries()?; - - let mut extracted_files = ExtractedFiles::new(); - - // Delay any directory entries until the end (they will be created if needed by - // descendants), to ensure that directory permissions do not interfer with descendant - // extraction. - let mut directories = Vec::new(); - - while let Some(mut entry) = entries.next().transpose()? { - match entry.header().entry_type() { - tar::EntryType::Regular => { - // unpack_in returns false if the path contains ".." - // and is skipped. - if entry.unpack_in(dst)? { - let path = entry.path()?; - - // create normalized_path in the same way - // tar::Entry::unpack_in would normalize the path. - let mut normalized_path = PathBuf::new(); - - for part in path.components() { - match part { - Component::Prefix(..) | Component::RootDir | Component::CurDir => { - continue - } - - // unpack_in would return false if this happens. - Component::ParentDir => unreachable!(), - - Component::Normal(part) => normalized_path.push(part), - } - } - - extracted_files.add_file(&normalized_path); - } - } - tar::EntryType::Directory => { - directories.push(entry); - } - _ => (), - } - } - - for mut dir in directories { - if dir.unpack_in(dst)? { - extracted_files.add_dir(&dir.path()?); - } - } - - Ok(extracted_files) - }) - .await -} - -fn extract_with_blocking_decoder( - stream: S, - path: &Path, - f: F, -) -> impl Future> -where - S: Stream> + Send + Sync + Unpin, - F: FnOnce(mpsc::Receiver, &Path) -> io::Result + Send + Sync + 'static, - T: Send + 'static, -{ - let path = path.to_owned(); - - extract_with_blocking_task(stream, move |rx| { - if let Some(parent) = path.parent() { - fs::create_dir_all(parent)?; - } - - f(rx, &path) - }) -} - -fn write_stream_to_file(mut rx: mpsc::Receiver, f: fs::File) -> io::Result { - let mut f = io::BufWriter::new(f); - - while let Some(bytes) = rx.blocking_recv() { - f.write_all(&bytes)?; - } - - f.flush()?; - - f.into_inner().map_err(io::IntoInnerError::into_error) -} diff --git a/crates/binstalk-downloader/src/download/async_tar_visitor.rs b/crates/binstalk-downloader/src/download/async_tar_visitor.rs deleted file mode 100644 index b6cdfa7d..00000000 --- a/crates/binstalk-downloader/src/download/async_tar_visitor.rs +++ /dev/null @@ -1,125 +0,0 @@ -use std::{borrow::Cow, fmt::Debug, io, path::Path, pin::Pin}; - -use async_compression::tokio::bufread; -use bytes::Bytes; -use futures_util::{Stream, StreamExt}; -use tokio::io::{copy, sink, AsyncRead}; -use tokio_tar::{Archive, Entry, EntryType}; -use tokio_util::io::StreamReader; -use tracing::debug; - -use super::{ - DownloadError, - TarBasedFmt::{self, *}, -}; - -pub trait TarEntry: AsyncRead + Send + Sync + Unpin + Debug { - /// Returns the path name for this entry. - /// - /// This method may fail if the pathname is not valid Unicode and - /// this is called on a Windows platform. - /// - /// Note that this function will convert any `\` characters to - /// directory separators. - fn path(&self) -> io::Result>; - - fn size(&self) -> io::Result; - - fn entry_type(&self) -> TarEntryType; -} - -impl TarEntry for &mut T { - fn path(&self) -> io::Result> { - T::path(self) - } - - fn size(&self) -> io::Result { - T::size(self) - } - - fn entry_type(&self) -> TarEntryType { - T::entry_type(self) - } -} - -impl TarEntry for Entry { - fn path(&self) -> io::Result> { - Entry::path(self) - } - - fn size(&self) -> io::Result { - self.header().size() - } - - fn entry_type(&self) -> TarEntryType { - match self.header().entry_type() { - EntryType::Regular => TarEntryType::Regular, - EntryType::Link => TarEntryType::Link, - EntryType::Symlink => TarEntryType::Symlink, - EntryType::Char => TarEntryType::Char, - EntryType::Block => TarEntryType::Block, - EntryType::Directory => TarEntryType::Directory, - EntryType::Fifo => TarEntryType::Fifo, - // Implementation-defined ‘high-performance’ type, treated as regular file - EntryType::Continuous => TarEntryType::Regular, - _ => TarEntryType::Unknown, - } - } -} - -#[derive(Copy, Clone, Debug)] -#[non_exhaustive] -pub enum TarEntryType { - Regular, - Link, - Symlink, - Char, - Block, - Directory, - Fifo, - Unknown, -} - -/// Visitor must iterate over all entries. -/// Entires can be in arbitary order. -#[async_trait::async_trait] -pub trait TarEntriesVisitor: Send + Sync { - /// Will be called once per entry - async fn visit(&mut self, entry: &mut dyn TarEntry) -> Result<(), DownloadError>; -} - -pub(crate) async fn extract_tar_based_stream_and_visit( - stream: S, - fmt: TarBasedFmt, - visitor: &mut dyn TarEntriesVisitor, -) -> Result<(), DownloadError> -where - S: Stream> + Send + Sync, -{ - debug!("Extracting from {fmt} archive to process it in memory"); - - let reader = StreamReader::new(stream); - let decoder: Pin> = match fmt { - Tar => Box::pin(reader), - Tbz2 => Box::pin(bufread::BzDecoder::new(reader)), - Tgz => Box::pin(bufread::GzipDecoder::new(reader)), - Txz => Box::pin(bufread::XzDecoder::new(reader)), - Tzstd => Box::pin(bufread::ZstdDecoder::new(reader)), - }; - - let mut tar = Archive::new(decoder); - let mut entries = tar.entries()?; - - let mut sink = sink(); - - while let Some(res) = entries.next().await { - let mut entry = res?; - visitor.visit(&mut entry).await?; - - // Consume all remaining data so that next iteration would work fine - // instead of reading the data of prevoius entry. - copy(&mut entry, &mut sink).await?; - } - - Ok(()) -} diff --git a/crates/binstalk-downloader/src/download/extracted_files.rs b/crates/binstalk-downloader/src/download/extracted_files.rs deleted file mode 100644 index ac45d4fb..00000000 --- a/crates/binstalk-downloader/src/download/extracted_files.rs +++ /dev/null @@ -1,108 +0,0 @@ -use std::{ - collections::{hash_map::Entry as HashMapEntry, HashMap, HashSet}, - ffi::OsStr, - path::Path, -}; - -#[derive(Debug)] -#[cfg_attr(test, derive(Eq, PartialEq))] -pub enum ExtractedFilesEntry { - Dir(Box>>), - File, -} - -impl ExtractedFilesEntry { - fn new_dir(file_name: Option<&OsStr>) -> Self { - ExtractedFilesEntry::Dir(Box::new( - file_name - .map(|file_name| HashSet::from([file_name.into()])) - .unwrap_or_default(), - )) - } -} - -#[derive(Debug)] -pub struct ExtractedFiles(pub(super) HashMap, ExtractedFilesEntry>); - -impl ExtractedFiles { - pub(super) fn new() -> Self { - Self(Default::default()) - } - - /// * `path` - must be canonical and must not be empty - /// - /// NOTE that if the entry for the `path` is previously set to a dir, - /// it would be replaced with a file. - pub(super) fn add_file(&mut self, path: &Path) { - self.0.insert(path.into(), ExtractedFilesEntry::File); - self.add_dir_if_has_parent(path); - } - - fn add_dir_if_has_parent(&mut self, path: &Path) { - if let Some(parent) = path.parent() { - if !parent.as_os_str().is_empty() { - self.add_dir_inner(parent, path.file_name()); - self.add_dir_if_has_parent(parent); - } else { - self.add_dir_inner(Path::new("."), path.file_name()) - } - } - } - - /// * `path` - must be canonical and must not be empty - /// - /// NOTE that if the entry for the `path` is previously set to a dir, - /// it would be replaced with an empty Dir entry. - pub(super) fn add_dir(&mut self, path: &Path) { - self.add_dir_inner(path, None); - self.add_dir_if_has_parent(path); - } - - /// * `path` - must be canonical and must not be empty - /// - /// NOTE that if the entry for the `path` is previously set to a dir, - /// it would be replaced with a Dir entry containing `file_name` if it - /// is `Some(..)`, or an empty Dir entry. - fn add_dir_inner(&mut self, path: &Path, file_name: Option<&OsStr>) { - match self.0.entry(path.into()) { - HashMapEntry::Vacant(entry) => { - entry.insert(ExtractedFilesEntry::new_dir(file_name)); - } - HashMapEntry::Occupied(entry) => match entry.into_mut() { - ExtractedFilesEntry::Dir(hash_set) => { - if let Some(file_name) = file_name { - hash_set.insert(file_name.into()); - } - } - entry => *entry = ExtractedFilesEntry::new_dir(file_name), - }, - } - } - - /// * `path` - must be a relative path without `.`, `..`, `/`, `prefix:/` - /// and must not be empty, for these values it is guaranteed to - /// return `None`. - /// But could be set to "." for top-level. - pub fn get_entry(&self, path: &Path) -> Option<&ExtractedFilesEntry> { - self.0.get(path) - } - - /// * `path` - must be a relative path without `.`, `..`, `/`, `prefix:/` - /// and must not be empty, for these values it is guaranteed to - /// return `None`. - /// But could be set to "." for top-level. - pub fn get_dir(&self, path: &Path) -> Option<&HashSet>> { - match self.get_entry(path)? { - ExtractedFilesEntry::Dir(file_names) => Some(file_names), - ExtractedFilesEntry::File => None, - } - } - - /// * `path` - must be a relative path without `.`, `..`, `/`, `prefix:/` - /// and must not be empty, for these values it is guaranteed to - /// return `false`. - /// But could be set to "." for top-level. - pub fn has_file(&self, path: &Path) -> bool { - matches!(self.get_entry(path), Some(ExtractedFilesEntry::File)) - } -} diff --git a/crates/binstalk-downloader/src/download/extracter.rs b/crates/binstalk-downloader/src/download/extracter.rs deleted file mode 100644 index 32e131e6..00000000 --- a/crates/binstalk-downloader/src/download/extracter.rs +++ /dev/null @@ -1,31 +0,0 @@ -use std::io::{self, BufRead, Read}; - -use bzip2::bufread::BzDecoder; -use flate2::bufread::GzDecoder; -use tar::Archive; -use xz2::bufread::XzDecoder; -use zstd::stream::Decoder as ZstdDecoder; - -use super::TarBasedFmt; - -pub fn create_tar_decoder( - dat: impl BufRead + 'static, - fmt: TarBasedFmt, -) -> io::Result>> { - use TarBasedFmt::*; - - let r: Box = match fmt { - Tar => Box::new(dat), - Tbz2 => Box::new(BzDecoder::new(dat)), - Tgz => Box::new(GzDecoder::new(dat)), - Txz => Box::new(XzDecoder::new(dat)), - Tzstd => { - // The error can only come from raw::Decoder::with_dictionary as of zstd 0.10.2 and - // 0.11.2, which is specified as `&[]` by `ZstdDecoder::new`, thus `ZstdDecoder::new` - // should not return any error. - Box::new(ZstdDecoder::with_buffer(dat)?) - } - }; - - Ok(Archive::new(r)) -} diff --git a/crates/binstalk-downloader/src/download/zip_extraction.rs b/crates/binstalk-downloader/src/download/zip_extraction.rs deleted file mode 100644 index cb5af6e7..00000000 --- a/crates/binstalk-downloader/src/download/zip_extraction.rs +++ /dev/null @@ -1,68 +0,0 @@ -use std::{ - fs::{create_dir_all, File}, - io, - path::Path, -}; - -use cfg_if::cfg_if; -use rc_zip_sync::{rc_zip::parse::EntryKind, ReadZip}; - -use super::{DownloadError, ExtractedFiles}; - -pub(super) fn do_extract_zip(f: File, dir: &Path) -> Result { - let mut extracted_files = ExtractedFiles::new(); - - for entry in f.read_zip()?.entries() { - let Some(name) = entry.sanitized_name().map(Path::new) else { - continue; - }; - let path = dir.join(name); - - let do_extract_file = || { - let mut entry_writer = File::create(&path)?; - let mut entry_reader = entry.reader(); - io::copy(&mut entry_reader, &mut entry_writer)?; - - Ok::<_, io::Error>(()) - }; - - let parent = path - .parent() - .expect("all full entry paths should have parent paths"); - create_dir_all(parent)?; - - match entry.kind() { - EntryKind::Symlink => { - extracted_files.add_file(name); - cfg_if! { - if #[cfg(windows)] { - do_extract_file()?; - } else { - use std::{fs, io::Read}; - - match fs::symlink_metadata(&path) { - Ok(metadata) if metadata.is_file() => fs::remove_file(&path)?, - _ => (), - } - - let mut src = String::new(); - entry.reader().read_to_string(&mut src)?; - - // validate pointing path before creating a symbolic link - if src.contains("..") { - continue; - } - std::os::unix::fs::symlink(src, &path)?; - } - } - } - EntryKind::Directory => (), - EntryKind::File => { - extracted_files.add_file(name); - do_extract_file()?; - } - } - } - - Ok(extracted_files) -} diff --git a/crates/binstalk-downloader/src/lib.rs b/crates/binstalk-downloader/src/lib.rs deleted file mode 100644 index 1e4cfad7..00000000 --- a/crates/binstalk-downloader/src/lib.rs +++ /dev/null @@ -1,6 +0,0 @@ -#![cfg_attr(docsrs, feature(doc_auto_cfg))] - -pub use bytes; -pub mod download; -pub mod remote; -mod utils; diff --git a/crates/binstalk-downloader/src/remote.rs b/crates/binstalk-downloader/src/remote.rs deleted file mode 100644 index 1ab1391c..00000000 --- a/crates/binstalk-downloader/src/remote.rs +++ /dev/null @@ -1,414 +0,0 @@ -use std::{ - num::{NonZeroU16, NonZeroU64, NonZeroU8}, - ops::ControlFlow, - sync::Arc, - time::{Duration, SystemTime}, -}; - -use bytes::Bytes; -use futures_util::Stream; -use httpdate::parse_http_date; -use reqwest::{ - header::{HeaderMap, HeaderValue, RETRY_AFTER}, - Request, -}; -use thiserror::Error as ThisError; -use tracing::{debug, info, instrument}; - -pub use reqwest::{header, Error as ReqwestError, Method, StatusCode}; -pub use url::Url; - -mod delay_request; -use delay_request::DelayRequest; - -mod certificate; -pub use certificate::Certificate; - -mod request_builder; -pub use request_builder::{Body, RequestBuilder, Response}; - -mod tls_version; -pub use tls_version::TLSVersion; - -#[cfg(feature = "hickory-dns")] -mod resolver; -#[cfg(feature = "hickory-dns")] -use resolver::TrustDnsResolver; - -#[cfg(feature = "json")] -pub use request_builder::JsonError; - -const MAX_RETRY_DURATION: Duration = Duration::from_secs(120); -const MAX_RETRY_COUNT: u8 = 3; -const DEFAULT_RETRY_DURATION_FOR_RATE_LIMIT: Duration = Duration::from_millis(200); -const RETRY_DURATION_FOR_TIMEOUT: Duration = Duration::from_millis(200); -#[allow(dead_code)] -const DEFAULT_MIN_TLS: TLSVersion = TLSVersion::TLS_1_2; - -#[derive(Debug, ThisError)] -#[non_exhaustive] -pub enum Error { - #[error("Reqwest error: {0}")] - Reqwest(#[from] reqwest::Error), - - #[error(transparent)] - Http(Box), - - #[cfg(feature = "json")] - #[error("Failed to parse http response body as Json: {0}")] - Json(#[from] JsonError), -} - -#[derive(Debug, ThisError)] -#[error("could not {method} {url}: {err}")] -pub struct HttpError { - method: reqwest::Method, - url: url::Url, - #[source] - err: reqwest::Error, -} - -impl HttpError { - /// Returns true if the error is from [`Response::error_for_status`]. - pub fn is_status(&self) -> bool { - self.err.is_status() - } -} - -#[derive(Debug)] -struct Inner { - client: reqwest::Client, - service: DelayRequest, -} - -#[derive(Clone, Debug)] -pub struct Client(Arc); - -#[cfg_attr(not(feature = "__tls"), allow(unused_variables, unused_mut))] -impl Client { - /// Construct a new downloader client - /// - /// * `per_millis` - The duration (in millisecond) for which at most - /// `num_request` can be sent. Increase it if rate-limit errors - /// happen. - /// * `num_request` - maximum number of requests to be processed for - /// each `per_millis` duration. - /// - /// The [`reqwest::Client`] constructed has secure defaults, such as allowing - /// only TLS v1.2 and above, and disallowing plaintext HTTP altogether. If you - /// need more control, use the `from_builder` variant. - pub fn new( - user_agent: impl AsRef, - min_tls: Option, - per_millis: NonZeroU16, - num_request: NonZeroU64, - certificates: impl IntoIterator, - ) -> Result { - Self::from_builder( - Self::default_builder(user_agent.as_ref(), min_tls, &mut certificates.into_iter()), - per_millis, - num_request, - ) - } - - /// Constructs a default [`reqwest::ClientBuilder`]. - /// - /// This may be used alongside [`Client::from_builder`] to start from reasonable - /// defaults, but still be able to customise the reqwest instance. Arguments are - /// as [`Client::new`], but without generic parameters. - pub fn default_builder( - user_agent: &str, - min_tls: Option, - certificates: &mut dyn Iterator, - ) -> reqwest::ClientBuilder { - let mut builder = reqwest::ClientBuilder::new() - .user_agent(user_agent) - .https_only(true) - .tcp_nodelay(false); - - #[cfg(feature = "hickory-dns")] - { - builder = builder.dns_resolver(Arc::new(TrustDnsResolver::default())); - } - - #[cfg(feature = "__tls")] - { - let tls_ver = min_tls - .map(|tls| tls.max(DEFAULT_MIN_TLS)) - .unwrap_or(DEFAULT_MIN_TLS); - - builder = builder.min_tls_version(tls_ver.into()); - - for certificate in certificates { - builder = builder.add_root_certificate(certificate.0); - } - } - - builder - } - - /// Construct a custom client from a [`reqwest::ClientBuilder`]. - /// - /// You may want to also use [`Client::default_builder`]. - pub fn from_builder( - builder: reqwest::ClientBuilder, - per_millis: NonZeroU16, - num_request: NonZeroU64, - ) -> Result { - let client = builder.build()?; - - Ok(Client(Arc::new(Inner { - client: client.clone(), - service: DelayRequest::new( - num_request, - Duration::from_millis(per_millis.get() as u64), - client, - ), - }))) - } - - /// Return inner reqwest client. - pub fn get_inner(&self) -> &reqwest::Client { - &self.0.client - } - - /// Return `Err(_)` for fatal error tht cannot be retried. - /// - /// Return `Ok(ControlFlow::Continue(res))` for retryable error, `res` - /// will contain the previous `Result`. - /// A retryable error could be a `ReqwestError` or `Response` with - /// unsuccessful status code. - /// - /// Return `Ok(ControlFlow::Break(response))` when succeeds and no need - /// to retry. - #[instrument( - skip(self, url), - fields( - url = format_args!("{url}"), - ), - )] - async fn do_send_request( - &self, - request: Request, - url: &Url, - ) -> Result>, ReqwestError> - { - static HEADER_VALUE_0: HeaderValue = HeaderValue::from_static("0"); - - let response = match self.0.service.call(request).await { - Err(err) if err.is_timeout() || err.is_connect() => { - let duration = RETRY_DURATION_FOR_TIMEOUT; - - info!("Received timeout error from reqwest. Delay future request by {duration:#?}"); - - self.0.service.add_urls_to_delay(&[url], duration); - - return Ok(ControlFlow::Continue(Err(err))); - } - res => res?, - }; - - let status = response.status(); - - let add_delay_and_continue = |response: reqwest::Response, duration| { - info!("Received status code {status}, will wait for {duration:#?} and retry"); - - self.0 - .service - .add_urls_to_delay(&[url, response.url()], duration); - - Ok(ControlFlow::Continue(Ok(response))) - }; - - let headers = response.headers(); - - // Some server (looking at you, github GraphQL API) may returns a rate limit - // even when OK is returned or on other status code (e.g. 453 forbidden). - if let Some(duration) = parse_header_retry_after(headers) { - add_delay_and_continue(response, duration.min(MAX_RETRY_DURATION)) - } else if headers.get("x-ratelimit-remaining") == Some(&HEADER_VALUE_0) { - let duration = headers - .get("x-ratelimit-reset") - .and_then(|value| { - let secs = value.to_str().ok()?.parse().ok()?; - Some(Duration::from_secs(secs)) - }) - .unwrap_or(DEFAULT_RETRY_DURATION_FOR_RATE_LIMIT) - .min(MAX_RETRY_DURATION); - - add_delay_and_continue(response, duration) - } else { - match status { - // Delay further request on rate limit - StatusCode::SERVICE_UNAVAILABLE | StatusCode::TOO_MANY_REQUESTS => { - add_delay_and_continue(response, DEFAULT_RETRY_DURATION_FOR_RATE_LIMIT) - } - - // Delay further request on timeout - StatusCode::REQUEST_TIMEOUT | StatusCode::GATEWAY_TIMEOUT => { - add_delay_and_continue(response, RETRY_DURATION_FOR_TIMEOUT) - } - - _ => Ok(ControlFlow::Break(response)), - } - } - } - - /// * `request` - `Request::try_clone` must always return `Some`. - async fn send_request_inner( - &self, - request: &Request, - ) -> Result { - let mut count = 0; - let max_retry_count = NonZeroU8::new(MAX_RETRY_COUNT).unwrap(); - - // Since max_retry_count is non-zero, there is at least one iteration. - loop { - // Increment the counter before checking for terminal condition. - count += 1; - - match self - .do_send_request(request.try_clone().unwrap(), request.url()) - .await? - { - ControlFlow::Break(response) => break Ok(response), - ControlFlow::Continue(res) if count >= max_retry_count.get() => { - break res; - } - _ => (), - } - } - } - - /// * `request` - `Request::try_clone` must always return `Some`. - async fn send_request( - &self, - request: Request, - error_for_status: bool, - ) -> Result { - debug!("Downloading from: '{}'", request.url()); - - self.send_request_inner(&request) - .await - .and_then(|response| { - if error_for_status { - response.error_for_status() - } else { - Ok(response) - } - }) - .map_err(|err| { - Error::Http(Box::new(HttpError { - method: request.method().clone(), - url: request.url().clone(), - err, - })) - }) - } - - async fn head_or_fallback_to_get( - &self, - url: Url, - error_for_status: bool, - ) -> Result { - let res = self - .send_request(Request::new(Method::HEAD, url.clone()), error_for_status) - .await; - - let retry_with_get = move || async move { - // Retry using GET - info!("HEAD on {url} is not allowed, fallback to GET"); - self.send_request(Request::new(Method::GET, url), error_for_status) - .await - }; - - let is_retryable = |status| { - matches!( - status, - StatusCode::BAD_REQUEST // 400 - | StatusCode::UNAUTHORIZED // 401 - | StatusCode::FORBIDDEN // 403 - | StatusCode::NOT_FOUND // 404 - | StatusCode::METHOD_NOT_ALLOWED // 405 - | StatusCode::GONE // 410 - ) - }; - - match res { - Err(Error::Http(http_error)) - if http_error.err.status().map(is_retryable).unwrap_or(false) => - { - retry_with_get().await - } - Ok(response) if is_retryable(response.status()) => retry_with_get().await, - res => res, - } - } - - /// Check if remote exists using `Method::GET`. - pub async fn remote_gettable(&self, url: Url) -> Result { - Ok(self.get(url).send(false).await?.status().is_success()) - } - - /// Attempt to get final redirected url using `Method::HEAD` or fallback - /// to `Method::GET`. - pub async fn get_redirected_final_url(&self, url: Url) -> Result { - self.head_or_fallback_to_get(url, true) - .await - .map(|response| response.url().clone()) - } - - /// Create `GET` request to `url` and return a stream of the response data. - /// On status code other than 200, it will return an error. - pub async fn get_stream( - &self, - url: Url, - ) -> Result>, Error> { - Ok(self.get(url).send(true).await?.bytes_stream()) - } - - /// Create a new request. - pub fn request(&self, method: Method, url: Url) -> RequestBuilder { - RequestBuilder { - client: self.clone(), - inner: self.0.client.request(method, url), - } - } - - /// Create a new GET request. - pub fn get(&self, url: Url) -> RequestBuilder { - self.request(Method::GET, url) - } - - /// Create a new POST request. - pub fn post(&self, url: Url, body: impl Into) -> RequestBuilder { - self.request(Method::POST, url).body(body.into()) - } -} - -fn parse_header_retry_after(headers: &HeaderMap) -> Option { - let header = headers - .get_all(RETRY_AFTER) - .into_iter() - .next_back()? - .to_str() - .ok()?; - - match header.parse::() { - Ok(dur) => Some(Duration::from_secs(dur)), - Err(_) => { - let system_time = parse_http_date(header).ok()?; - - let retry_after_unix_timestamp = - system_time.duration_since(SystemTime::UNIX_EPOCH).ok()?; - - let curr_time_unix_timestamp = SystemTime::now() - .duration_since(SystemTime::UNIX_EPOCH) - .expect("SystemTime before UNIX EPOCH!"); - - // retry_after_unix_timestamp - curr_time_unix_timestamp - // If underflows, returns Duration::ZERO. - Some(retry_after_unix_timestamp.saturating_sub(curr_time_unix_timestamp)) - } - } -} diff --git a/crates/binstalk-downloader/src/remote/certificate.rs b/crates/binstalk-downloader/src/remote/certificate.rs deleted file mode 100644 index 29e9b93f..00000000 --- a/crates/binstalk-downloader/src/remote/certificate.rs +++ /dev/null @@ -1,32 +0,0 @@ -#[cfg(feature = "__tls")] -use reqwest::tls; - -use super::Error; - -#[derive(Clone, Debug)] -pub struct Certificate(#[cfg(feature = "__tls")] pub(super) tls::Certificate); - -#[cfg_attr(not(feature = "__tls"), allow(unused_variables))] -impl Certificate { - /// Create a Certificate from a binary DER encoded certificate - pub fn from_der(der: impl AsRef<[u8]>) -> Result { - #[cfg(not(feature = "__tls"))] - return Ok(Self()); - - #[cfg(feature = "__tls")] - tls::Certificate::from_der(der.as_ref()) - .map(Self) - .map_err(Error::from) - } - - /// Create a Certificate from a PEM encoded certificate - pub fn from_pem(pem: impl AsRef<[u8]>) -> Result { - #[cfg(not(feature = "__tls"))] - return Ok(Self()); - - #[cfg(feature = "__tls")] - tls::Certificate::from_pem(pem.as_ref()) - .map(Self) - .map_err(Error::from) - } -} diff --git a/crates/binstalk-downloader/src/remote/delay_request.rs b/crates/binstalk-downloader/src/remote/delay_request.rs deleted file mode 100644 index 3feb804d..00000000 --- a/crates/binstalk-downloader/src/remote/delay_request.rs +++ /dev/null @@ -1,245 +0,0 @@ -use std::{ - collections::HashMap, future::Future, iter::Peekable, num::NonZeroU64, ops::ControlFlow, - sync::Mutex, -}; - -use compact_str::{CompactString, ToCompactString}; -use reqwest::{Request, Url}; -use tokio::time::{sleep_until, Duration, Instant}; -use tracing::debug; - -pub(super) type RequestResult = Result; - -trait IterExt: Iterator { - fn dedup(self) -> Dedup - where - Self: Sized, - Self::Item: PartialEq, - { - Dedup(self.peekable()) - } -} - -impl IterExt for It {} - -struct Dedup(Peekable); - -impl Iterator for Dedup -where - It: Iterator, - It::Item: PartialEq, -{ - type Item = It::Item; - - fn next(&mut self) -> Option { - let curr = self.0.next()?; - - // Drop all consecutive dup values - while self.0.next_if_eq(&curr).is_some() {} - - Some(curr) - } -} - -#[derive(Debug)] -struct Inner { - client: reqwest::Client, - num_request: NonZeroU64, - per: Duration, - until: Instant, - state: State, -} - -#[derive(Debug)] -enum State { - Limited, - Ready { rem: NonZeroU64 }, -} - -impl Inner { - fn new(num_request: NonZeroU64, per: Duration, client: reqwest::Client) -> Self { - Inner { - client, - per, - num_request, - until: Instant::now() + per, - state: State::Ready { rem: num_request }, - } - } - - fn inc_rate_limit(&mut self) { - if let Some(num_request) = NonZeroU64::new(self.num_request.get() / 2) { - // If self.num_request.get() > 1, then cut it by half - self.num_request = num_request; - if let State::Ready { rem, .. } = &mut self.state { - *rem = num_request.min(*rem) - } - } - - let per = self.per; - if per < Duration::from_millis(700) { - self.per = per.mul_f32(1.2); - self.until += self.per - per; - } - } - - fn ready(&mut self) -> Readiness { - match self.state { - State::Ready { .. } => Readiness::Ready, - State::Limited => { - if self.until.elapsed().is_zero() { - Readiness::Limited(self.until) - } else { - // rate limit can be reset now and is ready - self.until = Instant::now() + self.per; - self.state = State::Ready { - rem: self.num_request, - }; - - Readiness::Ready - } - } - } - } - - fn call(&mut self, req: Request) -> impl Future { - match &mut self.state { - State::Ready { rem } => { - let now = Instant::now(); - - // If the period has elapsed, reset it. - if now >= self.until { - self.until = now + self.per; - *rem = self.num_request; - } - - if let Some(new_rem) = NonZeroU64::new(rem.get() - 1) { - *rem = new_rem; - } else { - // The service is disabled until further notice - self.state = State::Limited; - } - - // Call the inner future - self.client.execute(req) - } - State::Limited => panic!("service not ready; poll_ready must be called first"), - } - } -} - -enum Readiness { - Limited(Instant), - Ready, -} - -#[derive(Debug)] -pub(super) struct DelayRequest { - inner: Mutex, - hosts_to_delay: Mutex>, -} - -impl DelayRequest { - pub(super) fn new(num_request: NonZeroU64, per: Duration, client: reqwest::Client) -> Self { - Self { - inner: Mutex::new(Inner::new(num_request, per, client)), - hosts_to_delay: Default::default(), - } - } - - pub(super) fn add_urls_to_delay(&self, urls: &[&Url], delay_duration: Duration) { - let deadline = Instant::now() + delay_duration; - - let mut hosts_to_delay = self.hosts_to_delay.lock().unwrap(); - - urls.iter() - .filter_map(|url| url.host_str()) - .dedup() - .for_each(|host| { - hosts_to_delay - .entry(host.to_compact_string()) - .and_modify(|old_dl| { - *old_dl = deadline.max(*old_dl); - }) - .or_insert(deadline); - }); - } - - fn get_delay_until(&self, host: &str) -> Option { - let mut hosts_to_delay = self.hosts_to_delay.lock().unwrap(); - - hosts_to_delay.get(host).copied().and_then(|until| { - if until.elapsed().is_zero() { - Some(until) - } else { - // We have already gone past the deadline, - // so we should remove it instead. - hosts_to_delay.remove(host); - None - } - }) - } - - // Define a new function so that the guard will be dropped ASAP and not - // included in the future. - fn call_inner( - &self, - counter: &mut u32, - req: &mut Option, - ) -> ControlFlow, Instant> { - // Wait until we are ready to send next requests - // (client-side rate-limit throttler). - let mut guard = self.inner.lock().unwrap(); - - if let Readiness::Limited(until) = guard.ready() { - ControlFlow::Continue(until) - } else if let Some(until) = req - .as_ref() - .unwrap() - .url() - .host_str() - .and_then(|host| self.get_delay_until(host)) - { - // If the host rate-limit us, then wait until then - // and try again (server-side rate-limit throttler). - - // Try increasing client-side rate-limit throttler to prevent - // rate-limit in the future. - guard.inc_rate_limit(); - - let additional_delay = - Duration::from_millis(200) + Duration::from_millis(100) * 20.min(*counter); - - *counter += 1; - - debug!("server-side rate limit exceeded; sleeping."); - ControlFlow::Continue(until + additional_delay) - } else { - ControlFlow::Break(guard.call(req.take().unwrap())) - } - } - - pub(super) async fn call(&self, req: Request) -> RequestResult { - // Put all variables in a block so that will be dropped before polling - // the future returned by reqwest. - { - let mut counter = 0; - // Use Option here so that we don't have to move entire `Request` - // twice when calling `self.call_inner` while retain the ability to - // take its value without boxing. - // - // This will be taken when `ControlFlow::Break` is then it will - // break the loop, so it will never call `self.call_inner` with - // a `None`. - let mut req = Some(req); - - loop { - match self.call_inner(&mut counter, &mut req) { - ControlFlow::Continue(until) => sleep_until(until).await, - ControlFlow::Break(future) => break future, - } - } - } - .await - } -} diff --git a/crates/binstalk-downloader/src/remote/request_builder.rs b/crates/binstalk-downloader/src/remote/request_builder.rs deleted file mode 100644 index 4dfacd30..00000000 --- a/crates/binstalk-downloader/src/remote/request_builder.rs +++ /dev/null @@ -1,120 +0,0 @@ -use std::fmt; - -use bytes::Bytes; -use futures_util::{Stream, StreamExt}; -use reqwest::Method; - -use super::{header, Client, Error, HttpError, StatusCode, Url}; - -pub use reqwest::Body; - -#[cfg(feature = "json")] -pub use serde_json::Error as JsonError; - -#[derive(Debug)] -pub struct RequestBuilder { - pub(super) client: Client, - pub(super) inner: reqwest::RequestBuilder, -} - -impl RequestBuilder { - pub fn bearer_auth(self, token: &dyn fmt::Display) -> Self { - Self { - client: self.client, - inner: self.inner.bearer_auth(token), - } - } - - pub fn header(self, key: &str, value: &str) -> Self { - Self { - client: self.client, - inner: self.inner.header(key, value), - } - } - - pub fn body(self, body: impl Into) -> Self { - Self { - client: self.client, - inner: self.inner.body(body.into()), - } - } - - pub async fn send(self, error_for_status: bool) -> Result { - let request = self.inner.build()?; - let method = request.method().clone(); - Ok(Response { - inner: self.client.send_request(request, error_for_status).await?, - method, - }) - } -} - -#[derive(Debug)] -pub struct Response { - inner: reqwest::Response, - method: Method, -} - -impl Response { - pub async fn bytes(self) -> Result { - self.inner.bytes().await.map_err(Error::from) - } - - pub fn bytes_stream(self) -> impl Stream> { - let url = Box::new(self.inner.url().clone()); - let method = self.method; - - self.inner.bytes_stream().map(move |res| { - res.map_err(|err| { - Error::Http(Box::new(HttpError { - method: method.clone(), - url: Url::clone(&*url), - err, - })) - }) - }) - } - - pub fn status(&self) -> StatusCode { - self.inner.status() - } - - pub fn url(&self) -> &Url { - self.inner.url() - } - - pub fn method(&self) -> &Method { - &self.method - } - - pub fn error_for_status_ref(&self) -> Result<&Self, Error> { - match self.inner.error_for_status_ref() { - Ok(_) => Ok(self), - Err(err) => Err(Error::Http(Box::new(HttpError { - method: self.method().clone(), - url: self.url().clone(), - err, - }))), - } - } - - pub fn error_for_status(self) -> Result { - match self.error_for_status_ref() { - Ok(_) => Ok(self), - Err(err) => Err(err), - } - } - - pub fn headers(&self) -> &header::HeaderMap { - self.inner.headers() - } - - #[cfg(feature = "json")] - pub async fn json(self) -> Result - where - T: serde::de::DeserializeOwned, - { - let bytes = self.error_for_status()?.bytes().await?; - Ok(serde_json::from_slice(&bytes)?) - } -} diff --git a/crates/binstalk-downloader/src/remote/resolver.rs b/crates/binstalk-downloader/src/remote/resolver.rs deleted file mode 100644 index 1df98aab..00000000 --- a/crates/binstalk-downloader/src/remote/resolver.rs +++ /dev/null @@ -1,94 +0,0 @@ -use std::{net::SocketAddr, sync::Arc}; - -use hickory_resolver::{ - config::{LookupIpStrategy, ResolverConfig, ResolverOpts}, - system_conf, TokioResolver as TokioAsyncResolver, -}; -use once_cell::sync::OnceCell; -use reqwest::dns::{Addrs, Name, Resolve, Resolving}; -use tracing::{debug, instrument, warn}; - -#[cfg(windows)] -use hickory_resolver::{config::NameServerConfig, proto::xfer::Protocol}; - -type BoxError = Box; - -#[derive(Debug, Default, Clone)] -pub struct TrustDnsResolver(Arc>); - -impl Resolve for TrustDnsResolver { - fn resolve(&self, name: Name) -> Resolving { - let resolver = self.clone(); - Box::pin(async move { - let resolver = resolver.0.get_or_try_init(new_resolver)?; - - let lookup = resolver.lookup_ip(name.as_str()).await?; - let addrs: Addrs = Box::new(lookup.into_iter().map(|ip| SocketAddr::new(ip, 0))); - Ok(addrs) - }) - } -} - -#[cfg(unix)] -fn get_configs() -> Result<(ResolverConfig, ResolverOpts), BoxError> { - debug!("Using system DNS resolver configuration"); - system_conf::read_system_conf().map_err(Into::into) -} - -#[cfg(windows)] -fn get_configs() -> Result<(ResolverConfig, ResolverOpts), BoxError> { - debug!("Using custom DNS resolver configuration"); - let mut config = ResolverConfig::new(); - let opts = ResolverOpts::default(); - - get_adapter()?.dns_servers().iter().for_each(|addr| { - tracing::trace!("Adding DNS server: {}", addr); - let socket_addr = SocketAddr::new(*addr, 53); - for protocol in [Protocol::Udp, Protocol::Tcp] { - config.add_name_server(NameServerConfig { - socket_addr, - protocol, - tls_dns_name: None, - trust_negative_responses: false, - bind_addr: None, - http_endpoint: None, - }) - } - }); - - Ok((config, opts)) -} - -#[instrument] -fn new_resolver() -> Result { - let (config, mut opts) = get_configs()?; - - debug!("Resolver configuration complete"); - opts.ip_strategy = LookupIpStrategy::Ipv4AndIpv6; - let mut builder = TokioAsyncResolver::builder_with_config(config, Default::default()); - *builder.options_mut() = opts; - Ok(builder.build()) -} - -#[cfg(windows)] -#[instrument] -fn get_adapter() -> Result { - debug!("Retrieving local IP address"); - let local_ip = - default_net::interface::get_local_ipaddr().ok_or("Local IP address not found")?; - debug!("Local IP address: {local_ip}"); - debug!("Retrieving network adapters"); - let adapters = ipconfig::get_adapters()?; - debug!("Found {} network adapters", adapters.len()); - debug!("Searching for adapter with IP address {local_ip}"); - let adapter = adapters - .into_iter() - .find(|adapter| adapter.ip_addresses().contains(&local_ip)) - .ok_or("Adapter not found")?; - debug!( - "Using adapter {} with {} DNS servers", - adapter.friendly_name(), - adapter.dns_servers().len() - ); - Ok(adapter) -} diff --git a/crates/binstalk-downloader/src/remote/tls_version.rs b/crates/binstalk-downloader/src/remote/tls_version.rs deleted file mode 100644 index 06bbedff..00000000 --- a/crates/binstalk-downloader/src/remote/tls_version.rs +++ /dev/null @@ -1,37 +0,0 @@ -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] -enum Inner { - Tls1_2 = 0, - Tls1_3 = 1, -} - -/// TLS version for [`crate::remote::Client`]. -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] -pub struct TLSVersion(Inner); - -impl TLSVersion { - pub const TLS_1_2: TLSVersion = TLSVersion(Inner::Tls1_2); - pub const TLS_1_3: TLSVersion = TLSVersion(Inner::Tls1_3); -} - -#[cfg(feature = "__tls")] -impl From for reqwest::tls::Version { - fn from(ver: TLSVersion) -> reqwest::tls::Version { - use reqwest::tls::Version; - use Inner::*; - - match ver.0 { - Tls1_2 => Version::TLS_1_2, - Tls1_3 => Version::TLS_1_3, - } - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn test_tls_version_order() { - assert!(TLSVersion::TLS_1_2 < TLSVersion::TLS_1_3); - } -} diff --git a/crates/binstalk-downloader/src/utils.rs b/crates/binstalk-downloader/src/utils.rs deleted file mode 100644 index 65f38918..00000000 --- a/crates/binstalk-downloader/src/utils.rs +++ /dev/null @@ -1,172 +0,0 @@ -use std::{ - future::Future, - io::{self, BufRead, Read}, -}; - -use bytes::{Buf, Bytes}; -use futures_util::{FutureExt, Stream, StreamExt}; -use tokio::{sync::mpsc, task}; - -pub(super) fn extract_with_blocking_task( - stream: S, - f: F, -) -> impl Future> -where - T: Send + 'static, - E: From, - E: From, - S: Stream> + Send + Sync + Unpin, - F: FnOnce(mpsc::Receiver) -> io::Result + Send + Sync + 'static, -{ - async fn inner( - mut stream: S, - task: Fut, - tx: mpsc::Sender, - ) -> Result - where - E: From, - E: From, - // We do not use trait object for S since there will only be one - // S used with this function. - S: Stream> + Send + Sync + Unpin, - // asyncify would always return the same future, so no need to - // use trait object here. - Fut: Future> + Send + Sync, - { - let read_fut = async move { - while let Some(bytes) = stream.next().await.transpose()? { - if bytes.is_empty() { - continue; - } - - if tx.send(bytes).await.is_err() { - // The extract tar returns, which could be that: - // - Extraction fails with an error - // - Extraction success without the rest of the data - // - // - // It's hard to tell the difference here, so we assume - // the first scienario occurs. - // - // Even if the second scienario occurs, it won't affect the - // extraction process anyway, so we can jsut ignore it. - return Ok(()); - } - } - - Ok::<_, E>(()) - }; - tokio::pin!(read_fut); - - let task_fut = async move { task.await.map_err(E::from) }; - tokio::pin!(task_fut); - - tokio::select! { - biased; - - res = &mut read_fut => { - // The stream reaches eof, propagate error and wait for - // read task to be done. - res?; - - task_fut.await - }, - res = &mut task_fut => { - // The task finishes before the read task, return early - // after checking for errors in read_fut. - if let Some(Err(err)) = read_fut.now_or_never() { - Err(err) - } else { - res - } - } - } - } - - // Use channel size = 5 to minimize the waiting time in the extraction task - let (tx, rx) = mpsc::channel(5); - - let task = asyncify(move || f(rx)); - - inner(stream, task, tx) -} - -/// Copied from tokio https://docs.rs/tokio/latest/src/tokio/fs/mod.rs.html#132 -pub(super) fn asyncify(f: F) -> impl Future> + Send + Sync + 'static -where - F: FnOnce() -> io::Result + Send + 'static, - T: Send + 'static, -{ - async fn inner(handle: task::JoinHandle>) -> io::Result { - match handle.await { - Ok(res) => res, - Err(err) => Err(io::Error::new( - io::ErrorKind::Other, - format!("background task failed: {err}"), - )), - } - } - - inner(task::spawn_blocking(f)) -} - -/// This wraps an AsyncIterator as a `Read`able. -/// It must be used in non-async context only, -/// meaning you have to use it with -/// `tokio::task::{block_in_place, spawn_blocking}` or -/// `std::thread::spawn`. -pub(super) struct StreamReadable { - rx: mpsc::Receiver, - bytes: Bytes, -} - -impl StreamReadable { - pub(super) fn new(rx: mpsc::Receiver) -> Self { - Self { - rx, - bytes: Bytes::new(), - } - } -} - -impl Read for StreamReadable { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - if buf.is_empty() { - return Ok(0); - } - - if self.fill_buf()?.is_empty() { - return Ok(0); - } - - let bytes = &mut self.bytes; - - // copy_to_slice requires the bytes to have enough remaining bytes - // to fill buf. - let n = buf.len().min(bytes.remaining()); - - // ::copy_to_slice copies and consumes the bytes - bytes.copy_to_slice(&mut buf[..n]); - - Ok(n) - } -} - -impl BufRead for StreamReadable { - fn fill_buf(&mut self) -> io::Result<&[u8]> { - let bytes = &mut self.bytes; - - if !bytes.has_remaining() { - if let Some(new_bytes) = self.rx.blocking_recv() { - // new_bytes are guaranteed to be non-empty. - *bytes = new_bytes; - } - } - - Ok(&*bytes) - } - - fn consume(&mut self, amt: usize) { - self.bytes.advance(amt); - } -} diff --git a/crates/binstalk-fetchers/CHANGELOG.md b/crates/binstalk-fetchers/CHANGELOG.md deleted file mode 100644 index 5158130e..00000000 --- a/crates/binstalk-fetchers/CHANGELOG.md +++ /dev/null @@ -1,133 +0,0 @@ -# Changelog -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - -## [Unreleased] - -## [0.10.18](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-fetchers-v0.10.17...binstalk-fetchers-v0.10.18) - 2025-04-05 - -### Other - -- updated the following local packages: binstalk-downloader, binstalk-downloader, binstalk-git-repo-api - -## [0.10.17](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-fetchers-v0.10.16...binstalk-fetchers-v0.10.17) - 2025-03-19 - -### Other - -- updated the following local packages: binstalk-downloader, binstalk-downloader - -## [0.10.16](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-fetchers-v0.10.15...binstalk-fetchers-v0.10.16) - 2025-03-15 - -### Other - -- *(deps)* bump the deps group with 2 updates ([#2084](https://github.com/cargo-bins/cargo-binstall/pull/2084)) -- *(deps)* bump tokio from 1.43.0 to 1.44.0 in the deps group ([#2079](https://github.com/cargo-bins/cargo-binstall/pull/2079)) - -## [0.10.15](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-fetchers-v0.10.14...binstalk-fetchers-v0.10.15) - 2025-03-07 - -### Other - -- *(deps)* bump the deps group with 3 updates ([#2072](https://github.com/cargo-bins/cargo-binstall/pull/2072)) - -## [0.10.14](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-fetchers-v0.10.13...binstalk-fetchers-v0.10.14) - 2025-02-28 - -### Other - -- updated the following local packages: binstalk-downloader, binstalk-downloader - -## [0.10.13](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-fetchers-v0.10.12...binstalk-fetchers-v0.10.13) - 2025-02-11 - -### Other - -- *(deps)* bump the deps group with 2 updates (#2044) - -## [0.10.12](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-fetchers-v0.10.11...binstalk-fetchers-v0.10.12) - 2025-02-04 - -### Other - -- updated the following local packages: binstalk-downloader, binstalk-downloader - -## [0.10.11](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-fetchers-v0.10.10...binstalk-fetchers-v0.10.11) - 2025-01-19 - -### Other - -- update Cargo.lock dependencies - -## [0.10.10](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-fetchers-v0.10.9...binstalk-fetchers-v0.10.10) - 2025-01-13 - -### Other - -- update Cargo.lock dependencies - -## [0.10.9](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-fetchers-v0.10.8...binstalk-fetchers-v0.10.9) - 2025-01-11 - -### Other - -- *(deps)* bump the deps group with 3 updates (#2015) - -## [0.10.8](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-fetchers-v0.10.7...binstalk-fetchers-v0.10.8) - 2025-01-04 - -### Other - -- *(deps)* bump the deps group with 2 updates (#2010) - -## [0.10.7](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-fetchers-v0.10.6...binstalk-fetchers-v0.10.7) - 2024-12-14 - -### Other - -- *(deps)* bump the deps group with 2 updates (#1997) - -## [0.10.6](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-fetchers-v0.10.5...binstalk-fetchers-v0.10.6) - 2024-11-29 - -### Other - -- Upgrade transitive dependencies ([#1985](https://github.com/cargo-bins/cargo-binstall/pull/1985)) - -## [0.10.5](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-fetchers-v0.10.4...binstalk-fetchers-v0.10.5) - 2024-11-23 - -### Other - -- *(deps)* bump the deps group with 2 updates ([#1981](https://github.com/cargo-bins/cargo-binstall/pull/1981)) - -## [0.10.4](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-fetchers-v0.10.3...binstalk-fetchers-v0.10.4) - 2024-11-09 - -### Other - -- *(deps)* bump the deps group with 3 updates ([#1966](https://github.com/cargo-bins/cargo-binstall/pull/1966)) - -## [0.10.3](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-fetchers-v0.10.2...binstalk-fetchers-v0.10.3) - 2024-11-05 - -### Other - -- *(deps)* bump the deps group with 3 updates ([#1954](https://github.com/cargo-bins/cargo-binstall/pull/1954)) - -## [0.10.2](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-fetchers-v0.10.1...binstalk-fetchers-v0.10.2) - 2024-11-02 - -### Other - -- updated the following local packages: binstalk-downloader, binstalk-downloader - -## [0.10.1](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-fetchers-v0.10.0...binstalk-fetchers-v0.10.1) - 2024-10-12 - -### Other - -- updated the following local packages: binstalk-git-repo-api - -## [0.10.0](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-fetchers-v0.9.1...binstalk-fetchers-v0.10.0) - 2024-09-11 - -### Other - -- report to new stats server (with status) ([#1912](https://github.com/cargo-bins/cargo-binstall/pull/1912)) -- Improve quickinstall telemetry failure message ([#1910](https://github.com/cargo-bins/cargo-binstall/pull/1910)) - -## [0.9.1](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-fetchers-v0.9.0...binstalk-fetchers-v0.9.1) - 2024-08-12 - -### Other -- updated the following local packages: binstalk-downloader, binstalk-downloader - -## [0.9.0](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-fetchers-v0.8.0...binstalk-fetchers-v0.9.0) - 2024-08-10 - -### Other -- updated the following local packages: binstalk-types, binstalk-downloader, binstalk-downloader diff --git a/crates/binstalk-fetchers/Cargo.toml b/crates/binstalk-fetchers/Cargo.toml deleted file mode 100644 index 4d525049..00000000 --- a/crates/binstalk-fetchers/Cargo.toml +++ /dev/null @@ -1,44 +0,0 @@ -[package] -name = "binstalk-fetchers" -version = "0.10.18" -edition = "2021" - -description = "The binstall fetchers" -repository = "https://github.com/cargo-bins/cargo-binstall" -documentation = "https://docs.rs/binstalk-fetchers" -rust-version = "1.70.0" -authors = ["Jiahao XU "] -license = "GPL-3.0-only" - -[dependencies] -async-trait = "0.1.88" -binstalk-downloader = { version = "0.13.17", path = "../binstalk-downloader", default-features = false } -binstalk-git-repo-api = { version = "0.5.19", path = "../binstalk-git-repo-api" } -binstalk-types = { version = "0.9.4", path = "../binstalk-types" } -bytes = "1.4.0" -compact_str = { version = "0.9.0" } -either = "1.11.0" -itertools = "0.14.0" -leon = "3.0.0" -leon-macros = "1.0.1" -miette = "7.0.0" -minisign-verify = "0.2.1" -once_cell = "1.18.0" -strum = "0.27.0" -thiserror = "2.0.11" -tokio = { version = "1.44.0", features = [ - "rt", - "sync", -], default-features = false } -tracing = "0.1.39" -url = "2.5.4" - -[dev-dependencies] -binstalk-downloader = { version = "0.13.17", path = "../binstalk-downloader" } - -[features] -quickinstall = [] - -[package.metadata.docs.rs] -rustdoc-args = ["--cfg", "docsrs"] -all-features = true diff --git a/crates/binstalk-fetchers/LICENSE b/crates/binstalk-fetchers/LICENSE deleted file mode 100644 index f288702d..00000000 --- a/crates/binstalk-fetchers/LICENSE +++ /dev/null @@ -1,674 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. diff --git a/crates/binstalk-fetchers/src/common.rs b/crates/binstalk-fetchers/src/common.rs deleted file mode 100644 index ad9e1d68..00000000 --- a/crates/binstalk-fetchers/src/common.rs +++ /dev/null @@ -1,120 +0,0 @@ -#![allow(unused)] - -use std::{ - future::Future, - sync::{ - atomic::{AtomicBool, Ordering::Relaxed}, - Once, - }, -}; - -pub(super) use binstalk_downloader::{ - download::{Download, ExtractedFiles}, - remote::{Client, Url}, -}; -pub(super) use binstalk_git_repo_api::gh_api_client::GhApiClient; -use binstalk_git_repo_api::gh_api_client::{GhApiError, GhReleaseArtifact, GhReleaseArtifactUrl}; -pub(super) use binstalk_types::cargo_toml_binstall::{PkgFmt, PkgMeta}; -pub(super) use compact_str::CompactString; -pub(super) use tokio::task::JoinHandle; -pub(super) use tracing::{debug, instrument, warn}; - -use crate::FetchError; - -static WARN_RATE_LIMIT_ONCE: Once = Once::new(); -static WARN_UNAUTHORIZED_ONCE: Once = Once::new(); - -/// Return Ok(Some(api_artifact_url)) if exists, or Ok(None) if it doesn't. -/// -/// Caches info on all artifacts matching (repo, tag). -pub(super) async fn get_gh_release_artifact_url( - gh_api_client: GhApiClient, - artifact: GhReleaseArtifact, -) -> Result, GhApiError> { - debug!("Using GitHub API to check for existence of artifact, which will also cache the API response"); - - // The future returned has the same size as a pointer - match gh_api_client.has_release_artifact(artifact).await { - Ok(ret) => Ok(ret), - Err(GhApiError::NotFound) => Ok(None), - - Err(GhApiError::RateLimit { retry_after }) => { - WARN_RATE_LIMIT_ONCE.call_once(|| { - warn!("Your GitHub API token (if any) has reached its rate limit and cannot be used again until {retry_after:?}, so we will fallback to HEAD/GET on the url."); - warn!("If you did not supply a github token, consider doing so: GitHub limits unauthorized users to 60 requests per hour per origin IP address."); - }); - Err(GhApiError::RateLimit { retry_after }) - } - Err(GhApiError::Unauthorized) => { - WARN_UNAUTHORIZED_ONCE.call_once(|| { - warn!("GitHub API somehow requires a token for the API access, so we will fallback to HEAD/GET on the url."); - warn!("Please consider supplying a token to cargo-binstall to speedup resolution."); - }); - Err(GhApiError::Unauthorized) - } - - Err(err) => Err(err), - } -} - -/// Check if the URL exists by querying the GitHub API. -/// -/// Caches info on all artifacts matching (repo, tag). -/// -/// This function returns a future where its size should be at most size of -/// 2-4 pointers. -pub(super) async fn does_url_exist( - client: Client, - gh_api_client: GhApiClient, - url: &Url, -) -> Result { - static GH_API_CLIENT_FAILED: AtomicBool = AtomicBool::new(false); - - debug!("Checking for package at: '{url}'"); - - if !GH_API_CLIENT_FAILED.load(Relaxed) { - if let Some(artifact) = GhReleaseArtifact::try_extract_from_url(url) { - match get_gh_release_artifact_url(gh_api_client, artifact).await { - Ok(ret) => return Ok(ret.is_some()), - - Err(GhApiError::RateLimit { .. }) | Err(GhApiError::Unauthorized) => {} - - Err(err) => return Err(err.into()), - } - - GH_API_CLIENT_FAILED.store(true, Relaxed); - } - } - - Ok(Box::pin(client.remote_gettable(url.clone())).await?) -} - -#[derive(Debug)] -pub(super) struct AutoAbortJoinHandle(JoinHandle); - -impl AutoAbortJoinHandle -where - T: Send + 'static, -{ - pub(super) fn spawn(future: F) -> Self - where - F: Future + Send + 'static, - { - Self(tokio::spawn(future)) - } -} - -impl Drop for AutoAbortJoinHandle { - fn drop(&mut self) { - self.0.abort(); - } -} - -impl AutoAbortJoinHandle> -where - E: Into, -{ - pub(super) async fn flattened_join(mut self) -> Result { - (&mut self.0).await?.map_err(Into::into) - } -} diff --git a/crates/binstalk-fetchers/src/futures_resolver.rs b/crates/binstalk-fetchers/src/futures_resolver.rs deleted file mode 100644 index 461ab462..00000000 --- a/crates/binstalk-fetchers/src/futures_resolver.rs +++ /dev/null @@ -1,86 +0,0 @@ -use std::{fmt::Debug, future::Future, pin::Pin}; - -use tokio::sync::mpsc; -use tracing::warn; - -/// Given multiple futures with output = `Result, E>`, -/// returns the the first one that returns either `Err(_)` or -/// `Ok(Some(_))`. -pub struct FuturesResolver { - rx: mpsc::Receiver>, - tx: mpsc::Sender>, -} - -impl Default for FuturesResolver { - fn default() -> Self { - // We only need the first one, so the channel is of size 1. - let (tx, rx) = mpsc::channel(1); - Self { tx, rx } - } -} - -impl FuturesResolver { - /// Insert new future into this resolver, they will start running - /// right away. - pub fn push(&self, fut: Fut) - where - Fut: Future, E>> + Send + 'static, - { - let tx = self.tx.clone(); - - tokio::spawn(async move { - tokio::pin!(fut); - - Self::spawn_inner(fut, tx).await; - }); - } - - async fn spawn_inner( - fut: Pin<&mut (dyn Future, E>> + Send)>, - tx: mpsc::Sender>, - ) { - let res = tokio::select! { - biased; - - _ = tx.closed() => return, - res = fut => res, - }; - - if let Some(res) = res.transpose() { - // try_send can only fail due to being full or being closed. - // - // In both cases, this could means some other future has - // completed first. - // - // For closed, it could additionally means that the task - // is cancelled. - tx.try_send(res).ok(); - } - } - - /// Insert multiple futures into this resolver, they will start running - /// right away. - pub fn extend(&self, iter: Iter) - where - Fut: Future, E>> + Send + 'static, - Iter: IntoIterator, - { - iter.into_iter().for_each(|fut| self.push(fut)); - } - - /// Return the resolution. - pub fn resolve(self) -> impl Future> { - let mut rx = self.rx; - drop(self.tx); - - async move { - loop { - match rx.recv().await { - Some(Ok(ret)) => return Some(ret), - Some(Err(err)) => warn!(?err, "Fail to resolve the future"), - None => return None, - } - } - } - } -} diff --git a/crates/binstalk-fetchers/src/gh_crate_meta.rs b/crates/binstalk-fetchers/src/gh_crate_meta.rs deleted file mode 100644 index d1df0637..00000000 --- a/crates/binstalk-fetchers/src/gh_crate_meta.rs +++ /dev/null @@ -1,660 +0,0 @@ -use std::{borrow::Cow, fmt, iter, path::Path, sync::Arc}; - -use binstalk_git_repo_api::gh_api_client::{GhApiError, GhReleaseArtifact, GhReleaseArtifactUrl}; -use binstalk_types::cargo_toml_binstall::Strategy; -use compact_str::{CompactString, ToCompactString}; -use either::Either; -use leon::Template; -use once_cell::sync::OnceCell; -use strum::IntoEnumIterator; -use tokio::time::sleep; -use tracing::{debug, info, trace, warn}; -use url::Url; - -use crate::{ - common::*, futures_resolver::FuturesResolver, Data, FetchError, InvalidPkgFmtError, RepoInfo, - SignaturePolicy, SignatureVerifier, TargetDataErased, DEFAULT_GH_API_RETRY_DURATION, -}; - -pub const FETCHER_GH_CRATE_META: &str = "GhCrateMeta"; - -pub(crate) mod hosting; - -pub struct GhCrateMeta { - client: Client, - gh_api_client: GhApiClient, - data: Arc, - target_data: Arc, - signature_policy: SignaturePolicy, - resolution: OnceCell, -} - -#[derive(Debug)] -struct Resolved { - url: Url, - pkg_fmt: PkgFmt, - archive_suffix: Option, - repo: Option, - subcrate: Option, - gh_release_artifact_url: Option, - is_repo_private: bool, -} - -impl GhCrateMeta { - fn launch_baseline_find_tasks( - &self, - futures_resolver: &FuturesResolver, - pkg_fmt: PkgFmt, - pkg_url: &Template<'_>, - repo: Option<&str>, - subcrate: Option<&str>, - is_repo_private: bool, - ) { - let render_url = |ext| { - let ctx = Context::from_data_with_repo( - &self.data, - &self.target_data.target, - &self.target_data.target_related_info, - ext, - repo, - subcrate, - ); - match ctx.render_url_with(pkg_url) { - Ok(url) => Some(url), - Err(err) => { - warn!("Failed to render url for {ctx:#?}: {err}"); - None - } - } - }; - - let is_windows = self.target_data.target.contains("windows"); - - let urls = if pkg_url.has_any_of_keys(&["format", "archive-format", "archive-suffix"]) { - // build up list of potential URLs - Either::Left( - pkg_fmt - .extensions(is_windows) - .iter() - .filter_map(|ext| render_url(Some(ext)).map(|url| (url, Some(ext)))), - ) - } else { - Either::Right(render_url(None).map(|url| (url, None)).into_iter()) - }; - - // go check all potential URLs at once - futures_resolver.extend(urls.map(move |(url, ext)| { - let client = self.client.clone(); - let gh_api_client = self.gh_api_client.clone(); - - let repo = repo.map(ToString::to_string); - let subcrate = subcrate.map(ToString::to_string); - let archive_suffix = ext.map(ToString::to_string); - let gh_release_artifact = GhReleaseArtifact::try_extract_from_url(&url); - - async move { - debug!("Checking for package at: '{url}'"); - - let mut resolved = Resolved { - url: url.clone(), - pkg_fmt, - repo, - subcrate, - archive_suffix, - is_repo_private, - gh_release_artifact_url: None, - }; - - if let Some(artifact) = gh_release_artifact { - loop { - match get_gh_release_artifact_url(gh_api_client.clone(), artifact.clone()) - .await - { - Ok(Some(artifact_url)) => { - resolved.gh_release_artifact_url = Some(artifact_url); - return Ok(Some(resolved)); - } - Ok(None) => return Ok(None), - - Err(GhApiError::RateLimit { retry_after }) => { - sleep(retry_after.unwrap_or(DEFAULT_GH_API_RETRY_DURATION)).await; - } - Err(GhApiError::Unauthorized) if !is_repo_private => break, - - Err(err) => return Err(err.into()), - } - } - } - - Ok(Box::pin(client.remote_gettable(url)) - .await? - .then_some(resolved)) - } - })); - } -} - -#[async_trait::async_trait] -impl super::Fetcher for GhCrateMeta { - fn new( - client: Client, - gh_api_client: GhApiClient, - data: Arc, - target_data: Arc, - signature_policy: SignaturePolicy, - ) -> Arc { - Arc::new(Self { - client, - gh_api_client, - data, - target_data, - signature_policy, - resolution: OnceCell::new(), - }) - } - - fn find(self: Arc) -> JoinHandle> { - tokio::spawn(async move { - let info = self.data.get_repo_info(&self.gh_api_client).await?; - - let repo = info.map(|info| &info.repo); - let subcrate = info.and_then(|info| info.subcrate.as_deref()); - let is_repo_private = info.map(|info| info.is_private).unwrap_or_default(); - - let mut pkg_fmt = self.target_data.meta.pkg_fmt; - - let pkg_urls = if let Some(pkg_url) = self.target_data.meta.pkg_url.as_deref() { - let template = Template::parse(pkg_url)?; - - if pkg_fmt.is_none() - && !template.has_any_of_keys(&["format", "archive-format", "archive-suffix"]) - { - // The crate does not specify the pkg-fmt, yet its pkg-url - // template doesn't contains format, archive-format or - // archive-suffix which is required for automatically - // deducing the pkg-fmt. - // - // We will attempt to guess the pkg-fmt there, but this is - // just a best-effort - pkg_fmt = PkgFmt::guess_pkg_format(pkg_url); - - let crate_name = &self.data.name; - let version = &self.data.version; - let target = &self.target_data.target; - - if pkg_fmt.is_none() { - return Err(InvalidPkgFmtError { - crate_name: crate_name.clone(), - version: version.clone(), - target: target.into(), - pkg_url: pkg_url.into(), - reason: - &"pkg-fmt is not specified, yet pkg-url does not contain format, \ - archive-format or archive-suffix which is required for automatically \ - deducing pkg-fmt", - } - .into()); - } - - warn!( - "Crate {crate_name}@{version} on target {target} does not specify pkg-fmt \ - but its pkg-url also does not contain key format, archive-format or \ - archive-suffix.\nbinstall was able to guess that from pkg-url, but \ - just note that it could be wrong:\npkg-fmt=\"{pkg_fmt}\", pkg-url=\"{pkg_url}\"", - pkg_fmt = pkg_fmt.unwrap(), - ); - } - - Either::Left(iter::once(template)) - } else if let Some(RepoInfo { - repo, - repository_host, - .. - }) = info - { - if let Some(pkg_urls) = repository_host.get_default_pkg_url_template() { - let has_subcrate = subcrate.is_some(); - - Either::Right( - pkg_urls - .map(Template::cast) - // If subcrate is Some, then all templates will be included. - // Otherwise, only templates without key "subcrate" will be - // included. - .filter(move |template| has_subcrate || !template.has_key("subcrate")), - ) - } else { - warn!( - concat!( - "Unknown repository {}, cargo-binstall cannot provide default pkg_url for it.\n", - "Please ask the upstream to provide it for target {}." - ), - repo, self.target_data.target - ); - - return Ok(false); - } - } else { - warn!( - concat!( - "Package does not specify repository, cargo-binstall cannot provide default pkg_url for it.\n", - "Please ask the upstream to provide it for target {}." - ), - self.target_data.target - ); - - return Ok(false); - }; - - // Convert Option to Option to reduce size of future. - let repo = repo.map(|u| u.as_str().trim_end_matches('/')); - - // Use reference to self to fix error of closure - // launch_baseline_find_tasks which moves `this` - let this = &self; - - let pkg_fmts = if let Some(pkg_fmt) = pkg_fmt { - Either::Left(iter::once(pkg_fmt)) - } else { - Either::Right(PkgFmt::iter()) - }; - - let resolver = FuturesResolver::default(); - - // Iterate over pkg_urls first to avoid String::clone. - for pkg_url in pkg_urls { - // Clone iter pkg_fmts to ensure all pkg_fmts is - // iterated over for each pkg_url, which is - // basically cartesian product. - // | - for pkg_fmt in pkg_fmts.clone() { - this.launch_baseline_find_tasks( - &resolver, - pkg_fmt, - &pkg_url, - repo, - subcrate, - is_repo_private, - ); - } - } - - if let Some(resolved) = resolver.resolve().await { - debug!(?resolved, "Winning URL found!"); - self.resolution - .set(resolved) - .expect("find() should be only called once"); - Ok(true) - } else { - Ok(false) - } - }) - } - - async fn fetch_and_extract(&self, dst: &Path) -> Result { - let resolved = self - .resolution - .get() - .expect("find() should be called once before fetch_and_extract()"); - trace!(?resolved, "preparing to fetch"); - - let verifier = match (self.signature_policy, &self.target_data.meta.signing) { - (SignaturePolicy::Ignore, _) | (SignaturePolicy::IfPresent, None) => { - SignatureVerifier::Noop - } - (SignaturePolicy::Require, None) => { - return Err(FetchError::MissingSignature); - } - (_, Some(config)) => { - let template = match config.file.as_deref() { - Some(file) => Template::parse(file)?, - None => leon_macros::template!("{ url }.sig"), - }; - trace!(?template, "parsed signature file template"); - - let sign_url = Context::from_data_with_repo( - &self.data, - &self.target_data.target, - &self.target_data.target_related_info, - resolved.archive_suffix.as_deref(), - resolved.repo.as_deref(), - resolved.subcrate.as_deref(), - ) - .with_url(&resolved.url) - .render_url_with(&template)?; - - debug!(?sign_url, "Downloading signature"); - let signature = Download::new(self.client.clone(), sign_url) - .into_bytes() - .await?; - trace!(?signature, "got signature contents"); - - SignatureVerifier::new(config, &signature)? - } - }; - - debug!( - url=%resolved.url, - dst=%dst.display(), - fmt=?resolved.pkg_fmt, - "Downloading package", - ); - let mut data_verifier = verifier.data_verifier()?; - let files = match resolved.gh_release_artifact_url.as_ref() { - Some(artifact_url) if resolved.is_repo_private => self - .gh_api_client - .download_artifact(artifact_url.clone()) - .await? - .with_data_verifier(data_verifier.as_mut()), - _ => Download::new_with_data_verifier( - self.client.clone(), - resolved.url.clone(), - data_verifier.as_mut(), - ), - } - .and_extract(resolved.pkg_fmt, dst) - .await?; - trace!("validating signature (if any)"); - if data_verifier.validate() { - if let Some(info) = verifier.info() { - info!( - "Verified signature for package '{}': {info}", - self.data.name - ); - } - Ok(files) - } else { - Err(FetchError::InvalidSignature) - } - } - - fn pkg_fmt(&self) -> PkgFmt { - self.resolution.get().unwrap().pkg_fmt - } - - fn target_meta(&self) -> PkgMeta { - let mut meta = self.target_data.meta.clone(); - meta.pkg_fmt = Some(self.pkg_fmt()); - meta - } - - fn source_name(&self) -> CompactString { - self.resolution - .get() - .map(|resolved| { - if let Some(domain) = resolved.url.domain() { - domain.to_compact_string() - } else if let Some(host) = resolved.url.host_str() { - host.to_compact_string() - } else { - resolved.url.to_compact_string() - } - }) - .unwrap_or_else(|| "invalid url".into()) - } - - fn fetcher_name(&self) -> &'static str { - FETCHER_GH_CRATE_META - } - - fn strategy(&self) -> Strategy { - Strategy::CrateMetaData - } - - fn is_third_party(&self) -> bool { - false - } - - fn target(&self) -> &str { - &self.target_data.target - } - - fn target_data(&self) -> &Arc { - &self.target_data - } -} - -/// Template for constructing download paths -#[derive(Clone)] -struct Context<'c> { - name: &'c str, - repo: Option<&'c str>, - target: &'c str, - version: &'c str, - - /// Archive format e.g. tar.gz, zip - archive_format: Option<&'c str>, - - archive_suffix: Option<&'c str>, - - /// Filename extension on the binary, i.e. .exe on Windows, nothing otherwise - binary_ext: &'c str, - - /// Workspace of the crate inside the repository. - subcrate: Option<&'c str>, - - /// Url of the file being downloaded (only for signing.file) - url: Option<&'c Url>, - - target_related_info: &'c dyn leon::Values, -} - -impl fmt::Debug for Context<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Context") - .field("name", &self.name) - .field("repo", &self.repo) - .field("target", &self.target) - .field("version", &self.version) - .field("archive_format", &self.archive_format) - .field("binary_ext", &self.binary_ext) - .field("subcrate", &self.subcrate) - .field("url", &self.url) - .finish_non_exhaustive() - } -} - -impl leon::Values for Context<'_> { - fn get_value<'s>(&'s self, key: &str) -> Option> { - match key { - "name" => Some(Cow::Borrowed(self.name)), - "repo" => self.repo.map(Cow::Borrowed), - "target" => Some(Cow::Borrowed(self.target)), - "version" => Some(Cow::Borrowed(self.version)), - - "archive-format" => self.archive_format.map(Cow::Borrowed), - - // Soft-deprecated alias for archive-format - "format" => self.archive_format.map(Cow::Borrowed), - - "archive-suffix" => self.archive_suffix.map(Cow::Borrowed), - - "binary-ext" => Some(Cow::Borrowed(self.binary_ext)), - - "subcrate" => self.subcrate.map(Cow::Borrowed), - - "url" => self.url.map(|url| Cow::Borrowed(url.as_str())), - - key => self.target_related_info.get_value(key), - } - } -} - -impl<'c> Context<'c> { - fn from_data_with_repo( - data: &'c Data, - target: &'c str, - target_related_info: &'c dyn leon::Values, - archive_suffix: Option<&'c str>, - repo: Option<&'c str>, - subcrate: Option<&'c str>, - ) -> Self { - let archive_format = archive_suffix.map(|archive_suffix| { - if archive_suffix.is_empty() { - // Empty archive_suffix means PkgFmt::Bin - "bin" - } else { - debug_assert!(archive_suffix.starts_with('.'), "{archive_suffix}"); - - &archive_suffix[1..] - } - }); - - Self { - name: &data.name, - repo, - target, - - version: &data.version, - archive_format, - archive_suffix, - binary_ext: if target.contains("windows") { - ".exe" - } else { - "" - }, - subcrate, - url: None, - - target_related_info, - } - } - - fn with_url(&mut self, url: &'c Url) -> &mut Self { - self.url = Some(url); - self - } - - fn render_url_with(&self, template: &Template<'_>) -> Result { - debug!(?template, context=?self, "render url template"); - Ok(Url::parse(&template.render(self)?)?) - } - - #[cfg(test)] - fn render_url(&self, template: &str) -> Result { - self.render_url_with(&Template::parse(template)?) - } -} - -#[cfg(test)] -mod test { - use super::{super::Data, Context}; - use compact_str::ToCompactString; - use url::Url; - - const DEFAULT_PKG_URL: &str = "{ repo }/releases/download/v{ version }/{ name }-{ target }-v{ version }.{ archive-format }"; - - fn assert_context_rendering( - data: &Data, - target: &str, - archive_format: &str, - template: &str, - expected_url: &str, - ) { - // The template provided doesn't need this, so just returning None - // is OK. - let target_info = leon::vals(|_| None); - - let ctx = Context::from_data_with_repo( - data, - target, - &target_info, - Some(archive_format), - data.repo.as_deref(), - None, - ); - - let expected_url = Url::parse(expected_url).unwrap(); - assert_eq!(ctx.render_url(template).unwrap(), expected_url); - } - - #[test] - fn defaults() { - assert_context_rendering( - &Data::new( - "cargo-binstall".to_compact_string(), - "1.2.3".to_compact_string(), - Some("https://github.com/ryankurte/cargo-binstall".to_string()), - ), - "x86_64-unknown-linux-gnu", - ".tgz", - DEFAULT_PKG_URL, - "https://github.com/ryankurte/cargo-binstall/releases/download/v1.2.3/cargo-binstall-x86_64-unknown-linux-gnu-v1.2.3.tgz" - ); - } - - #[test] - fn no_repo_but_full_url() { - assert_context_rendering( - &Data::new( - "cargo-binstall".to_compact_string(), - "1.2.3".to_compact_string(), - None, - ), - "x86_64-unknown-linux-gnu", - ".tgz", - &format!("https://example.com{}", &DEFAULT_PKG_URL[8..]), - "https://example.com/releases/download/v1.2.3/cargo-binstall-x86_64-unknown-linux-gnu-v1.2.3.tgz" - ); - } - - #[test] - fn different_url() { - assert_context_rendering( - &Data::new( - "radio-sx128x".to_compact_string(), - "0.14.1-alpha.5".to_compact_string(), - Some("https://github.com/rust-iot/rust-radio-sx128x".to_string()), - ), - "x86_64-unknown-linux-gnu", - ".tgz", - "{ repo }/releases/download/v{ version }/sx128x-util-{ target }-v{ version }.{ archive-format }", - "https://github.com/rust-iot/rust-radio-sx128x/releases/download/v0.14.1-alpha.5/sx128x-util-x86_64-unknown-linux-gnu-v0.14.1-alpha.5.tgz" - ); - } - - #[test] - fn deprecated_format() { - assert_context_rendering( - &Data::new( - "radio-sx128x".to_compact_string(), - "0.14.1-alpha.5".to_compact_string(), - Some("https://github.com/rust-iot/rust-radio-sx128x".to_string()), - ), - "x86_64-unknown-linux-gnu", - ".tgz", - "{ repo }/releases/download/v{ version }/sx128x-util-{ target }-v{ version }.{ format }", - "https://github.com/rust-iot/rust-radio-sx128x/releases/download/v0.14.1-alpha.5/sx128x-util-x86_64-unknown-linux-gnu-v0.14.1-alpha.5.tgz" - ); - } - - #[test] - fn different_ext() { - assert_context_rendering( - &Data::new( - "cargo-watch".to_compact_string(), - "9.0.0".to_compact_string(), - Some("https://github.com/watchexec/cargo-watch".to_string()), - ), - "aarch64-apple-darwin", - ".txz", - "{ repo }/releases/download/v{ version }/{ name }-v{ version }-{ target }.tar.xz", - "https://github.com/watchexec/cargo-watch/releases/download/v9.0.0/cargo-watch-v9.0.0-aarch64-apple-darwin.tar.xz" - ); - } - - #[test] - fn no_archive() { - assert_context_rendering( - &Data::new( - "cargo-watch".to_compact_string(), - "9.0.0".to_compact_string(), - Some("https://github.com/watchexec/cargo-watch".to_string()), - ), - "aarch64-pc-windows-msvc", - ".bin", - "{ repo }/releases/download/v{ version }/{ name }-v{ version }-{ target }{ binary-ext }", - "https://github.com/watchexec/cargo-watch/releases/download/v9.0.0/cargo-watch-v9.0.0-aarch64-pc-windows-msvc.exe" - ); - } -} diff --git a/crates/binstalk-fetchers/src/gh_crate_meta/hosting.rs b/crates/binstalk-fetchers/src/gh_crate_meta/hosting.rs deleted file mode 100644 index db8652a9..00000000 --- a/crates/binstalk-fetchers/src/gh_crate_meta/hosting.rs +++ /dev/null @@ -1,117 +0,0 @@ -use itertools::Itertools; -use leon::{Item, Template}; -use leon_macros::template; -use url::Url; - -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub enum RepositoryHost { - GitHub, - GitLab, - BitBucket, - SourceForge, - Unknown, -} - -/// Make sure to update possible_dirs in `bins::infer_bin_dir_template` -/// if you modified FULL_FILENAMES or NOVERSION_FILENAMES. -pub const FULL_FILENAMES: &[Template<'_>] = &[ - template!("/{ name }-{ target }-v{ version }{ archive-suffix }"), - template!("/{ name }-{ target }-{ version }{ archive-suffix }"), - template!("/{ name }-{ version }-{ target }{ archive-suffix }"), - template!("/{ name }-v{ version }-{ target }{ archive-suffix }"), - template!("/{ name }_{ target }_v{ version }{ archive-suffix }"), - template!("/{ name }_{ target }_{ version }{ archive-suffix }"), - template!("/{ name }_{ version }_{ target }{ archive-suffix }"), - template!("/{ name }_v{ version }_{ target }{ archive-suffix }"), -]; - -pub const NOVERSION_FILENAMES: &[Template<'_>] = &[ - template!("/{ name }-{ target }{ archive-suffix }"), - template!("/{ name }_{ target }{ archive-suffix }"), -]; - -const GITHUB_RELEASE_PATHS: &[Template<'_>] = &[ - template!("{ repo }/releases/download/{ version }"), - template!("{ repo }/releases/download/v{ version }"), - // %2F is escaped form of '/' - template!("{ repo }/releases/download/{ subcrate }%2F{ version }"), - template!("{ repo }/releases/download/{ subcrate }%2Fv{ version }"), -]; - -const GITLAB_RELEASE_PATHS: &[Template<'_>] = &[ - template!("{ repo }/-/releases/{ version }/downloads/binaries"), - template!("{ repo }/-/releases/v{ version }/downloads/binaries"), - // %2F is escaped form of '/' - template!("{ repo }/-/releases/{ subcrate }%2F{ version }/downloads/binaries"), - template!("{ repo }/-/releases/{ subcrate }%2Fv{ version }/downloads/binaries"), -]; - -const BITBUCKET_RELEASE_PATHS: &[Template<'_>] = &[template!("{ repo }/downloads")]; - -const SOURCEFORGE_RELEASE_PATHS: &[Template<'_>] = &[ - template!("{ repo }/files/binaries/{ version }"), - template!("{ repo }/files/binaries/v{ version }"), - // %2F is escaped form of '/' - template!("{ repo }/files/binaries/{ subcrate }%2F{ version }"), - template!("{ repo }/files/binaries/{ subcrate }%2Fv{ version }"), -]; - -impl RepositoryHost { - pub fn guess_git_hosting_services(repo: &Url) -> Self { - use RepositoryHost::*; - - match repo.domain() { - Some(domain) if domain.starts_with("github") => GitHub, - Some(domain) if domain.starts_with("gitlab") => GitLab, - Some("bitbucket.org") => BitBucket, - Some("sourceforge.net") => SourceForge, - _ => Unknown, - } - } - - pub fn get_default_pkg_url_template( - self, - ) -> Option> + Clone + 'static> { - use RepositoryHost::*; - - match self { - GitHub => Some(apply_filenames_to_paths( - GITHUB_RELEASE_PATHS, - &[FULL_FILENAMES, NOVERSION_FILENAMES], - "", - )), - GitLab => Some(apply_filenames_to_paths( - GITLAB_RELEASE_PATHS, - &[FULL_FILENAMES, NOVERSION_FILENAMES], - "", - )), - BitBucket => Some(apply_filenames_to_paths( - BITBUCKET_RELEASE_PATHS, - &[FULL_FILENAMES], - "", - )), - SourceForge => Some(apply_filenames_to_paths( - SOURCEFORGE_RELEASE_PATHS, - &[FULL_FILENAMES, NOVERSION_FILENAMES], - "/download", - )), - Unknown => None, - } - } -} - -fn apply_filenames_to_paths( - paths: &'static [Template<'static>], - filenames: &'static [&'static [Template<'static>]], - suffix: &'static str, -) -> impl Iterator> + Clone + 'static { - filenames - .iter() - .flat_map(|fs| fs.iter()) - .cartesian_product(paths.iter()) - .map(move |(filename, path)| { - let mut template = path.clone() + filename; - template += Item::Text(suffix); - template - }) -} diff --git a/crates/binstalk-fetchers/src/lib.rs b/crates/binstalk-fetchers/src/lib.rs deleted file mode 100644 index 49a86894..00000000 --- a/crates/binstalk-fetchers/src/lib.rs +++ /dev/null @@ -1,457 +0,0 @@ -#![cfg_attr(docsrs, feature(doc_auto_cfg))] - -use std::{path::Path, sync::Arc, time::Duration}; - -use binstalk_downloader::{download::DownloadError, remote::Error as RemoteError}; -use binstalk_git_repo_api::gh_api_client::{GhApiError, GhRepo, RepoInfo as GhRepoInfo}; -use binstalk_types::cargo_toml_binstall::{SigningAlgorithm, Strategy}; -use thiserror::Error as ThisError; -use tokio::{sync::OnceCell, task::JoinError, time::sleep}; -pub use url::ParseError as UrlParseError; - -mod gh_crate_meta; -pub use gh_crate_meta::*; - -#[cfg(feature = "quickinstall")] -mod quickinstall; -#[cfg(feature = "quickinstall")] -pub use quickinstall::*; - -mod common; -use common::*; - -mod signing; -use signing::*; - -mod futures_resolver; - -use gh_crate_meta::hosting::RepositoryHost; - -static DEFAULT_GH_API_RETRY_DURATION: Duration = Duration::from_secs(1); - -#[derive(Debug, ThisError)] -#[error("Invalid pkg-url {pkg_url} for {crate_name}@{version} on {target}: {reason}")] -pub struct InvalidPkgFmtError { - pub crate_name: CompactString, - pub version: CompactString, - pub target: CompactString, - pub pkg_url: Box, - pub reason: &'static &'static str, -} - -#[derive(Debug, ThisError, miette::Diagnostic)] -#[non_exhaustive] -pub enum FetchError { - #[error(transparent)] - Download(#[from] DownloadError), - - #[error("Failed to parse template: {0}")] - #[diagnostic(transparent)] - TemplateParse(#[from] leon::ParseError), - - #[error("Failed to render template: {0}")] - #[diagnostic(transparent)] - TemplateRender(#[from] leon::RenderError), - - #[error("Failed to render template: {0}")] - GhApi(#[from] GhApiError), - - #[error(transparent)] - InvalidPkgFmt(Box), - - #[error("Failed to parse url: {0}")] - UrlParse(#[from] UrlParseError), - - #[error("Signing algorithm not supported: {0:?}")] - UnsupportedSigningAlgorithm(SigningAlgorithm), - - #[error("No signature present")] - MissingSignature, - - #[error("Failed to verify signature")] - InvalidSignature, - - #[error("Failed to wait for task: {0}")] - TaskJoinError(#[from] JoinError), -} - -impl From for FetchError { - fn from(e: RemoteError) -> Self { - DownloadError::from(e).into() - } -} - -impl From for FetchError { - fn from(e: InvalidPkgFmtError) -> Self { - Self::InvalidPkgFmt(Box::new(e)) - } -} - -#[async_trait::async_trait] -pub trait Fetcher: Send + Sync { - /// Create a new fetcher from some data - #[allow(clippy::new_ret_no_self)] - fn new( - client: Client, - gh_api_client: GhApiClient, - data: Arc, - target_data: Arc, - signature_policy: SignaturePolicy, - ) -> Arc - where - Self: Sized; - - /// Fetch a package and extract - async fn fetch_and_extract(&self, dst: &Path) -> Result; - - /// Find the package, if it is available for download - /// - /// This may look for multiple remote targets, but must write (using some form of interior - /// mutability) the best one to the implementing struct in some way so `fetch_and_extract` can - /// proceed without additional work. - /// - /// Must return `true` if a package is available, `false` if none is, and reserve errors to - /// fatal conditions only. - fn find(self: Arc) -> JoinHandle>; - - /// Report to upstream that cargo-binstall tries to use this fetcher. - /// Currently it is only overriden by [`quickinstall::QuickInstall`]. - fn report_to_upstream(self: Arc) {} - - /// Return the package format - fn pkg_fmt(&self) -> PkgFmt; - - /// Return finalized target meta. - fn target_meta(&self) -> PkgMeta; - - /// A short human-readable name or descriptor for the package source - fn source_name(&self) -> CompactString; - - /// A short human-readable name, must contains only characters - /// and numbers and it also must be unique. - /// - /// It is used to create a temporary dir where it is used for - /// [`Fetcher::fetch_and_extract`]. - fn fetcher_name(&self) -> &'static str; - - /// The strategy used by this fetcher - fn strategy(&self) -> Strategy; - - /// Should return true if the remote is from a third-party source - fn is_third_party(&self) -> bool; - - /// Return the target for this fetcher - fn target(&self) -> &str; - - fn target_data(&self) -> &Arc; -} - -#[derive(Clone, Debug)] -struct RepoInfo { - repo: Url, - repository_host: RepositoryHost, - subcrate: Option, - is_private: bool, -} - -/// What to do about package signatures -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub enum SignaturePolicy { - /// Don't process any signing information at all - Ignore, - - /// Verify and fail if a signature is found, but pass a signature-less package - IfPresent, - - /// Require signatures to be present (and valid) - Require, -} - -/// Data required to fetch a package -#[derive(Clone, Debug)] -pub struct Data { - name: CompactString, - version: CompactString, - repo: Option, - repo_info: OnceCell>, -} - -impl Data { - pub fn new(name: CompactString, version: CompactString, repo: Option) -> Self { - Self { - name, - version, - repo, - repo_info: OnceCell::new(), - } - } - - #[instrument(skip(client))] - async fn get_repo_info(&self, client: &GhApiClient) -> Result, FetchError> { - async fn gh_get_repo_info( - client: &GhApiClient, - gh_repo: &GhRepo, - ) -> Result { - loop { - match client.get_repo_info(gh_repo).await { - Ok(Some(gh_repo_info)) => break Ok(gh_repo_info), - Ok(None) => break Err(GhApiError::NotFound), - Err(GhApiError::RateLimit { retry_after }) => { - sleep(retry_after.unwrap_or(DEFAULT_GH_API_RETRY_DURATION)).await - } - Err(err) => break Err(err), - } - } - } - - async fn get_repo_info_inner( - repo: &str, - client: &GhApiClient, - ) -> Result { - let repo = Url::parse(repo)?; - let mut repo = client - .remote_client() - .get_redirected_final_url(repo.clone()) - .await - .unwrap_or(repo); - let repository_host = RepositoryHost::guess_git_hosting_services(&repo); - - let subcrate = RepoInfo::detect_subcrate(&mut repo, repository_host); - - if let Some(repo) = repo - .as_str() - .strip_suffix(".git") - .and_then(|s| Url::parse(s).ok()) - { - let repository_host = RepositoryHost::guess_git_hosting_services(&repo); - match GhRepo::try_extract_from_url(&repo) { - Some(gh_repo) if client.has_gh_token() => { - if let Ok(gh_repo_info) = gh_get_repo_info(client, &gh_repo).await { - return Ok(RepoInfo { - subcrate, - repository_host, - repo, - is_private: gh_repo_info.is_private(), - }); - } - } - _ => { - if let Ok(repo) = - client.remote_client().get_redirected_final_url(repo).await - { - return Ok(RepoInfo { - subcrate, - repository_host: RepositoryHost::guess_git_hosting_services(&repo), - repo, - is_private: false, - }); - } - } - } - } - - Ok(RepoInfo { - is_private: match GhRepo::try_extract_from_url(&repo) { - Some(gh_repo) if client.has_gh_token() => { - gh_get_repo_info(client, &gh_repo).await?.is_private() - } - _ => false, - }, - subcrate, - repo, - repository_host, - }) - } - - self.repo_info - .get_or_try_init(move || { - Box::pin(async move { - let Some(repo) = self.repo.as_deref() else { - return Ok(None); - }; - - let repo_info = get_repo_info_inner(repo, client).await?; - - debug!("Resolved repo_info = {repo_info:#?}"); - - Ok(Some(repo_info)) - }) - }) - .await - .map(Option::as_ref) - } -} - -impl RepoInfo { - /// If `repo` contains a subcrate, then extracts and returns it. - /// It will also remove that subcrate path from `repo` to match - /// `scheme:/{repo_owner}/{repo_name}` - fn detect_subcrate(repo: &mut Url, repository_host: RepositoryHost) -> Option { - match repository_host { - RepositoryHost::GitHub => Self::detect_subcrate_common(repo, &["tree"]), - RepositoryHost::GitLab => Self::detect_subcrate_common(repo, &["-", "blob"]), - _ => None, - } - } - - fn detect_subcrate_common(repo: &mut Url, seps: &[&str]) -> Option { - let mut path_segments = repo.path_segments()?; - - let _repo_owner = path_segments.next()?; - let _repo_name = path_segments.next()?; - - // Skip separators - for sep in seps.iter().copied() { - if path_segments.next()? != sep { - return None; - } - } - - // Skip branch name - let _branch_name = path_segments.next()?; - - let (subcrate, is_crate_present) = match path_segments.next()? { - // subcrate url is of path /crates/$subcrate_name, e.g. wasm-bindgen-cli - "crates" => (path_segments.next()?, true), - // subcrate url is of path $subcrate_name, e.g. cargo-audit - subcrate => (subcrate, false), - }; - - if path_segments.next().is_some() { - // A subcrate url should not contain anything more. - None - } else { - let subcrate = subcrate.into(); - - // Pop subcrate path to match regular repo style: - // - // scheme:/{addr}/{repo_owner}/{repo_name} - // - // path_segments() succeeds, so path_segments_mut() - // must also succeeds. - let mut paths = repo.path_segments_mut().unwrap(); - - paths.pop(); // pop subcrate - if is_crate_present { - paths.pop(); // pop crate - } - paths.pop(); // pop branch name - seps.iter().for_each(|_| { - paths.pop(); - }); // pop separators - - Some(subcrate) - } - } -} - -/// Target specific data required to fetch a package -#[derive(Clone, Debug)] -pub struct TargetData { - pub target: String, - pub meta: PkgMeta, - /// More target related info, it's recommend to provide the following keys: - /// - target_family, - /// - target_arch - /// - target_libc - /// - target_vendor - pub target_related_info: T, -} - -pub type TargetDataErased = TargetData; - -#[cfg(test)] -mod test { - use std::num::{NonZeroU16, NonZeroU64}; - - use super::*; - - #[test] - fn test_detect_subcrate_github() { - // cargo-audit - let urls = [ - "https://github.com/RustSec/rustsec/tree/main/cargo-audit", - "https://github.com/RustSec/rustsec/tree/master/cargo-audit", - ]; - for url in urls { - let mut repo = Url::parse(url).unwrap(); - - let repository_host = RepositoryHost::guess_git_hosting_services(&repo); - assert_eq!(repository_host, RepositoryHost::GitHub); - - let subcrate_prefix = RepoInfo::detect_subcrate(&mut repo, repository_host).unwrap(); - assert_eq!(subcrate_prefix, "cargo-audit"); - - assert_eq!( - repo, - Url::parse("https://github.com/RustSec/rustsec").unwrap() - ); - } - - // wasm-bindgen-cli - let urls = [ - "https://github.com/rustwasm/wasm-bindgen/tree/main/crates/cli", - "https://github.com/rustwasm/wasm-bindgen/tree/master/crates/cli", - ]; - for url in urls { - let mut repo = Url::parse(url).unwrap(); - - let repository_host = RepositoryHost::guess_git_hosting_services(&repo); - assert_eq!(repository_host, RepositoryHost::GitHub); - - let subcrate_prefix = RepoInfo::detect_subcrate(&mut repo, repository_host).unwrap(); - assert_eq!(subcrate_prefix, "cli"); - - assert_eq!( - repo, - Url::parse("https://github.com/rustwasm/wasm-bindgen").unwrap() - ); - } - } - - #[test] - fn test_detect_subcrate_gitlab() { - let urls = [ - "https://gitlab.kitware.com/NobodyXu/hello/-/blob/main/cargo-binstall", - "https://gitlab.kitware.com/NobodyXu/hello/-/blob/master/cargo-binstall", - ]; - for url in urls { - let mut repo = Url::parse(url).unwrap(); - - let repository_host = RepositoryHost::guess_git_hosting_services(&repo); - assert_eq!(repository_host, RepositoryHost::GitLab); - - let subcrate_prefix = RepoInfo::detect_subcrate(&mut repo, repository_host).unwrap(); - assert_eq!(subcrate_prefix, "cargo-binstall"); - - assert_eq!( - repo, - Url::parse("https://gitlab.kitware.com/NobodyXu/hello").unwrap() - ); - } - } - - #[tokio::test] - async fn test_ignore_dot_git_for_github_repos() { - let url_without_git = "https://github.com/cargo-bins/cargo-binstall"; - let url_with_git = format!("{}.git", url_without_git); - - let data = Data::new("cargo-binstall".into(), "v1.2.3".into(), Some(url_with_git)); - - let gh_client = GhApiClient::new( - Client::new( - "user-agent", - None, - NonZeroU16::new(1000).unwrap(), - NonZeroU64::new(1000).unwrap(), - [], - ) - .unwrap(), - None, - ); - - let repo_info = data.get_repo_info(&gh_client).await.unwrap().unwrap(); - - assert_eq!(url_without_git, repo_info.repo.as_str()); - } -} diff --git a/crates/binstalk-fetchers/src/quickinstall.rs b/crates/binstalk-fetchers/src/quickinstall.rs deleted file mode 100644 index ebf6976a..00000000 --- a/crates/binstalk-fetchers/src/quickinstall.rs +++ /dev/null @@ -1,383 +0,0 @@ -use std::{ - borrow::Cow, - path::Path, - sync::{Arc, Mutex, OnceLock}, -}; - -use binstalk_downloader::remote::Method; -use binstalk_types::cargo_toml_binstall::{PkgFmt, PkgMeta, PkgSigning, Strategy}; -use tokio::sync::OnceCell; -use tracing::{error, info, trace}; -use url::Url; - -use crate::{ - common::*, Data, FetchError, SignaturePolicy, SignatureVerifier, SigningAlgorithm, - TargetDataErased, -}; - -const BASE_URL: &str = "https://github.com/cargo-bins/cargo-quickinstall/releases/download"; -pub const QUICKINSTALL_STATS_URL: &str = - "https://cargo-quickinstall-stats-server.fly.dev/record-install"; - -const QUICKINSTALL_SIGN_KEY: Cow<'static, str> = - Cow::Borrowed("RWTdnnab2pAka9OdwgCMYyOE66M/BlQoFWaJ/JjwcPV+f3n24IRTj97t"); -const QUICKINSTALL_SUPPORTED_TARGETS_URL: &str = - "https://raw.githubusercontent.com/cargo-bins/cargo-quickinstall/main/supported-targets"; - -fn is_universal_macos(target: &str) -> bool { - ["universal-apple-darwin", "universal2-apple-darwin"].contains(&target) -} - -async fn get_quickinstall_supported_targets( - client: &Client, -) -> Result<&'static [CompactString], FetchError> { - static SUPPORTED_TARGETS: OnceCell> = OnceCell::const_new(); - - SUPPORTED_TARGETS - .get_or_try_init(|| async { - let bytes = client - .get(Url::parse(QUICKINSTALL_SUPPORTED_TARGETS_URL)?) - .send(true) - .await? - .bytes() - .await?; - - let mut v: Vec = String::from_utf8_lossy(&bytes) - .split_whitespace() - .map(CompactString::new) - .collect(); - v.sort_unstable(); - v.dedup(); - Ok(v.into()) - }) - .await - .map(Box::as_ref) -} - -pub struct QuickInstall { - client: Client, - gh_api_client: GhApiClient, - is_supported_v: OnceCell, - - data: Arc, - package: String, - package_url: Url, - signature_url: Url, - signature_policy: SignaturePolicy, - - target_data: Arc, - - signature_verifier: OnceLock, - status: Mutex, -} - -#[derive(Debug, Clone, Copy)] -enum Status { - Start, - NotFound, - Found, - AttemptingInstall, - InvalidSignature, - InstalledFromTarball, -} - -impl Status { - fn as_str(&self) -> &'static str { - match self { - Status::Start => "start", - Status::NotFound => "not-found", - Status::Found => "found", - Status::AttemptingInstall => "attempting-install", - Status::InvalidSignature => "invalid-signature", - Status::InstalledFromTarball => "installed-from-tarball", - } - } -} - -impl QuickInstall { - async fn is_supported(&self) -> Result { - self.is_supported_v - .get_or_try_init(|| async { - Ok(get_quickinstall_supported_targets(&self.client) - .await? - .binary_search(&CompactString::new(&self.target_data.target)) - .is_ok()) - }) - .await - .copied() - } - - fn download_signature( - self: Arc, - ) -> AutoAbortJoinHandle> { - AutoAbortJoinHandle::spawn(async move { - if self.signature_policy == SignaturePolicy::Ignore { - Ok(SignatureVerifier::Noop) - } else { - debug!(url=%self.signature_url, "Downloading signature"); - match Download::new(self.client.clone(), self.signature_url.clone()) - .into_bytes() - .await - { - Ok(signature) => { - trace!(?signature, "got signature contents"); - let config = PkgSigning { - algorithm: SigningAlgorithm::Minisign, - pubkey: QUICKINSTALL_SIGN_KEY, - file: None, - }; - SignatureVerifier::new(&config, &signature) - } - Err(err) => { - if self.signature_policy == SignaturePolicy::Require { - error!("Failed to download signature: {err}"); - Err(FetchError::MissingSignature) - } else { - debug!("Failed to download signature, skipping verification: {err}"); - Ok(SignatureVerifier::Noop) - } - } - } - } - }) - } - - fn get_status(&self) -> Status { - *self.status.lock().unwrap() - } - - fn set_status(&self, status: Status) { - *self.status.lock().unwrap() = status; - } -} - -#[async_trait::async_trait] -impl super::Fetcher for QuickInstall { - fn new( - client: Client, - gh_api_client: GhApiClient, - data: Arc, - target_data: Arc, - signature_policy: SignaturePolicy, - ) -> Arc { - let crate_name = &data.name; - let version = &data.version; - let target = &target_data.target; - - let package = format!("{crate_name}-{version}-{target}"); - - let url = format!("{BASE_URL}/{crate_name}-{version}/{package}.tar.gz"); - - Arc::new(Self { - client, - data, - gh_api_client, - is_supported_v: OnceCell::new(), - - package_url: Url::parse(&url) - .expect("package_url is pre-generated and should never be invalid url"), - signature_url: Url::parse(&format!("{url}.sig")) - .expect("signature_url is pre-generated and should never be invalid url"), - package, - signature_policy, - - target_data, - - signature_verifier: OnceLock::new(), - status: Mutex::new(Status::Start), - }) - } - - fn find(self: Arc) -> JoinHandle> { - tokio::spawn(async move { - if !self.is_supported().await? { - return Ok(false); - } - - let download_signature_task = self.clone().download_signature(); - - let is_found = does_url_exist( - self.client.clone(), - self.gh_api_client.clone(), - &self.package_url, - ) - .await?; - - if !is_found { - self.set_status(Status::NotFound); - return Ok(false); - } - - if self - .signature_verifier - .set(download_signature_task.flattened_join().await?) - .is_err() - { - panic!("::find is run twice"); - } - - self.set_status(Status::Found); - Ok(true) - }) - } - - fn report_to_upstream(self: Arc) { - if cfg!(debug_assertions) { - debug!("Not sending quickinstall report in debug mode"); - } else if is_universal_macos(&self.target_data.target) { - debug!( - r#"Not sending quickinstall report for universal-apple-darwin -and universal2-apple-darwin. -Quickinstall does not support these targets, it only supports targets supported -by rust officially."#, - ); - } else if self.is_supported_v.get().copied() != Some(false) { - tokio::spawn(async move { - if let Err(err) = self.report().await { - warn!( - "Failed to send quickinstall report for package {} (NOTE that this does not affect package resolution): {err}", - self.package - ) - } - }); - } - } - - async fn fetch_and_extract(&self, dst: &Path) -> Result { - self.set_status(Status::AttemptingInstall); - let Some(verifier) = self.signature_verifier.get() else { - panic!("::find has not been called yet!") - }; - - debug!(url=%self.package_url, "Downloading package"); - let mut data_verifier = verifier.data_verifier()?; - let files = Download::new_with_data_verifier( - self.client.clone(), - self.package_url.clone(), - data_verifier.as_mut(), - ) - .and_extract(self.pkg_fmt(), dst) - .await?; - trace!("validating signature (if any)"); - if data_verifier.validate() { - if let Some(info) = verifier.info() { - info!("Verified signature for package '{}': {info}", self.package); - } - self.set_status(Status::InstalledFromTarball); - Ok(files) - } else { - self.set_status(Status::InvalidSignature); - Err(FetchError::InvalidSignature) - } - } - - fn pkg_fmt(&self) -> PkgFmt { - PkgFmt::Tgz - } - - fn target_meta(&self) -> PkgMeta { - let mut meta = self.target_data.meta.clone(); - meta.pkg_fmt = Some(self.pkg_fmt()); - meta.bin_dir = Some("{ bin }{ binary-ext }".to_string()); - meta - } - - fn source_name(&self) -> CompactString { - CompactString::from("QuickInstall") - } - - fn fetcher_name(&self) -> &'static str { - "QuickInstall" - } - - fn strategy(&self) -> Strategy { - Strategy::QuickInstall - } - - fn is_third_party(&self) -> bool { - true - } - - fn target(&self) -> &str { - &self.target_data.target - } - - fn target_data(&self) -> &Arc { - &self.target_data - } -} - -impl QuickInstall { - pub async fn report(&self) -> Result<(), FetchError> { - if !self.is_supported().await? { - debug!( - "Not sending quickinstall report for {} since Quickinstall does not support these targets.", - self.target_data.target - ); - - return Ok(()); - } - - let mut url = Url::parse(QUICKINSTALL_STATS_URL) - .expect("stats_url is pre-generated and should never be invalid url"); - url.query_pairs_mut() - .append_pair("crate", &self.data.name) - .append_pair("version", &self.data.version) - .append_pair("target", &self.target_data.target) - .append_pair( - "agent", - concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION")), - ) - .append_pair("status", self.get_status().as_str()); - debug!("Sending installation report to quickinstall ({url})"); - - self.client.request(Method::POST, url).send(true).await?; - - Ok(()) - } -} - -#[cfg(test)] -mod test { - use super::{get_quickinstall_supported_targets, Client, CompactString}; - use std::num::NonZeroU16; - - /// Mark this as an async fn so that you won't accidentally use it in - /// sync context. - async fn create_client() -> Client { - Client::new( - concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION")), - None, - NonZeroU16::new(10).unwrap(), - 1.try_into().unwrap(), - [], - ) - .unwrap() - } - - #[tokio::test] - async fn test_get_quickinstall_supported_targets() { - let supported_targets = get_quickinstall_supported_targets(&create_client().await) - .await - .unwrap(); - - [ - "x86_64-pc-windows-msvc", - "x86_64-apple-darwin", - "aarch64-apple-darwin", - "x86_64-unknown-linux-gnu", - "x86_64-unknown-linux-musl", - "aarch64-unknown-linux-gnu", - "aarch64-unknown-linux-musl", - "aarch64-pc-windows-msvc", - "armv7-unknown-linux-musleabihf", - "armv7-unknown-linux-gnueabihf", - ] - .into_iter() - .for_each(|known_supported_target| { - supported_targets - .binary_search(&CompactString::new(known_supported_target)) - .unwrap(); - }); - } -} diff --git a/crates/binstalk-fetchers/src/signing.rs b/crates/binstalk-fetchers/src/signing.rs deleted file mode 100644 index b231b440..00000000 --- a/crates/binstalk-fetchers/src/signing.rs +++ /dev/null @@ -1,91 +0,0 @@ -use binstalk_downloader::download::DataVerifier; -use binstalk_types::cargo_toml_binstall::{PkgSigning, SigningAlgorithm}; -use bytes::Bytes; -use minisign_verify::{PublicKey, Signature, StreamVerifier}; -use tracing::{error, trace}; - -use crate::FetchError; - -pub enum SignatureVerifier { - Noop, - Minisign(Box), -} - -impl SignatureVerifier { - pub fn new(config: &PkgSigning, signature: &[u8]) -> Result { - match config.algorithm { - SigningAlgorithm::Minisign => MinisignVerifier::new(config, signature) - .map(Box::new) - .map(Self::Minisign), - algorithm => Err(FetchError::UnsupportedSigningAlgorithm(algorithm)), - } - } - - pub fn data_verifier(&self) -> Result, FetchError> { - match self { - Self::Noop => Ok(Box::new(())), - Self::Minisign(v) => v.data_verifier(), - } - } - - pub fn info(&self) -> Option { - match self { - Self::Noop => None, - Self::Minisign(v) => Some(v.signature.trusted_comment().into()), - } - } -} - -pub struct MinisignVerifier { - pubkey: PublicKey, - signature: Signature, -} - -impl MinisignVerifier { - pub fn new(config: &PkgSigning, signature: &[u8]) -> Result { - trace!(key=?config.pubkey, "parsing public key"); - let pubkey = PublicKey::from_base64(&config.pubkey).map_err(|err| { - error!("Package public key is invalid: {err}"); - FetchError::InvalidSignature - })?; - - trace!(?signature, "parsing signature"); - let signature = Signature::decode(std::str::from_utf8(signature).map_err(|err| { - error!(?signature, "Signature file is not UTF-8! {err}"); - FetchError::InvalidSignature - })?) - .map_err(|err| { - error!("Signature file is invalid: {err}"); - FetchError::InvalidSignature - })?; - - Ok(Self { pubkey, signature }) - } - - pub fn data_verifier(&self) -> Result, FetchError> { - self.pubkey - .verify_stream(&self.signature) - .map(|vs| Box::new(MinisignDataVerifier(vs)) as _) - .map_err(|err| { - error!("Failed to setup stream verifier: {err}"); - FetchError::InvalidSignature - }) - } -} - -pub struct MinisignDataVerifier<'a>(StreamVerifier<'a>); - -impl DataVerifier for MinisignDataVerifier<'_> { - fn update(&mut self, data: &Bytes) { - self.0.update(data); - } - - fn validate(&mut self) -> bool { - if let Err(err) = self.0.finalize() { - error!("Failed to finalize signature verify: {err}"); - false - } else { - true - } - } -} diff --git a/crates/binstalk-git-repo-api/CHANGELOG.md b/crates/binstalk-git-repo-api/CHANGELOG.md deleted file mode 100644 index 1d6ac3c8..00000000 --- a/crates/binstalk-git-repo-api/CHANGELOG.md +++ /dev/null @@ -1,129 +0,0 @@ -# Changelog -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - -## [Unreleased] - -## [0.5.19](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-git-repo-api-v0.5.18...binstalk-git-repo-api-v0.5.19) - 2025-04-05 - -### Other - -- updated the following local packages: binstalk-downloader, binstalk-downloader - -## [0.5.18](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-git-repo-api-v0.5.17...binstalk-git-repo-api-v0.5.18) - 2025-03-19 - -### Other - -- updated the following local packages: binstalk-downloader, binstalk-downloader - -## [0.5.17](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-git-repo-api-v0.5.16...binstalk-git-repo-api-v0.5.17) - 2025-03-15 - -### Other - -- *(deps)* bump tokio from 1.43.0 to 1.44.0 in the deps group ([#2079](https://github.com/cargo-bins/cargo-binstall/pull/2079)) - -## [0.5.16](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-git-repo-api-v0.5.15...binstalk-git-repo-api-v0.5.16) - 2025-03-07 - -### Other - -- *(deps)* bump the deps group with 3 updates ([#2072](https://github.com/cargo-bins/cargo-binstall/pull/2072)) - -## [0.5.15](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-git-repo-api-v0.5.14...binstalk-git-repo-api-v0.5.15) - 2025-02-28 - -### Other - -- updated the following local packages: binstalk-downloader, binstalk-downloader - -## [0.5.14](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-git-repo-api-v0.5.13...binstalk-git-repo-api-v0.5.14) - 2025-02-11 - -### Other - -- updated the following local packages: binstalk-downloader, binstalk-downloader - -## [0.5.13](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-git-repo-api-v0.5.12...binstalk-git-repo-api-v0.5.13) - 2025-02-04 - -### Other - -- updated the following local packages: binstalk-downloader, binstalk-downloader - -## [0.5.12](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-git-repo-api-v0.5.11...binstalk-git-repo-api-v0.5.12) - 2025-01-19 - -### Other - -- update Cargo.lock dependencies - -## [0.5.11](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-git-repo-api-v0.5.10...binstalk-git-repo-api-v0.5.11) - 2025-01-13 - -### Other - -- update Cargo.lock dependencies - -## [0.5.10](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-git-repo-api-v0.5.9...binstalk-git-repo-api-v0.5.10) - 2025-01-11 - -### Other - -- *(deps)* bump the deps group with 3 updates (#2015) - -## [0.5.9](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-git-repo-api-v0.5.8...binstalk-git-repo-api-v0.5.9) - 2025-01-04 - -### Other - -- updated the following local packages: binstalk-downloader, binstalk-downloader - -## [0.5.8](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-git-repo-api-v0.5.7...binstalk-git-repo-api-v0.5.8) - 2024-12-14 - -### Other - -- *(deps)* bump the deps group with 2 updates (#1997) - -## [0.5.7](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-git-repo-api-v0.5.6...binstalk-git-repo-api-v0.5.7) - 2024-11-23 - -### Other - -- *(deps)* bump the deps group with 2 updates ([#1981](https://github.com/cargo-bins/cargo-binstall/pull/1981)) - -## [0.5.6](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-git-repo-api-v0.5.5...binstalk-git-repo-api-v0.5.6) - 2024-11-09 - -### Other - -- *(deps)* bump the deps group with 3 updates ([#1966](https://github.com/cargo-bins/cargo-binstall/pull/1966)) - -## [0.5.5](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-git-repo-api-v0.5.4...binstalk-git-repo-api-v0.5.5) - 2024-11-05 - -### Other - -- *(deps)* bump the deps group with 3 updates ([#1954](https://github.com/cargo-bins/cargo-binstall/pull/1954)) - -## [0.5.4](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-git-repo-api-v0.5.3...binstalk-git-repo-api-v0.5.4) - 2024-11-02 - -### Other - -- updated the following local packages: binstalk-downloader, binstalk-downloader - -## [0.5.3](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-git-repo-api-v0.5.2...binstalk-git-repo-api-v0.5.3) - 2024-10-12 - -### Fixed - -- *(gh_api_client)* remote client should never being shared everywhere bacause the underlying connection pool will be reused. ([#1930](https://github.com/cargo-bins/cargo-binstall/pull/1930)) - -### Other - -- Fix binstalk-git-repo-api on PR of forks ([#1932](https://github.com/cargo-bins/cargo-binstall/pull/1932)) - -## [0.5.2](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-git-repo-api-v0.5.1...binstalk-git-repo-api-v0.5.2) - 2024-09-11 - -### Other - -- report to new stats server (with status) ([#1912](https://github.com/cargo-bins/cargo-binstall/pull/1912)) - -## [0.5.1](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-git-repo-api-v0.5.0...binstalk-git-repo-api-v0.5.1) - 2024-08-12 - -### Other -- updated the following local packages: binstalk-downloader, binstalk-downloader - -## [0.5.0](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-git-repo-api-v0.4.0...binstalk-git-repo-api-v0.5.0) - 2024-08-10 - -### Other -- updated the following local packages: binstalk-downloader, binstalk-downloader diff --git a/crates/binstalk-git-repo-api/Cargo.toml b/crates/binstalk-git-repo-api/Cargo.toml deleted file mode 100644 index 1ce83fb4..00000000 --- a/crates/binstalk-git-repo-api/Cargo.toml +++ /dev/null @@ -1,30 +0,0 @@ -[package] -name = "binstalk-git-repo-api" -description = "The binstall toolkit for accessing API for git repository" -repository = "https://github.com/cargo-bins/cargo-binstall" -documentation = "https://docs.rs/binstalk-git-repo-api" -version = "0.5.19" -rust-version = "1.70.0" -authors = ["Jiahao XU "] -edition = "2021" -license = "Apache-2.0 OR MIT" - -[dependencies] -binstalk-downloader = { version = "0.13.17", path = "../binstalk-downloader", default-features = false, features = [ - "json", -] } -compact_str = "0.9.0" -percent-encoding = "2.2.0" -serde = { version = "1.0.163", features = ["derive"] } -serde-tuple-vec-map = "1.0.1" -serde_json = { version = "1.0.107" } -thiserror = "2.0.11" -tokio = { version = "1.44.0", features = ["sync"], default-features = false } -tracing = "0.1.39" -url = "2.5.4" -zeroize = "1.8.1" - -[dev-dependencies] -binstalk-downloader = { version = "0.13.17", path = "../binstalk-downloader" } -tracing-subscriber = "0.3" -once_cell = "1" diff --git a/crates/binstalk-git-repo-api/LICENSE-APACHE b/crates/binstalk-git-repo-api/LICENSE-APACHE deleted file mode 100644 index 1b5ec8b7..00000000 --- a/crates/binstalk-git-repo-api/LICENSE-APACHE +++ /dev/null @@ -1,176 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS diff --git a/crates/binstalk-git-repo-api/LICENSE-MIT b/crates/binstalk-git-repo-api/LICENSE-MIT deleted file mode 100644 index 31aa7938..00000000 --- a/crates/binstalk-git-repo-api/LICENSE-MIT +++ /dev/null @@ -1,23 +0,0 @@ -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/crates/binstalk-git-repo-api/src/gh_api_client.rs b/crates/binstalk-git-repo-api/src/gh_api_client.rs deleted file mode 100644 index 6af635ab..00000000 --- a/crates/binstalk-git-repo-api/src/gh_api_client.rs +++ /dev/null @@ -1,730 +0,0 @@ -use std::{ - collections::HashMap, - future::Future, - ops::Deref, - sync::{ - atomic::{AtomicBool, Ordering::Relaxed}, - Arc, Mutex, RwLock, - }, - time::{Duration, Instant}, -}; - -use binstalk_downloader::{download::Download, remote}; -use compact_str::{format_compact, CompactString, ToCompactString}; -use tokio::sync::OnceCell; -use tracing::{instrument, Level}; -use url::Url; -use zeroize::Zeroizing; - -mod common; -mod error; -mod release_artifacts; -mod repo_info; - -use common::{check_http_status_and_header, percent_decode_http_url_path}; -pub use error::{GhApiContextError, GhApiError, GhGraphQLErrors}; -pub use repo_info::RepoInfo; - -/// default retry duration if x-ratelimit-reset is not found in response header -const DEFAULT_RETRY_DURATION: Duration = Duration::from_secs(10 * 60); - -#[derive(Clone, Eq, PartialEq, Hash, Debug)] -pub struct GhRepo { - pub owner: CompactString, - pub repo: CompactString, -} -impl GhRepo { - pub fn repo_url(&self) -> Result { - Url::parse(&format_compact!( - "https://github.com/{}/{}", - self.owner, - self.repo - )) - } - - pub fn try_extract_from_url(url: &Url) -> Option { - if url.domain() != Some("github.com") { - return None; - } - - let mut path_segments = url.path_segments()?; - - Some(Self { - owner: path_segments.next()?.to_compact_string(), - repo: path_segments.next()?.to_compact_string(), - }) - } -} - -/// The keys required to identify a github release. -#[derive(Clone, Eq, PartialEq, Hash, Debug)] -pub struct GhRelease { - pub repo: GhRepo, - pub tag: CompactString, -} - -/// The Github Release and one of its artifact. -#[derive(Clone, Eq, PartialEq, Hash, Debug)] -pub struct GhReleaseArtifact { - pub release: GhRelease, - pub artifact_name: CompactString, -} - -impl GhReleaseArtifact { - /// Create [`GhReleaseArtifact`] from url. - pub fn try_extract_from_url(url: &remote::Url) -> Option { - if url.domain() != Some("github.com") { - return None; - } - - let mut path_segments = url.path_segments()?; - - let owner = path_segments.next()?; - let repo = path_segments.next()?; - - if (path_segments.next()?, path_segments.next()?) != ("releases", "download") { - return None; - } - - let tag = path_segments.next()?; - let artifact_name = path_segments.next()?; - - (path_segments.next().is_none() && url.fragment().is_none() && url.query().is_none()).then( - || Self { - release: GhRelease { - repo: GhRepo { - owner: percent_decode_http_url_path(owner), - repo: percent_decode_http_url_path(repo), - }, - tag: percent_decode_http_url_path(tag), - }, - artifact_name: percent_decode_http_url_path(artifact_name), - }, - ) - } -} - -#[derive(Debug)] -struct Map(RwLock>>); - -impl Default for Map { - fn default() -> Self { - Self(Default::default()) - } -} - -impl Map -where - K: Eq + std::hash::Hash, - V: Default, -{ - fn get(&self, k: K) -> Arc { - let optional_value = self.0.read().unwrap().deref().get(&k).cloned(); - optional_value.unwrap_or_else(|| Arc::clone(self.0.write().unwrap().entry(k).or_default())) - } -} - -#[derive(Debug)] -struct Inner { - client: remote::Client, - release_artifacts: Map>>, - retry_after: Mutex>, - - auth_token: Option>>, - is_auth_token_valid: AtomicBool, - - only_use_restful_api: AtomicBool, -} - -/// Github API client for querying whether a release artifact exitsts. -/// Can only handle github.com for now. -#[derive(Clone, Debug)] -pub struct GhApiClient(Arc); - -impl GhApiClient { - pub fn new(client: remote::Client, auth_token: Option>>) -> Self { - Self(Arc::new(Inner { - client, - release_artifacts: Default::default(), - retry_after: Default::default(), - - auth_token, - is_auth_token_valid: AtomicBool::new(true), - - only_use_restful_api: AtomicBool::new(false), - })) - } - - /// If you don't want to use GitHub GraphQL API for whatever reason, call this. - pub fn set_only_use_restful_api(&self) { - self.0.only_use_restful_api.store(true, Relaxed); - } - - pub fn remote_client(&self) -> &remote::Client { - &self.0.client - } -} - -impl GhApiClient { - fn check_retry_after(&self) -> Result<(), GhApiError> { - let mut guard = self.0.retry_after.lock().unwrap(); - - if let Some(retry_after) = *guard { - if retry_after.elapsed().is_zero() { - return Err(GhApiError::RateLimit { - retry_after: Some(retry_after - Instant::now()), - }); - } else { - // Instant retry_after is already reached. - *guard = None; - } - } - - Ok(()) - } - - fn get_auth_token(&self) -> Option<&str> { - if self.0.is_auth_token_valid.load(Relaxed) { - self.0.auth_token.as_deref().map(|s| &**s) - } else { - None - } - } - - pub fn has_gh_token(&self) -> bool { - self.get_auth_token().is_some() - } - - async fn do_fetch( - &self, - graphql_func: GraphQLFn, - restful_func: RestfulFn, - data: &T, - ) -> Result - where - GraphQLFn: Fn(&remote::Client, &T, &str) -> GraphQLFut, - RestfulFn: Fn(&remote::Client, &T, Option<&str>) -> RestfulFut, - GraphQLFut: Future> + Send + 'static, - RestfulFut: Future> + Send + 'static, - { - self.check_retry_after()?; - - if !self.0.only_use_restful_api.load(Relaxed) { - if let Some(auth_token) = self.get_auth_token() { - match graphql_func(&self.0.client, data, auth_token).await { - Err(GhApiError::Unauthorized) => { - self.0.is_auth_token_valid.store(false, Relaxed); - } - res => return res.map_err(|err| err.context("GraphQL API")), - } - } - } - - restful_func(&self.0.client, data, self.get_auth_token()) - .await - .map_err(|err| err.context("Restful API")) - } - - #[instrument(skip(self), ret(level = Level::DEBUG))] - pub async fn get_repo_info(&self, repo: &GhRepo) -> Result, GhApiError> { - match self - .do_fetch( - repo_info::fetch_repo_info_graphql_api, - repo_info::fetch_repo_info_restful_api, - repo, - ) - .await - { - Ok(repo_info) => Ok(repo_info), - Err(GhApiError::NotFound) => Ok(None), - Err(err) => Err(err), - } - } -} - -#[derive(Clone, Debug, Eq, PartialEq, Hash)] -pub struct GhReleaseArtifactUrl(Url); - -impl GhApiClient { - /// Return `Ok(Some(api_artifact_url))` if exists. - /// - /// Caches info on all artifacts matching (repo, tag). - /// - /// The returned future is guaranteed to be pointer size. - #[instrument(skip(self), ret(level = Level::DEBUG))] - pub async fn has_release_artifact( - &self, - GhReleaseArtifact { - release, - artifact_name, - }: GhReleaseArtifact, - ) -> Result, GhApiError> { - let once_cell = self.0.release_artifacts.get(release.clone()); - let res = once_cell - .get_or_try_init(|| { - Box::pin(async { - match self - .do_fetch( - release_artifacts::fetch_release_artifacts_graphql_api, - release_artifacts::fetch_release_artifacts_restful_api, - &release, - ) - .await - { - Ok(artifacts) => Ok(Some(artifacts)), - Err(GhApiError::NotFound) => Ok(None), - Err(err) => Err(err), - } - }) - }) - .await; - - match res { - Ok(Some(artifacts)) => Ok(artifacts - .get_artifact_url(&artifact_name) - .map(GhReleaseArtifactUrl)), - Ok(None) => Ok(None), - Err(GhApiError::RateLimit { retry_after }) => { - *self.0.retry_after.lock().unwrap() = - Some(Instant::now() + retry_after.unwrap_or(DEFAULT_RETRY_DURATION)); - - Err(GhApiError::RateLimit { retry_after }) - } - Err(err) => Err(err), - } - } - - pub async fn download_artifact( - &self, - artifact_url: GhReleaseArtifactUrl, - ) -> Result, GhApiError> { - self.check_retry_after()?; - - let Some(auth_token) = self.get_auth_token() else { - return Err(GhApiError::Unauthorized); - }; - - let response = self - .0 - .client - .get(artifact_url.0) - .header("Accept", "application/octet-stream") - .bearer_auth(&auth_token) - .send(false) - .await?; - - match check_http_status_and_header(response) { - Err(GhApiError::Unauthorized) => { - self.0.is_auth_token_valid.store(false, Relaxed); - Err(GhApiError::Unauthorized) - } - res => res.map(Download::from_response), - } - } -} - -#[cfg(test)] -mod test { - use super::*; - use compact_str::{CompactString, ToCompactString}; - use std::{env, num::NonZeroU16, time::Duration}; - use tokio::time::sleep; - use tracing::subscriber::set_global_default; - use tracing_subscriber::{filter::LevelFilter, fmt::fmt}; - - static DEFAULT_RETRY_AFTER: Duration = Duration::from_secs(1); - - mod cargo_binstall_v0_20_1 { - use super::{CompactString, GhRelease, GhRepo}; - - pub(super) const RELEASE: GhRelease = GhRelease { - repo: GhRepo { - owner: CompactString::const_new("cargo-bins"), - repo: CompactString::const_new("cargo-binstall"), - }, - tag: CompactString::const_new("v0.20.1"), - }; - - pub(super) const ARTIFACTS: &[&str] = &[ - "cargo-binstall-aarch64-apple-darwin.full.zip", - "cargo-binstall-aarch64-apple-darwin.zip", - "cargo-binstall-aarch64-pc-windows-msvc.full.zip", - "cargo-binstall-aarch64-pc-windows-msvc.zip", - "cargo-binstall-aarch64-unknown-linux-gnu.full.tgz", - "cargo-binstall-aarch64-unknown-linux-gnu.tgz", - "cargo-binstall-aarch64-unknown-linux-musl.full.tgz", - "cargo-binstall-aarch64-unknown-linux-musl.tgz", - "cargo-binstall-armv7-unknown-linux-gnueabihf.full.tgz", - "cargo-binstall-armv7-unknown-linux-gnueabihf.tgz", - "cargo-binstall-armv7-unknown-linux-musleabihf.full.tgz", - "cargo-binstall-armv7-unknown-linux-musleabihf.tgz", - "cargo-binstall-universal-apple-darwin.full.zip", - "cargo-binstall-universal-apple-darwin.zip", - "cargo-binstall-x86_64-apple-darwin.full.zip", - "cargo-binstall-x86_64-apple-darwin.zip", - "cargo-binstall-x86_64-pc-windows-msvc.full.zip", - "cargo-binstall-x86_64-pc-windows-msvc.zip", - "cargo-binstall-x86_64-unknown-linux-gnu.full.tgz", - "cargo-binstall-x86_64-unknown-linux-gnu.tgz", - "cargo-binstall-x86_64-unknown-linux-musl.full.tgz", - "cargo-binstall-x86_64-unknown-linux-musl.tgz", - ]; - } - - mod cargo_audit_v_0_17_6 { - use super::*; - - pub(super) const RELEASE: GhRelease = GhRelease { - repo: GhRepo { - owner: CompactString::const_new("rustsec"), - repo: CompactString::const_new("rustsec"), - }, - tag: CompactString::const_new("cargo-audit/v0.17.6"), - }; - - #[allow(unused)] - pub(super) const ARTIFACTS: &[&str] = &[ - "cargo-audit-aarch64-unknown-linux-gnu-v0.17.6.tgz", - "cargo-audit-armv7-unknown-linux-gnueabihf-v0.17.6.tgz", - "cargo-audit-x86_64-apple-darwin-v0.17.6.tgz", - "cargo-audit-x86_64-pc-windows-msvc-v0.17.6.zip", - "cargo-audit-x86_64-unknown-linux-gnu-v0.17.6.tgz", - "cargo-audit-x86_64-unknown-linux-musl-v0.17.6.tgz", - ]; - - #[test] - fn extract_with_escaped_characters() { - let release_artifact = try_extract_artifact_from_str( -"https://github.com/rustsec/rustsec/releases/download/cargo-audit%2Fv0.17.6/cargo-audit-aarch64-unknown-linux-gnu-v0.17.6.tgz" - ).unwrap(); - - assert_eq!( - release_artifact, - GhReleaseArtifact { - release: RELEASE, - artifact_name: CompactString::from( - "cargo-audit-aarch64-unknown-linux-gnu-v0.17.6.tgz", - ) - } - ); - } - } - - #[test] - fn gh_repo_extract_from_and_to_url() { - [ - "https://github.com/cargo-bins/cargo-binstall", - "https://github.com/rustsec/rustsec", - ] - .into_iter() - .for_each(|url| { - let url = Url::parse(url).unwrap(); - assert_eq!( - GhRepo::try_extract_from_url(&url) - .unwrap() - .repo_url() - .unwrap(), - url - ); - }) - } - - fn try_extract_artifact_from_str(s: &str) -> Option { - GhReleaseArtifact::try_extract_from_url(&url::Url::parse(s).unwrap()) - } - - fn assert_extract_gh_release_artifacts_failures(urls: &[&str]) { - for url in urls { - assert_eq!(try_extract_artifact_from_str(url), None); - } - } - - #[test] - fn extract_gh_release_artifacts_failure() { - use cargo_binstall_v0_20_1::*; - - let GhRelease { - repo: GhRepo { owner, repo }, - tag, - } = RELEASE; - - assert_extract_gh_release_artifacts_failures(&[ - "https://examle.com", - "https://github.com", - &format!("https://github.com/{owner}"), - &format!("https://github.com/{owner}/{repo}"), - &format!("https://github.com/{owner}/{repo}/123e"), - &format!("https://github.com/{owner}/{repo}/releases/21343"), - &format!("https://github.com/{owner}/{repo}/releases/download"), - &format!("https://github.com/{owner}/{repo}/releases/download/{tag}"), - &format!("https://github.com/{owner}/{repo}/releases/download/{tag}/a/23"), - &format!("https://github.com/{owner}/{repo}/releases/download/{tag}/a#a=12"), - &format!("https://github.com/{owner}/{repo}/releases/download/{tag}/a?page=3"), - ]); - } - - #[test] - fn extract_gh_release_artifacts_success() { - use cargo_binstall_v0_20_1::*; - - let GhRelease { - repo: GhRepo { owner, repo }, - tag, - } = RELEASE; - - for artifact in ARTIFACTS { - let GhReleaseArtifact { - release, - artifact_name, - } = try_extract_artifact_from_str(&format!( - "https://github.com/{owner}/{repo}/releases/download/{tag}/{artifact}" - )) - .unwrap(); - - assert_eq!(release, RELEASE); - assert_eq!(artifact_name, artifact); - } - } - - fn init_logger() { - // Disable time, target, file, line_num, thread name/ids to make the - // output more readable - let subscriber = fmt() - .without_time() - .with_target(false) - .with_file(false) - .with_line_number(false) - .with_thread_names(false) - .with_thread_ids(false) - .with_test_writer() - .with_max_level(LevelFilter::DEBUG) - .finish(); - - // Setup global subscriber - let _ = set_global_default(subscriber); - } - - fn create_remote_client() -> remote::Client { - remote::Client::new( - concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION")), - None, - NonZeroU16::new(300).unwrap(), - 1.try_into().unwrap(), - [], - ) - .unwrap() - } - - /// Mark this as an async fn so that you won't accidentally use it in - /// sync context. - fn create_client() -> Vec { - let client = create_remote_client(); - - let auth_token = match env::var("CI_UNIT_TEST_GITHUB_TOKEN") { - Ok(auth_token) if !auth_token.is_empty() => { - Some(zeroize::Zeroizing::new(auth_token.into_boxed_str())) - } - _ => None, - }; - - let gh_client = GhApiClient::new(client.clone(), auth_token.clone()); - gh_client.set_only_use_restful_api(); - - let mut gh_clients = vec![gh_client]; - - if auth_token.is_some() { - gh_clients.push(GhApiClient::new(client, auth_token)); - } - - gh_clients - } - - #[tokio::test] - async fn rate_limited_test_get_repo_info() { - const PUBLIC_REPOS: [GhRepo; 1] = [GhRepo { - owner: CompactString::const_new("cargo-bins"), - repo: CompactString::const_new("cargo-binstall"), - }]; - const PRIVATE_REPOS: [GhRepo; 1] = [GhRepo { - owner: CompactString::const_new("cargo-bins"), - repo: CompactString::const_new("private-repo-for-testing"), - }]; - const NON_EXISTENT_REPOS: [GhRepo; 1] = [GhRepo { - owner: CompactString::const_new("cargo-bins"), - repo: CompactString::const_new("ttt"), - }]; - - init_logger(); - - let mut tests: Vec<(_, _)> = Vec::new(); - - for client in create_client() { - let spawn_get_repo_info_task = |repo| { - let client = client.clone(); - tokio::spawn(async move { - loop { - match client.get_repo_info(&repo).await { - Err(GhApiError::RateLimit { retry_after }) => { - sleep(retry_after.unwrap_or(DEFAULT_RETRY_AFTER)).await - } - res => break res, - } - } - }) - }; - - for repo in PUBLIC_REPOS { - tests.push(( - Some(RepoInfo::new(repo.clone(), false)), - spawn_get_repo_info_task(repo), - )); - } - - for repo in NON_EXISTENT_REPOS { - tests.push((None, spawn_get_repo_info_task(repo))); - } - - if client.has_gh_token() { - for repo in PRIVATE_REPOS { - tests.push(( - Some(RepoInfo::new(repo.clone(), true)), - spawn_get_repo_info_task(repo), - )); - } - } - } - - for (expected, task) in tests { - assert_eq!(task.await.unwrap().unwrap(), expected); - } - } - - #[tokio::test] - async fn rate_limited_test_has_release_artifact_and_download_artifacts() { - const RELEASES: [(GhRelease, &[&str]); 1] = [( - cargo_binstall_v0_20_1::RELEASE, - cargo_binstall_v0_20_1::ARTIFACTS, - )]; - const NON_EXISTENT_RELEASES: [GhRelease; 1] = [GhRelease { - repo: GhRepo { - owner: CompactString::const_new("cargo-bins"), - repo: CompactString::const_new("cargo-binstall"), - }, - // We are currently at v0.20.1 and we would never release - // anything older than v0.20.1 - tag: CompactString::const_new("v0.18.2"), - }]; - - init_logger(); - - let mut tasks = Vec::new(); - - for client in create_client() { - async fn has_release_artifact( - client: &GhApiClient, - artifact: &GhReleaseArtifact, - ) -> Result, GhApiError> { - loop { - match client.has_release_artifact(artifact.clone()).await { - Err(GhApiError::RateLimit { retry_after }) => { - sleep(retry_after.unwrap_or(DEFAULT_RETRY_AFTER)).await - } - res => break res, - } - } - } - - for (release, artifacts) in RELEASES { - for artifact_name in artifacts { - let client = client.clone(); - let release = release.clone(); - tasks.push(tokio::spawn(async move { - let artifact = GhReleaseArtifact { - release, - artifact_name: artifact_name.to_compact_string(), - }; - - let browser_download_task = client.get_auth_token().map(|_| { - tokio::spawn( - Download::new( - client.remote_client().clone(), - Url::parse(&format!( - "https://github.com/{}/{}/releases/download/{}/{}", - artifact.release.repo.owner, - artifact.release.repo.repo, - artifact.release.tag, - artifact.artifact_name, - )) - .unwrap(), - ) - .into_bytes(), - ) - }); - let artifact_url = has_release_artifact(&client, &artifact) - .await - .unwrap() - .unwrap(); - - if let Some(browser_download_task) = browser_download_task { - let artifact_download_data = loop { - match client.download_artifact(artifact_url.clone()).await { - Err(GhApiError::RateLimit { retry_after }) => { - sleep(retry_after.unwrap_or(DEFAULT_RETRY_AFTER)).await - } - res => break res.unwrap(), - } - } - .into_bytes() - .await - .unwrap(); - - let browser_download_data = - browser_download_task.await.unwrap().unwrap(); - - assert_eq!(artifact_download_data, browser_download_data); - } - })); - } - - let client = client.clone(); - tasks.push(tokio::spawn(async move { - assert_eq!( - has_release_artifact( - &client, - &GhReleaseArtifact { - release, - artifact_name: "123z".to_compact_string(), - } - ) - .await - .unwrap(), - None - ); - })); - } - - for release in NON_EXISTENT_RELEASES { - let client = client.clone(); - - tasks.push(tokio::spawn(async move { - assert_eq!( - has_release_artifact( - &client, - &GhReleaseArtifact { - release, - artifact_name: "1234".to_compact_string(), - } - ) - .await - .unwrap(), - None - ); - })); - } - } - - for task in tasks { - task.await.unwrap(); - } - } -} diff --git a/crates/binstalk-git-repo-api/src/gh_api_client/common.rs b/crates/binstalk-git-repo-api/src/gh_api_client/common.rs deleted file mode 100644 index 723834ec..00000000 --- a/crates/binstalk-git-repo-api/src/gh_api_client/common.rs +++ /dev/null @@ -1,130 +0,0 @@ -use std::{fmt::Debug, future::Future, sync::OnceLock}; - -use binstalk_downloader::remote::{self, Response, Url}; -use compact_str::CompactString; -use percent_encoding::percent_decode_str; -use serde::{de::DeserializeOwned, Deserialize, Serialize}; -use serde_json::to_string as to_json_string; -use tracing::debug; - -use super::{GhApiError, GhGraphQLErrors}; - -pub(super) fn percent_decode_http_url_path(input: &str) -> CompactString { - if input.contains('%') { - percent_decode_str(input).decode_utf8_lossy().into() - } else { - // No '%', no need to decode. - CompactString::new(input) - } -} - -pub(super) fn check_http_status_and_header(response: Response) -> Result { - match response.status() { - remote::StatusCode::UNAUTHORIZED => Err(GhApiError::Unauthorized), - remote::StatusCode::NOT_FOUND => Err(GhApiError::NotFound), - - _ => Ok(response.error_for_status()?), - } -} - -fn get_api_endpoint() -> &'static Url { - static API_ENDPOINT: OnceLock = OnceLock::new(); - - API_ENDPOINT.get_or_init(|| { - Url::parse("https://api.github.com/").expect("Literal provided must be a valid url") - }) -} - -pub(super) fn issue_restful_api( - client: &remote::Client, - path: &[&str], - auth_token: Option<&str>, -) -> impl Future> + Send + 'static -where - T: DeserializeOwned, -{ - let mut url = get_api_endpoint().clone(); - - url.path_segments_mut() - .expect("get_api_endpoint() should return a https url") - .extend(path); - - debug!("Getting restful API: {url}"); - - let mut request_builder = client - .get(url) - .header("Accept", "application/vnd.github+json") - .header("X-GitHub-Api-Version", "2022-11-28"); - - if let Some(auth_token) = auth_token { - request_builder = request_builder.bearer_auth(&auth_token); - } - - let future = request_builder.send(false); - - async move { - let response = check_http_status_and_header(future.await?)?; - - Ok(response.json().await?) - } -} - -#[derive(Debug, Deserialize)] -struct GraphQLResponse { - data: T, - errors: Option, -} - -#[derive(Serialize)] -struct GraphQLQuery { - query: String, -} - -fn get_graphql_endpoint() -> Url { - let mut graphql_endpoint = get_api_endpoint().clone(); - - graphql_endpoint - .path_segments_mut() - .expect("get_api_endpoint() should return a https url") - .push("graphql"); - - graphql_endpoint -} - -pub(super) fn issue_graphql_query( - client: &remote::Client, - query: String, - auth_token: &str, -) -> impl Future> + Send + 'static -where - T: DeserializeOwned + Debug, -{ - let res = to_json_string(&GraphQLQuery { query }) - .map_err(remote::Error::from) - .map(|graphql_query| { - let graphql_endpoint = get_graphql_endpoint(); - - debug!("Sending graphql query to {graphql_endpoint}: '{graphql_query}'"); - - let request_builder = client - .post(graphql_endpoint, graphql_query) - .header("Accept", "application/vnd.github+json") - .bearer_auth(&auth_token); - - request_builder.send(false) - }); - - async move { - let response = check_http_status_and_header(res?.await?)?; - - let mut response: GraphQLResponse = response.json().await?; - - debug!("response = {response:?}"); - - if let Some(error) = response.errors.take() { - Err(error.into()) - } else { - Ok(response.data) - } - } -} diff --git a/crates/binstalk-git-repo-api/src/gh_api_client/error.rs b/crates/binstalk-git-repo-api/src/gh_api_client/error.rs deleted file mode 100644 index 0a2918a9..00000000 --- a/crates/binstalk-git-repo-api/src/gh_api_client/error.rs +++ /dev/null @@ -1,203 +0,0 @@ -use std::{error, fmt, io, time::Duration}; - -use binstalk_downloader::remote; -use compact_str::{CompactString, ToCompactString}; -use serde::{de::Deserializer, Deserialize}; -use thiserror::Error as ThisError; - -#[derive(ThisError, Debug)] -#[error("Context: '{context}', err: '{err}'")] -pub struct GhApiContextError { - context: CompactString, - #[source] - err: GhApiError, -} - -#[derive(ThisError, Debug)] -#[non_exhaustive] -pub enum GhApiError { - #[error("IO Error: {0}")] - Io(#[from] io::Error), - - #[error("Remote Error: {0}")] - Remote(#[from] remote::Error), - - #[error("Failed to parse url: {0}")] - InvalidUrl(#[from] url::ParseError), - - /// A wrapped error providing the context the error is about. - #[error(transparent)] - Context(Box), - - #[error("Remote failed to process GraphQL query: {0}")] - GraphQLErrors(GhGraphQLErrors), - - #[error("Hit rate-limit, retry after {retry_after:?}")] - RateLimit { retry_after: Option }, - - #[error("Corresponding resource is not found")] - NotFound, - - #[error("Does not have permission to access the API")] - Unauthorized, -} - -impl GhApiError { - /// Attach context to [`GhApiError`] - pub fn context(self, context: impl fmt::Display) -> Self { - use GhApiError::*; - - if matches!(self, RateLimit { .. } | NotFound | Unauthorized) { - self - } else { - Self::Context(Box::new(GhApiContextError { - context: context.to_compact_string(), - err: self, - })) - } - } -} - -impl From for GhApiError { - fn from(e: GhGraphQLErrors) -> Self { - if e.is_rate_limited() { - Self::RateLimit { retry_after: None } - } else if e.is_not_found_error() { - Self::NotFound - } else { - Self::GraphQLErrors(e) - } - } -} - -#[derive(Debug, Deserialize)] -pub struct GhGraphQLErrors(Box<[GraphQLError]>); - -impl GhGraphQLErrors { - fn is_rate_limited(&self) -> bool { - self.0 - .iter() - .any(|error| matches!(error.error_type, GraphQLErrorType::RateLimited)) - } - - fn is_not_found_error(&self) -> bool { - self.0 - .iter() - .any(|error| matches!(&error.error_type, GraphQLErrorType::Other(error_type) if *error_type == "NOT_FOUND")) - } -} - -impl error::Error for GhGraphQLErrors {} - -impl fmt::Display for GhGraphQLErrors { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let last_error_index = self.0.len() - 1; - - for (i, error) in self.0.iter().enumerate() { - write!( - f, - "type: '{error_type}', msg: '{msg}'", - error_type = error.error_type, - msg = error.message, - )?; - - for location in error.locations.as_deref().into_iter().flatten() { - write!( - f, - ", occured on query line {line} col {col}", - line = location.line, - col = location.column - )?; - } - - for (k, v) in &error.others { - write!(f, ", {k}: {v}")?; - } - - if i < last_error_index { - f.write_str("\n")?; - } - } - - Ok(()) - } -} - -#[derive(Debug, Deserialize)] -struct GraphQLError { - message: CompactString, - locations: Option>, - - #[serde(rename = "type")] - error_type: GraphQLErrorType, - - #[serde(flatten, with = "tuple_vec_map")] - others: Vec<(CompactString, serde_json::Value)>, -} - -#[derive(Debug)] -pub(super) enum GraphQLErrorType { - RateLimited, - Other(CompactString), -} - -impl fmt::Display for GraphQLErrorType { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(match self { - GraphQLErrorType::RateLimited => "RATE_LIMITED", - GraphQLErrorType::Other(s) => s, - }) - } -} - -impl<'de> Deserialize<'de> for GraphQLErrorType { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let s = CompactString::deserialize(deserializer)?; - Ok(match &*s { - "RATE_LIMITED" => GraphQLErrorType::RateLimited, - _ => GraphQLErrorType::Other(s), - }) - } -} - -#[derive(Debug, Deserialize)] -struct GraphQLLocation { - line: u64, - column: u64, -} - -#[cfg(test)] -mod test { - use super::*; - use serde::de::value::{BorrowedStrDeserializer, Error}; - - macro_rules! assert_matches { - ($expression:expr, $pattern:pat $(if $guard:expr)? $(,)?) => { - match $expression { - $pattern $(if $guard)? => true, - expr => { - panic!( - "assertion failed: `{expr:?}` does not match `{}`", - stringify!($pattern $(if $guard)?) - ) - } - } - } - } - - #[test] - fn test_graph_ql_error_type() { - let deserialize = |input: &str| { - GraphQLErrorType::deserialize(BorrowedStrDeserializer::<'_, Error>::new(input)).unwrap() - }; - - assert_matches!(deserialize("RATE_LIMITED"), GraphQLErrorType::RateLimited); - assert_matches!( - deserialize("rATE_LIMITED"), - GraphQLErrorType::Other(val) if val == CompactString::const_new("rATE_LIMITED") - ); - } -} diff --git a/crates/binstalk-git-repo-api/src/gh_api_client/release_artifacts.rs b/crates/binstalk-git-repo-api/src/gh_api_client/release_artifacts.rs deleted file mode 100644 index 5b738842..00000000 --- a/crates/binstalk-git-repo-api/src/gh_api_client/release_artifacts.rs +++ /dev/null @@ -1,192 +0,0 @@ -use std::{ - borrow::Borrow, - collections::HashSet, - fmt, - future::Future, - hash::{Hash, Hasher}, -}; - -use binstalk_downloader::remote::{self}; -use compact_str::{CompactString, ToCompactString}; -use serde::Deserialize; -use url::Url; - -use super::{ - common::{issue_graphql_query, issue_restful_api}, - GhApiError, GhRelease, GhRepo, -}; - -// Only include fields we do care about - -#[derive(Eq, Deserialize, Debug)] -struct Artifact { - name: CompactString, - url: Url, -} - -// Manually implement PartialEq and Hash to ensure it will always produce the -// same hash as a str with the same content, and that the comparison will be -// the same to coparing a string. - -impl PartialEq for Artifact { - fn eq(&self, other: &Self) -> bool { - self.name.eq(&other.name) - } -} - -impl Hash for Artifact { - fn hash(&self, state: &mut H) - where - H: Hasher, - { - let s: &str = self.name.as_str(); - s.hash(state) - } -} - -// Implement Borrow so that we can use call -// `HashSet::contains::` - -impl Borrow for Artifact { - fn borrow(&self) -> &str { - &self.name - } -} - -#[derive(Debug, Default, Deserialize)] -pub(super) struct Artifacts { - assets: HashSet, -} - -impl Artifacts { - /// get url for downloading the artifact using GitHub API (for private repository). - pub(super) fn get_artifact_url(&self, artifact_name: &str) -> Option { - self.assets - .get(artifact_name) - .map(|artifact| artifact.url.clone()) - } -} - -pub(super) fn fetch_release_artifacts_restful_api( - client: &remote::Client, - GhRelease { - repo: GhRepo { owner, repo }, - tag, - }: &GhRelease, - auth_token: Option<&str>, -) -> impl Future> + Send + 'static { - issue_restful_api( - client, - &["repos", owner, repo, "releases", "tags", tag], - auth_token, - ) -} - -#[derive(Debug, Deserialize)] -struct GraphQLData { - repository: Option, -} - -#[derive(Debug, Deserialize)] -struct GraphQLRepo { - release: Option, -} - -#[derive(Debug, Deserialize)] -struct GraphQLRelease { - #[serde(rename = "releaseAssets")] - assets: GraphQLReleaseAssets, -} - -#[derive(Debug, Deserialize)] -struct GraphQLReleaseAssets { - nodes: Vec, - #[serde(rename = "pageInfo")] - page_info: GraphQLPageInfo, -} - -#[derive(Debug, Deserialize)] -struct GraphQLPageInfo { - #[serde(rename = "endCursor")] - end_cursor: Option, - #[serde(rename = "hasNextPage")] - has_next_page: bool, -} - -enum FilterCondition { - Init, - After(CompactString), -} - -impl fmt::Display for FilterCondition { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - // GitHub imposes a limit of 100 for the value passed to param "first" - FilterCondition::Init => f.write_str("first:100"), - FilterCondition::After(end_cursor) => write!(f, r#"first:100,after:"{end_cursor}""#), - } - } -} - -pub(super) fn fetch_release_artifacts_graphql_api( - client: &remote::Client, - GhRelease { - repo: GhRepo { owner, repo }, - tag, - }: &GhRelease, - auth_token: &str, -) -> impl Future> + Send + 'static { - let client = client.clone(); - let auth_token = auth_token.to_compact_string(); - - let base_query_prefix = format!( - r#" -query {{ - repository(owner:"{owner}",name:"{repo}") {{ - release(tagName:"{tag}") {{"# - ); - - let base_query_suffix = r#" - nodes { name url } - pageInfo { endCursor hasNextPage } -}}}}"# - .trim(); - - async move { - let mut artifacts = Artifacts::default(); - let mut cond = FilterCondition::Init; - let base_query_prefix = base_query_prefix.trim(); - - loop { - let query = format!( - r#" -{base_query_prefix} -releaseAssets({cond}) {{ -{base_query_suffix}"# - ); - - let data: GraphQLData = issue_graphql_query(&client, query, &auth_token).await?; - - let assets = data - .repository - .and_then(|repository| repository.release) - .map(|release| release.assets); - - if let Some(assets) = assets { - artifacts.assets.extend(assets.nodes); - - match assets.page_info { - GraphQLPageInfo { - end_cursor: Some(end_cursor), - has_next_page: true, - } => { - cond = FilterCondition::After(end_cursor); - } - _ => break Ok(artifacts), - } - } else { - break Err(GhApiError::NotFound); - } - } - } -} diff --git a/crates/binstalk-git-repo-api/src/gh_api_client/repo_info.rs b/crates/binstalk-git-repo-api/src/gh_api_client/repo_info.rs deleted file mode 100644 index d95cca81..00000000 --- a/crates/binstalk-git-repo-api/src/gh_api_client/repo_info.rs +++ /dev/null @@ -1,91 +0,0 @@ -use std::{fmt, future::Future}; - -use compact_str::CompactString; -use serde::Deserialize; - -use super::{ - common::{issue_graphql_query, issue_restful_api}, - remote, GhApiError, GhRepo, -}; - -#[derive(Clone, Eq, PartialEq, Hash, Debug, Deserialize)] -struct Owner { - login: CompactString, -} - -#[derive(Clone, Eq, PartialEq, Hash, Debug, Deserialize)] -pub struct RepoInfo { - owner: Owner, - name: CompactString, - private: bool, -} - -impl fmt::Display for RepoInfo { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "RepoInfo {{ owner: {}, name: {}, is_private: {} }}", - self.owner.login, self.name, self.private - ) - } -} - -impl RepoInfo { - #[cfg(test)] - pub(crate) fn new(GhRepo { owner, repo }: GhRepo, private: bool) -> Self { - Self { - owner: Owner { login: owner }, - name: repo, - private, - } - } - pub fn repo(&self) -> GhRepo { - GhRepo { - owner: self.owner.login.clone(), - repo: self.name.clone(), - } - } - - pub fn is_private(&self) -> bool { - self.private - } -} - -pub(super) fn fetch_repo_info_restful_api( - client: &remote::Client, - GhRepo { owner, repo }: &GhRepo, - auth_token: Option<&str>, -) -> impl Future, GhApiError>> + Send + 'static { - issue_restful_api(client, &["repos", owner, repo], auth_token) -} - -#[derive(Debug, Deserialize)] -struct GraphQLData { - repository: Option, -} - -pub(super) fn fetch_repo_info_graphql_api( - client: &remote::Client, - GhRepo { owner, repo }: &GhRepo, - auth_token: &str, -) -> impl Future, GhApiError>> + Send + 'static { - let query = format!( - r#" -query {{ - repository(owner:"{owner}",name:"{repo}") {{ - owner {{ - login - }} - name - private: isPrivate - }} -}}"# - ); - - let future = issue_graphql_query(client, query, auth_token); - - async move { - let data: GraphQLData = future.await?; - Ok(data.repository) - } -} diff --git a/crates/binstalk-git-repo-api/src/lib.rs b/crates/binstalk-git-repo-api/src/lib.rs deleted file mode 100644 index 7d7dd52c..00000000 --- a/crates/binstalk-git-repo-api/src/lib.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod gh_api_client; diff --git a/crates/binstalk-manifests/CHANGELOG.md b/crates/binstalk-manifests/CHANGELOG.md deleted file mode 100644 index aa0fe9bf..00000000 --- a/crates/binstalk-manifests/CHANGELOG.md +++ /dev/null @@ -1,183 +0,0 @@ -# Changelog -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - -## [Unreleased] - -## [0.15.28](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-manifests-v0.15.27...binstalk-manifests-v0.15.28) - 2025-04-05 - -### Other - -- Fix clippy lints ([#2111](https://github.com/cargo-bins/cargo-binstall/pull/2111)) - -## [0.15.27](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-manifests-v0.15.26...binstalk-manifests-v0.15.27) - 2025-03-19 - -### Other - -- updated the following local packages: detect-targets, fs-lock - -## [0.15.26](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-manifests-v0.15.25...binstalk-manifests-v0.15.26) - 2025-03-15 - -### Other - -- updated the following local packages: detect-targets - -## [0.15.25](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-manifests-v0.15.24...binstalk-manifests-v0.15.25) - 2025-03-07 - -### Other - -- *(deps)* bump the deps group with 3 updates ([#2072](https://github.com/cargo-bins/cargo-binstall/pull/2072)) - -## [0.15.24](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-manifests-v0.15.23...binstalk-manifests-v0.15.24) - 2025-02-28 - -### Other - -- updated the following local packages: detect-targets - -## [0.15.23](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-manifests-v0.15.22...binstalk-manifests-v0.15.23) - 2025-02-22 - -### Other - -- Log when FileLock::drop fails to unlock file ([#2064](https://github.com/cargo-bins/cargo-binstall/pull/2064)) - -## [0.15.22](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-manifests-v0.15.21...binstalk-manifests-v0.15.22) - 2025-02-15 - -### Other - -- updated the following local packages: detect-targets - -## [0.15.21](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-manifests-v0.15.20...binstalk-manifests-v0.15.21) - 2025-02-11 - -### Other - -- updated the following local packages: binstalk-types, detect-targets - -## [0.15.20](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-manifests-v0.15.19...binstalk-manifests-v0.15.20) - 2025-02-04 - -### Other - -- updated the following local packages: detect-targets - -## [0.15.19](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-manifests-v0.15.18...binstalk-manifests-v0.15.19) - 2025-01-19 - -### Other - -- update Cargo.lock dependencies - -## [0.15.18](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-manifests-v0.15.17...binstalk-manifests-v0.15.18) - 2025-01-13 - -### Other - -- update Cargo.lock dependencies - -## [0.15.17](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-manifests-v0.15.16...binstalk-manifests-v0.15.17) - 2025-01-11 - -### Other - -- *(deps)* bump the deps group with 3 updates (#2015) - -## [0.15.16](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-manifests-v0.15.15...binstalk-manifests-v0.15.16) - 2025-01-04 - -### Other - -- updated the following local packages: detect-targets - -## [0.15.15](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-manifests-v0.15.14...binstalk-manifests-v0.15.15) - 2024-12-28 - -### Other - -- updated the following local packages: detect-targets - -## [0.15.14](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-manifests-v0.15.13...binstalk-manifests-v0.15.14) - 2024-12-14 - -### Other - -- *(deps)* bump the deps group with 2 updates (#1997) - -## [0.15.13](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-manifests-v0.15.12...binstalk-manifests-v0.15.13) - 2024-12-07 - -### Other - -- updated the following local packages: detect-targets, fs-lock - -## [0.15.12](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-manifests-v0.15.11...binstalk-manifests-v0.15.12) - 2024-11-29 - -### Other - -- updated the following local packages: detect-targets - -## [0.15.11](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-manifests-v0.15.10...binstalk-manifests-v0.15.11) - 2024-11-23 - -### Other - -- *(deps)* bump the deps group with 2 updates ([#1981](https://github.com/cargo-bins/cargo-binstall/pull/1981)) - -## [0.15.10](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-manifests-v0.15.9...binstalk-manifests-v0.15.10) - 2024-11-18 - -### Other - -- updated the following local packages: detect-targets - -## [0.15.9](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-manifests-v0.15.8...binstalk-manifests-v0.15.9) - 2024-11-09 - -### Other - -- *(deps)* bump the deps group with 3 updates ([#1966](https://github.com/cargo-bins/cargo-binstall/pull/1966)) - -## [0.15.8](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-manifests-v0.15.7...binstalk-manifests-v0.15.8) - 2024-11-05 - -### Other - -- *(deps)* bump the deps group with 3 updates ([#1954](https://github.com/cargo-bins/cargo-binstall/pull/1954)) - -## [0.15.7](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-manifests-v0.15.6...binstalk-manifests-v0.15.7) - 2024-11-02 - -### Other - -- updated the following local packages: detect-targets - -## [0.15.6](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-manifests-v0.15.5...binstalk-manifests-v0.15.6) - 2024-10-25 - -### Other - -- updated the following local packages: detect-targets - -## [0.15.5](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-manifests-v0.15.4...binstalk-manifests-v0.15.5) - 2024-10-12 - -### Other - -- updated the following local packages: detect-targets, fs-lock - -## [0.15.4](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-manifests-v0.15.3...binstalk-manifests-v0.15.4) - 2024-10-04 - -### Other - -- updated the following local packages: detect-targets - -## [0.15.3](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-manifests-v0.15.2...binstalk-manifests-v0.15.3) - 2024-09-22 - -### Other - -- updated the following local packages: detect-targets - -## [0.15.2](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-manifests-v0.15.1...binstalk-manifests-v0.15.2) - 2024-09-06 - -### Other -- updated the following local packages: detect-targets - -## [0.15.1](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-manifests-v0.15.0...binstalk-manifests-v0.15.1) - 2024-08-25 - -### Other -- updated the following local packages: detect-targets - -## [0.15.0](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-manifests-v0.14.1...binstalk-manifests-v0.15.0) - 2024-08-10 - -### Other -- updated the following local packages: binstalk-types, detect-targets - -## [0.14.1](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-manifests-v0.14.0...binstalk-manifests-v0.14.1) - 2024-08-04 - -### Other -- updated the following local packages: detect-targets, fs-lock diff --git a/crates/binstalk-manifests/Cargo.toml b/crates/binstalk-manifests/Cargo.toml deleted file mode 100644 index 8f650dfb..00000000 --- a/crates/binstalk-manifests/Cargo.toml +++ /dev/null @@ -1,29 +0,0 @@ -[package] -name = "binstalk-manifests" -description = "The binstall toolkit for manipulating with manifest" -repository = "https://github.com/cargo-bins/cargo-binstall" -documentation = "https://docs.rs/binstalk-manifests" -version = "0.15.28" -rust-version = "1.61.0" -authors = ["ryan "] -edition = "2021" -license = "Apache-2.0 OR MIT" - -[dependencies] -beef = { version = "0.5.2", features = ["impl_serde"] } -binstalk-types = { version = "0.9.4", path = "../binstalk-types" } -compact_str = { version = "0.9.0", features = ["serde"] } -fs-lock = { version = "0.1.10", path = "../fs-lock", features = ["tracing"] } -home = "0.5.9" -miette = "7.0.0" -semver = { version = "1.0.17", features = ["serde"] } -serde = { version = "1.0.163", features = ["derive"] } -serde-tuple-vec-map = "1.0.1" -serde_json = "1.0.107" -thiserror = "2.0.11" -toml_edit = { version = "0.22.12", features = ["serde"] } -url = { version = "2.5.4", features = ["serde"] } - -[dev-dependencies] -detect-targets = { version = "0.1.47", path = "../detect-targets" } -tempfile = "3.5.0" diff --git a/crates/binstalk-manifests/LICENSE-APACHE b/crates/binstalk-manifests/LICENSE-APACHE deleted file mode 100644 index 1b5ec8b7..00000000 --- a/crates/binstalk-manifests/LICENSE-APACHE +++ /dev/null @@ -1,176 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS diff --git a/crates/binstalk-manifests/LICENSE-MIT b/crates/binstalk-manifests/LICENSE-MIT deleted file mode 100644 index 31aa7938..00000000 --- a/crates/binstalk-manifests/LICENSE-MIT +++ /dev/null @@ -1,23 +0,0 @@ -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/crates/binstalk-manifests/src/binstall_crates_v1.rs b/crates/binstalk-manifests/src/binstall_crates_v1.rs deleted file mode 100644 index e9282d61..00000000 --- a/crates/binstalk-manifests/src/binstall_crates_v1.rs +++ /dev/null @@ -1,327 +0,0 @@ -//! Binstall's `crates-v1.json` manifest. -//! -//! This manifest is used by Binstall to record which crates were installed, and may be used by -//! other (third party) tooling to act upon these crates (e.g. upgrade them, list them, etc). -//! -//! The format is a series of JSON object concatenated together. It is _not_ NLJSON, though writing -//! NLJSON to the file will be understood fine. - -use std::{ - borrow::Borrow, - cmp, - collections::{btree_set, BTreeSet}, - fs, - io::{self, Seek, Write}, - iter::{IntoIterator, Iterator}, - path::{Path, PathBuf}, -}; - -use compact_str::CompactString; -use fs_lock::FileLock; -use home::cargo_home; -use miette::Diagnostic; -use serde::{Deserialize, Serialize}; -use thiserror::Error; - -use crate::{crate_info::CrateInfo, helpers::create_if_not_exist}; - -/// Buffer size for loading and writing binstall_crates_v1 manifest. -const BUFFER_SIZE: usize = 4096 * 5; - -#[derive(Debug, Diagnostic, Error)] -#[non_exhaustive] -pub enum Error { - #[error("I/O Error: {0}")] - Io(#[from] io::Error), - - #[error("Failed to parse json: {0}")] - SerdeJsonParse(#[from] serde_json::Error), -} - -pub fn append_to_path(path: impl AsRef, iter: Iter) -> Result<(), Error> -where - Iter: IntoIterator, - Data: From, -{ - let path = path.as_ref(); - let mut file = create_if_not_exist(path)?; - // Move the cursor to EOF - file.seek(io::SeekFrom::End(0))?; - - write_to(&mut file, &mut iter.into_iter().map(Data::from)) -} - -pub fn append(iter: Iter) -> Result<(), Error> -where - Iter: IntoIterator, - Data: From, -{ - append_to_path(default_path()?, iter) -} - -pub fn write_to(file: &mut FileLock, iter: &mut dyn Iterator) -> Result<(), Error> { - let writer = io::BufWriter::with_capacity(BUFFER_SIZE, file); - - let mut ser = serde_json::Serializer::new(writer); - - for item in iter { - item.serialize(&mut ser)?; - } - - ser.into_inner().flush()?; - - Ok(()) -} - -pub fn default_path() -> Result { - let dir = cargo_home()?.join("binstall"); - - fs::create_dir_all(&dir)?; - - Ok(dir.join("crates-v1.json")) -} - -#[derive(Debug, Deserialize, Serialize)] -pub struct Data { - #[serde(flatten)] - pub crate_info: CrateInfo, - - /// Forwards compatibility. Unknown keys from future versions - /// will be stored here and retained when the file is saved. - /// - /// We use an `Vec` here since it is never accessed in Rust. - #[serde(flatten, with = "tuple_vec_map")] - pub other: Vec<(CompactString, serde_json::Value)>, -} - -impl From for Data { - fn from(crate_info: CrateInfo) -> Self { - Self { - crate_info, - other: Vec::new(), - } - } -} - -impl From for CrateInfo { - fn from(data: Data) -> Self { - data.crate_info - } -} - -impl Borrow for Data { - fn borrow(&self) -> &str { - &self.crate_info.name - } -} - -impl PartialEq for Data { - fn eq(&self, other: &Self) -> bool { - self.crate_info.name == other.crate_info.name - } -} -impl PartialEq for Data { - fn eq(&self, other: &CrateInfo) -> bool { - self.crate_info.name == other.name - } -} -impl PartialEq for CrateInfo { - fn eq(&self, other: &Data) -> bool { - self.name == other.crate_info.name - } -} -impl Eq for Data {} - -impl PartialOrd for Data { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for Data { - fn cmp(&self, other: &Self) -> cmp::Ordering { - self.crate_info.name.cmp(&other.crate_info.name) - } -} - -#[derive(Debug)] -pub struct Records { - file: FileLock, - /// Use BTreeSet to dedup the metadata - data: BTreeSet, -} - -impl Records { - fn load_impl(&mut self) -> Result<(), Error> { - let reader = io::BufReader::with_capacity(BUFFER_SIZE, &mut self.file); - let stream_deser = serde_json::Deserializer::from_reader(reader).into_iter(); - - for res in stream_deser { - let item = res?; - - self.data.replace(item); - } - - Ok(()) - } - - pub fn load_from_path(path: impl AsRef) -> Result { - let mut this = Self { - file: create_if_not_exist(path.as_ref())?, - data: BTreeSet::default(), - }; - this.load_impl()?; - Ok(this) - } - - pub fn load() -> Result { - Self::load_from_path(default_path()?) - } - - /// **Warning: This will overwrite all existing records!** - pub fn overwrite(mut self) -> Result<(), Error> { - self.file.rewind()?; - write_to(&mut self.file, &mut self.data.into_iter())?; - - let len = self.file.stream_position()?; - self.file.set_len(len)?; - - Ok(()) - } - - pub fn get(&self, value: impl AsRef) -> Option<&CrateInfo> { - self.data.get(value.as_ref()).map(|data| &data.crate_info) - } - - pub fn contains(&self, value: impl AsRef) -> bool { - self.data.contains(value.as_ref()) - } - - /// Adds a value to the set. - /// If the set did not have an equal element present, true is returned. - /// If the set did have an equal element present, false is returned, - /// and the entry is not updated. - pub fn insert(&mut self, value: CrateInfo) -> bool { - self.data.insert(Data::from(value)) - } - - /// Return the previous `CrateInfo` for the package if there is any. - pub fn replace(&mut self, value: CrateInfo) -> Option { - self.data.replace(Data::from(value)).map(CrateInfo::from) - } - - pub fn remove(&mut self, value: impl AsRef) -> bool { - self.data.remove(value.as_ref()) - } - - pub fn take(&mut self, value: impl AsRef) -> Option { - self.data.take(value.as_ref()).map(CrateInfo::from) - } - - pub fn len(&self) -> usize { - self.data.len() - } - - pub fn is_empty(&self) -> bool { - self.data.is_empty() - } -} - -impl<'a> IntoIterator for &'a Records { - type Item = &'a Data; - - type IntoIter = btree_set::Iter<'a, Data>; - - fn into_iter(self) -> Self::IntoIter { - self.data.iter() - } -} - -#[cfg(test)] -mod test { - use super::*; - use crate::crate_info::CrateSource; - - use compact_str::CompactString; - use detect_targets::TARGET; - use semver::Version; - use tempfile::NamedTempFile; - - macro_rules! assert_records_eq { - ($records:expr, $metadata_set:expr) => { - assert_eq!($records.len(), $metadata_set.len()); - for (record, metadata) in $records.into_iter().zip($metadata_set.iter()) { - assert_eq!(record, metadata); - } - }; - } - - #[test] - fn rw_test() { - let target = CompactString::from(TARGET); - - let named_tempfile = NamedTempFile::new().unwrap(); - let path = named_tempfile.path(); - - let metadata_vec = [ - CrateInfo { - name: "a".into(), - version_req: "*".into(), - current_version: Version::new(0, 1, 0), - source: CrateSource::cratesio_registry(), - target: target.clone(), - bins: vec!["1".into(), "2".into()], - }, - CrateInfo { - name: "b".into(), - version_req: "0.1.0".into(), - current_version: Version::new(0, 1, 0), - source: CrateSource::cratesio_registry(), - target: target.clone(), - bins: vec!["1".into(), "2".into()], - }, - CrateInfo { - name: "a".into(), - version_req: "*".into(), - current_version: Version::new(0, 2, 0), - source: CrateSource::cratesio_registry(), - target: target.clone(), - bins: vec!["1".into()], - }, - ]; - - append_to_path(path, metadata_vec.clone()).unwrap(); - - let mut iter = metadata_vec.into_iter(); - iter.next().unwrap(); - - let mut metadata_set: BTreeSet<_> = iter.collect(); - - let mut records = Records::load_from_path(path).unwrap(); - assert_records_eq!(&records, &metadata_set); - - assert!(records.remove("b")); - metadata_set.remove("b"); - assert_eq!(records.len(), metadata_set.len()); - records.overwrite().unwrap(); - - let records = Records::load_from_path(path).unwrap(); - assert_records_eq!(&records, &metadata_set); - // Drop the exclusive file lock - drop(records); - - let new_metadata = CrateInfo { - name: "b".into(), - version_req: "0.1.0".into(), - current_version: Version::new(0, 1, 1), - source: CrateSource::cratesio_registry(), - target, - bins: vec!["1".into(), "2".into()], - }; - append_to_path(path, [new_metadata.clone()]).unwrap(); - metadata_set.insert(new_metadata); - - let records = Records::load_from_path(path).unwrap(); - assert_records_eq!(&records, &metadata_set); - } -} diff --git a/crates/binstalk-manifests/src/cargo_config.rs b/crates/binstalk-manifests/src/cargo_config.rs deleted file mode 100644 index 22e25971..00000000 --- a/crates/binstalk-manifests/src/cargo_config.rs +++ /dev/null @@ -1,240 +0,0 @@ -//! Cargo's `.cargo/config.toml` -//! -//! This manifest is used by Cargo to load configurations stored by users. -//! -//! Binstall reads from them to be compatible with `cargo-install`'s behavior. - -use std::{ - borrow::Cow, - collections::BTreeMap, - fs::File, - io, - path::{Path, PathBuf}, -}; - -use compact_str::CompactString; -use fs_lock::FileLock; -use home::cargo_home; -use miette::Diagnostic; -use serde::Deserialize; -use thiserror::Error; - -#[derive(Debug, Deserialize)] -pub struct Install { - /// `cargo install` destination directory - pub root: Option, -} - -#[derive(Debug, Deserialize)] -pub struct Http { - /// HTTP proxy in libcurl format: "host:port" - /// - /// env: CARGO_HTTP_PROXY or HTTPS_PROXY or https_proxy or http_proxy - pub proxy: Option, - /// timeout for each HTTP request, in seconds - /// - /// env: CARGO_HTTP_TIMEOUT or HTTP_TIMEOUT - pub timeout: Option, - /// path to Certificate Authority (CA) bundle - pub cainfo: Option, -} - -#[derive(Eq, PartialEq, Debug, Deserialize)] -#[serde(untagged)] -pub enum Env { - Value(CompactString), - WithOptions { - value: CompactString, - force: Option, - relative: Option, - }, -} - -#[derive(Debug, Deserialize)] -pub struct Registry { - pub index: Option, -} - -#[derive(Debug, Deserialize)] -pub struct DefaultRegistry { - pub default: Option, -} - -#[derive(Debug, Default, Deserialize)] -pub struct Config { - pub install: Option, - pub http: Option, - pub env: Option>, - pub registries: Option>, - pub registry: Option, -} - -fn join_if_relative(path: Option<&mut PathBuf>, dir: &Path) { - match path { - Some(path) if path.is_relative() => *path = dir.join(&*path), - _ => (), - } -} - -impl Config { - pub fn default_path() -> Result { - Ok(cargo_home()?.join("config.toml")) - } - - pub fn load() -> Result { - Self::load_from_path(Self::default_path()?) - } - - /// * `dir` - path to the dir where the config.toml is located. - /// For relative path in the config, `Config::load_from_reader` - /// will join the `dir` and the relative path to form the final - /// path. - pub fn load_from_reader( - mut reader: R, - dir: &Path, - ) -> Result { - fn inner(reader: &mut dyn io::Read, dir: &Path) -> Result { - let mut vec = Vec::new(); - reader.read_to_end(&mut vec)?; - - if vec.is_empty() { - Ok(Default::default()) - } else { - let mut config: Config = toml_edit::de::from_slice(&vec)?; - join_if_relative( - config - .install - .as_mut() - .and_then(|install| install.root.as_mut()), - dir, - ); - join_if_relative( - config.http.as_mut().and_then(|http| http.cainfo.as_mut()), - dir, - ); - if let Some(envs) = config.env.as_mut() { - for env in envs.values_mut() { - if let Env::WithOptions { - value, - relative: Some(true), - .. - } = env - { - let path = Cow::Borrowed(Path::new(&value)); - if path.is_relative() { - *value = dir.join(&path).to_string_lossy().into(); - } - } - } - } - Ok(config) - } - } - - inner(&mut reader, dir) - } - - pub fn load_from_path(path: impl AsRef) -> Result { - fn inner(path: &Path) -> Result { - match File::open(path) { - Ok(file) => { - let file = FileLock::new_shared(file)?.set_file_path(path); - // Any regular file must have a parent dir - Config::load_from_reader(file, path.parent().unwrap()) - } - Err(err) if err.kind() == io::ErrorKind::NotFound => Ok(Default::default()), - Err(err) => Err(err.into()), - } - } - - inner(path.as_ref()) - } -} - -#[derive(Debug, Diagnostic, Error)] -#[non_exhaustive] -pub enum ConfigLoadError { - #[error("I/O Error: {0}")] - Io(#[from] io::Error), - - #[error("Failed to deserialize toml: {0}")] - TomlParse(Box), -} - -impl From for ConfigLoadError { - fn from(e: toml_edit::de::Error) -> Self { - ConfigLoadError::TomlParse(Box::new(e)) - } -} - -impl From for ConfigLoadError { - fn from(e: toml_edit::TomlError) -> Self { - ConfigLoadError::TomlParse(Box::new(e.into())) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - use std::{io::Cursor, path::MAIN_SEPARATOR}; - - use compact_str::format_compact; - - const CONFIG: &str = r#" -[env] -# Set ENV_VAR_NAME=value for any process run by Cargo -ENV_VAR_NAME = "value" -# Set even if already present in environment -ENV_VAR_NAME_2 = { value = "value", force = true } -# Value is relative to .cargo directory containing `config.toml`, make absolute -ENV_VAR_NAME_3 = { value = "relative-path", relative = true } - -[http] -debug = false # HTTP debugging -proxy = "host:port" # HTTP proxy in libcurl format -timeout = 30 # timeout for each HTTP request, in seconds -cainfo = "cert.pem" # path to Certificate Authority (CA) bundle - -[install] -root = "/some/path" # `cargo install` destination directory - "#; - - #[test] - fn test_loading() { - let config = Config::load_from_reader(Cursor::new(&CONFIG), Path::new("root")).unwrap(); - - assert_eq!( - config.install.unwrap().root.as_deref().unwrap(), - Path::new("/some/path") - ); - - let http = config.http.unwrap(); - assert_eq!(http.proxy.unwrap(), CompactString::const_new("host:port")); - assert_eq!(http.timeout.unwrap(), 30); - assert_eq!(http.cainfo.unwrap(), Path::new("root").join("cert.pem")); - - let env = config.env.unwrap(); - assert_eq!(env.len(), 3); - assert_eq!( - env.get("ENV_VAR_NAME").unwrap(), - &Env::Value(CompactString::const_new("value")) - ); - assert_eq!( - env.get("ENV_VAR_NAME_2").unwrap(), - &Env::WithOptions { - value: CompactString::new("value"), - force: Some(true), - relative: None, - } - ); - assert_eq!( - env.get("ENV_VAR_NAME_3").unwrap(), - &Env::WithOptions { - value: format_compact!("root{MAIN_SEPARATOR}relative-path"), - force: None, - relative: Some(true), - } - ); - } -} diff --git a/crates/binstalk-manifests/src/cargo_crates_v1.rs b/crates/binstalk-manifests/src/cargo_crates_v1.rs deleted file mode 100644 index 3cf0794e..00000000 --- a/crates/binstalk-manifests/src/cargo_crates_v1.rs +++ /dev/null @@ -1,313 +0,0 @@ -//! Cargo's `.crates.toml` manifest. -//! -//! This manifest is used by Cargo to record which crates were installed by `cargo-install` and by -//! other Cargo (first and third party) tooling to act upon these crates (e.g. upgrade them, list -//! them, etc). -//! -//! Binstall writes to this manifest when installing a crate, for interoperability with the Cargo -//! ecosystem. - -use std::{ - collections::BTreeMap, - fs::File, - io::{self, Seek}, - iter::IntoIterator, - path::{Path, PathBuf}, -}; - -use beef::Cow; -use compact_str::CompactString; -use fs_lock::FileLock; -use home::cargo_home; -use miette::Diagnostic; -use semver::Version; -use serde::{Deserialize, Serialize}; -use thiserror::Error; - -use crate::helpers::create_if_not_exist; - -use super::crate_info::CrateInfo; - -mod crate_version_source; -use crate_version_source::*; - -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -pub struct CratesToml<'a> { - #[serde(with = "tuple_vec_map")] - v1: Vec<(String, Cow<'a, [CompactString]>)>, -} - -impl CratesToml<'_> { - pub fn default_path() -> Result { - Ok(cargo_home()?.join(".crates.toml")) - } - - pub fn load() -> Result { - Self::load_from_path(Self::default_path()?) - } - - pub fn load_from_reader(mut reader: R) -> Result { - fn inner(reader: &mut dyn io::Read) -> Result, CratesTomlParseError> { - let mut vec = Vec::new(); - reader.read_to_end(&mut vec)?; - - if vec.is_empty() { - Ok(CratesToml::default()) - } else { - toml_edit::de::from_slice(&vec).map_err(CratesTomlParseError::from) - } - } - - inner(&mut reader) - } - - pub fn load_from_path(path: impl AsRef) -> Result { - let path = path.as_ref(); - let file = FileLock::new_shared(File::open(path)?)?.set_file_path(path); - Self::load_from_reader(file) - } - - pub fn remove(&mut self, name: &str) { - self.v1.retain(|(s, _bin)| { - s.split_once(' ') - .map(|(crate_name, _rest)| crate_name != name) - .unwrap_or_default() - }); - } - - pub fn write(&self) -> Result<(), CratesTomlParseError> { - self.write_to_path(Self::default_path()?) - } - - pub fn write_to_writer(&self, mut writer: W) -> Result<(), CratesTomlParseError> { - fn inner( - this: &CratesToml<'_>, - writer: &mut dyn io::Write, - ) -> Result<(), CratesTomlParseError> { - let data = toml_edit::ser::to_string_pretty(&this)?; - writer.write_all(data.as_bytes())?; - Ok(()) - } - - inner(self, &mut writer) - } - - pub fn write_to_file(&self, file: &mut File) -> Result<(), CratesTomlParseError> { - self.write_to_writer(&mut *file)?; - let pos = file.stream_position()?; - file.set_len(pos)?; - - Ok(()) - } - - pub fn write_to_path(&self, path: impl AsRef) -> Result<(), CratesTomlParseError> { - let path = path.as_ref(); - let mut file = FileLock::new_exclusive(File::create(path)?)?.set_file_path(path); - self.write_to_file(&mut file) - } - - pub fn append_to_file<'a, Iter>(file: &mut File, iter: Iter) -> Result<(), CratesTomlParseError> - where - Iter: IntoIterator, - { - fn inner( - file: &mut File, - iter: &mut dyn Iterator, - ) -> Result<(), CratesTomlParseError> { - let mut c1 = CratesToml::load_from_reader(&mut *file)?; - - for metadata in iter { - let name = &metadata.name; - let version = &metadata.current_version; - let source = Source::from(&metadata.source); - - c1.remove(name); - c1.v1.push(( - format!("{name} {version} ({source})"), - Cow::borrowed(&metadata.bins), - )); - } - - file.rewind()?; - c1.write_to_file(file)?; - - Ok(()) - } - - inner(file, &mut iter.into_iter()) - } - - pub fn append_to_path<'a, Iter>( - path: impl AsRef, - iter: Iter, - ) -> Result<(), CratesTomlParseError> - where - Iter: IntoIterator, - { - let mut file = create_if_not_exist(path.as_ref())?; - Self::append_to_file(&mut file, iter) - } - - pub fn append<'a, Iter>(iter: Iter) -> Result<(), CratesTomlParseError> - where - Iter: IntoIterator, - { - Self::append_to_path(Self::default_path()?, iter) - } - - /// Return BTreeMap with crate name as key and its corresponding version - /// as value. - pub fn collect_into_crates_versions( - self, - ) -> Result, CratesTomlParseError> { - fn parse_name_ver(s: &str) -> Result<(CompactString, Version), CvsParseError> { - match s.splitn(3, ' ').collect::>()[..] { - [name, version, _source] => Ok((CompactString::new(name), version.parse()?)), - _ => Err(CvsParseError::BadFormat), - } - } - - self.v1 - .into_iter() - .map(|(s, _bins)| parse_name_ver(&s).map_err(CratesTomlParseError::from)) - .collect() - } -} - -#[derive(Debug, Diagnostic, Error)] -#[non_exhaustive] -pub enum CratesTomlParseError { - #[error("I/O Error: {0}")] - Io(#[from] io::Error), - - #[error("Failed to deserialize toml: {0}")] - TomlParse(Box), - - #[error("Failed to serialie toml: {0}")] - TomlWrite(Box), - - #[error(transparent)] - CvsParse(Box), -} - -impl From for CratesTomlParseError { - fn from(e: CvsParseError) -> Self { - CratesTomlParseError::CvsParse(Box::new(e)) - } -} - -impl From for CratesTomlParseError { - fn from(e: toml_edit::ser::Error) -> Self { - CratesTomlParseError::TomlWrite(Box::new(e)) - } -} - -impl From for CratesTomlParseError { - fn from(e: toml_edit::de::Error) -> Self { - CratesTomlParseError::TomlParse(Box::new(e)) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::crate_info::CrateSource; - - use detect_targets::TARGET; - use semver::Version; - use tempfile::TempDir; - - #[test] - fn test_empty() { - let tempdir = TempDir::new().unwrap(); - let path = tempdir.path().join("crates-v1.toml"); - - CratesToml::append_to_path( - &path, - &[CrateInfo { - name: "cargo-binstall".into(), - version_req: "*".into(), - current_version: Version::new(0, 11, 1), - source: CrateSource::cratesio_registry(), - target: TARGET.into(), - bins: vec!["cargo-binstall".into()], - }], - ) - .unwrap(); - - let crates = CratesToml::load_from_path(&path) - .unwrap() - .collect_into_crates_versions() - .unwrap(); - - assert_eq!(crates.len(), 1); - - assert_eq!( - crates.get("cargo-binstall").unwrap(), - &Version::new(0, 11, 1) - ); - - // Update - CratesToml::append_to_path( - &path, - &[CrateInfo { - name: "cargo-binstall".into(), - version_req: "*".into(), - current_version: Version::new(0, 12, 0), - source: CrateSource::cratesio_registry(), - target: TARGET.into(), - bins: vec!["cargo-binstall".into()], - }], - ) - .unwrap(); - - let crates = CratesToml::load_from_path(&path) - .unwrap() - .collect_into_crates_versions() - .unwrap(); - - assert_eq!(crates.len(), 1); - - assert_eq!( - crates.get("cargo-binstall").unwrap(), - &Version::new(0, 12, 0) - ); - } - - #[test] - fn test_empty_file() { - let tempdir = TempDir::new().unwrap(); - let path = tempdir.path().join("crates-v1.toml"); - - File::create(&path).unwrap(); - - assert!(CratesToml::load_from_path(&path).unwrap().v1.is_empty()); - } - - #[test] - fn test_loading() { - let raw_data = br#" -[v1] -"alacritty 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = ["alacritty"] -"cargo-audit 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)" = ["cargo-audit"] -"cargo-binstall 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = ["cargo-binstall"] -"cargo-criterion 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = ["cargo-criterion"] -"cargo-edit 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = ["cargo-add", "cargo-rm", "cargo-set-version", "cargo-upgrade"] -"cargo-expand 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)" = ["cargo-expand"] -"cargo-geiger 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = ["cargo-geiger"] -"cargo-hack 0.5.15 (registry+https://github.com/rust-lang/crates.io-index)" = ["cargo-hack"] -"cargo-nextest 0.9.26 (registry+https://github.com/rust-lang/crates.io-index)" = ["cargo-nextest"] -"cargo-supply-chain 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = ["cargo-supply-chain"] -"cargo-tarpaulin 0.20.1 (registry+https://github.com/rust-lang/crates.io-index)" = ["cargo-tarpaulin"] -"cargo-update 8.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = ["cargo-install-update", "cargo-install-update-config"] -"cargo-watch 8.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = ["cargo-watch"] -"cargo-with 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = ["cargo-with"] -"cross 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = ["cross", "cross-util"] -"irust 1.63.3 (registry+https://github.com/rust-lang/crates.io-index)" = ["irust"] -"tokei 12.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = ["tokei"] -"xargo 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)" = ["xargo", "xargo-check"] - "#; - - CratesToml::load_from_reader(raw_data.as_slice()).unwrap(); - } -} diff --git a/crates/binstalk-manifests/src/cargo_crates_v1/crate_version_source.rs b/crates/binstalk-manifests/src/cargo_crates_v1/crate_version_source.rs deleted file mode 100644 index a705bba2..00000000 --- a/crates/binstalk-manifests/src/cargo_crates_v1/crate_version_source.rs +++ /dev/null @@ -1,149 +0,0 @@ -use std::{borrow::Cow, fmt, str::FromStr}; - -use binstalk_types::maybe_owned::MaybeOwned; -use compact_str::CompactString; -use miette::Diagnostic; -use semver::Version; -use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use thiserror::Error; -use url::Url; - -use crate::crate_info::{CrateInfo, CrateSource, SourceType}; - -#[derive(Clone, Debug, Ord, PartialOrd, Eq, PartialEq)] -pub struct CrateVersionSource { - pub name: CompactString, - pub version: Version, - pub source: Source<'static>, -} - -impl From<&CrateInfo> for CrateVersionSource { - fn from(metadata: &CrateInfo) -> Self { - use SourceType::*; - - let url = metadata.source.url.clone(); - - super::CrateVersionSource { - name: metadata.name.clone(), - version: metadata.current_version.clone(), - source: match metadata.source.source_type { - Git => Source::Git(url), - Path => Source::Path(url), - Registry => Source::Registry(url), - }, - } - } -} - -#[derive(Clone, Debug, Ord, PartialOrd, Eq, PartialEq)] -pub enum Source<'a> { - Git(MaybeOwned<'a, Url>), - Path(MaybeOwned<'a, Url>), - Registry(MaybeOwned<'a, Url>), -} - -impl<'a> From<&'a CrateSource> for Source<'a> { - fn from(source: &'a CrateSource) -> Self { - use SourceType::*; - - let url = MaybeOwned::Borrowed(source.url.as_ref()); - - match source.source_type { - Git => Self::Git(url), - Path => Self::Path(url), - Registry => Self::Registry(url), - } - } -} - -impl FromStr for CrateVersionSource { - type Err = CvsParseError; - fn from_str(s: &str) -> Result { - match s.splitn(3, ' ').collect::>()[..] { - [name, version, source] => { - let version = version.parse()?; - let source = match source - .trim_matches(&['(', ')'][..]) - .splitn(2, '+') - .collect::>()[..] - { - ["git", url] => Source::Git(Url::parse(url)?.into()), - ["path", url] => Source::Path(Url::parse(url)?.into()), - ["registry", url] => Source::Registry(Url::parse(url)?.into()), - [kind, arg] => { - return Err(CvsParseError::UnknownSourceType { - kind: kind.to_string().into_boxed_str(), - arg: arg.to_string().into_boxed_str(), - }) - } - _ => return Err(CvsParseError::BadSource), - }; - Ok(Self { - name: name.into(), - version, - source, - }) - } - _ => Err(CvsParseError::BadFormat), - } - } -} - -#[derive(Debug, Diagnostic, Error)] -#[non_exhaustive] -pub enum CvsParseError { - #[error("Failed to parse url in cvs: {0}")] - UrlParse(#[from] url::ParseError), - - #[error("Failed to parse version in cvs: {0}")] - VersionParse(#[from] semver::Error), - - #[error("unknown source type {kind}+{arg}")] - UnknownSourceType { kind: Box, arg: Box }, - - #[error("bad source format")] - BadSource, - - #[error("bad CVS format")] - BadFormat, -} - -impl fmt::Display for CrateVersionSource { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { - name, - version, - source, - } = &self; - write!(f, "{name} {version} ({source})") - } -} - -impl fmt::Display for Source<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Source::Git(url) => write!(f, "git+{url}"), - Source::Path(url) => write!(f, "path+{url}"), - Source::Registry(url) => write!(f, "registry+{url}"), - } - } -} - -impl Serialize for CrateVersionSource { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serializer.serialize_str(&self.to_string()) - } -} - -impl<'de> Deserialize<'de> for CrateVersionSource { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let s = Cow::<'_, str>::deserialize(deserializer)?; - Self::from_str(&s).map_err(serde::de::Error::custom) - } -} diff --git a/crates/binstalk-manifests/src/crates_manifests.rs b/crates/binstalk-manifests/src/crates_manifests.rs deleted file mode 100644 index 3208b5e5..00000000 --- a/crates/binstalk-manifests/src/crates_manifests.rs +++ /dev/null @@ -1,89 +0,0 @@ -use std::{ - collections::BTreeMap, - fs, - io::{self, Seek}, - path::Path, -}; - -use fs_lock::FileLock; -use miette::Diagnostic; -use thiserror::Error as ThisError; - -use crate::{ - binstall_crates_v1::{Error as BinstallCratesV1Error, Records as BinstallCratesV1Records}, - cargo_crates_v1::{CratesToml, CratesTomlParseError}, - crate_info::CrateInfo, - helpers::create_if_not_exist, - CompactString, Version, -}; - -#[derive(Debug, Diagnostic, ThisError)] -#[non_exhaustive] -pub enum ManifestsError { - #[error("failed to parse binstall crates-v1 manifest: {0}")] - #[diagnostic(transparent)] - BinstallCratesV1(#[from] BinstallCratesV1Error), - - #[error("failed to parse cargo v1 manifest: {0}")] - #[diagnostic(transparent)] - CargoManifestV1(#[from] CratesTomlParseError), - - #[error("I/O error: {0}")] - Io(#[from] io::Error), -} - -pub struct Manifests { - binstall: BinstallCratesV1Records, - cargo_crates_v1: FileLock, -} - -impl Manifests { - pub fn open_exclusive(cargo_roots: &Path) -> Result { - // Read cargo_binstall_metadata - let metadata_path = cargo_roots.join("binstall/crates-v1.json"); - fs::create_dir_all(metadata_path.parent().unwrap())?; - - let binstall = BinstallCratesV1Records::load_from_path(&metadata_path)?; - - // Read cargo_install_v1_metadata - let manifest_path = cargo_roots.join(".crates.toml"); - - let cargo_crates_v1 = create_if_not_exist(&manifest_path)?; - - Ok(Self { - binstall, - cargo_crates_v1, - }) - } - - fn rewind_cargo_crates_v1(&mut self) -> Result<(), ManifestsError> { - self.cargo_crates_v1.rewind().map_err(ManifestsError::from) - } - - /// `cargo-uninstall` can be called to uninstall crates, - /// but it only updates .crates.toml. - /// - /// So here we will honour .crates.toml only. - pub fn load_installed_crates( - &mut self, - ) -> Result, ManifestsError> { - self.rewind_cargo_crates_v1()?; - - CratesToml::load_from_reader(&mut self.cargo_crates_v1) - .and_then(CratesToml::collect_into_crates_versions) - .map_err(ManifestsError::from) - } - - pub fn update(mut self, metadata_vec: Vec) -> Result<(), ManifestsError> { - self.rewind_cargo_crates_v1()?; - - CratesToml::append_to_file(&mut self.cargo_crates_v1, &metadata_vec)?; - - for metadata in metadata_vec { - self.binstall.replace(metadata); - } - self.binstall.overwrite()?; - - Ok(()) - } -} diff --git a/crates/binstalk-manifests/src/helpers.rs b/crates/binstalk-manifests/src/helpers.rs deleted file mode 100644 index 45d55fb8..00000000 --- a/crates/binstalk-manifests/src/helpers.rs +++ /dev/null @@ -1,15 +0,0 @@ -use std::{fs, io, path::Path}; - -use fs_lock::FileLock; - -/// Return exclusively locked file that is readable and writable. -pub(crate) fn create_if_not_exist(path: &Path) -> io::Result { - fs::File::options() - .read(true) - .write(true) - .create(true) - .truncate(false) - .open(path) - .and_then(FileLock::new_exclusive) - .map(|file_lock| file_lock.set_file_path(path)) -} diff --git a/crates/binstalk-manifests/src/lib.rs b/crates/binstalk-manifests/src/lib.rs deleted file mode 100644 index c61b5181..00000000 --- a/crates/binstalk-manifests/src/lib.rs +++ /dev/null @@ -1,22 +0,0 @@ -//! Manifest formats and utilities. -//! -//! There are three types of manifests Binstall may deal with: -//! - manifests that define how to fetch and install a package -//! ([Cargo.toml's `[metadata.binstall]`][cargo_toml_binstall]); -//! - manifests that record which packages _are_ installed -//! ([Cargo's `.crates.toml`][cargo_crates_v1] and -//! [Binstall's `.crates-v1.json`][binstall_crates_v1]); -//! - manifests that specify which packages _to_ install (currently none). - -mod helpers; - -pub mod binstall_crates_v1; -pub mod cargo_config; -pub mod cargo_crates_v1; -/// Contains both [`binstall_crates_v1`] and [`cargo_crates_v1`]. -pub mod crates_manifests; - -pub use binstalk_types::{cargo_toml_binstall, crate_info}; -pub use compact_str::CompactString; -pub use semver::Version; -pub use url::Url; diff --git a/crates/binstalk-registry/CHANGELOG.md b/crates/binstalk-registry/CHANGELOG.md deleted file mode 100644 index e1f6edfc..00000000 --- a/crates/binstalk-registry/CHANGELOG.md +++ /dev/null @@ -1,120 +0,0 @@ -# Changelog -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - -## [Unreleased] - -## [0.11.18](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-registry-v0.11.17...binstalk-registry-v0.11.18) - 2025-04-05 - -### Other - -- updated the following local packages: binstalk-downloader, binstalk-downloader - -## [0.11.17](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-registry-v0.11.16...binstalk-registry-v0.11.17) - 2025-03-19 - -### Other - -- updated the following local packages: binstalk-downloader, binstalk-downloader - -## [0.11.16](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-registry-v0.11.15...binstalk-registry-v0.11.16) - 2025-03-15 - -### Other - -- *(deps)* bump the deps group with 2 updates ([#2084](https://github.com/cargo-bins/cargo-binstall/pull/2084)) -- *(deps)* bump tokio from 1.43.0 to 1.44.0 in the deps group ([#2079](https://github.com/cargo-bins/cargo-binstall/pull/2079)) - -## [0.11.15](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-registry-v0.11.14...binstalk-registry-v0.11.15) - 2025-03-07 - -### Other - -- *(deps)* bump the deps group with 3 updates ([#2072](https://github.com/cargo-bins/cargo-binstall/pull/2072)) - -## [0.11.14](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-registry-v0.11.13...binstalk-registry-v0.11.14) - 2025-02-28 - -### Other - -- updated the following local packages: binstalk-downloader, binstalk-downloader - -## [0.11.13](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-registry-v0.11.12...binstalk-registry-v0.11.13) - 2025-02-11 - -### Other - -- updated the following local packages: binstalk-types, binstalk-downloader, binstalk-downloader - -## [0.11.12](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-registry-v0.11.11...binstalk-registry-v0.11.12) - 2025-02-04 - -### Other - -- updated the following local packages: binstalk-downloader, binstalk-downloader - -## [0.11.11](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-registry-v0.11.10...binstalk-registry-v0.11.11) - 2025-01-19 - -### Other - -- update Cargo.lock dependencies - -## [0.11.10](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-registry-v0.11.9...binstalk-registry-v0.11.10) - 2025-01-13 - -### Other - -- update Cargo.lock dependencies - -## [0.11.9](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-registry-v0.11.8...binstalk-registry-v0.11.9) - 2025-01-11 - -### Other - -- *(deps)* bump the deps group with 3 updates (#2015) - -## [0.11.8](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-registry-v0.11.7...binstalk-registry-v0.11.8) - 2025-01-04 - -### Other - -- *(deps)* bump the deps group with 2 updates (#2010) - -## [0.11.7](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-registry-v0.11.6...binstalk-registry-v0.11.7) - 2024-12-14 - -### Other - -- *(deps)* bump the deps group with 2 updates (#1997) - -## [0.11.6](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-registry-v0.11.5...binstalk-registry-v0.11.6) - 2024-12-07 - -### Other - -- updated the following local packages: cargo-toml-workspace - -## [0.11.5](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-registry-v0.11.4...binstalk-registry-v0.11.5) - 2024-11-23 - -### Other - -- *(deps)* bump the deps group with 2 updates ([#1981](https://github.com/cargo-bins/cargo-binstall/pull/1981)) - -## [0.11.4](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-registry-v0.11.3...binstalk-registry-v0.11.4) - 2024-11-09 - -### Other - -- *(deps)* bump the deps group with 3 updates ([#1966](https://github.com/cargo-bins/cargo-binstall/pull/1966)) - -## [0.11.3](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-registry-v0.11.2...binstalk-registry-v0.11.3) - 2024-11-05 - -### Other - -- *(deps)* bump the deps group with 3 updates ([#1954](https://github.com/cargo-bins/cargo-binstall/pull/1954)) - -## [0.11.2](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-registry-v0.11.1...binstalk-registry-v0.11.2) - 2024-11-02 - -### Other - -- updated the following local packages: binstalk-downloader, binstalk-downloader - -## [0.11.1](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-registry-v0.11.0...binstalk-registry-v0.11.1) - 2024-08-12 - -### Other -- updated the following local packages: binstalk-downloader, binstalk-downloader - -## [0.11.0](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-registry-v0.10.0...binstalk-registry-v0.11.0) - 2024-08-10 - -### Other -- updated the following local packages: binstalk-types, binstalk-downloader, binstalk-downloader diff --git a/crates/binstalk-registry/Cargo.toml b/crates/binstalk-registry/Cargo.toml deleted file mode 100644 index 4c6b879d..00000000 --- a/crates/binstalk-registry/Cargo.toml +++ /dev/null @@ -1,57 +0,0 @@ -[package] -name = "binstalk-registry" -version = "0.11.18" -edition = "2021" -rust-version = "1.65.0" - -description = "The binstall toolkit for fetching package from arbitrary registry" -repository = "https://github.com/cargo-bins/cargo-binstall" -documentation = "https://docs.rs/binstalk-registry" -authors = ["Jiahao_XU@outlook "] -license = "Apache-2.0 OR MIT" - -[dependencies] -async-trait = "0.1.88" -base16 = "0.2.1" -binstalk-downloader = { version = "0.13.17", path = "../binstalk-downloader", default-features = false, features = [ - "json", -] } -binstalk-types = { version = "0.9.4", path = "../binstalk-types" } -cargo-toml-workspace = { version = "7.0.6", path = "../cargo-toml-workspace" } -compact_str = { version = "0.9.0", features = ["serde"] } -leon = "3.0.0" -miette = "7.0.0" -normalize-path = { version = "0.2.1", path = "../normalize-path" } -once_cell = "1.18.0" -semver = { version = "1.0.17", features = ["serde"] } -serde = { version = "1.0.163", features = ["derive"] } -serde_json = "1.0.107" -sha2 = "0.10.7" -simple-git = { version = "0.2.4", optional = true } -tempfile = "3.5.0" -thiserror = "2.0.11" -tokio = { version = "1.44.0", features = [ - "rt", - "sync", -], default-features = false } -tracing = "0.1.39" -url = "2.5.4" - -[dev-dependencies] -tokio = { version = "1", features = ["rt-multi-thread", "macros"] } -toml_edit = { version = "0.22.12", features = ["serde"] } -binstalk-downloader = { version = "0.13.17", path = "../binstalk-downloader", default-features = false, features = [ - "rustls", -] } - -[features] -git = ["simple-git"] - -rustls = ["simple-git?/rustls"] -native-tls = ["simple-git?/native-tls"] - -crates_io_api = [] - -[package.metadata.docs.rs] -rustdoc-args = ["--cfg", "docsrs"] -all-features = true diff --git a/crates/binstalk-registry/LICENSE-APACHE b/crates/binstalk-registry/LICENSE-APACHE deleted file mode 100644 index 1b5ec8b7..00000000 --- a/crates/binstalk-registry/LICENSE-APACHE +++ /dev/null @@ -1,176 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS diff --git a/crates/binstalk-registry/LICENSE-MIT b/crates/binstalk-registry/LICENSE-MIT deleted file mode 100644 index 31aa7938..00000000 --- a/crates/binstalk-registry/LICENSE-MIT +++ /dev/null @@ -1,23 +0,0 @@ -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/crates/binstalk-registry/src/common.rs b/crates/binstalk-registry/src/common.rs deleted file mode 100644 index cd426298..00000000 --- a/crates/binstalk-registry/src/common.rs +++ /dev/null @@ -1,216 +0,0 @@ -use std::borrow::Cow; - -use base16::{decode as decode_base16, encode_lower as encode_base16}; -use binstalk_downloader::{ - bytes::Bytes, - download::{DataVerifier, Download}, - remote::{Client, Url}, -}; -use binstalk_types::cargo_toml_binstall::{Meta, TarBasedFmt}; -use cargo_toml_workspace::cargo_toml::Manifest; -use compact_str::{format_compact, CompactString, ToCompactString}; -use leon::{Template, Values}; -use semver::{Version, VersionReq}; -use serde::Deserialize; -use serde_json::Error as JsonError; -use sha2::{Digest, Sha256}; -use tracing::{debug, instrument}; - -use crate::{visitor::ManifestVisitor, RegistryError}; - -#[derive(Deserialize)] -pub(super) struct RegistryConfig { - pub(super) dl: CompactString, -} - -struct Sha256Digest { - expected: Vec, - actual: Option>, - state: Option, -} - -impl Sha256Digest { - fn new(checksum: Vec) -> Self { - Self { - expected: checksum, - actual: None, - state: Some(Sha256::new()), - } - } -} - -impl DataVerifier for Sha256Digest { - fn update(&mut self, data: &Bytes) { - if let Some(ref mut state) = &mut self.state { - state.update(data); - } - } - - fn validate(&mut self) -> bool { - if let Some(state) = self.state.take() { - self.actual = Some(state.finalize().to_vec()); - } - - self.actual.as_ref().unwrap() == &self.expected - } -} - -#[instrument( - skip(client, crate_url), - fields( - crate_url = format_args!("{crate_url}"), - ), -)] -pub(super) async fn parse_manifest( - client: Client, - crate_name: &str, - crate_url: Url, - MatchedVersion { version, cksum }: MatchedVersion, -) -> Result, RegistryError> { - debug!("Fetching crate from: {crate_url} and extracting Cargo.toml from it"); - - let mut manifest_visitor = ManifestVisitor::new(format!("{crate_name}-{version}").into()); - - let checksum = decode_base16(cksum.as_bytes()).map_err(RegistryError::from)?; - let mut digest = Sha256Digest::new(checksum); - - Download::new_with_data_verifier(client, crate_url, &mut digest) - .and_visit_tar(TarBasedFmt::Tgz, &mut manifest_visitor) - .await?; - - if !digest.validate() { - Err(RegistryError::UnmatchedChecksum { - expected: encode_base16(digest.expected.as_slice()).into(), - actual: encode_base16(digest.actual.unwrap().as_slice()).into(), - }) - } else { - manifest_visitor.load_manifest() - } -} - -/// Return components of crate prefix -pub(super) fn crate_prefix_components( - crate_name: &str, -) -> Result<(CompactString, Option), RegistryError> { - let mut chars = crate_name.chars(); - - match (chars.next(), chars.next(), chars.next(), chars.next()) { - (None, None, None, None) => Err(RegistryError::NotFound(crate_name.into())), - (Some(_), None, None, None) => Ok((CompactString::const_new("1"), None)), - (Some(_), Some(_), None, None) => Ok((CompactString::const_new("2"), None)), - (Some(ch), Some(_), Some(_), None) => Ok(( - CompactString::const_new("3"), - Some(ch.to_lowercase().to_compact_string()), - )), - (Some(a), Some(b), Some(c), Some(d)) => Ok(( - format_compact!("{}{}", a.to_lowercase(), b.to_lowercase()), - Some(format_compact!("{}{}", c.to_lowercase(), d.to_lowercase())), - )), - _ => unreachable!(), - } -} - -pub(super) fn render_dl_template( - dl_template: &str, - crate_name: &str, - (c1, c2): &(CompactString, Option), - MatchedVersion { version, cksum }: &MatchedVersion, -) -> Result { - let template = Template::parse(dl_template)?; - if template.keys().next().is_some() { - let mut crate_prefix = c1.clone(); - if let Some(c2) = c2 { - crate_prefix.push('/'); - crate_prefix.push_str(c2); - } - - struct Context<'a> { - crate_name: &'a str, - crate_prefix: CompactString, - crate_lowerprefix: CompactString, - version: &'a str, - cksum: &'a str, - } - impl Values for Context<'_> { - fn get_value(&self, key: &str) -> Option> { - match key { - "crate" => Some(Cow::Borrowed(self.crate_name)), - "version" => Some(Cow::Borrowed(self.version)), - "prefix" => Some(Cow::Borrowed(&self.crate_prefix)), - "lowerprefix" => Some(Cow::Borrowed(&self.crate_lowerprefix)), - "sha256-checksum" => Some(Cow::Borrowed(self.cksum)), - _ => None, - } - } - } - Ok(template.render(&Context { - crate_name, - crate_lowerprefix: crate_prefix.to_lowercase(), - crate_prefix, - version, - cksum, - })?) - } else { - Ok(format!("{dl_template}/{crate_name}/{version}/download")) - } -} - -#[derive(Deserialize)] -pub(super) struct RegistryIndexEntry { - vers: CompactString, - yanked: bool, - cksum: String, -} - -pub(super) struct MatchedVersion { - pub(super) version: CompactString, - /// sha256 checksum encoded in base16 - pub(super) cksum: String, -} - -impl MatchedVersion { - pub(super) fn find( - it: &mut dyn Iterator>, - version_req: &VersionReq, - ) -> Result { - let mut ret = Option::<(Self, Version)>::None; - - for res in it { - let entry = res.map_err(RegistryError::from)?; - - if entry.yanked { - continue; - } - - let num = entry.vers; - - // Parse out version - let Ok(ver) = Version::parse(&num) else { - continue; - }; - - // Filter by version match - if !version_req.matches(&ver) { - continue; - } - - let matched = Self { - version: num, - cksum: entry.cksum, - }; - - if let Some((_, max_ver)) = &ret { - if ver > *max_ver { - ret = Some((matched, ver)); - } - } else { - ret = Some((matched, ver)); - } - } - - ret.map(|(num, _)| num) - .ok_or_else(|| RegistryError::VersionMismatch { - req: version_req.clone(), - }) - } -} diff --git a/crates/binstalk-registry/src/crates_io_registry.rs b/crates/binstalk-registry/src/crates_io_registry.rs deleted file mode 100644 index b07b6a1a..00000000 --- a/crates/binstalk-registry/src/crates_io_registry.rs +++ /dev/null @@ -1,166 +0,0 @@ -use binstalk_downloader::remote::{Client, Error as RemoteError, Url}; -use binstalk_types::cargo_toml_binstall::Meta; -use cargo_toml_workspace::cargo_toml::Manifest; -use compact_str::{CompactString, ToCompactString}; -use semver::{Comparator, Op as ComparatorOp, Version as SemVersion, VersionReq}; -use serde::Deserialize; -use tracing::{debug, instrument}; - -use crate::{parse_manifest, MatchedVersion, RegistryError}; - -/// Return `Some(checksum)` if the version is not yanked, otherwise `None`. -async fn is_crate_yanked(client: &Client, url: Url) -> Result, RemoteError> { - #[derive(Deserialize)] - struct CrateInfo { - version: Inner, - } - - #[derive(Deserialize)] - struct Inner { - yanked: bool, - checksum: String, - } - - // Fetch / update index - debug!("Looking up crate information"); - - let info: CrateInfo = client.get(url).send(true).await?.json().await?; - let version = info.version; - - Ok((!version.yanked).then_some(version.checksum)) -} - -async fn fetch_crate_cratesio_version_matched( - client: &Client, - url: Url, - version_req: &VersionReq, -) -> Result, RemoteError> { - #[derive(Deserialize)] - struct CrateInfo { - #[serde(rename = "crate")] - inner: CrateInfoInner, - - versions: Vec, - } - - #[derive(Deserialize)] - struct CrateInfoInner { - max_stable_version: CompactString, - } - - #[derive(Deserialize)] - struct Version { - num: CompactString, - yanked: bool, - checksum: String, - } - - // Fetch / update index - debug!("Looking up crate information"); - - let crate_info: CrateInfo = client.get(url).send(true).await?.json().await?; - - let version_with_checksum = if version_req == &VersionReq::STAR { - let version = crate_info.inner.max_stable_version; - crate_info - .versions - .into_iter() - .find_map(|v| (v.num.as_str() == version.as_str()).then_some(v.checksum)) - .map(|checksum| (version, checksum)) - } else { - crate_info - .versions - .into_iter() - .filter_map(|item| { - if !item.yanked { - // Remove leading `v` for git tags - let num = if let Some(num) = item.num.strip_prefix('v') { - num.into() - } else { - item.num - }; - - // Parse out version - let ver = semver::Version::parse(&num).ok()?; - - // Filter by version match - version_req - .matches(&ver) - .then_some((num, ver, item.checksum)) - } else { - None - } - }) - // Return highest version - .max_by( - |(_ver_str_x, ver_x, _checksum_x), (_ver_str_y, ver_y, _checksum_y)| { - ver_x.cmp(ver_y) - }, - ) - .map(|(ver_str, _, checksum)| (ver_str, checksum)) - }; - - Ok(version_with_checksum) -} - -/// Find the crate by name, get its latest stable version matches `version_req`, -/// retrieve its Cargo.toml and infer all its bins. -#[instrument( - skip(client), - fields( - version_req = format_args!("{version_req}"), - ) -)] -pub async fn fetch_crate_cratesio_api( - client: Client, - name: &str, - version_req: &VersionReq, -) -> Result, RegistryError> { - let url = Url::parse(&format!("https://crates.io/api/v1/crates/{name}"))?; - - let (version, cksum) = match version_req.comparators.as_slice() { - [Comparator { - op: ComparatorOp::Exact, - major, - minor: Some(minor), - patch: Some(patch), - pre, - }] => { - let version = SemVersion { - major: *major, - minor: *minor, - patch: *patch, - pre: pre.clone(), - build: Default::default(), - } - .to_compact_string(); - - let mut url = url.clone(); - url.path_segments_mut().unwrap().push(&version); - - is_crate_yanked(&client, url) - .await - .map(|ret| ret.map(|checksum| (version, checksum))) - } - _ => fetch_crate_cratesio_version_matched(&client, url.clone(), version_req).await, - } - .map_err(|e| match e { - RemoteError::Http(e) if e.is_status() => RegistryError::NotFound(name.into()), - e => e.into(), - })? - .ok_or_else(|| RegistryError::VersionMismatch { - req: version_req.clone(), - })?; - - debug!("Found information for crate version: '{version}'"); - - // Download crate to temporary dir (crates.io or git?) - let mut crate_url = url; - crate_url - .path_segments_mut() - .unwrap() - .push(&version) - .push("download"); - - parse_manifest(client, name, crate_url, MatchedVersion { version, cksum }).await -} diff --git a/crates/binstalk-registry/src/git_registry.rs b/crates/binstalk-registry/src/git_registry.rs deleted file mode 100644 index b707b602..00000000 --- a/crates/binstalk-registry/src/git_registry.rs +++ /dev/null @@ -1,154 +0,0 @@ -use std::{fmt::Display, io, path::PathBuf, sync::Arc}; - -use binstalk_downloader::remote::Client; -use binstalk_types::cargo_toml_binstall::Meta; -use cargo_toml_workspace::cargo_toml::Manifest; -use compact_str::{CompactString, ToCompactString}; -use once_cell::sync::OnceCell; -use semver::VersionReq; -use serde_json::{from_slice as json_from_slice, Deserializer as JsonDeserializer}; -use simple_git::{GitCancellationToken, GitUrl, Repository}; -use tempfile::TempDir; -use tokio::task::spawn_blocking; -use tracing::instrument; -use url::Url; - -use crate::{ - crate_prefix_components, parse_manifest, render_dl_template, MatchedVersion, RegistryConfig, - RegistryError, -}; - -#[derive(Debug)] -struct GitIndex { - _tempdir: TempDir, - repo: Repository, - dl_template: CompactString, -} - -impl GitIndex { - fn new(url: GitUrl, cancellation_token: GitCancellationToken) -> Result { - let tempdir = TempDir::new()?; - - let repo = Repository::shallow_clone_bare( - url.clone(), - tempdir.as_ref(), - Some(cancellation_token), - )?; - - let config: RegistryConfig = { - let config = repo - .get_head_commit_entry_data_by_path("config.json")? - .ok_or_else(|| { - io::Error::new( - io::ErrorKind::NotFound, - format!("config.json not found in repository `{url}`"), - ) - })?; - - json_from_slice(&config).map_err(RegistryError::from)? - }; - - Ok(Self { - _tempdir: tempdir, - repo, - dl_template: config.dl, - }) - } -} - -#[derive(Debug)] -struct GitRegistryInner { - url: GitUrl, - git_index: OnceCell, -} - -#[derive(Clone, Debug)] -pub struct GitRegistry(Arc); - -impl GitRegistry { - pub fn new(url: GitUrl) -> Self { - Self(Arc::new(GitRegistryInner { - url, - git_index: Default::default(), - })) - } - - pub fn url(&self) -> impl Display + '_ { - &self.0.url - } - - /// WARNING: This is a blocking operation. - fn find_crate_matched_ver( - repo: &Repository, - crate_name: &str, - (c1, c2): &(CompactString, Option), - version_req: &VersionReq, - ) -> Result { - let mut path = PathBuf::with_capacity(128); - path.push(&**c1); - if let Some(c2) = c2 { - path.push(&**c2); - } - - path.push(&*crate_name.to_lowercase()); - let crate_versions = repo - .get_head_commit_entry_data_by_path(path)? - .ok_or_else(|| RegistryError::NotFound(crate_name.into()))?; - - MatchedVersion::find( - &mut JsonDeserializer::from_slice(&crate_versions).into_iter(), - version_req, - ) - } - - #[instrument( - skip(self, client, version_req), - fields( - version_req = format_args!("{version_req}"), - ), - )] - pub async fn fetch_crate_matched( - &self, - client: Client, - name: &str, - version_req: &VersionReq, - ) -> Result, RegistryError> { - let crate_prefix = crate_prefix_components(name)?; - let crate_name = name.to_compact_string(); - let version_req = version_req.clone(); - let this = self.clone(); - - let cancellation_token = GitCancellationToken::default(); - // Cancel git operation if the future is cancelled (dropped). - let cancel_on_drop = cancellation_token.clone().cancel_on_drop(); - - let (matched_version, dl_url) = spawn_blocking(move || { - let GitIndex { - _tempdir: _, - repo, - dl_template, - } = this - .0 - .git_index - .get_or_try_init(|| GitIndex::new(this.0.url.clone(), cancellation_token))?; - - let matched_version = - Self::find_crate_matched_ver(repo, &crate_name, &crate_prefix, &version_req)?; - - let url = Url::parse(&render_dl_template( - dl_template, - &crate_name, - &crate_prefix, - &matched_version, - )?)?; - - Ok::<_, RegistryError>((matched_version, url)) - }) - .await??; - - // Git operation done, disarm it - cancel_on_drop.disarm(); - - parse_manifest(client, name, dl_url, matched_version).await - } -} diff --git a/crates/binstalk-registry/src/lib.rs b/crates/binstalk-registry/src/lib.rs deleted file mode 100644 index 6dd19ecc..00000000 --- a/crates/binstalk-registry/src/lib.rs +++ /dev/null @@ -1,324 +0,0 @@ -#![cfg_attr(docsrs, feature(doc_auto_cfg))] - -use std::{fmt, io, str::FromStr, sync::Arc}; - -use base16::DecodeError as Base16DecodeError; -use binstalk_downloader::{ - download::DownloadError, - remote::{Client, Error as RemoteError}, -}; -use binstalk_types::cargo_toml_binstall::Meta; -use cargo_toml_workspace::cargo_toml::{Error as CargoTomlError, Manifest}; -use compact_str::CompactString; -use leon::{ParseError, RenderError}; -use miette::Diagnostic; -use semver::VersionReq; -use serde_json::Error as JsonError; -use thiserror::Error as ThisError; -use tokio::task; -use url::{ParseError as UrlParseError, Url}; - -#[cfg(feature = "git")] -pub use simple_git::{GitError, GitUrl, GitUrlParseError}; - -mod vfs; - -mod visitor; - -mod common; -use common::*; - -#[cfg(feature = "git")] -mod git_registry; -#[cfg(feature = "git")] -pub use git_registry::GitRegistry; - -#[cfg(any(feature = "crates_io_api", test))] -mod crates_io_registry; -#[cfg(any(feature = "crates_io_api", test))] -pub use crates_io_registry::fetch_crate_cratesio_api; - -mod sparse_registry; -pub use sparse_registry::SparseRegistry; - -#[derive(Debug, ThisError, Diagnostic)] -#[diagnostic(severity(error), code(binstall::cargo_registry))] -#[non_exhaustive] -pub enum RegistryError { - #[error(transparent)] - Remote(#[from] RemoteError), - - #[error("{0} is not found")] - #[diagnostic( - help("Check that the crate name you provided is correct.\nYou can also search for a matching crate at: https://lib.rs/search?q={0}") - )] - NotFound(CompactString), - - #[error(transparent)] - Json(#[from] JsonError), - - #[error("Failed to parse dl config: {0}")] - ParseDlConfig(#[from] ParseError), - - #[error("Failed to render dl config: {0}")] - RenderDlConfig(#[from] RenderError), - - #[error("Failed to parse checksum encoded in hex: {0}")] - InvalidHex(#[from] Base16DecodeError), - - #[error("Expected checksum `{expected}`, actual checksum `{actual}`")] - UnmatchedChecksum { - expected: Box, - actual: Box, - }, - - #[error("no version matching requirement '{req}'")] - VersionMismatch { req: semver::VersionReq }, - - #[error("Failed to parse cargo manifest: {0}")] - #[diagnostic(help("If you used --manifest-path, check the Cargo.toml syntax."))] - CargoManifest(#[from] Box), - - #[error("Failed to parse url: {0}")] - UrlParse(#[from] url::ParseError), - - #[error(transparent)] - Download(#[from] DownloadError), - - #[error("I/O Error: {0}")] - Io(#[from] io::Error), - - #[error(transparent)] - TaskJoinError(#[from] task::JoinError), - - #[cfg(feature = "git")] - #[error("Failed to shallow clone git repository: {0}")] - GitError(#[from] GitError), -} - -impl From for RegistryError { - fn from(e: CargoTomlError) -> Self { - Self::from(Box::new(e)) - } -} - -#[derive(Clone, Debug)] -#[non_exhaustive] -pub enum Registry { - Sparse(Arc), - - #[cfg(feature = "git")] - Git(GitRegistry), -} - -impl Default for Registry { - fn default() -> Self { - Self::crates_io_sparse_registry() - } -} - -#[derive(Debug, ThisError)] -#[error("Invalid registry `{src}`, {inner}")] -pub struct InvalidRegistryError { - src: CompactString, - #[source] - inner: InvalidRegistryErrorInner, -} - -#[derive(Debug, ThisError)] -enum InvalidRegistryErrorInner { - #[cfg(feature = "git")] - #[error("failed to parse git url {0}")] - GitUrlParseErr(#[from] Box), - - #[error("failed to parse sparse registry url: {0}")] - UrlParseErr(#[from] UrlParseError), - - #[error("expected protocol http(s), actual url `{0}`")] - InvalidScheme(Box), - - #[cfg(not(feature = "git"))] - #[error("git registry not supported")] - GitRegistryNotSupported, -} - -impl Registry { - /// Return a crates.io sparse registry - pub fn crates_io_sparse_registry() -> Self { - Self::Sparse(Arc::new(SparseRegistry::new( - Url::parse("https://index.crates.io/").unwrap(), - ))) - } - - fn from_str_inner(s: &str) -> Result { - if let Some(s) = s.strip_prefix("sparse+") { - let url = Url::parse(s.trim_end_matches('/'))?; - - let scheme = url.scheme(); - if scheme != "http" && scheme != "https" { - Err(InvalidRegistryErrorInner::InvalidScheme(Box::new(url))) - } else { - Ok(Self::Sparse(Arc::new(SparseRegistry::new(url)))) - } - } else { - #[cfg(not(feature = "git"))] - { - Err(InvalidRegistryErrorInner::GitRegistryNotSupported) - } - #[cfg(feature = "git")] - { - let url = GitUrl::from_str(s).map_err(Box::new)?; - Ok(Self::Git(GitRegistry::new(url))) - } - } - } - - /// Fetch the latest crate with `crate_name` and with version matching - /// `version_req`. - pub async fn fetch_crate_matched( - &self, - client: Client, - crate_name: &str, - version_req: &VersionReq, - ) -> Result, RegistryError> { - match self { - Self::Sparse(sparse_registry) => { - sparse_registry - .fetch_crate_matched(client, crate_name, version_req) - .await - } - #[cfg(feature = "git")] - Self::Git(git_registry) => { - git_registry - .fetch_crate_matched(client, crate_name, version_req) - .await - } - } - } -} - -impl fmt::Display for Registry { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - #[cfg(feature = "git")] - Registry::Git(registry) => fmt::Display::fmt(®istry.url(), f), - Registry::Sparse(registry) => fmt::Display::fmt(®istry.url(), f), - } - } -} - -impl FromStr for Registry { - type Err = InvalidRegistryError; - - fn from_str(s: &str) -> Result { - Self::from_str_inner(s).map_err(|inner| InvalidRegistryError { - src: s.into(), - inner, - }) - } -} - -#[cfg(test)] -mod test { - use std::num::NonZeroU16; - - use toml_edit::ser::to_string; - - use super::*; - - /// Mark this as an async fn so that you won't accidentally use it in - /// sync context. - fn create_client() -> Client { - Client::new( - concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION")), - None, - NonZeroU16::new(10).unwrap(), - 1.try_into().unwrap(), - [], - ) - .unwrap() - } - - #[tokio::test] - async fn test_crates_io_sparse_registry() { - let client = create_client(); - - let crate_name = "cargo-binstall"; - let version_req = &VersionReq::parse("=1.0.0").unwrap(); - - let serialized_manifest_from_sparse_task = tokio::spawn({ - let client = client.clone(); - let version_req = version_req.clone(); - - async move { - let sparse_registry: Registry = Registry::crates_io_sparse_registry(); - assert!( - matches!(sparse_registry, Registry::Sparse(_)), - "{:?}", - sparse_registry - ); - - let manifest_from_sparse = sparse_registry - .fetch_crate_matched(client, crate_name, &version_req) - .await - .unwrap(); - - to_string(&manifest_from_sparse).unwrap() - } - }); - - let manifest_from_cratesio_api = fetch_crate_cratesio_api(client, crate_name, version_req) - .await - .unwrap(); - - let serialized_manifest_from_cratesio_api = to_string(&manifest_from_cratesio_api).unwrap(); - - assert_eq!( - serialized_manifest_from_sparse_task.await.unwrap(), - serialized_manifest_from_cratesio_api - ); - } - - #[cfg(feature = "git")] - #[tokio::test] - async fn test_crates_io_git_registry() { - let client = create_client(); - - let crate_name = "cargo-binstall"; - let version_req = &VersionReq::parse("=1.0.0").unwrap(); - - let serialized_manifest_from_git_task = tokio::spawn({ - let version_req = version_req.clone(); - let client = client.clone(); - - async move { - let git_registry: Registry = "https://github.com/rust-lang/crates.io-index" - .parse() - .unwrap(); - assert!( - matches!(git_registry, Registry::Git(_)), - "{:?}", - git_registry - ); - - let manifest_from_git = git_registry - .fetch_crate_matched(client, crate_name, &version_req) - .await - .unwrap(); - to_string(&manifest_from_git).unwrap() - } - }); - - let manifest_from_cratesio_api = Registry::default() - .fetch_crate_matched(client, crate_name, version_req) - .await - .unwrap(); - - let serialized_manifest_from_cratesio_api = to_string(&manifest_from_cratesio_api).unwrap(); - - assert_eq!( - serialized_manifest_from_git_task.await.unwrap(), - serialized_manifest_from_cratesio_api - ); - } -} diff --git a/crates/binstalk-registry/src/sparse_registry.rs b/crates/binstalk-registry/src/sparse_registry.rs deleted file mode 100644 index 48a4f5b2..00000000 --- a/crates/binstalk-registry/src/sparse_registry.rs +++ /dev/null @@ -1,119 +0,0 @@ -use std::fmt::Display; - -use binstalk_downloader::remote::{Client, Error as RemoteError}; -use binstalk_types::cargo_toml_binstall::Meta; -use cargo_toml_workspace::cargo_toml::Manifest; -use compact_str::CompactString; -use semver::VersionReq; -use serde_json::Deserializer as JsonDeserializer; -use tokio::sync::OnceCell; -use tracing::instrument; -use url::Url; - -use crate::{ - crate_prefix_components, parse_manifest, render_dl_template, MatchedVersion, RegistryConfig, - RegistryError, -}; - -#[derive(Debug)] -pub struct SparseRegistry { - url: Url, - dl_template: OnceCell, -} - -impl SparseRegistry { - /// * `url` - `url.cannot_be_a_base()` must be `false` - pub fn new(url: Url) -> Self { - Self { - url, - dl_template: Default::default(), - } - } - - pub fn url(&self) -> impl Display + '_ { - &self.url - } - - async fn get_dl_template(&self, client: &Client) -> Result<&str, RegistryError> { - self.dl_template - .get_or_try_init(|| { - Box::pin(async { - let mut url = self.url.clone(); - url.path_segments_mut().unwrap().push("config.json"); - let config: RegistryConfig = client.get(url).send(true).await?.json().await?; - Ok(config.dl) - }) - }) - .await - .map(AsRef::as_ref) - } - - /// `url` must be a valid http(s) url. - async fn find_crate_matched_ver( - client: &Client, - mut url: Url, - crate_name: &str, - (c1, c2): &(CompactString, Option), - version_req: &VersionReq, - ) -> Result { - { - let mut path = url.path_segments_mut().unwrap(); - - path.push(c1); - if let Some(c2) = c2 { - path.push(c2); - } - - path.push(&crate_name.to_lowercase()); - } - - let body = client - .get(url) - .send(true) - .await - .map_err(|e| match e { - RemoteError::Http(e) if e.is_status() => RegistryError::NotFound(crate_name.into()), - e => e.into(), - })? - .bytes() - .await - .map_err(RegistryError::from)?; - MatchedVersion::find( - &mut JsonDeserializer::from_slice(&body).into_iter(), - version_req, - ) - } - - #[instrument( - skip(self, client, version_req), - fields( - registry_url = format_args!("{}", self.url), - version_req = format_args!("{version_req}"), - ) - )] - pub async fn fetch_crate_matched( - &self, - client: Client, - crate_name: &str, - version_req: &VersionReq, - ) -> Result, RegistryError> { - let crate_prefix = crate_prefix_components(crate_name)?; - let dl_template = self.get_dl_template(&client).await?; - let matched_version = Self::find_crate_matched_ver( - &client, - self.url.clone(), - crate_name, - &crate_prefix, - version_req, - ) - .await?; - let dl_url = Url::parse(&render_dl_template( - dl_template, - crate_name, - &crate_prefix, - &matched_version, - )?)?; - - parse_manifest(client, crate_name, dl_url, matched_version).await - } -} diff --git a/crates/binstalk-registry/src/vfs.rs b/crates/binstalk-registry/src/vfs.rs deleted file mode 100644 index 7779b431..00000000 --- a/crates/binstalk-registry/src/vfs.rs +++ /dev/null @@ -1,43 +0,0 @@ -use std::{ - collections::{hash_set::HashSet, BTreeMap}, - io, - path::Path, -}; - -use cargo_toml_workspace::cargo_toml::AbstractFilesystem; -use normalize_path::NormalizePath; - -/// This type stores the filesystem structure for the crate tarball -/// extracted in memory and can be passed to -/// `cargo_toml::Manifest::complete_from_abstract_filesystem`. -#[derive(Debug, Default)] -pub(super) struct Vfs(BTreeMap, HashSet>>); - -impl Vfs { - /// * `path` - must be canonical, must not be empty. - pub(super) fn add_path(&mut self, mut path: &Path) { - while let Some(parent) = path.parent() { - // Since path has parent, it must have a filename - let filename = path.file_name().unwrap(); - - // `cargo_toml`'s implementation does the same thing. - // https://docs.rs/cargo_toml/0.11.5/src/cargo_toml/afs.rs.html#24 - let filename = filename.to_string_lossy(); - - self.0 - .entry(parent.into()) - .or_insert_with(|| HashSet::with_capacity(4)) - .insert(filename.into()); - - path = parent; - } - } -} - -impl AbstractFilesystem for Vfs { - fn file_names_in(&self, rel_path: &str) -> io::Result>> { - let rel_path = Path::new(rel_path).normalize(); - - Ok(self.0.get(&*rel_path).cloned().unwrap_or_default()) - } -} diff --git a/crates/binstalk-registry/src/visitor.rs b/crates/binstalk-registry/src/visitor.rs deleted file mode 100644 index e7ecaacd..00000000 --- a/crates/binstalk-registry/src/visitor.rs +++ /dev/null @@ -1,81 +0,0 @@ -use std::path::{Path, PathBuf}; - -use binstalk_downloader::download::{DownloadError, TarEntriesVisitor, TarEntry}; -use binstalk_types::cargo_toml_binstall::Meta; -use cargo_toml_workspace::cargo_toml::{Manifest, Value}; -use normalize_path::NormalizePath; -use tokio::io::AsyncReadExt; -use tracing::debug; - -use crate::{vfs::Vfs, RegistryError}; - -#[derive(Debug)] -pub(super) struct ManifestVisitor { - cargo_toml_content: Vec, - /// manifest_dir_path is treated as the current dir. - manifest_dir_path: PathBuf, - - vfs: Vfs, -} - -impl ManifestVisitor { - pub(super) fn new(manifest_dir_path: PathBuf) -> Self { - Self { - // Cargo.toml is quite large usually. - cargo_toml_content: Vec::with_capacity(2000), - manifest_dir_path, - vfs: Vfs::default(), - } - } -} - -#[async_trait::async_trait] -impl TarEntriesVisitor for ManifestVisitor { - async fn visit(&mut self, entry: &mut dyn TarEntry) -> Result<(), DownloadError> { - let path = entry.path()?; - let path = path.normalize(); - - let path = if let Ok(path) = path.strip_prefix(&self.manifest_dir_path) { - path - } else { - // The path is outside of the curr dir (manifest dir), - // ignore it. - return Ok(()); - }; - - if path == Path::new("Cargo.toml") - || path == Path::new("src/main.rs") - || path.starts_with("src/bin") - { - self.vfs.add_path(path); - } - - if path == Path::new("Cargo.toml") { - // Since it is possible for the same Cargo.toml to appear - // multiple times using `tar --keep-old-files`, here we - // clear the buffer first before reading into it. - self.cargo_toml_content.clear(); - self.cargo_toml_content - .reserve_exact(entry.size()?.try_into().unwrap_or(usize::MAX)); - entry.read_to_end(&mut self.cargo_toml_content).await?; - } - - Ok(()) - } -} - -impl ManifestVisitor { - /// Load binstall metadata using the extracted information stored in memory. - pub(super) fn load_manifest(self) -> Result, RegistryError> { - debug!("Loading manifest directly from extracted file"); - - // Load and parse manifest - let mut manifest = Manifest::from_slice_with_metadata(&self.cargo_toml_content)?; - debug!("Manifest: {manifest:?}"); - // Checks vfs for binary output names - manifest.complete_from_abstract_filesystem::(&self.vfs, None)?; - - // Return metadata - Ok(manifest) - } -} diff --git a/crates/binstalk-types/CHANGELOG.md b/crates/binstalk-types/CHANGELOG.md deleted file mode 100644 index 8d7e76d6..00000000 --- a/crates/binstalk-types/CHANGELOG.md +++ /dev/null @@ -1,36 +0,0 @@ -# Changelog -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - -## [Unreleased] - -## [0.9.4](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-types-v0.9.3...binstalk-types-v0.9.4) - 2025-03-07 - -### Other - -- *(deps)* bump the deps group with 3 updates ([#2072](https://github.com/cargo-bins/cargo-binstall/pull/2072)) - -## [0.9.3](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-types-v0.9.2...binstalk-types-v0.9.3) - 2025-02-11 - -### Other - -- *(deps)* bump the deps group with 2 updates (#2044) - -## [0.9.2](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-types-v0.9.1...binstalk-types-v0.9.2) - 2024-11-23 - -### Other - -- *(deps)* bump the deps group with 2 updates ([#1981](https://github.com/cargo-bins/cargo-binstall/pull/1981)) - -## [0.9.1](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-types-v0.9.0...binstalk-types-v0.9.1) - 2024-11-05 - -### Other - -- *(deps)* bump the deps group with 3 updates ([#1954](https://github.com/cargo-bins/cargo-binstall/pull/1954)) - -## [0.9.0](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-types-v0.8.0...binstalk-types-v0.9.0) - 2024-08-10 - -### Added -- Merge disable strategies ([#1868](https://github.com/cargo-bins/cargo-binstall/pull/1868)) diff --git a/crates/binstalk-types/Cargo.toml b/crates/binstalk-types/Cargo.toml deleted file mode 100644 index 70f83f2e..00000000 --- a/crates/binstalk-types/Cargo.toml +++ /dev/null @@ -1,23 +0,0 @@ -[package] -name = "binstalk-types" -description = "The binstall toolkit that contains basic types for binstalk crates" -repository = "https://github.com/cargo-bins/cargo-binstall" -documentation = "https://docs.rs/binstalk-types" -version = "0.9.4" -rust-version = "1.61.0" -authors = ["ryan "] -edition = "2021" -license = "Apache-2.0 OR MIT" - -[dependencies] -compact_str = { version = "0.9.0", features = ["serde"] } -maybe-owned = { version = "0.3.4", features = ["serde"] } -once_cell = "1.18.0" -semver = { version = "1.0.17", features = ["serde"] } -serde = { version = "1.0.163", features = ["derive"] } -strum = "0.27.0" -strum_macros = "0.27.0" -url = { version = "2.5.4", features = ["serde"] } - -[dev-dependencies] -serde_json = "1" diff --git a/crates/binstalk-types/LICENSE-APACHE b/crates/binstalk-types/LICENSE-APACHE deleted file mode 100644 index 1b5ec8b7..00000000 --- a/crates/binstalk-types/LICENSE-APACHE +++ /dev/null @@ -1,176 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS diff --git a/crates/binstalk-types/LICENSE-MIT b/crates/binstalk-types/LICENSE-MIT deleted file mode 100644 index 31aa7938..00000000 --- a/crates/binstalk-types/LICENSE-MIT +++ /dev/null @@ -1,23 +0,0 @@ -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/crates/binstalk-types/src/cargo_toml_binstall.rs b/crates/binstalk-types/src/cargo_toml_binstall.rs deleted file mode 100644 index 74330e9e..00000000 --- a/crates/binstalk-types/src/cargo_toml_binstall.rs +++ /dev/null @@ -1,228 +0,0 @@ -//! The format of the `[package.metadata.binstall]` manifest. -//! -//! This manifest defines how a particular binary crate may be installed by Binstall. - -use std::{borrow::Cow, collections::BTreeMap}; - -use serde::{Deserialize, Serialize}; -use strum_macros::{EnumCount, VariantArray}; - -mod package_formats; -#[doc(inline)] -pub use package_formats::*; - -/// `binstall` metadata container -/// -/// Required to nest metadata under `package.metadata.binstall` -#[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(rename_all = "kebab-case")] -pub struct Meta { - pub binstall: Option, -} - -/// Strategies to use for binary discovery -#[derive( - Debug, - Copy, - Clone, - Eq, - PartialEq, - Ord, - PartialOrd, - EnumCount, - VariantArray, - Deserialize, - Serialize, -)] -#[serde(rename_all = "kebab-case")] -pub enum Strategy { - /// Attempt to download official pre-built artifacts using - /// information provided in `Cargo.toml`. - CrateMetaData, - /// Query third-party QuickInstall for the crates. - QuickInstall, - /// Build the crates from source using `cargo-build`. - Compile, -} - -impl Strategy { - pub const fn to_str(self) -> &'static str { - match self { - Strategy::CrateMetaData => "crate-meta-data", - Strategy::QuickInstall => "quick-install", - Strategy::Compile => "compile", - } - } -} - -/// Metadata for binary installation use. -/// -/// Exposed via `[package.metadata]` in `Cargo.toml` -#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "kebab-case", default)] -pub struct PkgMeta { - /// URL template for package downloads - pub pkg_url: Option, - - /// Format for package downloads - pub pkg_fmt: Option, - - /// Path template for binary files in packages - pub bin_dir: Option, - - /// Package signing configuration - pub signing: Option, - - /// Stratgies to disable - pub disabled_strategies: Option>, - - /// Target specific overrides - pub overrides: BTreeMap, -} - -impl PkgMeta { - /// Merge configuration overrides into object - pub fn merge(&mut self, pkg_override: &PkgOverride) { - if let Some(o) = &pkg_override.pkg_url { - self.pkg_url = Some(o.clone()); - } - if let Some(o) = &pkg_override.pkg_fmt { - self.pkg_fmt = Some(*o); - } - if let Some(o) = &pkg_override.bin_dir { - self.bin_dir = Some(o.clone()); - } - } - - /// Merge configuration overrides into object - /// - /// * `pkg_overrides` - ordered in preference - pub fn merge_overrides<'a, It>(&self, pkg_overrides: It) -> Self - where - It: IntoIterator + Clone, - { - let ignore_disabled_strategies = pkg_overrides - .clone() - .into_iter() - .any(|pkg_override| pkg_override.ignore_disabled_strategies); - - Self { - pkg_url: pkg_overrides - .clone() - .into_iter() - .find_map(|pkg_override| pkg_override.pkg_url.clone()) - .or_else(|| self.pkg_url.clone()), - - pkg_fmt: pkg_overrides - .clone() - .into_iter() - .find_map(|pkg_override| pkg_override.pkg_fmt) - .or(self.pkg_fmt), - - bin_dir: pkg_overrides - .clone() - .into_iter() - .find_map(|pkg_override| pkg_override.bin_dir.clone()) - .or_else(|| self.bin_dir.clone()), - - signing: pkg_overrides - .clone() - .into_iter() - .find_map(|pkg_override| pkg_override.signing.clone()) - .or_else(|| self.signing.clone()), - - disabled_strategies: if ignore_disabled_strategies { - None - } else { - let mut disabled_strategies = pkg_overrides - .into_iter() - .filter_map(|pkg_override| pkg_override.disabled_strategies.as_deref()) - .flatten() - .chain(self.disabled_strategies.as_deref().into_iter().flatten()) - .copied() - .collect::>(); - - disabled_strategies.sort_unstable(); - disabled_strategies.dedup(); - - Some(disabled_strategies.into_boxed_slice()) - }, - - overrides: Default::default(), - } - } -} - -/// Target specific overrides for binary installation -/// -/// Exposed via `[package.metadata.TARGET]` in `Cargo.toml` -#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "kebab-case", default)] -pub struct PkgOverride { - /// URL template override for package downloads - pub pkg_url: Option, - - /// Format override for package downloads - pub pkg_fmt: Option, - - /// Path template override for binary files in packages - pub bin_dir: Option, - - /// Stratgies to disable - pub disabled_strategies: Option>, - - /// Package signing configuration - pub signing: Option, - - #[serde(skip)] - pub ignore_disabled_strategies: bool, -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "kebab-case")] -pub struct BinMeta { - /// Binary name - pub name: String, - - /// Binary template (path within package) - pub path: String, -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "kebab-case")] -pub struct PkgSigning { - /// Signing algorithm supported by Binstall. - pub algorithm: SigningAlgorithm, - - /// Signing public key - pub pubkey: Cow<'static, str>, - - /// Signature file override template (url to download) - #[serde(default)] - pub file: Option, -} - -#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "kebab-case")] -#[non_exhaustive] -pub enum SigningAlgorithm { - /// [minisign](https://jedisct1.github.io/minisign/) - Minisign, -} - -#[cfg(test)] -mod tests { - use strum::VariantArray; - - use super::*; - - #[test] - fn test_strategy_ser() { - Strategy::VARIANTS.iter().for_each(|strategy| { - assert_eq!( - serde_json::to_string(&strategy).unwrap(), - format!(r#""{}""#, strategy.to_str()) - ) - }); - } -} diff --git a/crates/binstalk-types/src/cargo_toml_binstall/package_formats.rs b/crates/binstalk-types/src/cargo_toml_binstall/package_formats.rs deleted file mode 100644 index 8619bddc..00000000 --- a/crates/binstalk-types/src/cargo_toml_binstall/package_formats.rs +++ /dev/null @@ -1,134 +0,0 @@ -use serde::{Deserialize, Serialize}; -use strum_macros::{Display, EnumIter, EnumString}; - -/// Binary format enumeration -#[derive( - Debug, Display, Copy, Clone, Eq, PartialEq, Serialize, Deserialize, EnumString, EnumIter, -)] -#[serde(rename_all = "snake_case")] -#[strum(ascii_case_insensitive)] -pub enum PkgFmt { - /// Download format is TAR (uncompressed) - Tar, - /// Download format is TAR + Bzip2 - Tbz2, - /// Download format is TGZ (TAR + GZip) - Tgz, - /// Download format is TAR + XZ - Txz, - /// Download format is TAR + Zstd - Tzstd, - /// Download format is Zip - Zip, - /// Download format is raw / binary - Bin, -} - -impl Default for PkgFmt { - fn default() -> Self { - Self::Tgz - } -} - -impl PkgFmt { - /// If self is one of the tar based formats, return Some. - pub fn decompose(self) -> PkgFmtDecomposed { - match self { - PkgFmt::Tar => PkgFmtDecomposed::Tar(TarBasedFmt::Tar), - PkgFmt::Tbz2 => PkgFmtDecomposed::Tar(TarBasedFmt::Tbz2), - PkgFmt::Tgz => PkgFmtDecomposed::Tar(TarBasedFmt::Tgz), - PkgFmt::Txz => PkgFmtDecomposed::Tar(TarBasedFmt::Txz), - PkgFmt::Tzstd => PkgFmtDecomposed::Tar(TarBasedFmt::Tzstd), - PkgFmt::Bin => PkgFmtDecomposed::Bin, - PkgFmt::Zip => PkgFmtDecomposed::Zip, - } - } - - /// List of possible file extensions for the format - /// (with prefix `.`). - /// - /// * `is_windows` - if true and `self == PkgFmt::Bin`, then it will return - /// `.exe` in additional to other bin extension names. - pub fn extensions(self, is_windows: bool) -> &'static [&'static str] { - match self { - PkgFmt::Tar => &[".tar"], - PkgFmt::Tbz2 => &[".tbz2", ".tar.bz2"], - PkgFmt::Tgz => &[".tgz", ".tar.gz"], - PkgFmt::Txz => &[".txz", ".tar.xz"], - PkgFmt::Tzstd => &[".tzstd", ".tzst", ".tar.zst"], - PkgFmt::Bin => { - if is_windows { - &[".bin", "", ".exe"] - } else { - &[".bin", ""] - } - } - PkgFmt::Zip => &[".zip"], - } - } - - /// Given the pkg-url template, guess the possible pkg-fmt. - pub fn guess_pkg_format(pkg_url: &str) -> Option { - let mut it = pkg_url.rsplitn(3, '.'); - - let guess = match it.next()? { - "tar" => Some(PkgFmt::Tar), - - "tbz2" => Some(PkgFmt::Tbz2), - "bz2" if it.next() == Some("tar") => Some(PkgFmt::Tbz2), - - "tgz" => Some(PkgFmt::Tgz), - "gz" if it.next() == Some("tar") => Some(PkgFmt::Tgz), - - "txz" => Some(PkgFmt::Txz), - "xz" if it.next() == Some("tar") => Some(PkgFmt::Txz), - - "tzstd" | "tzst" => Some(PkgFmt::Tzstd), - "zst" if it.next() == Some("tar") => Some(PkgFmt::Tzstd), - - "exe" | "bin" => Some(PkgFmt::Bin), - "zip" => Some(PkgFmt::Zip), - - _ => None, - }; - - if it.next().is_some() { - guess - } else { - None - } - } -} - -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -pub enum PkgFmtDecomposed { - Tar(TarBasedFmt), - Bin, - Zip, -} - -#[derive(Debug, Display, Copy, Clone, Eq, PartialEq)] -pub enum TarBasedFmt { - /// Download format is TAR (uncompressed) - Tar, - /// Download format is TAR + Bzip2 - Tbz2, - /// Download format is TGZ (TAR + GZip) - Tgz, - /// Download format is TAR + XZ - Txz, - /// Download format is TAR + Zstd - Tzstd, -} - -impl From for PkgFmt { - fn from(fmt: TarBasedFmt) -> Self { - match fmt { - TarBasedFmt::Tar => PkgFmt::Tar, - TarBasedFmt::Tbz2 => PkgFmt::Tbz2, - TarBasedFmt::Tgz => PkgFmt::Tgz, - TarBasedFmt::Txz => PkgFmt::Txz, - TarBasedFmt::Tzstd => PkgFmt::Tzstd, - } - } -} diff --git a/crates/binstalk-types/src/crate_info.rs b/crates/binstalk-types/src/crate_info.rs deleted file mode 100644 index 9d46d82e..00000000 --- a/crates/binstalk-types/src/crate_info.rs +++ /dev/null @@ -1,83 +0,0 @@ -//! Common structure for crate information for post-install manifests. - -use std::{borrow, cmp, hash}; - -use compact_str::CompactString; -use maybe_owned::MaybeOwned; -use once_cell::sync::Lazy; -use semver::Version; -use serde::{Deserialize, Serialize}; -use url::Url; - -pub fn cratesio_url() -> &'static Url { - static CRATESIO: Lazy Url> = - Lazy::new(|| Url::parse("https://github.com/rust-lang/crates.io-index").unwrap()); - - &CRATESIO -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct CrateInfo { - pub name: CompactString, - pub version_req: CompactString, - pub current_version: Version, - pub source: CrateSource, - pub target: CompactString, - pub bins: Vec, -} - -impl borrow::Borrow for CrateInfo { - fn borrow(&self) -> &str { - &self.name - } -} - -impl PartialEq for CrateInfo { - fn eq(&self, other: &Self) -> bool { - self.name == other.name - } -} -impl Eq for CrateInfo {} - -impl PartialOrd for CrateInfo { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for CrateInfo { - fn cmp(&self, other: &Self) -> cmp::Ordering { - self.name.cmp(&other.name) - } -} - -impl hash::Hash for CrateInfo { - fn hash(&self, state: &mut H) - where - H: hash::Hasher, - { - self.name.hash(state) - } -} - -#[derive(Debug, Copy, Clone, Serialize, Deserialize)] -pub enum SourceType { - Git, - Path, - Registry, -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct CrateSource { - pub source_type: SourceType, - pub url: MaybeOwned<'static, Url>, -} - -impl CrateSource { - pub fn cratesio_registry() -> CrateSource { - Self { - source_type: SourceType::Registry, - url: MaybeOwned::Borrowed(cratesio_url()), - } - } -} diff --git a/crates/binstalk-types/src/lib.rs b/crates/binstalk-types/src/lib.rs deleted file mode 100644 index 25096520..00000000 --- a/crates/binstalk-types/src/lib.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub mod cargo_toml_binstall; -pub mod crate_info; - -pub use maybe_owned; diff --git a/crates/binstalk/CHANGELOG.md b/crates/binstalk/CHANGELOG.md deleted file mode 100644 index ce5bb284..00000000 --- a/crates/binstalk/CHANGELOG.md +++ /dev/null @@ -1,200 +0,0 @@ -# Changelog -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - -## [Unreleased] - -## [0.28.31](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-v0.28.30...binstalk-v0.28.31) - 2025-04-05 - -### Other - -- updated the following local packages: binstalk-downloader, detect-targets, binstalk-git-repo-api, binstalk-fetchers, binstalk-registry - -## [0.28.30](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-v0.28.29...binstalk-v0.28.30) - 2025-03-19 - -### Other - -- Use zlib-rs for gitoxide and avoid pulling in zlib-ng ([#2099](https://github.com/cargo-bins/cargo-binstall/pull/2099)) - -## [0.28.29](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-v0.28.28...binstalk-v0.28.29) - 2025-03-15 - -### Other - -- *(deps)* bump tokio from 1.43.0 to 1.44.0 in the deps group ([#2079](https://github.com/cargo-bins/cargo-binstall/pull/2079)) - -## [0.28.28](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-v0.28.27...binstalk-v0.28.28) - 2025-03-07 - -### Other - -- *(deps)* bump the deps group with 3 updates ([#2072](https://github.com/cargo-bins/cargo-binstall/pull/2072)) - -## [0.28.27](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-v0.28.26...binstalk-v0.28.27) - 2025-02-28 - -### Other - -- Use flate2/zlib-rs for dev/release build ([#2068](https://github.com/cargo-bins/cargo-binstall/pull/2068)) - -## [0.28.26](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-v0.28.25...binstalk-v0.28.26) - 2025-02-22 - -### Other - -- updated the following local packages: detect-targets - -## [0.28.25](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-v0.28.24...binstalk-v0.28.25) - 2025-02-15 - -### Other - -- updated the following local packages: detect-targets - -## [0.28.24](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-v0.28.23...binstalk-v0.28.24) - 2025-02-11 - -### Other - -- *(deps)* bump the deps group with 2 updates (#2044) - -## [0.28.23](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-v0.28.22...binstalk-v0.28.23) - 2025-02-04 - -### Other - -- update Cargo.lock dependencies - -## [0.28.22](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-v0.28.21...binstalk-v0.28.22) - 2025-01-19 - -### Other - -- update Cargo.lock dependencies - -## [0.28.21](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-v0.28.20...binstalk-v0.28.21) - 2025-01-13 - -### Other - -- update Cargo.lock dependencies - -## [0.28.20](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-v0.28.19...binstalk-v0.28.20) - 2025-01-11 - -### Other - -- *(deps)* bump the deps group with 3 updates (#2015) - -## [0.28.19](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-v0.28.18...binstalk-v0.28.19) - 2025-01-04 - -### Other - -- *(deps)* bump the deps group with 2 updates (#2010) - -## [0.28.18](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-v0.28.17...binstalk-v0.28.18) - 2024-12-28 - -### Other - -- updated the following local packages: detect-targets - -## [0.28.17](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-v0.28.16...binstalk-v0.28.17) - 2024-12-14 - -### Other - -- *(deps)* bump the deps group with 2 updates (#1997) - -## [0.28.16](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-v0.28.15...binstalk-v0.28.16) - 2024-12-07 - -### Other - -- *(deps)* bump the deps group with 3 updates ([#1993](https://github.com/cargo-bins/cargo-binstall/pull/1993)) - -## [0.28.15](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-v0.28.14...binstalk-v0.28.15) - 2024-11-29 - -### Other - -- updated the following local packages: binstalk-fetchers, detect-targets - -## [0.28.14](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-v0.28.13...binstalk-v0.28.14) - 2024-11-23 - -### Other - -- *(deps)* bump the deps group with 2 updates ([#1981](https://github.com/cargo-bins/cargo-binstall/pull/1981)) - -## [0.28.13](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-v0.28.12...binstalk-v0.28.13) - 2024-11-18 - -### Other - -- updated the following local packages: detect-targets - -## [0.28.12](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-v0.28.11...binstalk-v0.28.12) - 2024-11-09 - -### Other - -- *(deps)* bump the deps group with 3 updates ([#1966](https://github.com/cargo-bins/cargo-binstall/pull/1966)) - -## [0.28.11](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-v0.28.10...binstalk-v0.28.11) - 2024-11-05 - -### Other - -- *(deps)* bump the deps group with 3 updates ([#1954](https://github.com/cargo-bins/cargo-binstall/pull/1954)) - -## [0.28.10](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-v0.28.9...binstalk-v0.28.10) - 2024-11-02 - -### Other - -- updated the following local packages: binstalk-bins, binstalk-downloader, detect-targets - -## [0.28.9](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-v0.28.8...binstalk-v0.28.9) - 2024-10-25 - -### Other - -- updated the following local packages: detect-targets - -## [0.28.8](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-v0.28.7...binstalk-v0.28.8) - 2024-10-12 - -### Other - -- updated the following local packages: binstalk-git-repo-api - -## [0.28.7](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-v0.28.6...binstalk-v0.28.7) - 2024-10-12 - -### Other - -- updated the following local packages: detect-targets - -## [0.28.6](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-v0.28.5...binstalk-v0.28.6) - 2024-10-04 - -### Other - -- updated the following local packages: detect-targets - -## [0.28.5](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-v0.28.4...binstalk-v0.28.5) - 2024-09-22 - -### Other - -- updated the following local packages: detect-targets - -## [0.28.4](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-v0.28.3...binstalk-v0.28.4) - 2024-09-11 - -### Other - -- report to new stats server (with status) ([#1912](https://github.com/cargo-bins/cargo-binstall/pull/1912)) - -## [0.28.3](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-v0.28.2...binstalk-v0.28.3) - 2024-09-06 - -### Other -- Send telemetry report to quickinstall if no pre-built is found ([#1905](https://github.com/cargo-bins/cargo-binstall/pull/1905)) - -## [0.28.2](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-v0.28.1...binstalk-v0.28.2) - 2024-08-25 - -### Other -- updated the following local packages: detect-targets - -## [0.28.1](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-v0.28.0...binstalk-v0.28.1) - 2024-08-12 - -### Other -- updated the following local packages: binstalk-downloader - -## [0.28.0](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-v0.27.1...binstalk-v0.27.2) - 2024-08-10 - -### Other -- updated the following local packages: binstalk-types, binstalk-downloader, detect-targets - -## [0.27.1](https://github.com/cargo-bins/cargo-binstall/compare/binstalk-v0.27.0...binstalk-v0.27.1) - 2024-08-04 - -### Other -- Add `--maximum-resolution-timeout` ([#1862](https://github.com/cargo-bins/cargo-binstall/pull/1862)) diff --git a/crates/binstalk/Cargo.toml b/crates/binstalk/Cargo.toml deleted file mode 100644 index 3c26c8a7..00000000 --- a/crates/binstalk/Cargo.toml +++ /dev/null @@ -1,74 +0,0 @@ -[package] -name = "binstalk" -description = "The binstall toolkit (library interface)" -repository = "https://github.com/cargo-bins/cargo-binstall" -documentation = "https://docs.rs/binstalk" -version = "0.28.31" -rust-version = "1.79.0" -authors = ["ryan "] -edition = "2021" -license = "GPL-3.0-only" - -[dependencies] -binstalk-bins = { version = "0.6.13", path = "../binstalk-bins" } -binstalk-downloader = { version = "0.13.17", path = "../binstalk-downloader", default-features = false } -binstalk-git-repo-api = { version = "0.5.19", path = "../binstalk-git-repo-api" } -binstalk-fetchers = { version = "0.10.18", path = "../binstalk-fetchers", features = [ - "quickinstall", -] } -binstalk-registry = { version = "0.11.18", path = "../binstalk-registry" } -binstalk-types = { version = "0.9.4", path = "../binstalk-types" } -cargo-toml-workspace = { version = "7.0.6", path = "../cargo-toml-workspace" } -command-group = { version = "5.0.1", features = ["with-tokio"] } -compact_str = { version = "0.9.0", features = ["serde"] } -detect-targets = { version = "0.1.47", path = "../detect-targets", features = [ - "tracing", -] } -either = "1.11.0" -itertools = "0.14.0" -jobslot = { version = "0.2.11", features = ["tokio"] } -leon = "3.0.0" -maybe-owned = "0.3.4" -miette = "7.0.0" -semver = { version = "1.0.17", features = ["serde"] } -simple-git = { version = "0.2.18", optional = true } -strum = "0.27.0" -target-lexicon = { version = "0.13.0", features = ["std"] } -tempfile = "3.5.0" -thiserror = "2.0.11" -tokio = { version = "1.44.0", features = [ - "rt", - "process", - "sync", - "time", -], default-features = false } -tracing = "0.1.39" -url = { version = "2.5.4", features = ["serde"] } -zeroize = "1.8.1" - -[features] -default = ["static", "rustls", "git"] - -git = ["binstalk-registry/git", "simple-git"] -git-max-perf = ["git", "simple-git/git-max-perf-safe", "zlib-rs"] - -static = ["binstalk-downloader/static"] -pkg-config = ["binstalk-downloader/pkg-config"] - -zlib-ng = ["binstalk-downloader/zlib-ng"] -zlib-rs = ["binstalk-downloader/zlib-rs"] - -rustls = ["binstalk-downloader/rustls", "binstalk-registry/rustls"] -native-tls = ["binstalk-downloader/native-tls", "binstalk-registry/native-tls"] - -trust-dns = ["binstalk-downloader/trust-dns"] - -# Experimental HTTP/3 client, this would require `--cfg reqwest_unstable` -# to be passed to `rustc`. -http3 = ["binstalk-downloader/http3"] - -zstd-thin = ["binstalk-downloader/zstd-thin"] -cross-lang-fat-lto = ["binstalk-downloader/cross-lang-fat-lto"] - -[package.metadata.docs.rs] -rustdoc-args = ["--cfg", "docsrs"] diff --git a/crates/binstalk/LICENSE b/crates/binstalk/LICENSE deleted file mode 100644 index f288702d..00000000 --- a/crates/binstalk/LICENSE +++ /dev/null @@ -1,674 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. diff --git a/crates/binstalk/src/errors.rs b/crates/binstalk/src/errors.rs deleted file mode 100644 index 80686ef1..00000000 --- a/crates/binstalk/src/errors.rs +++ /dev/null @@ -1,599 +0,0 @@ -use std::{ - fmt, io, ops, - path::PathBuf, - process::{ExitCode, ExitStatus, Termination}, -}; - -use binstalk_downloader::{download::DownloadError, remote::Error as RemoteError}; -use binstalk_fetchers::FetchError; -use compact_str::CompactString; -use itertools::Itertools; -use miette::{Diagnostic, Report}; -use target_lexicon::ParseError as TargetTripleParseError; -use thiserror::Error; -use tokio::task; -use tracing::{error, warn}; - -use crate::{ - bins, - helpers::{ - cargo_toml::Error as CargoTomlError, - cargo_toml_workspace::Error as LoadManifestFromWSError, gh_api_client::GhApiError, - }, - registry::{InvalidRegistryError, RegistryError}, -}; - -#[derive(Debug, Error)] -#[error("version string '{v}' is not semver: {err}")] -pub struct VersionParseError { - pub v: CompactString, - #[source] - pub err: semver::Error, -} - -#[derive(Debug, Diagnostic, Error)] -#[error("For crate {crate_name}: {err}")] -pub struct CrateContextError { - crate_name: CompactString, - #[source] - #[diagnostic(transparent)] - err: BinstallError, -} - -#[derive(Debug)] -pub struct CrateErrors(Box<[Box]>); - -impl CrateErrors { - fn iter(&self) -> impl Iterator + Clone { - self.0.iter().map(ops::Deref::deref) - } - - fn get_iter_for<'a, T: 'a>( - &'a self, - f: fn(&'a CrateContextError) -> Option, - ) -> Option + 'a> { - let iter = self.iter().filter_map(f); - - if iter.clone().next().is_none() { - None - } else { - Some(iter) - } - } -} - -impl fmt::Display for CrateErrors { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(&self.0.iter().format(", "), f) - } -} - -impl std::error::Error for CrateErrors { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - self.0.first().map(|e| e as _) - } -} - -impl miette::Diagnostic for CrateErrors { - fn code<'a>(&'a self) -> Option> { - Some(Box::new("binstall::many_failure")) - } - - fn severity(&self) -> Option { - self.iter().filter_map(miette::Diagnostic::severity).max() - } - - fn help<'a>(&'a self) -> Option> { - Some(Box::new( - self.get_iter_for(miette::Diagnostic::help)?.format("\n"), - )) - } - - fn url<'a>(&'a self) -> Option> { - Some(Box::new( - self.get_iter_for(miette::Diagnostic::url)?.format("\n"), - )) - } - - fn source_code(&self) -> Option<&dyn miette::SourceCode> { - self.iter().find_map(miette::Diagnostic::source_code) - } - - fn labels(&self) -> Option + '_>> { - let get_iter = || self.iter().filter_map(miette::Diagnostic::labels).flatten(); - - if get_iter().next().is_none() { - None - } else { - Some(Box::new(get_iter())) - } - } - - fn related<'a>(&'a self) -> Option + 'a>> { - Some(Box::new( - self.iter().map(|e| e as _).chain( - self.iter() - .filter_map(miette::Diagnostic::related) - .flatten(), - ), - )) - } - - fn diagnostic_source(&self) -> Option<&dyn miette::Diagnostic> { - self.0.first().map(|err| &**err as _) - } -} - -#[derive(Debug, Error)] -#[error("Invalid pkg-url {pkg_url} for {crate_name}@{version} on {target}: {reason}")] -pub struct InvalidPkgFmtError { - pub crate_name: CompactString, - pub version: CompactString, - pub target: String, - pub pkg_url: String, - pub reason: &'static str, -} - -/// Error kinds emitted by cargo-binstall. -#[derive(Error, Diagnostic, Debug)] -#[non_exhaustive] -pub enum BinstallError { - /// Internal: a task could not be joined. - /// - /// - Code: `binstall::internal::task_join` - /// - Exit: 17 - #[error(transparent)] - #[diagnostic(severity(error), code(binstall::internal::task_join))] - TaskJoinError(#[from] task::JoinError), - - /// The installation was cancelled by a user at a confirmation prompt, - /// or user send a ctrl_c on all platforms or - /// `SIGINT`, `SIGHUP`, `SIGTERM` or `SIGQUIT` on unix to the program. - /// - /// - Code: `binstall::user_abort` - /// - Exit: 32 - #[error("installation cancelled by user")] - #[diagnostic(severity(info), code(binstall::user_abort))] - UserAbort, - - /// Package is not signed and policy requires it. - /// - /// - Code: `binstall::signature::invalid` - /// - Exit: 40 - #[error("Crate {crate_name} is signed and package {package_name} failed verification")] - #[diagnostic(severity(error), code(binstall::signature::invalid))] - InvalidSignature { - crate_name: CompactString, - package_name: CompactString, - }, - - /// Package is not signed and policy requires it. - /// - /// - Code: `binstall::signature::missing` - /// - Exit: 41 - #[error("Crate {0} does not have signing information")] - #[diagnostic(severity(error), code(binstall::signature::missing))] - MissingSignature(CompactString), - - /// A URL is invalid. - /// - /// This may be the result of a template in a Cargo manifest. - /// - /// - Code: `binstall::url_parse` - /// - Exit: 65 - #[error("Failed to parse url: {0}")] - #[diagnostic(severity(error), code(binstall::url_parse))] - UrlParse(#[from] url::ParseError), - - /// Failed to parse template. - /// - /// - Code: `binstall::template` - /// - Exit: 67 - #[error(transparent)] - #[diagnostic(severity(error), code(binstall::template))] - #[source_code(transparent)] - #[label(transparent)] - TemplateParseError( - #[from] - #[diagnostic_source] - leon::ParseError, - ), - - /// Failed to fetch pre-built binaries. - /// - /// - Code: `binstall::fetch` - /// - Exit: 68 - #[error(transparent)] - #[diagnostic(severity(error), code(binstall::fetch))] - #[source_code(transparent)] - #[label(transparent)] - FetchError(Box), - - /// Failed to download or failed to decode the body. - /// - /// - Code: `binstall::download` - /// - Exit: 68 - #[error(transparent)] - #[diagnostic(severity(error), code(binstall::download))] - Download(#[from] DownloadError), - - /// A subprocess failed. - /// - /// This is often about cargo-install calls. - /// - /// - Code: `binstall::subprocess` - /// - Exit: 70 - #[error("subprocess {command} errored with {status}")] - #[diagnostic(severity(error), code(binstall::subprocess))] - SubProcess { - command: Box, - status: ExitStatus, - }, - - /// A generic I/O error. - /// - /// - Code: `binstall::io` - /// - Exit: 74 - #[error("I/O Error: {0}")] - #[diagnostic(severity(error), code(binstall::io))] - Io(io::Error), - - /// Unknown registry name - /// - /// - Code: `binstall::cargo_registry` - /// - Exit: 75 - #[error("Unknown registry name {0}, env `CARGO_REGISTRIES_{0}_INDEX` nor is it in .cargo/config.toml")] - #[diagnostic(severity(error), code(binstall::cargo_registry))] - UnknownRegistryName(CompactString), - - /// An error interacting with the crates.io API. - /// - /// This could either be a "not found" or a server/transport error. - /// - /// - Code: `binstall::cargo_registry` - /// - Exit: 76 - #[error(transparent)] - #[diagnostic(transparent)] - RegistryError(#[from] Box), - - /// The override path to the cargo manifest is invalid or cannot be resolved. - /// - /// - Code: `binstall::cargo_manifest_path` - /// - Exit: 77 - #[error("the --manifest-path is invalid or cannot be resolved")] - #[diagnostic(severity(error), code(binstall::cargo_manifest_path))] - CargoManifestPath, - - /// A parsing or validation error in a cargo manifest. - /// - /// This should be rare, as manifests are generally fetched from crates.io, which does its own - /// validation upstream. The most common failure will therefore be for direct repository access - /// and with the `--manifest-path` option. - /// - /// - Code: `binstall::cargo_manifest` - /// - Exit: 78 - #[error("Failed to parse cargo manifest: {0}")] - #[diagnostic( - severity(error), - code(binstall::cargo_manifest), - help("If you used --manifest-path, check the Cargo.toml syntax.") - )] - CargoManifest(Box), - - /// Failure to parse registry index url - /// - /// - Code: `binstall::cargo_registry` - /// - Exit: 79 - #[error(transparent)] - #[diagnostic(severity(error), code(binstall::cargo_registry))] - RegistryParseError(#[from] Box), - - /// A version is not valid semver. - /// - /// Note that we use the [`semver`] crate, which parses Cargo version syntax; this may be - /// somewhat stricter or very slightly different from other semver implementations. - /// - /// - Code: `binstall::version::parse` - /// - Exit: 80 - #[error(transparent)] - #[diagnostic(severity(error), code(binstall::version::parse))] - VersionParse(#[from] Box), - - /// The crate@version syntax was used at the same time as the --version option. - /// - /// You can't do that as it's ambiguous which should apply. - /// - /// - Code: `binstall::conflict::version` - /// - Exit: 84 - #[error("superfluous version specification")] - #[diagnostic( - severity(error), - code(binstall::conflict::version), - help("You cannot use both crate@version and the --version option. Remove one.") - )] - SuperfluousVersionOption, - - /// No binaries were found for the crate. - /// - /// When installing, either the binaries are specified in the crate's Cargo.toml, or they're - /// inferred from the crate layout (e.g. src/main.rs or src/bins/name.rs). If no binaries are - /// found through these methods, we can't know what to install! - /// - /// - Code: `binstall::resolve::binaries` - /// - Exit: 86 - #[error("no binaries specified nor inferred")] - #[diagnostic( - severity(error), - code(binstall::resolve::binaries), - help("This crate doesn't specify any binaries, so there's nothing to install.") - )] - UnspecifiedBinaries, - - /// No viable targets were found. - /// - /// When installing, we attempt to find which targets the host (your computer) supports, and - /// discover builds for these targets from the remote binary source. This error occurs when we - /// fail to discover the host's target. - /// - /// You should in this case specify --target manually. - /// - /// - Code: `binstall::targets::none_host` - /// - Exit: 87 - #[error("failed to discovered a viable target from the host")] - #[diagnostic( - severity(error), - code(binstall::targets::none_host), - help("Try to specify --target") - )] - NoViableTargets, - - /// Failed to find or install binaries. - /// - /// - Code: `binstall::bins` - /// - Exit: 88 - #[error("failed to find or install binaries: {0}")] - #[diagnostic( - severity(error), - code(binstall::targets::none_host), - help("Try to specify --target") - )] - BinFile(#[from] bins::Error), - - /// `Cargo.toml` of the crate does not have section "Package". - /// - /// - Code: `binstall::cargo_manifest` - /// - Exit: 89 - #[error("Cargo.toml of crate {0} does not have section \"Package\"")] - #[diagnostic(severity(error), code(binstall::cargo_manifest))] - CargoTomlMissingPackage(CompactString), - - /// bin-dir configuration provided generates duplicate source path. - /// - /// - Code: `binstall::cargo_manifest` - /// - Exit: 90 - #[error("bin-dir configuration provided generates duplicate source path: {path}")] - #[diagnostic(severity(error), code(binstall::SourceFilePath))] - DuplicateSourceFilePath { path: PathBuf }, - - /// Fallback to `cargo-install` is disabled. - /// - /// - Code: `binstall::no_fallback_to_cargo_install` - /// - Exit: 94 - #[error("Fallback to cargo-install is disabled")] - #[diagnostic(severity(error), code(binstall::no_fallback_to_cargo_install))] - NoFallbackToCargoInstall, - - /// Fallback to `cargo-install` is disabled. - /// - /// - Code: `binstall::invalid_pkg_fmt` - /// - Exit: 95 - #[error(transparent)] - #[diagnostic(severity(error), code(binstall::invalid_pkg_fmt))] - InvalidPkgFmt(Box), - - /// Request to GitHub API failed - /// - /// - Code: `binstall::gh_api_failure` - /// - Exit: 96 - #[error("Request to GitHub API failed: {0}")] - #[diagnostic(severity(error), code(binstall::gh_api_failure))] - GhApiErr(#[source] Box), - - /// Failed to parse target triple - /// - /// - Code: `binstall::target_triple_parse_error` - /// - Exit: 97 - #[error("Failed to parse target triple: {0}")] - #[diagnostic(severity(error), code(binstall::target_triple_parse_error))] - TargetTripleParseError(#[source] Box), - - /// Failed to shallow clone git repository - /// - /// - Code: `binstall::git` - /// - Exit: 98 - #[cfg(feature = "git")] - #[error("Failed to shallow clone git repository: {0}")] - #[diagnostic(severity(error), code(binstall::git))] - GitError(#[from] crate::helpers::git::GitError), - - /// Failed to load manifest from workspace - /// - /// - Code: `binstall::load_manifest_from_workspace` - /// - Exit: 99 - #[error(transparent)] - #[diagnostic(severity(error), code(binstall::load_manifest_from_workspace))] - LoadManifestFromWSError(#[from] Box), - - /// A wrapped error providing the context of which crate the error is about. - #[error(transparent)] - #[diagnostic(transparent)] - CrateContext(Box), - - /// A wrapped error for failures of multiple crates when `--continue-on-failure` is specified. - #[error(transparent)] - #[diagnostic(transparent)] - Errors(CrateErrors), -} - -impl BinstallError { - fn exit_number(&self) -> u8 { - use BinstallError::*; - let code: u8 = match self { - TaskJoinError(_) => 17, - UserAbort => 32, - InvalidSignature { .. } => 40, - MissingSignature(_) => 41, - UrlParse(_) => 65, - TemplateParseError(..) => 67, - FetchError(..) => 68, - Download(_) => 68, - SubProcess { .. } => 70, - Io(_) => 74, - UnknownRegistryName(_) => 75, - RegistryError { .. } => 76, - CargoManifestPath => 77, - CargoManifest { .. } => 78, - RegistryParseError(..) => 79, - VersionParse { .. } => 80, - SuperfluousVersionOption => 84, - UnspecifiedBinaries => 86, - NoViableTargets => 87, - BinFile(_) => 88, - CargoTomlMissingPackage(_) => 89, - DuplicateSourceFilePath { .. } => 90, - NoFallbackToCargoInstall => 94, - InvalidPkgFmt(..) => 95, - GhApiErr(..) => 96, - TargetTripleParseError(..) => 97, - #[cfg(feature = "git")] - GitError(_) => 98, - LoadManifestFromWSError(_) => 99, - CrateContext(context) => context.err.exit_number(), - Errors(errors) => (errors.0)[0].err.exit_number(), - }; - - // reserved codes - debug_assert!(code != 64 && code != 16 && code != 1 && code != 2 && code != 0); - - code - } - - /// The recommended exit code for this error. - /// - /// This will never output: - /// - 0 (success) - /// - 1 and 2 (catchall and shell) - /// - 16 (binstall errors not handled here) - /// - 64 (generic error) - pub fn exit_code(&self) -> ExitCode { - self.exit_number().into() - } - - /// Add crate context to the error - pub fn crate_context(self, crate_name: impl Into) -> Self { - self.crate_context_inner(crate_name.into()) - } - - fn crate_context_inner(self, crate_name: CompactString) -> Self { - match self { - Self::CrateContext(mut crate_context_error) => { - crate_context_error.crate_name = crate_name; - Self::CrateContext(crate_context_error) - } - err => Self::CrateContext(Box::new(CrateContextError { err, crate_name })), - } - } - - pub fn crate_errors(mut errors: Vec>) -> Option { - if errors.is_empty() { - None - } else if errors.len() == 1 { - Some(Self::CrateContext(errors.pop().unwrap())) - } else { - Some(Self::Errors(CrateErrors(errors.into_boxed_slice()))) - } - } -} - -impl Termination for BinstallError { - fn report(self) -> ExitCode { - let code = self.exit_code(); - if let BinstallError::UserAbort = self { - warn!("Installation cancelled"); - } else { - error!("Fatal error:\n{:?}", Report::new(self)); - } - - code - } -} - -impl From for BinstallError { - fn from(err: io::Error) -> Self { - err.downcast::() - .unwrap_or_else(BinstallError::Io) - } -} - -impl From for io::Error { - fn from(e: BinstallError) -> io::Error { - match e { - BinstallError::Io(io_error) => io_error, - e => io::Error::new(io::ErrorKind::Other, e), - } - } -} - -impl From for BinstallError { - fn from(e: RemoteError) -> Self { - DownloadError::from(e).into() - } -} - -impl From for BinstallError { - fn from(e: CargoTomlError) -> Self { - BinstallError::CargoManifest(Box::new(e)) - } -} - -impl From for BinstallError { - fn from(e: InvalidPkgFmtError) -> Self { - BinstallError::InvalidPkgFmt(Box::new(e)) - } -} - -impl From for BinstallError { - fn from(e: GhApiError) -> Self { - BinstallError::GhApiErr(Box::new(e)) - } -} - -impl From for BinstallError { - fn from(e: target_lexicon::ParseError) -> Self { - BinstallError::TargetTripleParseError(Box::new(e)) - } -} - -impl From for BinstallError { - fn from(e: RegistryError) -> Self { - BinstallError::RegistryError(Box::new(e)) - } -} - -impl From for BinstallError { - fn from(e: InvalidRegistryError) -> Self { - BinstallError::RegistryParseError(Box::new(e)) - } -} - -impl From for BinstallError { - fn from(e: LoadManifestFromWSError) -> Self { - BinstallError::LoadManifestFromWSError(Box::new(e)) - } -} - -impl From for BinstallError { - fn from(e: FetchError) -> Self { - BinstallError::FetchError(Box::new(e)) - } -} diff --git a/crates/binstalk/src/helpers.rs b/crates/binstalk/src/helpers.rs deleted file mode 100644 index 5222f440..00000000 --- a/crates/binstalk/src/helpers.rs +++ /dev/null @@ -1,19 +0,0 @@ -pub mod jobserver_client; -pub mod remote { - pub use binstalk_downloader::remote::*; - pub use url::ParseError as UrlParseError; -} -pub mod lazy_gh_api_client; -pub(crate) mod target_triple; -pub mod tasks; - -pub(crate) use binstalk_downloader::download; -pub use binstalk_git_repo_api::gh_api_client; - -pub(crate) use cargo_toml_workspace::{self, cargo_toml}; -#[cfg(feature = "git")] -pub(crate) use simple_git as git; - -pub(crate) fn is_universal_macos(target: &str) -> bool { - ["universal-apple-darwin", "universal2-apple-darwin"].contains(&target) -} diff --git a/crates/binstalk/src/helpers/jobserver_client.rs b/crates/binstalk/src/helpers/jobserver_client.rs deleted file mode 100644 index ae9db6b2..00000000 --- a/crates/binstalk/src/helpers/jobserver_client.rs +++ /dev/null @@ -1,33 +0,0 @@ -use std::{num::NonZeroUsize, thread::available_parallelism}; - -use jobslot::Client; -use tokio::sync::OnceCell; - -use crate::errors::BinstallError; - -#[derive(Debug)] -pub struct LazyJobserverClient(OnceCell); - -impl LazyJobserverClient { - /// This must be called at the start of the program since - /// `Client::from_env` requires that. - #[allow(clippy::new_without_default)] - pub fn new() -> Self { - // Safety: - // - // Client::from_env is unsafe because from_raw_fd is unsafe. - // It doesn't do anything that is actually unsafe, like - // dereferencing pointer. - let opt = unsafe { Client::from_env() }; - Self(OnceCell::new_with(opt)) - } - - pub async fn get(&self) -> Result<&Client, BinstallError> { - self.0 - .get_or_try_init(|| async { - let ncore = available_parallelism().map(NonZeroUsize::get).unwrap_or(1); - Ok(Client::new(ncore)?) - }) - .await - } -} diff --git a/crates/binstalk/src/helpers/lazy_gh_api_client.rs b/crates/binstalk/src/helpers/lazy_gh_api_client.rs deleted file mode 100644 index 26869e91..00000000 --- a/crates/binstalk/src/helpers/lazy_gh_api_client.rs +++ /dev/null @@ -1,53 +0,0 @@ -use std::{future::Future, sync::Mutex}; - -use binstalk_git_repo_api::gh_api_client::GhApiClient; -use tokio::sync::OnceCell; -use zeroize::Zeroizing; - -use crate::{ - errors::BinstallError, - helpers::{remote, tasks::AutoAbortJoinHandle}, -}; - -pub type GitHubToken = Option>>; - -#[derive(Debug)] -pub struct LazyGhApiClient { - client: remote::Client, - inner: OnceCell, - task: Mutex>>, -} - -impl LazyGhApiClient { - pub fn new(client: remote::Client, auth_token: GitHubToken) -> Self { - Self { - inner: OnceCell::new_with(Some(GhApiClient::new(client.clone(), auth_token))), - client, - task: Mutex::new(None), - } - } - - pub fn with_get_gh_token_future(client: remote::Client, get_auth_token_future: Fut) -> Self - where - Fut: Future + Send + Sync + 'static, - { - Self { - inner: OnceCell::new(), - task: Mutex::new(Some(AutoAbortJoinHandle::spawn(get_auth_token_future))), - client, - } - } - - pub async fn get(&self) -> Result<&GhApiClient, BinstallError> { - self.inner - .get_or_try_init(|| async { - let task = self.task.lock().unwrap().take(); - Ok(if let Some(task) = task { - GhApiClient::new(self.client.clone(), task.await?) - } else { - GhApiClient::new(self.client.clone(), None) - }) - }) - .await - } -} diff --git a/crates/binstalk/src/helpers/target_triple.rs b/crates/binstalk/src/helpers/target_triple.rs deleted file mode 100644 index 260deac4..00000000 --- a/crates/binstalk/src/helpers/target_triple.rs +++ /dev/null @@ -1,52 +0,0 @@ -use std::{borrow::Cow, str::FromStr}; - -use compact_str::{CompactString, ToCompactString}; -use target_lexicon::Triple; - -use crate::{errors::BinstallError, helpers::is_universal_macos}; - -#[derive(Clone, Debug)] -pub struct TargetTriple { - pub target_family: Cow<'static, str>, - pub target_arch: Cow<'static, str>, - pub target_libc: Cow<'static, str>, - pub target_vendor: CompactString, -} - -impl FromStr for TargetTriple { - type Err = BinstallError; - - fn from_str(mut s: &str) -> Result { - let is_universal_macos = is_universal_macos(s); - - if is_universal_macos { - s = "x86_64-apple-darwin"; - } - - let triple = Triple::from_str(s)?; - - Ok(Self { - target_family: triple.operating_system.into_str(), - target_arch: if is_universal_macos { - Cow::Borrowed("universal") - } else { - triple.architecture.into_str() - }, - target_libc: triple.environment.into_str(), - target_vendor: triple.vendor.to_compact_string(), - }) - } -} - -impl leon::Values for TargetTriple { - fn get_value<'s>(&'s self, key: &str) -> Option> { - match key { - "target-family" => Some(Cow::Borrowed(&self.target_family)), - "target-arch" => Some(Cow::Borrowed(&self.target_arch)), - "target-libc" => Some(Cow::Borrowed(&self.target_libc)), - "target-vendor" => Some(Cow::Borrowed(&self.target_vendor)), - - _ => None, - } - } -} diff --git a/crates/binstalk/src/helpers/tasks.rs b/crates/binstalk/src/helpers/tasks.rs deleted file mode 100644 index 6530c444..00000000 --- a/crates/binstalk/src/helpers/tasks.rs +++ /dev/null @@ -1,70 +0,0 @@ -use std::{ - future::Future, - ops::{Deref, DerefMut}, - pin::Pin, - task::{Context, Poll}, -}; - -use tokio::task::JoinHandle; - -use crate::errors::BinstallError; - -#[derive(Debug)] -pub struct AutoAbortJoinHandle(JoinHandle); - -impl AutoAbortJoinHandle { - pub fn new(handle: JoinHandle) -> Self { - Self(handle) - } -} - -impl AutoAbortJoinHandle -where - T: Send + 'static, -{ - pub fn spawn(future: F) -> Self - where - F: Future + Send + 'static, - { - Self(tokio::spawn(future)) - } -} - -impl Drop for AutoAbortJoinHandle { - fn drop(&mut self) { - self.0.abort(); - } -} - -impl Deref for AutoAbortJoinHandle { - type Target = JoinHandle; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl DerefMut for AutoAbortJoinHandle { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - -impl Future for AutoAbortJoinHandle { - type Output = Result; - - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - Pin::new(&mut Pin::into_inner(self).0) - .poll(cx) - .map_err(BinstallError::TaskJoinError) - } -} - -impl AutoAbortJoinHandle> -where - E: Into, -{ - pub async fn flattened_join(self) -> Result { - self.await?.map_err(Into::into) - } -} diff --git a/crates/binstalk/src/lib.rs b/crates/binstalk/src/lib.rs deleted file mode 100644 index 83df1938..00000000 --- a/crates/binstalk/src/lib.rs +++ /dev/null @@ -1,13 +0,0 @@ -#![cfg_attr(docsrs, feature(doc_auto_cfg))] - -pub mod errors; -pub mod helpers; -pub mod ops; - -use binstalk_bins as bins; -pub use binstalk_fetchers as fetchers; -pub use binstalk_registry as registry; -pub use binstalk_types as manifests; -pub use detect_targets::{get_desired_targets, DesiredTargets, TARGET}; - -pub use fetchers::QUICKINSTALL_STATS_URL; diff --git a/crates/binstalk/src/ops.rs b/crates/binstalk/src/ops.rs deleted file mode 100644 index 8051daaa..00000000 --- a/crates/binstalk/src/ops.rs +++ /dev/null @@ -1,61 +0,0 @@ -//! Concrete Binstall operations. - -use std::{path::PathBuf, sync::Arc, time::Duration}; - -use semver::VersionReq; - -use crate::{ - fetchers::{Data, Fetcher, SignaturePolicy, TargetDataErased}, - helpers::{ - gh_api_client::GhApiClient, jobserver_client::LazyJobserverClient, - lazy_gh_api_client::LazyGhApiClient, remote::Client, - }, - manifests::cargo_toml_binstall::PkgOverride, - registry::Registry, - DesiredTargets, -}; - -pub mod resolve; - -pub type Resolver = - fn(Client, GhApiClient, Arc, Arc, SignaturePolicy) -> Arc; - -#[derive(Debug)] -#[non_exhaustive] -pub enum CargoTomlFetchOverride { - #[cfg(feature = "git")] - Git(crate::helpers::git::GitUrl), - Path(PathBuf), -} - -#[derive(Debug)] -pub struct Options { - pub no_symlinks: bool, - pub dry_run: bool, - pub force: bool, - pub quiet: bool, - pub locked: bool, - pub no_track: bool, - - pub version_req: Option, - pub cargo_toml_fetch_override: Option, - pub cli_overrides: PkgOverride, - - pub desired_targets: DesiredTargets, - pub resolvers: Vec, - pub cargo_install_fallback: bool, - - pub temp_dir: PathBuf, - pub install_path: PathBuf, - pub cargo_root: Option, - - pub client: Client, - pub gh_api_client: LazyGhApiClient, - pub jobserver_client: LazyJobserverClient, - pub registry: Registry, - - pub signature_policy: SignaturePolicy, - pub disable_telemetry: bool, - - pub maximum_resolution_timeout: Duration, -} diff --git a/crates/binstalk/src/ops/resolve.rs b/crates/binstalk/src/ops/resolve.rs deleted file mode 100644 index 265a18b6..00000000 --- a/crates/binstalk/src/ops/resolve.rs +++ /dev/null @@ -1,596 +0,0 @@ -use std::{ - borrow::Cow, - collections::{BTreeMap, BTreeSet}, - iter, mem, - path::Path, - str::FromStr, - sync::Arc, -}; - -use binstalk_fetchers::FETCHER_GH_CRATE_META; -use binstalk_types::{ - cargo_toml_binstall::Strategy, - crate_info::{CrateSource, SourceType}, -}; -use compact_str::{CompactString, ToCompactString}; -use itertools::Itertools; -use leon::Template; -use maybe_owned::MaybeOwned; -use semver::{Version, VersionReq}; -use tokio::{task::spawn_blocking, time::timeout}; -use tracing::{debug, error, info, instrument, warn}; -use url::Url; - -use crate::{ - bins, - errors::{BinstallError, VersionParseError}, - fetchers::{Data, Fetcher, TargetData}, - helpers::{ - cargo_toml::Manifest, cargo_toml_workspace::load_manifest_from_workspace, - download::ExtractedFiles, remote::Client, target_triple::TargetTriple, - tasks::AutoAbortJoinHandle, - }, - manifests::cargo_toml_binstall::{Meta, PkgMeta, PkgOverride}, - ops::{CargoTomlFetchOverride, Options}, -}; - -mod crate_name; -#[doc(inline)] -pub use crate_name::CrateName; - -mod version_ext; -#[doc(inline)] -pub use version_ext::VersionReqExt; - -mod resolution; -#[doc(inline)] -pub use resolution::{Resolution, ResolutionFetch, ResolutionSource}; - -#[instrument(skip_all)] -pub async fn resolve( - opts: Arc, - crate_name: CrateName, - curr_version: Option, -) -> Result { - let crate_name_name = crate_name.name.clone(); - let resolution = resolve_inner(opts, crate_name, curr_version) - .await - .map_err(|err| err.crate_context(crate_name_name))?; - - Ok(resolution) -} - -async fn resolve_inner( - opts: Arc, - crate_name: CrateName, - curr_version: Option, -) -> Result { - info!("Resolving package: '{}'", crate_name); - - let version_req = match (&crate_name.version_req, &opts.version_req) { - (Some(version), None) => MaybeOwned::Borrowed(version), - (None, Some(version)) => MaybeOwned::Borrowed(version), - (Some(_), Some(_)) => Err(BinstallError::SuperfluousVersionOption)?, - (None, None) => MaybeOwned::Owned(VersionReq::STAR), - }; - - let version_req_str = version_req.to_compact_string(); - - let Some(package_info) = PackageInfo::resolve( - &opts, - crate_name.name, - curr_version, - &version_req, - opts.client.clone(), - ) - .await? - else { - return Ok(Resolution::AlreadyUpToDate); - }; - - let desired_targets = opts - .desired_targets - .get() - .await - .iter() - .map(|target| { - debug!("Building metadata for target: {target}"); - - let meta = package_info.meta.merge_overrides( - iter::once(&opts.cli_overrides).chain(package_info.overrides.get(target)), - ); - - debug!("Found metadata: {meta:?}"); - - Ok(Arc::new(TargetData { - target: target.clone(), - meta, - target_related_info: TargetTriple::from_str(target)?, - })) - }) - .collect::, BinstallError>>()?; - let resolvers = &opts.resolvers; - - let binary_name = match package_info.binaries.as_slice() { - [bin] if bin.name != package_info.name => Some(CompactString::from(bin.name.as_str())), - _ => None, - }; - - let mut handles: Vec> = Vec::with_capacity( - desired_targets.len() * resolvers.len() - + if binary_name.is_some() { - desired_targets.len() - } else { - 0 - }, - ); - - let gh_api_client = opts.gh_api_client.get().await?; - - let mut handles_fn = - |data: Arc, filter_fetcher_by_name_predicate: fn(&'static str) -> bool| { - handles.extend( - resolvers - .iter() - .cartesian_product(&desired_targets) - .filter_map(|(f, target_data)| { - let fetcher = f( - opts.client.clone(), - gh_api_client.clone(), - data.clone(), - target_data.clone(), - opts.signature_policy, - ); - - if let Some(disabled_strategies) = - target_data.meta.disabled_strategies.as_deref() - { - if disabled_strategies.contains(&fetcher.strategy()) { - return None; - } - } - - filter_fetcher_by_name_predicate(fetcher.fetcher_name()).then_some(fetcher) - }), - ) - }; - - handles_fn( - Arc::new(Data::new( - package_info.name.clone(), - package_info.version_str.clone(), - package_info.repo.clone(), - )), - |_| true, - ); - - if let Some(binary_name) = binary_name { - handles_fn( - Arc::new(Data::new( - binary_name, - package_info.version_str.clone(), - package_info.repo.clone(), - )), - |name| name == FETCHER_GH_CRATE_META, - ); - } - - for fetcher in &handles { - match timeout( - opts.maximum_resolution_timeout, - AutoAbortJoinHandle::new(fetcher.clone().find()).flattened_join(), - ) - .await - { - Ok(ret) => match ret { - Ok(true) => { - // Generate temporary binary path - let bin_path = opts.temp_dir.join(format!( - "bin-{}-{}-{}", - package_info.name, - fetcher.target(), - fetcher.fetcher_name() - )); - - match download_extract_and_verify( - fetcher.as_ref(), - &bin_path, - &package_info, - &opts.install_path, - opts.no_symlinks, - ) - .await - { - Ok(bin_files) => { - if !bin_files.is_empty() { - fetcher.clone().report_to_upstream(); - return Ok(Resolution::Fetch(Box::new(ResolutionFetch { - fetcher: fetcher.clone(), - new_version: package_info.version, - name: package_info.name, - version_req: version_req_str, - source: package_info.source, - bin_files, - }))); - } else { - warn!( - "Error when checking binaries provided by fetcher {}: \ - The fetcher does not provide any optional binary", - fetcher.source_name(), - ); - } - } - Err(err) => { - if let BinstallError::UserAbort = err { - return Err(err); - } - warn!( - "Error while downloading and extracting from fetcher {}: {}", - fetcher.source_name(), - err - ); - } - } - } - Ok(false) => (), - Err(err) => { - warn!( - "Error while checking fetcher {}: {}", - fetcher.source_name(), - err - ); - } - }, - Err(err) => { - warn!( - "Timeout reached while checking fetcher {}: {}", - fetcher.source_name(), - err - ); - } - } - } - - // At this point, we don't know whether fallback to cargo install is allowed, or whether it will - // succeed, but things start to get convoluted when try to include that data, so this will do. - if !opts.disable_telemetry { - for fetcher in handles { - fetcher.report_to_upstream(); - } - } - - if !opts.cargo_install_fallback { - return Err(BinstallError::NoFallbackToCargoInstall); - } - - let meta = package_info - .meta - .merge_overrides(iter::once(&opts.cli_overrides)); - - let target_meta = desired_targets - .first() - .map(|target_data| &target_data.meta) - .unwrap_or(&meta); - - if let Some(disabled_strategies) = target_meta.disabled_strategies.as_deref() { - if disabled_strategies.contains(&Strategy::Compile) { - return Err(BinstallError::NoFallbackToCargoInstall); - } - } - - Ok(Resolution::InstallFromSource(ResolutionSource { - name: package_info.name, - version: package_info.version_str, - })) -} - -/// * `fetcher` - `fetcher.find()` must have returned `Ok(true)`. -/// -/// Can return empty Vec if all `BinFile` is optional and does not exist -/// in the archive downloaded. -async fn download_extract_and_verify( - fetcher: &dyn Fetcher, - bin_path: &Path, - package_info: &PackageInfo, - install_path: &Path, - no_symlinks: bool, -) -> Result, BinstallError> { - // Download and extract it. - // If that fails, then ignore this fetcher. - let extracted_files = fetcher.fetch_and_extract(bin_path).await?; - debug!("extracted_files = {extracted_files:#?}"); - - // Build final metadata - let meta = fetcher.target_meta(); - - // Verify that all non-optional bin_files exist - let bin_files = collect_bin_files( - fetcher, - package_info, - meta, - bin_path, - install_path, - no_symlinks, - &extracted_files, - )?; - - let name = &package_info.name; - - package_info - .binaries - .iter() - .zip(bin_files) - .filter_map(|(bin, bin_file)| { - match bin_file.check_source_exists(&mut |p| extracted_files.has_file(p)) { - Ok(()) => Some(Ok(bin_file)), - - // This binary is optional - Err(err) => { - let required_features = &bin.required_features; - let bin_name = bin.name.as_str(); - - if required_features.is_empty() { - error!( - "When resolving {name} bin {bin_name} is not found. \ -This binary is not optional so it must be included in the archive, please contact with \ -upstream to fix this issue." - ); - // This bin is not optional, error - Some(Err(err)) - } else { - // Optional, print a warning and continue. - let features = required_features.iter().format(","); - warn!( - "When resolving {name} bin {bin_name} is not found. \ -But since it requires features {features}, this bin is ignored." - ); - None - } - } - } - }) - .collect::, bins::Error>>() - .map_err(BinstallError::from) -} - -fn collect_bin_files( - fetcher: &dyn Fetcher, - package_info: &PackageInfo, - meta: PkgMeta, - bin_path: &Path, - install_path: &Path, - no_symlinks: bool, - extracted_files: &ExtractedFiles, -) -> Result, BinstallError> { - // List files to be installed - // based on those found via Cargo.toml - let bin_data = bins::Data { - name: &package_info.name, - target: fetcher.target(), - version: &package_info.version_str, - repo: package_info.repo.as_deref(), - meta, - bin_path, - install_path, - target_related_info: &fetcher.target_data().target_related_info, - }; - - let bin_dir = bin_data - .meta - .bin_dir - .as_deref() - .map(Cow::Borrowed) - .unwrap_or_else(|| { - bins::infer_bin_dir_template(&bin_data, &mut |p| extracted_files.get_dir(p).is_some()) - }); - - let template = Template::parse(&bin_dir)?; - - // Create bin_files - let bin_files = package_info - .binaries - .iter() - .map(|bin| bins::BinFile::new(&bin_data, bin.name.as_str(), &template, no_symlinks)) - .collect::, bins::Error>>()?; - - let mut source_set = BTreeSet::new(); - - for bin in &bin_files { - if !source_set.insert(&bin.source) { - return Err(BinstallError::DuplicateSourceFilePath { - path: bin.source.clone(), - }); - } - } - - Ok(bin_files) -} - -struct PackageInfo { - meta: PkgMeta, - binaries: Vec, - name: CompactString, - version_str: CompactString, - source: CrateSource, - version: Version, - repo: Option, - overrides: BTreeMap, -} - -struct Bin { - name: String, - required_features: Vec, -} - -impl PackageInfo { - /// Return `None` if already up-to-date. - async fn resolve( - opts: &Options, - name: CompactString, - curr_version: Option, - version_req: &VersionReq, - client: Client, - ) -> Result, BinstallError> { - use CargoTomlFetchOverride::*; - - // Fetch crate via crates.io, git, or use a local manifest path - let (manifest, source) = match opts.cargo_toml_fetch_override.as_ref() { - Some(Path(manifest_path)) => ( - spawn_blocking({ - let manifest_path = manifest_path.clone(); - let name = name.clone(); - - move || load_manifest_path(manifest_path, &name) - }) - .await??, - CrateSource { - source_type: SourceType::Path, - url: MaybeOwned::Owned(Url::parse(&format!( - "file://{}", - manifest_path.display() - ))?), - }, - ), - #[cfg(feature = "git")] - Some(Git(git_url)) => { - use crate::helpers::git::{GitCancellationToken, Repository as GitRepository}; - - let cancellation_token = GitCancellationToken::default(); - // Cancel git operation if the future is cancelled (dropped). - let cancel_on_drop = cancellation_token.clone().cancel_on_drop(); - - let (ret, commit_hash) = spawn_blocking({ - let git_url = git_url.clone(); - let name = name.clone(); - move || { - let dir = tempfile::TempDir::new()?; - let repo = GitRepository::shallow_clone( - git_url, - dir.as_ref(), - Some(cancellation_token), - )?; - - Ok::<_, BinstallError>(( - load_manifest_from_workspace(dir.as_ref(), &name) - .map_err(BinstallError::from)?, - repo.get_head_commit_hash()?, - )) - } - }) - .await??; - - // Git operation done, disarm it - cancel_on_drop.disarm(); - - ( - ret, - CrateSource { - source_type: SourceType::Git, - url: MaybeOwned::Owned(Url::parse(&format!("{git_url}#{commit_hash}"))?), - }, - ) - } - None => ( - Box::pin( - opts.registry - .fetch_crate_matched(client, &name, version_req), - ) - .await?, - { - let registry = format!("{}", opts.registry); - if registry == "https://index.crates.io/" { - CrateSource::cratesio_registry() - } else { - CrateSource { - source_type: SourceType::Registry, - url: MaybeOwned::Owned(Url::parse(®istry)?), - } - } - }, - ), - }; - - let Some(mut package) = manifest.package else { - return Err(BinstallError::CargoTomlMissingPackage(name)); - }; - - let new_version_str = package.version().to_compact_string(); - let new_version = match Version::parse(&new_version_str) { - Ok(new_version) => new_version, - Err(err) => { - return Err(Box::new(VersionParseError { - v: new_version_str, - err, - }) - .into()) - } - }; - - if let Some(curr_version) = curr_version { - if new_version == curr_version { - info!( - "{} v{curr_version} is already installed, use --force to override", - name - ); - return Ok(None); - } - } - - let (mut meta, binaries): (_, Vec) = ( - package - .metadata - .take() - .and_then(|m| m.binstall) - .unwrap_or_default(), - manifest - .bin - .into_iter() - .filter_map(|p| { - p.name.map(|name| Bin { - name, - required_features: p.required_features, - }) - }) - .collect(), - ); - - // Check binaries - if binaries.is_empty() { - Err(BinstallError::UnspecifiedBinaries) - } else { - Ok(Some(Self { - overrides: mem::take(&mut meta.overrides), - meta, - binaries, - name, - source, - version_str: new_version_str, - version: new_version, - repo: package.repository().map(ToString::to_string), - })) - } - } -} - -/// Load binstall metadata from the crate `Cargo.toml` at the provided path -/// -/// This is a blocking function. -pub fn load_manifest_path, N: AsRef>( - manifest_path: P, - name: N, -) -> Result, BinstallError> { - fn inner(manifest_path: &Path, crate_name: &str) -> Result, BinstallError> { - debug!( - "Reading crate {crate_name} manifest at local path: {}", - manifest_path.display() - ); - - // Load and parse manifest (this checks file system for binary output names) - let manifest = load_manifest_from_workspace(manifest_path, crate_name)?; - - // Return metadata - Ok(manifest) - } - - inner(manifest_path.as_ref(), name.as_ref()) -} diff --git a/crates/binstalk/src/ops/resolve/crate_name.rs b/crates/binstalk/src/ops/resolve/crate_name.rs deleted file mode 100644 index 775c1f33..00000000 --- a/crates/binstalk/src/ops/resolve/crate_name.rs +++ /dev/null @@ -1,111 +0,0 @@ -use std::{fmt, str::FromStr}; - -use compact_str::CompactString; -use itertools::Itertools; -use semver::{Error, VersionReq}; - -use super::version_ext::VersionReqExt; - -#[derive(Debug, Clone, Eq, PartialEq)] -pub struct CrateName { - pub name: CompactString, - pub version_req: Option, -} - -impl fmt::Display for CrateName { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.name)?; - - if let Some(version) = &self.version_req { - write!(f, "@{version}")?; - } - - Ok(()) - } -} - -impl FromStr for CrateName { - type Err = Error; - - fn from_str(s: &str) -> Result { - Ok(if let Some((name, version)) = s.split_once('@') { - CrateName { - name: name.into(), - version_req: Some(VersionReq::parse_from_cli(version)?), - } - } else { - CrateName { - name: s.into(), - version_req: None, - } - }) - } -} - -impl CrateName { - pub fn dedup(mut crate_names: Vec) -> impl Iterator { - crate_names.sort_by(|x, y| x.name.cmp(&y.name)); - crate_names.into_iter().coalesce(|previous, current| { - if previous.name == current.name { - Ok(current) - } else { - Err((previous, current)) - } - }) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - macro_rules! assert_dedup { - ([ $( ( $input_name:expr, $input_version:expr ) ),* ], [ $( ( $output_name:expr, $output_version:expr ) ),* ]) => { - let input_crate_names = vec![$( CrateName { - name: $input_name.into(), - version_req: Some($input_version.parse().unwrap()) - }, )*]; - - let mut output_crate_names: Vec = vec![$( CrateName { - name: $output_name.into(), version_req: Some($output_version.parse().unwrap()) - }, )*]; - output_crate_names.sort_by(|x, y| x.name.cmp(&y.name)); - - let crate_names: Vec<_> = CrateName::dedup(input_crate_names).collect(); - assert_eq!(crate_names, output_crate_names); - }; - } - - #[test] - fn test_dedup() { - // Base case 0: Empty input - assert_dedup!([], []); - - // Base case 1: With only one input - assert_dedup!([("a", "1")], [("a", "1")]); - - // Base Case 2: Only has duplicate names - assert_dedup!([("a", "1"), ("a", "2")], [("a", "2")]); - - // Complex Case 0: Having two crates - assert_dedup!( - [("a", "10"), ("b", "3"), ("a", "0"), ("b", "0"), ("a", "1")], - [("a", "1"), ("b", "0")] - ); - - // Complex Case 1: Having three crates - assert_dedup!( - [ - ("d", "1.1"), - ("a", "10"), - ("b", "3"), - ("d", "230"), - ("a", "0"), - ("b", "0"), - ("a", "1"), - ("d", "23") - ], - [("a", "1"), ("b", "0"), ("d", "23")] - ); - } -} diff --git a/crates/binstalk/src/ops/resolve/resolution.rs b/crates/binstalk/src/ops/resolve/resolution.rs deleted file mode 100644 index 75d4f398..00000000 --- a/crates/binstalk/src/ops/resolve/resolution.rs +++ /dev/null @@ -1,235 +0,0 @@ -use std::{borrow::Cow, env, ffi::OsStr, fmt, iter, path::Path, sync::Arc}; - -use command_group::AsyncCommandGroup; -use compact_str::{CompactString, ToCompactString}; -use either::Either; -use itertools::Itertools; -use semver::Version; -use tokio::process::Command; -use tracing::{debug, error, info, warn}; - -use crate::{ - bins, - errors::BinstallError, - fetchers::Fetcher, - manifests::crate_info::{CrateInfo, CrateSource}, - ops::Options, -}; - -pub struct ResolutionFetch { - pub fetcher: Arc, - pub new_version: Version, - pub name: CompactString, - pub version_req: CompactString, - pub bin_files: Vec, - pub source: CrateSource, -} - -pub struct ResolutionSource { - pub name: CompactString, - pub version: CompactString, -} - -pub enum Resolution { - Fetch(Box), - InstallFromSource(ResolutionSource), - AlreadyUpToDate, -} - -impl Resolution { - pub fn print(&self, opts: &Options) { - match self { - Resolution::Fetch(fetch) => { - fetch.print(opts); - } - Resolution::InstallFromSource(source) => { - source.print(); - } - Resolution::AlreadyUpToDate => (), - } - } -} - -impl ResolutionFetch { - pub fn install(self, opts: &Options) -> Result { - let crate_name = self.name.clone(); - self.install_inner(opts) - .map_err(|err| err.crate_context(crate_name)) - } - - fn install_inner(self, opts: &Options) -> Result { - type InstallFp = fn(&bins::BinFile) -> Result<(), bins::Error>; - - let (install_bin, install_link): (InstallFp, InstallFp) = match (opts.no_track, opts.force) - { - (true, true) | (false, _) => (bins::BinFile::install_bin, bins::BinFile::install_link), - (true, false) => ( - bins::BinFile::install_bin_noclobber, - bins::BinFile::install_link_noclobber, - ), - }; - - info!("Installing binaries..."); - for file in &self.bin_files { - install_bin(file)?; - } - - // Generate symlinks - if !opts.no_symlinks { - for file in &self.bin_files { - install_link(file)?; - } - } - - Ok(CrateInfo { - name: self.name, - version_req: self.version_req, - current_version: self.new_version, - source: self.source, - target: self.fetcher.target().to_compact_string(), - bins: self - .bin_files - .into_iter() - .map(|bin| bin.base_name) - .collect(), - }) - } - - pub fn print(&self, opts: &Options) { - let fetcher = &self.fetcher; - let bin_files = &self.bin_files; - let name = &self.name; - let new_version = &self.new_version; - let target = fetcher.target(); - - debug!( - "Found a binary install source: {} ({target})", - fetcher.source_name(), - ); - - warn!( - "The package {name} v{new_version} ({target}) has been downloaded from {}{}", - if fetcher.is_third_party() { - "third-party source " - } else { - "" - }, - fetcher.source_name() - ); - - info!("This will install the following binaries:"); - for file in bin_files { - info!(" - {}", file.preview_bin()); - } - - if !opts.no_symlinks { - info!("And create (or update) the following symlinks:"); - for file in bin_files { - info!(" - {}", file.preview_link()); - } - } - } -} - -impl ResolutionSource { - pub async fn install(self, opts: Arc) -> Result<(), BinstallError> { - let crate_name = self.name.clone(); - self.install_inner(opts) - .await - .map_err(|err| err.crate_context(crate_name)) - } - - async fn install_inner(self, opts: Arc) -> Result<(), BinstallError> { - let target = if let Some(targets) = opts.desired_targets.get_initialized() { - Some(targets.first().ok_or(BinstallError::NoViableTargets)?) - } else { - None - }; - - let name = &self.name; - let version = &self.version; - - let cargo = env::var_os("CARGO") - .map(Cow::Owned) - .unwrap_or_else(|| Cow::Borrowed(OsStr::new("cargo"))); - - let mut cmd = Command::new(cargo); - - cmd.arg("install") - .arg(name) - .arg("--version") - .arg(version) - .kill_on_drop(true); - - if let Some(target) = target { - cmd.arg("--target").arg(target); - } - - if opts.quiet { - cmd.arg("--quiet"); - } - - if opts.force { - cmd.arg("--force"); - } - - if opts.locked { - cmd.arg("--locked"); - } - - if let Some(cargo_root) = &opts.cargo_root { - cmd.arg("--root").arg(cargo_root); - } - - if opts.no_track { - cmd.arg("--no-track"); - } - - debug!("Running `{}`", format_cmd(&cmd)); - - if !opts.dry_run { - let mut child = opts - .jobserver_client - .get() - .await? - .configure_and_run(&mut cmd, |cmd| cmd.group_spawn())?; - - debug!("Spawned command pid={:?}", child.id()); - - let status = child.wait().await?; - if status.success() { - info!("Cargo finished successfully"); - Ok(()) - } else { - error!("Cargo errored! {status:?}"); - Err(BinstallError::SubProcess { - command: format_cmd(&cmd).to_string().into_boxed_str(), - status, - }) - } - } else { - info!("Dry-run: running `{}`", format_cmd(&cmd)); - Ok(()) - } - } - - pub fn print(&self) { - warn!( - "The package {} v{} will be installed from source (with cargo)", - self.name, self.version - ) - } -} - -fn format_cmd(cmd: &Command) -> impl fmt::Display + '_ { - let cmd = cmd.as_std(); - - let program = Either::Left(Path::new(cmd.get_program()).display()); - - let program_args = cmd - .get_args() - .map(OsStr::to_string_lossy) - .map(Either::Right); - - iter::once(program).chain(program_args).format(" ") -} diff --git a/crates/binstalk/src/ops/resolve/version_ext.rs b/crates/binstalk/src/ops/resolve/version_ext.rs deleted file mode 100644 index 7c83e045..00000000 --- a/crates/binstalk/src/ops/resolve/version_ext.rs +++ /dev/null @@ -1,99 +0,0 @@ -use compact_str::format_compact; -use semver::{Prerelease, Version, VersionReq}; - -/// Extension trait for [`VersionReq`]. -pub trait VersionReqExt { - /// Return `true` if `self.matches(version)` returns `true` - /// and the `version` is the latest one acceptable by `self`. - fn is_latest_compatible(&self, version: &Version) -> bool; - - /// Parse from CLI option. - /// - /// Notably, a bare version is treated as if preceded by `=`, not by `^` as in Cargo.toml - /// dependencies. - fn parse_from_cli(str: &str) -> Result - where - Self: Sized; -} - -impl VersionReqExt for VersionReq { - fn is_latest_compatible(&self, version: &Version) -> bool { - if !self.matches(version) { - return false; - } - - // Test if bumping patch will be accepted - let bumped_version = Version::new(version.major, version.minor, version.patch + 1); - - if self.matches(&bumped_version) { - return false; - } - - // Test if bumping prerelease will be accepted if version has one. - let pre = &version.pre; - if !pre.is_empty() { - // Bump pre by appending random number to the end. - let bumped_pre = format_compact!("{}.1", pre.as_str()); - - let bumped_version = Version { - major: version.major, - minor: version.minor, - patch: version.patch, - pre: Prerelease::new(&bumped_pre).unwrap(), - build: Default::default(), - }; - - if self.matches(&bumped_version) { - return false; - } - } - - true - } - - fn parse_from_cli(version: &str) -> Result { - if version - .chars() - .next() - .map(|ch| ch.is_ascii_digit()) - .unwrap_or(false) - { - format_compact!("={version}").parse() - } else { - version.parse() - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test() { - // Test star - assert!(!VersionReq::STAR.is_latest_compatible(&Version::parse("0.0.1").unwrap())); - assert!(!VersionReq::STAR.is_latest_compatible(&Version::parse("0.1.1").unwrap())); - assert!(!VersionReq::STAR.is_latest_compatible(&Version::parse("0.1.1-alpha").unwrap())); - - // Test ^x.y.z - assert!(!VersionReq::parse("^0.1") - .unwrap() - .is_latest_compatible(&Version::parse("0.1.99").unwrap())); - - // Test =x.y.z - assert!(VersionReq::parse("=0.1.0") - .unwrap() - .is_latest_compatible(&Version::parse("0.1.0").unwrap())); - - // Test =x.y.z-alpha - assert!(VersionReq::parse("=0.1.0-alpha") - .unwrap() - .is_latest_compatible(&Version::parse("0.1.0-alpha").unwrap())); - - // Test >=x.y.z-alpha - assert!(!VersionReq::parse(">=0.1.0-alpha") - .unwrap() - .is_latest_compatible(&Version::parse("0.1.0-alpha").unwrap())); - } -} diff --git a/crates/binstalk/tests/parse-meta.Cargo.toml b/crates/binstalk/tests/parse-meta.Cargo.toml deleted file mode 100644 index 26d6e052..00000000 --- a/crates/binstalk/tests/parse-meta.Cargo.toml +++ /dev/null @@ -1,18 +0,0 @@ -[package] -name = "cargo-binstall-test" -repository = "https://github.com/cargo-bins/cargo-binstall" -version = "1.2.3" - -[[bin]] -name = "cargo-binstall" -path = "src/main.rs" -edition = "2021" - -[package.metadata.binstall] -pkg-url = "{ repo }/releases/download/v{ version }/{ name }-{ target }.{ archive-format }" -bin-dir = "{ bin }{ binary-ext }" - -[package.metadata.binstall.overrides.x86_64-pc-windows-msvc] -pkg-fmt = "zip" -[package.metadata.binstall.overrides.x86_64-apple-darwin] -pkg-fmt = "zip" diff --git a/crates/binstalk/tests/parse-meta.rs b/crates/binstalk/tests/parse-meta.rs deleted file mode 100644 index 72da08f7..00000000 --- a/crates/binstalk/tests/parse-meta.rs +++ /dev/null @@ -1,31 +0,0 @@ -use binstalk::ops::resolve::load_manifest_path; -use cargo_toml_workspace::cargo_toml::{Edition, Product}; -use std::path::PathBuf; - -#[test] -fn parse_meta() { - let mut manifest_dir = PathBuf::from(std::env::var_os("CARGO_MANIFEST_DIR").unwrap()); - manifest_dir.push("tests/parse-meta.Cargo.toml"); - - let manifest = - load_manifest_path(&manifest_dir, "cargo-binstall-test").expect("Error parsing metadata"); - let package = manifest.package.unwrap(); - let meta = package.metadata.and_then(|m| m.binstall).unwrap(); - - assert_eq!(&package.name, "cargo-binstall-test"); - - assert_eq!( - meta.pkg_url.as_deref().unwrap(), - "{ repo }/releases/download/v{ version }/{ name }-{ target }.{ archive-format }" - ); - - assert_eq!( - manifest.bin.as_slice(), - &[Product { - name: Some("cargo-binstall".to_string()), - path: Some("src/main.rs".to_string()), - edition: Some(Edition::E2021), - ..Default::default() - },], - ); -} diff --git a/crates/cargo-toml-workspace/CHANGELOG.md b/crates/cargo-toml-workspace/CHANGELOG.md deleted file mode 100644 index a0846372..00000000 --- a/crates/cargo-toml-workspace/CHANGELOG.md +++ /dev/null @@ -1,62 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - -## [Unreleased] - -## [7.0.6](https://github.com/cargo-bins/cargo-binstall/compare/cargo-toml-workspace-v7.0.5...cargo-toml-workspace-v7.0.6) - 2025-03-15 - -### Other - -- *(deps)* bump the deps group with 2 updates ([#2084](https://github.com/cargo-bins/cargo-binstall/pull/2084)) - -## [7.0.5](https://github.com/cargo-bins/cargo-binstall/compare/cargo-toml-workspace-v7.0.4...cargo-toml-workspace-v7.0.5) - 2025-03-07 - -### Other - -- *(deps)* bump the deps group with 3 updates ([#2072](https://github.com/cargo-bins/cargo-binstall/pull/2072)) - -## [7.0.4](https://github.com/cargo-bins/cargo-binstall/compare/cargo-toml-workspace-v7.0.3...cargo-toml-workspace-v7.0.4) - 2025-01-19 - -### Other - -- update Cargo.lock dependencies - -## [7.0.3](https://github.com/cargo-bins/cargo-binstall/compare/cargo-toml-workspace-v7.0.2...cargo-toml-workspace-v7.0.3) - 2025-01-13 - -### Other - -- update Cargo.lock dependencies - -## [7.0.2](https://github.com/cargo-bins/cargo-binstall/compare/cargo-toml-workspace-v7.0.1...cargo-toml-workspace-v7.0.2) - 2025-01-11 - -### Other - -- *(deps)* bump the deps group with 3 updates (#2015) - -## [7.0.1](https://github.com/cargo-bins/cargo-binstall/compare/cargo-toml-workspace-v7.0.0...cargo-toml-workspace-v7.0.1) - 2024-12-14 - -### Other - -- *(deps)* bump the deps group with 2 updates (#1997) - -## [7.0.0](https://github.com/cargo-bins/cargo-binstall/compare/cargo-toml-workspace-v6.0.3...cargo-toml-workspace-v7.0.0) - 2024-12-07 - -### Other - -- *(deps)* bump the deps group with 3 updates ([#1993](https://github.com/cargo-bins/cargo-binstall/pull/1993)) - -## [6.0.3](https://github.com/cargo-bins/cargo-binstall/compare/cargo-toml-workspace-v6.0.2...cargo-toml-workspace-v6.0.3) - 2024-11-09 - -### Other - -- *(deps)* bump the deps group with 3 updates ([#1966](https://github.com/cargo-bins/cargo-binstall/pull/1966)) - -## [6.0.2](https://github.com/cargo-bins/cargo-binstall/compare/cargo-toml-workspace-v6.0.1...cargo-toml-workspace-v6.0.2) - 2024-11-05 - -### Other - -- *(deps)* bump the deps group with 3 updates ([#1954](https://github.com/cargo-bins/cargo-binstall/pull/1954)) diff --git a/crates/cargo-toml-workspace/Cargo.toml b/crates/cargo-toml-workspace/Cargo.toml deleted file mode 100644 index 0a7c62ac..00000000 --- a/crates/cargo-toml-workspace/Cargo.toml +++ /dev/null @@ -1,22 +0,0 @@ -[package] -name = "cargo-toml-workspace" -version = "7.0.6" -edition = "2021" -description = "Parse cargo workspace and load specific crate" -repository = "https://github.com/cargo-bins/cargo-binstall" -documentation = "https://docs.rs/cargo-toml-workspace" -rust-version = "1.65.0" -authors = ["Jiahao XU "] -license = "Apache-2.0 OR MIT" - -[dependencies] -cargo_toml = "0.22.1" -compact_str = { version = "0.9.0", features = ["serde"] } -glob = "0.3.1" -normalize-path = { version = "0.2.1", path = "../normalize-path" } -serde = "1.0.163" -thiserror = "2.0.11" -tracing = "0.1.39" - -[dev-dependencies] -tempfile = "3.5.0" diff --git a/crates/cargo-toml-workspace/LICENSE-APACHE b/crates/cargo-toml-workspace/LICENSE-APACHE deleted file mode 100644 index 1b5ec8b7..00000000 --- a/crates/cargo-toml-workspace/LICENSE-APACHE +++ /dev/null @@ -1,176 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS diff --git a/crates/cargo-toml-workspace/LICENSE-MIT b/crates/cargo-toml-workspace/LICENSE-MIT deleted file mode 100644 index 31aa7938..00000000 --- a/crates/cargo-toml-workspace/LICENSE-MIT +++ /dev/null @@ -1,23 +0,0 @@ -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/crates/cargo-toml-workspace/src/lib.rs b/crates/cargo-toml-workspace/src/lib.rs deleted file mode 100644 index 16c8050d..00000000 --- a/crates/cargo-toml-workspace/src/lib.rs +++ /dev/null @@ -1,272 +0,0 @@ -use std::{ - io, mem, - path::{Path, PathBuf}, -}; - -use cargo_toml::{Error as CargoTomlError, Manifest}; -use compact_str::CompactString; -use glob::PatternError; -use normalize_path::NormalizePath; -use serde::de::DeserializeOwned; -use thiserror::Error as ThisError; -use tracing::{debug, instrument, warn}; - -pub use cargo_toml; - -/// Load binstall metadata `Cargo.toml` from workspace at the provided path -/// -/// WARNING: This is a blocking operation. -/// -/// * `workspace_path` - can be a directory (path to workspace) or -/// a file (path to `Cargo.toml`). -pub fn load_manifest_from_workspace( - workspace_path: impl AsRef, - crate_name: impl AsRef, -) -> Result, Error> { - fn inner( - workspace_path: &Path, - crate_name: &str, - ) -> Result, Error> { - load_manifest_from_workspace_inner(workspace_path, crate_name).map_err(|inner| Error { - workspace_path: workspace_path.into(), - crate_name: crate_name.into(), - inner, - }) - } - - inner(workspace_path.as_ref(), crate_name.as_ref()) -} - -#[derive(Debug, ThisError)] -#[error("Failed to load {crate_name} from {}: {inner}", workspace_path.display())] -pub struct Error { - workspace_path: Box, - crate_name: CompactString, - #[source] - inner: ErrorInner, -} - -#[derive(Debug, ThisError)] -enum ErrorInner { - #[error("Invalid pattern in workspace.members or workspace.exclude: {0}")] - PatternError(#[from] PatternError), - - #[error("Invalid pattern `{0}`: It must be relative and point within current dir")] - InvalidPatternError(CompactString), - - #[error("Failed to parse cargo manifest: {0}")] - CargoManifest(#[from] CargoTomlError), - - #[error("I/O error: {0}")] - Io(#[from] io::Error), - - #[error("Not found")] - NotFound, -} - -#[instrument] -fn load_manifest_from_workspace_inner( - workspace_path: &Path, - crate_name: &str, -) -> Result, ErrorInner> { - debug!( - "Loading manifest of crate {crate_name} from workspace: {}", - workspace_path.display() - ); - - let manifest_path = if workspace_path.is_file() { - workspace_path.to_owned() - } else { - workspace_path.join("Cargo.toml") - }; - - let mut manifest_paths = vec![manifest_path]; - - while let Some(manifest_path) = manifest_paths.pop() { - let manifest = Manifest::::from_path_with_metadata(&manifest_path)?; - - let name = manifest.package.as_ref().map(|p| &*p.name); - debug!( - "Loading from {}, manifest.package.name = {:#?}", - manifest_path.display(), - name - ); - - if name == Some(crate_name) { - return Ok(manifest); - } - - if let Some(ws) = manifest.workspace { - let excludes = ws.exclude; - let members = ws.members; - - if members.is_empty() { - continue; - } - - let exclude_patterns = excludes - .into_iter() - .map(|pat| Pattern::new(&pat)) - .collect::, _>>()?; - - let workspace_path = manifest_path.parent().unwrap(); - - for member in members { - for path in Pattern::new(&member)?.glob_dirs(workspace_path)? { - if !exclude_patterns - .iter() - .any(|exclude| exclude.matches_with_trailing(&path)) - { - manifest_paths.push(workspace_path.join(path).join("Cargo.toml")); - } - } - } - } - } - - Err(ErrorInner::NotFound) -} - -struct Pattern(Vec); - -impl Pattern { - fn new(pat: &str) -> Result { - Path::new(pat) - .try_normalize() - .ok_or_else(|| ErrorInner::InvalidPatternError(pat.into()))? - .iter() - .map(|c| glob::Pattern::new(c.to_str().unwrap())) - .collect::, _>>() - .map_err(Into::into) - .map(Self) - } - - /// * `glob_path` - path to dir to glob for - /// - /// return paths relative to `glob_path`. - fn glob_dirs(&self, glob_path: &Path) -> Result, ErrorInner> { - let mut paths = vec![PathBuf::new()]; - - for pattern in &self.0 { - if paths.is_empty() { - break; - } - - for path in mem::take(&mut paths) { - let p = glob_path.join(&path); - let res = p.read_dir(); - if res.is_err() && !p.is_dir() { - continue; - } - drop(p); - - for res in res? { - let entry = res?; - - let is_dir = entry - .file_type() - .map(|file_type| file_type.is_dir() || file_type.is_symlink()) - .unwrap_or(false); - if !is_dir { - continue; - } - - let filename = entry.file_name(); - if filename != "." // Ignore current dir - && filename != ".." // Ignore parent dir - && pattern.matches(&filename.to_string_lossy()) - { - paths.push(path.join(filename)); - } - } - } - } - - Ok(paths) - } - - /// Return `true` if `path` matches the pattern. - /// It will still return `true` even if there are some trailing components. - fn matches_with_trailing(&self, path: &Path) -> bool { - let mut iter = path.iter().map(|os_str| os_str.to_string_lossy()); - for pattern in &self.0 { - match iter.next() { - Some(s) if pattern.matches(&s) => (), - _ => return false, - } - } - true - } -} - -#[cfg(test)] -mod test { - use std::fs::create_dir_all as mkdir; - - use tempfile::TempDir; - - use super::*; - - #[test] - fn test_glob_dirs() { - let pattern = Pattern::new("*/*/q/*").unwrap(); - let tempdir = TempDir::new().unwrap(); - - mkdir(tempdir.as_ref().join("a/b/c/efe")).unwrap(); - mkdir(tempdir.as_ref().join("a/b/q/ww")).unwrap(); - mkdir(tempdir.as_ref().join("d/233/q/d")).unwrap(); - - let mut paths = pattern.glob_dirs(tempdir.as_ref()).unwrap(); - paths.sort_unstable(); - assert_eq!( - paths, - vec![PathBuf::from("a/b/q/ww"), PathBuf::from("d/233/q/d")] - ); - } - - #[test] - fn test_matches_with_trailing() { - let pattern = Pattern::new("*/*/q/*").unwrap(); - - assert!(pattern.matches_with_trailing(Path::new("a/b/q/d/"))); - assert!(pattern.matches_with_trailing(Path::new("a/b/q/d"))); - assert!(pattern.matches_with_trailing(Path::new("a/b/q/d/234"))); - assert!(pattern.matches_with_trailing(Path::new("a/234/q/d/234"))); - - assert!(!pattern.matches_with_trailing(Path::new(""))); - assert!(!pattern.matches_with_trailing(Path::new("a/"))); - assert!(!pattern.matches_with_trailing(Path::new("a/234"))); - assert!(!pattern.matches_with_trailing(Path::new("a/234/q"))); - } - - #[test] - fn test_load() { - let p = Path::new(env!("CARGO_MANIFEST_DIR")) - .parent() - .unwrap() - .parent() - .unwrap() - .join("e2e-tests/manifests/workspace"); - - let manifest = - load_manifest_from_workspace::(&p, "cargo-binstall").unwrap(); - let package = manifest.package.unwrap(); - assert_eq!(package.name, "cargo-binstall"); - assert_eq!(package.version.as_ref().unwrap(), "0.12.0"); - assert_eq!(manifest.bin.len(), 1); - assert_eq!(manifest.bin[0].name.as_deref().unwrap(), "cargo-binstall"); - assert_eq!(manifest.bin[0].path.as_deref().unwrap(), "src/main.rs"); - - let err = load_manifest_from_workspace_inner::(&p, "cargo-binstall2") - .unwrap_err(); - assert!(matches!(err, ErrorInner::NotFound), "{:#?}", err); - - let manifest = - load_manifest_from_workspace::(&p, "cargo-watch").unwrap(); - let package = manifest.package.unwrap(); - assert_eq!(package.name, "cargo-watch"); - assert_eq!(package.version.as_ref().unwrap(), "8.4.0"); - assert_eq!(manifest.bin.len(), 1); - assert_eq!(manifest.bin[0].name.as_deref().unwrap(), "cargo-watch"); - } -} diff --git a/crates/detect-targets/CHANGELOG.md b/crates/detect-targets/CHANGELOG.md deleted file mode 100644 index d73d200d..00000000 --- a/crates/detect-targets/CHANGELOG.md +++ /dev/null @@ -1,183 +0,0 @@ -# Changelog -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - -## [Unreleased] - -## [0.1.47](https://github.com/cargo-bins/cargo-binstall/compare/detect-targets-v0.1.46...detect-targets-v0.1.47) - 2025-04-05 - -### Other - -- update Cargo.lock dependencies - -## [0.1.46](https://github.com/cargo-bins/cargo-binstall/compare/detect-targets-v0.1.45...detect-targets-v0.1.46) - 2025-03-19 - -### Other - -- Fix clippy warnings for detect-targets and binstalk-downloader ([#2098](https://github.com/cargo-bins/cargo-binstall/pull/2098)) - -## [0.1.45](https://github.com/cargo-bins/cargo-binstall/compare/detect-targets-v0.1.44...detect-targets-v0.1.45) - 2025-03-15 - -### Other - -- *(deps)* bump tokio from 1.43.0 to 1.44.0 in the deps group ([#2079](https://github.com/cargo-bins/cargo-binstall/pull/2079)) - -## [0.1.44](https://github.com/cargo-bins/cargo-binstall/compare/detect-targets-v0.1.43...detect-targets-v0.1.44) - 2025-03-07 - -### Other - -- Fix detect-targets musl fallback for android and alpine ([#2076](https://github.com/cargo-bins/cargo-binstall/pull/2076)) - -## [0.1.43](https://github.com/cargo-bins/cargo-binstall/compare/detect-targets-v0.1.42...detect-targets-v0.1.43) - 2025-02-28 - -### Other - -- update Cargo.lock dependencies - -## [0.1.42](https://github.com/cargo-bins/cargo-binstall/compare/detect-targets-v0.1.41...detect-targets-v0.1.42) - 2025-02-22 - -### Other - -- update Cargo.lock dependencies - -## [0.1.41](https://github.com/cargo-bins/cargo-binstall/compare/detect-targets-v0.1.40...detect-targets-v0.1.41) - 2025-02-15 - -### Other - -- update Cargo.lock dependencies - -## [0.1.40](https://github.com/cargo-bins/cargo-binstall/compare/detect-targets-v0.1.39...detect-targets-v0.1.40) - 2025-02-11 - -### Other - -- update Cargo.lock dependencies - -## [0.1.39](https://github.com/cargo-bins/cargo-binstall/compare/detect-targets-v0.1.38...detect-targets-v0.1.39) - 2025-02-04 - -### Other - -- update Cargo.lock dependencies - -## [0.1.38](https://github.com/cargo-bins/cargo-binstall/compare/detect-targets-v0.1.37...detect-targets-v0.1.38) - 2025-01-19 - -### Other - -- update Cargo.lock dependencies - -## [0.1.37](https://github.com/cargo-bins/cargo-binstall/compare/detect-targets-v0.1.36...detect-targets-v0.1.37) - 2025-01-13 - -### Other - -- update Cargo.lock dependencies - -## [0.1.36](https://github.com/cargo-bins/cargo-binstall/compare/detect-targets-v0.1.35...detect-targets-v0.1.36) - 2025-01-11 - -### Other - -- update Cargo.lock dependencies - -## [0.1.35](https://github.com/cargo-bins/cargo-binstall/compare/detect-targets-v0.1.34...detect-targets-v0.1.35) - 2025-01-04 - -### Other - -- update Cargo.lock dependencies - -## [0.1.34](https://github.com/cargo-bins/cargo-binstall/compare/detect-targets-v0.1.33...detect-targets-v0.1.34) - 2024-12-28 - -### Other - -- update Cargo.lock dependencies - -## [0.1.33](https://github.com/cargo-bins/cargo-binstall/compare/detect-targets-v0.1.32...detect-targets-v0.1.33) - 2024-12-14 - -### Other - -- update Cargo.lock dependencies - -## [0.1.32](https://github.com/cargo-bins/cargo-binstall/compare/detect-targets-v0.1.31...detect-targets-v0.1.32) - 2024-12-07 - -### Other - -- update Cargo.lock dependencies - -## [0.1.31](https://github.com/cargo-bins/cargo-binstall/compare/detect-targets-v0.1.30...detect-targets-v0.1.31) - 2024-11-29 - -### Other - -- update Cargo.lock dependencies - -## [0.1.30](https://github.com/cargo-bins/cargo-binstall/compare/detect-targets-v0.1.29...detect-targets-v0.1.30) - 2024-11-23 - -### Other - -- update Cargo.lock dependencies - -## [0.1.29](https://github.com/cargo-bins/cargo-binstall/compare/detect-targets-v0.1.28...detect-targets-v0.1.29) - 2024-11-18 - -### Other - -- update Cargo.lock dependencies - -## [0.1.28](https://github.com/cargo-bins/cargo-binstall/compare/detect-targets-v0.1.27...detect-targets-v0.1.28) - 2024-11-09 - -### Other - -- update Cargo.lock dependencies - -## [0.1.27](https://github.com/cargo-bins/cargo-binstall/compare/detect-targets-v0.1.26...detect-targets-v0.1.27) - 2024-11-05 - -### Other - -- update Cargo.lock dependencies - -## [0.1.26](https://github.com/cargo-bins/cargo-binstall/compare/detect-targets-v0.1.25...detect-targets-v0.1.26) - 2024-11-02 - -### Other - -- update Cargo.lock dependencies - -## [0.1.25](https://github.com/cargo-bins/cargo-binstall/compare/detect-targets-v0.1.24...detect-targets-v0.1.25) - 2024-10-25 - -### Other - -- update Cargo.lock dependencies - -## [0.1.24](https://github.com/cargo-bins/cargo-binstall/compare/detect-targets-v0.1.23...detect-targets-v0.1.24) - 2024-10-12 - -### Other - -- update Cargo.lock dependencies - -## [0.1.23](https://github.com/cargo-bins/cargo-binstall/compare/detect-targets-v0.1.22...detect-targets-v0.1.23) - 2024-10-04 - -### Other - -- update Cargo.lock dependencies - -## [0.1.22](https://github.com/cargo-bins/cargo-binstall/compare/detect-targets-v0.1.21...detect-targets-v0.1.22) - 2024-09-22 - -### Other - -- update Cargo.lock dependencies - -## [0.1.21](https://github.com/cargo-bins/cargo-binstall/compare/detect-targets-v0.1.20...detect-targets-v0.1.21) - 2024-09-06 - -### Other -- update Cargo.lock dependencies - -## [0.1.20](https://github.com/cargo-bins/cargo-binstall/compare/detect-targets-v0.1.19...detect-targets-v0.1.20) - 2024-08-25 - -### Other -- update Cargo.lock dependencies - -## [0.1.19](https://github.com/cargo-bins/cargo-binstall/compare/detect-targets-v0.1.18...detect-targets-v0.1.19) - 2024-08-10 - -### Other -- update Cargo.lock dependencies - -## [0.1.18](https://github.com/cargo-bins/cargo-binstall/compare/detect-targets-v0.1.17...detect-targets-v0.1.18) - 2024-08-04 - -### Other -- *(deps)* bump the deps group across 1 directory with 2 updates ([#1859](https://github.com/cargo-bins/cargo-binstall/pull/1859)) diff --git a/crates/detect-targets/Cargo.toml b/crates/detect-targets/Cargo.toml deleted file mode 100644 index cfc01aaa..00000000 --- a/crates/detect-targets/Cargo.toml +++ /dev/null @@ -1,41 +0,0 @@ -[package] -name = "detect-targets" -description = "Detect the target of the env at runtime" -repository = "https://github.com/cargo-bins/cargo-binstall" -documentation = "https://docs.rs/detect-targets" -version = "0.1.47" -rust-version = "1.62.0" -authors = ["Jiahao XU "] -edition = "2021" -license = "Apache-2.0 OR MIT" - -[dependencies] -tokio = { version = "1.44.0", features = [ - "rt", - "process", - "sync", -], default-features = false } -tracing = { version = "0.1.39", optional = true } -tracing-subscriber = { version = "0.3.17", features = [ - "fmt", -], default-features = false, optional = true } -cfg-if = "1.0.0" -guess_host_triple = "0.1.3" - -[features] -tracing = ["dep:tracing"] -cli-logging = ["tracing", "dep:tracing-subscriber"] - -[target.'cfg(target_os = "windows")'.dependencies] -windows-sys = { version = "0.59.0", features = [ - "Win32_System_Threading", - "Win32_System_SystemInformation", - "Win32_Foundation", - "Win32_System_LibraryLoader", -] } - -[dev-dependencies] -tokio = { version = "1.44.0", features = ["macros"], default-features = false } - -[package.metadata.binstall] -pkg-url = "{ repo }/releases/download/v{ version }/cargo-binstall-{ target }.full.{ archive-format }" diff --git a/crates/detect-targets/LICENSE-APACHE b/crates/detect-targets/LICENSE-APACHE deleted file mode 100644 index 1b5ec8b7..00000000 --- a/crates/detect-targets/LICENSE-APACHE +++ /dev/null @@ -1,176 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS diff --git a/crates/detect-targets/LICENSE-MIT b/crates/detect-targets/LICENSE-MIT deleted file mode 100644 index 31aa7938..00000000 --- a/crates/detect-targets/LICENSE-MIT +++ /dev/null @@ -1,23 +0,0 @@ -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/crates/detect-targets/src/desired_targets.rs b/crates/detect-targets/src/desired_targets.rs deleted file mode 100644 index 36726495..00000000 --- a/crates/detect-targets/src/desired_targets.rs +++ /dev/null @@ -1,69 +0,0 @@ -use crate::detect_targets; - -use std::sync::Arc; - -use tokio::sync::OnceCell; - -#[derive(Debug)] -enum DesiredTargetsInner { - AutoDetect(Arc>>), - Initialized(Vec), -} - -#[derive(Debug)] -pub struct DesiredTargets(DesiredTargetsInner); - -impl DesiredTargets { - fn initialized(targets: Vec) -> Self { - Self(DesiredTargetsInner::Initialized(targets)) - } - - fn auto_detect() -> Self { - let arc = Arc::new(OnceCell::new()); - - let once_cell = arc.clone(); - tokio::spawn(async move { - once_cell.get_or_init(detect_targets).await; - }); - - Self(DesiredTargetsInner::AutoDetect(arc)) - } - - pub async fn get(&self) -> &[String] { - use DesiredTargetsInner::*; - - match &self.0 { - Initialized(targets) => targets, - - // This will mostly just wait for the spawned task, - // on rare occausion though, it will poll the future - // returned by `detect_targets`. - AutoDetect(once_cell) => once_cell.get_or_init(detect_targets).await, - } - } - - /// If `DesiredTargets` is provided with a list of desired targets instead - /// of detecting the targets, then this function would return `Some`. - pub fn get_initialized(&self) -> Option<&[String]> { - use DesiredTargetsInner::*; - - match &self.0 { - Initialized(targets) => Some(targets), - AutoDetect(..) => None, - } - } -} - -/// If opts_targets is `Some`, then it will be used. -/// Otherwise, call `detect_targets` using `tokio::spawn` to detect targets. -/// -/// Since `detect_targets` internally spawns a process and wait for it, -/// it's pretty costy, it is recommended to run this fn ASAP and -/// reuse the result. -pub fn get_desired_targets(opts_targets: Option>) -> DesiredTargets { - if let Some(targets) = opts_targets { - DesiredTargets::initialized(targets) - } else { - DesiredTargets::auto_detect() - } -} diff --git a/crates/detect-targets/src/detect.rs b/crates/detect-targets/src/detect.rs deleted file mode 100644 index a3f4a67c..00000000 --- a/crates/detect-targets/src/detect.rs +++ /dev/null @@ -1,115 +0,0 @@ -use std::{ - borrow::Cow, - env, - ffi::OsStr, - process::{Output, Stdio}, -}; - -use cfg_if::cfg_if; -use tokio::process::Command; -#[cfg(feature = "tracing")] -use tracing::debug; - -cfg_if! { - if #[cfg(any(target_os = "linux", target_os = "android"))] { - mod linux; - } else if #[cfg(target_os = "macos")] { - mod macos; - } else if #[cfg(target_os = "windows")] { - mod windows; - } -} - -/// Detect the targets supported at runtime, -/// which might be different from `TARGET` which is detected -/// at compile-time. -/// -/// Return targets supported in the order of preference. -/// If target_os is linux and it support gnu, then it is preferred -/// to musl. -/// -/// If target_os is mac and it is aarch64, then aarch64 is preferred -/// to x86_64. -/// -/// Check [this issue](https://github.com/ryankurte/cargo-binstall/issues/155) -/// for more information. -pub async fn detect_targets() -> Vec { - let target = get_target_from_rustc().await; - #[cfg(feature = "tracing")] - debug!("get_target_from_rustc()={target:?}"); - let target = target.unwrap_or_else(|| { - let target = guess_host_triple::guess_host_triple(); - #[cfg(feature = "tracing")] - debug!("guess_host_triple::guess_host_triple()={target:?}"); - target.unwrap_or(crate::TARGET).to_string() - }); - - cfg_if! { - if #[cfg(target_os = "macos")] { - let mut targets = vec![target]; - targets.extend(macos::detect_alternative_targets(&targets[0]).await); - targets - } else if #[cfg(target_os = "windows")] { - let mut targets = vec![target]; - targets.extend(windows::detect_alternative_targets(&targets[0])); - targets - } else if #[cfg(any(target_os = "linux", target_os = "android"))] { - // Linux is a bit special, since the result from `guess_host_triple` - // might be wrong about whether glibc or musl is used. - linux::detect_targets(target).await - } else { - vec![target] - } - } -} - -/// Figure out what the host target is using `rustc`. -/// If `rustc` is absent, then it would return `None`. -/// -/// If environment variable `CARGO` is present, then -/// `$CARGO -vV` will be run instead. -/// -/// Otherwise, it will run `rustc -vV` to detect target. -async fn get_target_from_rustc() -> Option { - let cmd = env::var_os("CARGO") - .map(Cow::Owned) - .unwrap_or_else(|| Cow::Borrowed(OsStr::new("rustc"))); - - let Output { status, stdout, .. } = Command::new(cmd) - .arg("-vV") - .stdin(Stdio::null()) - .stdout(Stdio::piped()) - .stderr(Stdio::null()) - .spawn() - .ok()? - .wait_with_output() - .await - .ok()?; - - if !status.success() { - return None; - } - - let stdout = String::from_utf8_lossy(&stdout); - let target = stdout - .lines() - .find_map(|line| line.strip_prefix("host: "))?; - - // The target triplets have the form of 'arch-vendor-system'. - // - // When building for Linux (e.g. the 'system' part is - // 'linux-something'), replace the vendor with 'unknown' - // so that mapping to rust standard targets happens correctly. - // - // For example, alpine set `rustc` host triple to - // `x86_64-alpine-linux-musl`. - // - // Here we use splitn with n=4 since we just need to check - // the third part to see if it equals to "linux" and verify - // that we have at least three parts. - let mut parts: Vec<&str> = target.splitn(4, '-').collect(); - if *parts.get(2)? == "linux" { - parts[1] = "unknown"; - } - Some(parts.join("-")) -} diff --git a/crates/detect-targets/src/detect/linux.rs b/crates/detect-targets/src/detect/linux.rs deleted file mode 100644 index c1fcf75b..00000000 --- a/crates/detect-targets/src/detect/linux.rs +++ /dev/null @@ -1,168 +0,0 @@ -use std::{ - process::{Output, Stdio}, - str, -}; - -use tokio::{process::Command, task}; -#[cfg(feature = "tracing")] -use tracing::debug; - -pub(super) async fn detect_targets(target: String) -> Vec { - let (_, postfix) = target - .rsplit_once('-') - .expect("unwrap: target always has a -"); - - let (abi, libc) = if let Some(abi) = postfix.strip_prefix("musl") { - (abi, Libc::Musl) - } else if let Some(abi) = postfix.strip_prefix("gnu") { - (abi, Libc::Gnu) - } else if let Some(abi) = postfix.strip_prefix("android") { - (abi, Libc::Android) - } else { - (postfix, Libc::Unknown) - }; - - let cpu_arch = target - .split_once('-') - .expect("unwrap: target always has a - for cpu_arch") - .0; - - // For android the `-unknown-` is omitted, for alpine it has `-alpine-` - // instead of `-unknown-`. - let musl_fallback_target = || format!("{cpu_arch}-unknown-linux-musl{abi}"); - - match libc { - // guess_host_triple cannot detect whether the system is using glibc, - // musl libc or other libc. - // - // On Alpine, you can use `apk add gcompat` to install glibc - // and run glibc programs. - // - // As such, we need to launch the test ourselves. - Libc::Gnu | Libc::Musl => { - let handles: Vec<_> = { - let cpu_arch_suffix = cpu_arch.replace('_', "-"); - let filename = format!("ld-linux-{cpu_arch_suffix}.so.2"); - let dirname = format!("{cpu_arch}-linux-gnu"); - - [ - format!("/lib/{filename}"), - format!("/lib64/{filename}"), - format!("/lib/{dirname}/{filename}"), - format!("/lib64/{dirname}/{filename}"), - format!("/usr/lib/{dirname}/{filename}"), - format!("/usr/lib64/{dirname}/{filename}"), - ] - .into_iter() - .map(|p| AutoAbortHandle(tokio::spawn(is_gnu_ld(p)))) - .collect() - }; - - let has_glibc = async move { - for mut handle in handles { - if let Ok(true) = (&mut handle.0).await { - return true; - } - } - - false - } - .await; - - [ - has_glibc.then(|| format!("{cpu_arch}-unknown-linux-gnu{abi}")), - Some(musl_fallback_target()), - ] - } - Libc::Android | Libc::Unknown => [Some(target.clone()), Some(musl_fallback_target())], - } - .into_iter() - .flatten() - .collect() -} - -async fn is_gnu_ld(cmd: String) -> bool { - get_ld_flavor(&cmd).await == Some(Libc::Gnu) -} - -async fn get_ld_flavor(cmd: &str) -> Option { - let Output { - status, - stdout, - stderr, - } = match Command::new(cmd) - .arg("--version") - .stdin(Stdio::null()) - .output() - .await - { - Ok(output) => output, - Err(_err) => { - #[cfg(feature = "tracing")] - debug!("Running `{cmd} --version`: err={_err:?}"); - return None; - } - }; - - let stdout = String::from_utf8_lossy(&stdout); - let stderr = String::from_utf8_lossy(&stderr); - - #[cfg(feature = "tracing")] - debug!("`{cmd} --version`: status={status}, stdout='{stdout}', stderr='{stderr}'"); - - const ALPINE_GCOMPAT: &str = r#"This is the gcompat ELF interpreter stub. -You are not meant to run this directly. -"#; - - if status.success() { - // Executing glibc ldd or /lib/ld-linux-{cpu_arch}.so.1 will always - // succeeds. - (stdout.contains("GLIBC") || stdout.contains("GNU libc")).then_some(Libc::Gnu) - } else if status.code() == Some(1) { - // On Alpine, executing both the gcompat glibc and the ldd and - // /lib/ld-musl-{cpu_arch}.so.1 will fail with exit status 1. - if stdout == ALPINE_GCOMPAT { - // Alpine's gcompat package will output ALPINE_GCOMPAT to stdout - Some(Libc::Gnu) - } else if stderr.contains("musl libc") { - // Alpine/s ldd and musl dynlib will output to stderr - Some(Libc::Musl) - } else { - None - } - } else if status.code() == Some(127) { - // On Ubuntu 20.04 (glibc 2.31), the `--version` flag is not supported - // and it will exit with status 127. - let status = Command::new(cmd) - .arg("/bin/true") - .stdin(Stdio::null()) - .stdout(Stdio::null()) - .stderr(Stdio::null()) - .status() - .await - .ok()?; - - #[cfg(feature = "tracing")] - debug!("`{cmd} --version`: status={status}"); - - status.success().then_some(Libc::Gnu) - } else { - None - } -} - -#[derive(Eq, PartialEq)] -enum Libc { - Gnu, - Musl, - Android, - Unknown, -} - -struct AutoAbortHandle(task::JoinHandle); - -impl Drop for AutoAbortHandle { - fn drop(&mut self) { - self.0.abort(); - } -} diff --git a/crates/detect-targets/src/detect/macos.rs b/crates/detect-targets/src/detect/macos.rs deleted file mode 100644 index ceafb6a7..00000000 --- a/crates/detect-targets/src/detect/macos.rs +++ /dev/null @@ -1,67 +0,0 @@ -use std::process::Stdio; - -use tokio::process::Command; - -const AARCH64: &str = "aarch64-apple-darwin"; -const X86: &str = "x86_64-apple-darwin"; -/// https://doc.rust-lang.org/nightly/rustc/platform-support/x86_64h-apple-darwin.html -/// -/// This target is an x86_64 target that only supports Apple's late-gen -/// (Haswell-compatible) Intel chips. -/// -/// It enables a set of target features available on these chips (AVX2 and similar), -/// and MachO binaries built with this target may be used as the x86_64h entry in -/// universal binaries ("fat" MachO binaries), and will fail to load on machines -/// that do not support this. -/// -/// It is similar to x86_64-apple-darwin in nearly all respects, although -/// the minimum supported OS version is slightly higher (it requires 10.8 -/// rather than x86_64-apple-darwin's 10.7). -const X86H: &str = "x86_64h-apple-darwin"; -const UNIVERSAL: &str = "universal-apple-darwin"; -const UNIVERSAL2: &str = "universal2-apple-darwin"; - -async fn is_arch_supported(arch_name: &str) -> bool { - Command::new("arch") - .args(["-arch", arch_name, "/usr/bin/true"]) - .stdin(Stdio::null()) - .stdout(Stdio::null()) - .stderr(Stdio::null()) - .status() - .await - .map(|exit_status| exit_status.success()) - .unwrap_or(false) -} - -pub(super) async fn detect_alternative_targets(target: &str) -> impl Iterator { - match target { - AARCH64 => { - // Spawn `arch` in parallel (probably from different threads if - // mutlti-thread runtime is used). - // - // These two tasks are never cancelled, so it can only fail due to - // panic, in which cause we would propagate by also panic here. - let x86_64h_task = tokio::spawn(is_arch_supported("x86_64h")); - let x86_64_task = tokio::spawn(is_arch_supported("x86_64")); - [ - // Prefer universal as it provides native arm executable - Some(UNIVERSAL), - Some(UNIVERSAL2), - // Prefer x86h since it is more optimized - x86_64h_task.await.unwrap().then_some(X86H), - x86_64_task.await.unwrap().then_some(X86), - ] - } - X86 => [ - is_arch_supported("x86_64h").await.then_some(X86H), - Some(UNIVERSAL), - Some(UNIVERSAL2), - None, - ], - X86H => [Some(X86), Some(UNIVERSAL), Some(UNIVERSAL2), None], - _ => [None, None, None, None], - } - .into_iter() - .flatten() - .map(ToString::to_string) -} diff --git a/crates/detect-targets/src/detect/windows.rs b/crates/detect-targets/src/detect/windows.rs deleted file mode 100644 index 5bb397cb..00000000 --- a/crates/detect-targets/src/detect/windows.rs +++ /dev/null @@ -1,133 +0,0 @@ -use std::mem; -use windows_sys::Win32::{ - Foundation::{HMODULE, S_OK}, - System::{ - LibraryLoader::{GetProcAddress, LoadLibraryA}, - SystemInformation::{ - IMAGE_FILE_MACHINE, IMAGE_FILE_MACHINE_AMD64, IMAGE_FILE_MACHINE_ARM, - IMAGE_FILE_MACHINE_ARM64, IMAGE_FILE_MACHINE_I386, - }, - Threading::{GetMachineTypeAttributes, UserEnabled, Wow64Container, MACHINE_ATTRIBUTES}, - }, -}; - -struct LibraryHandle(HMODULE); - -impl LibraryHandle { - fn new(name: &[u8]) -> Option { - let handle = unsafe { LoadLibraryA(name.as_ptr() as _) }; - (!handle.is_null()).then_some(Self(handle)) - } - - /// Get a function pointer to a function in the library. - /// # SAFETY - /// - /// The caller must ensure that the function signature matches the actual function. - /// The easiest way to do this is to add an entry to windows_sys_no_link.list and use the - /// generated function for `func_signature`. - /// - /// The function returned cannot be used after the handle is dropped. - unsafe fn get_proc_address(&self, name: &[u8]) -> Option { - let symbol = unsafe { GetProcAddress(self.0, name.as_ptr() as _) }; - symbol.map(|symbol| unsafe { mem::transmute_copy(&symbol) }) - } -} - -type GetMachineTypeAttributesFuncType = - unsafe extern "system" fn(u16, *mut MACHINE_ATTRIBUTES) -> i32; -const _: () = { - // Ensure that our hand-written signature matches the actual function signature. - // We can't use `GetMachineTypeAttributes` outside of a const scope otherwise we'll end up statically linking to - // it, which will fail to load on older versions of Windows. - let _: GetMachineTypeAttributesFuncType = GetMachineTypeAttributes; -}; - -fn is_arch_supported_inner(arch: IMAGE_FILE_MACHINE) -> Option { - // GetMachineTypeAttributes is only available on Win11 22000+, so dynamically load it. - let kernel32 = LibraryHandle::new(b"kernel32.dll\0")?; - // SAFETY: GetMachineTypeAttributesFuncType is checked to match the real function signature. - let get_machine_type_attributes = unsafe { - kernel32.get_proc_address::(b"GetMachineTypeAttributes\0") - }?; - - let mut machine_attributes = mem::MaybeUninit::uninit(); - if unsafe { get_machine_type_attributes(arch, machine_attributes.as_mut_ptr()) } == S_OK { - let machine_attributes = unsafe { machine_attributes.assume_init() }; - Some((machine_attributes & (Wow64Container | UserEnabled)) != 0) - } else { - Some(false) - } -} - -fn is_arch_supported(arch: IMAGE_FILE_MACHINE) -> bool { - is_arch_supported_inner(arch).unwrap_or(false) -} - -pub(super) fn detect_alternative_targets(target: &str) -> impl Iterator { - let (prefix, abi) = target - .rsplit_once('-') - .expect("unwrap: target always has a -"); - - let arch = prefix - .split_once('-') - .expect("unwrap: target always has at least two -") - .0; - - let msvc_fallback_target = (abi != "msvc").then(|| format!("{prefix}-msvc")); - - let gnu_fallback_targets = (abi == "msvc") - .then(|| [format!("{prefix}-gnu"), format!("{prefix}-gnullvm")]) - .into_iter() - .flatten(); - - let x64_fallback_targets = (arch != "x86_64" && is_arch_supported(IMAGE_FILE_MACHINE_AMD64)) - .then_some([ - "x86_64-pc-windows-msvc", - "x86_64-pc-windows-gnu", - "x86_64-pc-windows-gnullvm", - ]) - .into_iter() - .flatten() - .map(ToString::to_string); - - let x86_fallback_targets = (arch != "x86" && is_arch_supported(IMAGE_FILE_MACHINE_I386)) - .then_some([ - "i586-pc-windows-msvc", - "i586-pc-windows-gnu", - "i586-pc-windows-gnullvm", - "i686-pc-windows-msvc", - "i686-pc-windows-gnu", - "i686-pc-windows-gnullvm", - ]) - .into_iter() - .flatten() - .map(ToString::to_string); - - let arm32_fallback_targets = (arch != "thumbv7a" && is_arch_supported(IMAGE_FILE_MACHINE_ARM)) - .then_some([ - "thumbv7a-pc-windows-msvc", - "thumbv7a-pc-windows-gnu", - "thumbv7a-pc-windows-gnullvm", - ]) - .into_iter() - .flatten() - .map(ToString::to_string); - - let arm64_fallback_targets = (arch != "aarch64" && is_arch_supported(IMAGE_FILE_MACHINE_ARM64)) - .then_some([ - "aarch64-pc-windows-msvc", - "aarch64-pc-windows-gnu", - "aarch64-pc-windows-gnullvm", - ]) - .into_iter() - .flatten() - .map(ToString::to_string); - - msvc_fallback_target - .into_iter() - .chain(gnu_fallback_targets) - .chain(x64_fallback_targets) - .chain(x86_fallback_targets) - .chain(arm32_fallback_targets) - .chain(arm64_fallback_targets) -} diff --git a/crates/detect-targets/src/lib.rs b/crates/detect-targets/src/lib.rs deleted file mode 100644 index a9a5c5c4..00000000 --- a/crates/detect-targets/src/lib.rs +++ /dev/null @@ -1,74 +0,0 @@ -//! Detect the target at the runtime. -//! -//! It runs `$CARGO -vV` if environment variable `CARGO` is present -//! for cargo subcommands, otherwise it would try running `rustc -vV`. -//! -//! If both `rustc` isn't present on the system, it will fallback -//! to using syscalls plus `ldd` on Linux to detect targets. -//! -//! Example use cases: -//! - The binary is built with musl libc to run on anywhere, but -//! the runtime supports glibc. -//! - The binary is built for x86_64-apple-darwin, but run on -//! aarch64-apple-darwin. -//! -//! This crate provides two API: -//! - [`detect_targets`] provides the API to get the target -//! at runtime, but the code is run on the current thread. -//! - [`get_desired_targets`] provides the API to either -//! use override provided by the users, or run [`detect_targets`] -//! in the background using [`tokio::spawn`]. -//! -//! # Example -//! -//! `detect_targets`: -//! -//! ```rust -//! use detect_targets::detect_targets; -//! # #[tokio::main(flavor = "current_thread")] -//! # async fn main() { -//! -//! let targets = detect_targets().await; -//! eprintln!("Your platform supports targets: {targets:#?}"); -//! # } -//! ``` -//! -//! `get_desired_targets` with user override: -//! -//! ```rust -//! use detect_targets::get_desired_targets; -//! # #[tokio::main(flavor = "current_thread")] -//! # async fn main() { -//! -//! assert_eq!( -//! get_desired_targets(Some(vec![ -//! "x86_64-apple-darwin".to_string(), -//! "aarch64-apple-darwin".to_string(), -//! ])).get().await, -//! &["x86_64-apple-darwin", "aarch64-apple-darwin"], -//! ); -//! # } -//! ``` -//! -//! `get_desired_targets` without user override: -//! -//! ```rust -//! use detect_targets::get_desired_targets; -//! # #[tokio::main(flavor = "current_thread")] -//! # async fn main() { -//! -//! eprintln!( -//! "Your platform supports targets: {:#?}", -//! get_desired_targets(None).get().await -//! ); -//! # } -//! ``` - -mod detect; -pub use detect::detect_targets; - -mod desired_targets; -pub use desired_targets::{get_desired_targets, DesiredTargets}; - -/// Compiled target triple, used as default for binary fetching -pub const TARGET: &str = env!("TARGET"); diff --git a/crates/detect-targets/src/main.rs b/crates/detect-targets/src/main.rs deleted file mode 100644 index 2623c077..00000000 --- a/crates/detect-targets/src/main.rs +++ /dev/null @@ -1,23 +0,0 @@ -use std::io; - -use detect_targets::detect_targets; -use tokio::runtime; - -fn main() -> io::Result<()> { - #[cfg(feature = "cli-logging")] - tracing_subscriber::fmt::fmt() - .with_max_level(tracing::Level::TRACE) - .with_writer(std::io::stderr) - .init(); - - let targets = runtime::Builder::new_current_thread() - .enable_all() - .build()? - .block_on(detect_targets()); - - for target in targets { - println!("{target}"); - } - - Ok(()) -} diff --git a/crates/detect-wasi/CHANGELOG.md b/crates/detect-wasi/CHANGELOG.md deleted file mode 100644 index 9466296f..00000000 --- a/crates/detect-wasi/CHANGELOG.md +++ /dev/null @@ -1,173 +0,0 @@ -# Changelog -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - -## [Unreleased] - -## [1.0.28](https://github.com/cargo-bins/cargo-binstall/compare/detect-wasi-v1.0.27...detect-wasi-v1.0.28) - 2025-04-05 - -### Other - -- update Cargo.lock dependencies - -## [1.0.27](https://github.com/cargo-bins/cargo-binstall/compare/detect-wasi-v1.0.26...detect-wasi-v1.0.27) - 2025-03-19 - -### Other - -- update Cargo.lock dependencies - -## [1.0.26](https://github.com/cargo-bins/cargo-binstall/compare/detect-wasi-v1.0.25...detect-wasi-v1.0.26) - 2025-03-15 - -### Other - -- update Cargo.lock dependencies - -## [1.0.25](https://github.com/cargo-bins/cargo-binstall/compare/detect-wasi-v1.0.24...detect-wasi-v1.0.25) - 2025-03-07 - -### Other - -- update Cargo.lock dependencies - -## [1.0.24](https://github.com/cargo-bins/cargo-binstall/compare/detect-wasi-v1.0.23...detect-wasi-v1.0.24) - 2025-02-28 - -### Other - -- update Cargo.lock dependencies - -## [1.0.23](https://github.com/cargo-bins/cargo-binstall/compare/detect-wasi-v1.0.22...detect-wasi-v1.0.23) - 2025-02-22 - -### Other - -- update Cargo.lock dependencies - -## [1.0.22](https://github.com/cargo-bins/cargo-binstall/compare/detect-wasi-v1.0.21...detect-wasi-v1.0.22) - 2025-02-11 - -### Other - -- update Cargo.lock dependencies - -## [1.0.21](https://github.com/cargo-bins/cargo-binstall/compare/detect-wasi-v1.0.20...detect-wasi-v1.0.21) - 2025-02-04 - -### Other - -- update Cargo.lock dependencies - -## [1.0.20](https://github.com/cargo-bins/cargo-binstall/compare/detect-wasi-v1.0.19...detect-wasi-v1.0.20) - 2025-01-19 - -### Other - -- update Cargo.lock dependencies - -## [1.0.19](https://github.com/cargo-bins/cargo-binstall/compare/detect-wasi-v1.0.18...detect-wasi-v1.0.19) - 2025-01-11 - -### Other - -- update Cargo.lock dependencies - -## [1.0.18](https://github.com/cargo-bins/cargo-binstall/compare/detect-wasi-v1.0.17...detect-wasi-v1.0.18) - 2025-01-04 - -### Other - -- update Cargo.lock dependencies - -## [1.0.17](https://github.com/cargo-bins/cargo-binstall/compare/detect-wasi-v1.0.16...detect-wasi-v1.0.17) - 2024-12-28 - -### Other - -- update Cargo.lock dependencies - -## [1.0.16](https://github.com/cargo-bins/cargo-binstall/compare/detect-wasi-v1.0.15...detect-wasi-v1.0.16) - 2024-12-14 - -### Other - -- update Cargo.lock dependencies - -## [1.0.15](https://github.com/cargo-bins/cargo-binstall/compare/detect-wasi-v1.0.14...detect-wasi-v1.0.15) - 2024-12-07 - -### Other - -- update Cargo.lock dependencies - -## [1.0.14](https://github.com/cargo-bins/cargo-binstall/compare/detect-wasi-v1.0.13...detect-wasi-v1.0.14) - 2024-11-29 - -### Other - -- update Cargo.lock dependencies - -## [1.0.13](https://github.com/cargo-bins/cargo-binstall/compare/detect-wasi-v1.0.12...detect-wasi-v1.0.13) - 2024-11-23 - -### Other - -- update Cargo.lock dependencies - -## [1.0.12](https://github.com/cargo-bins/cargo-binstall/compare/detect-wasi-v1.0.11...detect-wasi-v1.0.12) - 2024-11-18 - -### Other - -- update Cargo.lock dependencies - -## [1.0.11](https://github.com/cargo-bins/cargo-binstall/compare/detect-wasi-v1.0.10...detect-wasi-v1.0.11) - 2024-11-09 - -### Other - -- update Cargo.lock dependencies - -## [1.0.10](https://github.com/cargo-bins/cargo-binstall/compare/detect-wasi-v1.0.9...detect-wasi-v1.0.10) - 2024-11-05 - -### Other - -- update Cargo.lock dependencies - -## [1.0.9](https://github.com/cargo-bins/cargo-binstall/compare/detect-wasi-v1.0.8...detect-wasi-v1.0.9) - 2024-11-02 - -### Other - -- update Cargo.lock dependencies - -## [1.0.8](https://github.com/cargo-bins/cargo-binstall/compare/detect-wasi-v1.0.7...detect-wasi-v1.0.8) - 2024-10-25 - -### Other - -- update Cargo.lock dependencies - -## [1.0.7](https://github.com/cargo-bins/cargo-binstall/compare/detect-wasi-v1.0.6...detect-wasi-v1.0.7) - 2024-10-12 - -### Other - -- update Cargo.lock dependencies - -## [1.0.6](https://github.com/cargo-bins/cargo-binstall/compare/detect-wasi-v1.0.5...detect-wasi-v1.0.6) - 2024-10-04 - -### Other - -- update Cargo.lock dependencies - -## [1.0.5](https://github.com/cargo-bins/cargo-binstall/compare/detect-wasi-v1.0.4...detect-wasi-v1.0.5) - 2024-09-22 - -### Other - -- update Cargo.lock dependencies - -## [1.0.4](https://github.com/cargo-bins/cargo-binstall/compare/detect-wasi-v1.0.3...detect-wasi-v1.0.4) - 2024-09-06 - -### Other -- update Cargo.lock dependencies - -## [1.0.3](https://github.com/cargo-bins/cargo-binstall/compare/detect-wasi-v1.0.2...detect-wasi-v1.0.3) - 2024-08-25 - -### Other -- update Cargo.lock dependencies - -## [1.0.2](https://github.com/cargo-bins/cargo-binstall/compare/detect-wasi-v1.0.1...detect-wasi-v1.0.2) - 2024-08-10 - -### Other -- update Cargo.lock dependencies - -## [1.0.1](https://github.com/cargo-bins/cargo-binstall/compare/detect-wasi-v1.0.0...detect-wasi-v1.0.1) - 2024-08-04 - -### Other -- Bump tempfile from 3.4.0 to 3.5.0 ([#967](https://github.com/cargo-bins/cargo-binstall/pull/967)) -- Bump tempfile from 3.3.0 to 3.4.0 ([#834](https://github.com/cargo-bins/cargo-binstall/pull/834)) -- Migrate CI and builds to Just, add "full" builds ([#660](https://github.com/cargo-bins/cargo-binstall/pull/660)) diff --git a/crates/detect-wasi/Cargo.toml b/crates/detect-wasi/Cargo.toml deleted file mode 100644 index 65920f7a..00000000 --- a/crates/detect-wasi/Cargo.toml +++ /dev/null @@ -1,16 +0,0 @@ -[package] -name = "detect-wasi" -description = "Detect if WASI can be run" -repository = "https://github.com/cargo-bins/cargo-binstall" -documentation = "https://docs.rs/detect-wasi" -version = "1.0.28" -rust-version = "1.61.0" -authors = ["Félix Saparelli "] -edition = "2021" -license = "Apache-2.0 OR MIT" - -[dependencies] -tempfile = "3.5.0" - -[package.metadata.binstall] -pkg-url = "{ repo }/releases/download/v{ version }/cargo-binstall-{ target }.full.{ archive-format }" diff --git a/crates/detect-wasi/LICENSE-APACHE b/crates/detect-wasi/LICENSE-APACHE deleted file mode 100644 index 1b5ec8b7..00000000 --- a/crates/detect-wasi/LICENSE-APACHE +++ /dev/null @@ -1,176 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS diff --git a/crates/detect-wasi/LICENSE-MIT b/crates/detect-wasi/LICENSE-MIT deleted file mode 100644 index 31aa7938..00000000 --- a/crates/detect-wasi/LICENSE-MIT +++ /dev/null @@ -1,23 +0,0 @@ -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/crates/detect-wasi/src/bin/detect-wasi.rs b/crates/detect-wasi/src/bin/detect-wasi.rs deleted file mode 100644 index c5cc8f99..00000000 --- a/crates/detect-wasi/src/bin/detect-wasi.rs +++ /dev/null @@ -1,13 +0,0 @@ -use std::process::exit; - -use detect_wasi::detect_wasi_runability; - -fn main() { - if detect_wasi_runability().unwrap() { - println!("WASI is runnable!"); - exit(0); - } else { - println!("WASI is not runnable"); - exit(1); - } -} diff --git a/crates/detect-wasi/src/lib.rs b/crates/detect-wasi/src/lib.rs deleted file mode 100644 index e780276f..00000000 --- a/crates/detect-wasi/src/lib.rs +++ /dev/null @@ -1,42 +0,0 @@ -use std::{ - fs::File, - io::{Result, Write}, - process::Command, -}; -#[cfg(unix)] -use std::{fs::Permissions, os::unix::fs::PermissionsExt}; - -use tempfile::tempdir; - -const WASI_PROGRAM: &[u8] = include_bytes!("miniwasi.wasm"); - -/// Detect the ability to run WASI -/// -/// This attempts to run a small embedded WASI program, and returns true if no errors happened. -/// Errors returned by the `Result` are I/O errors from the establishment of the context, not -/// errors from the run attempt. -/// -/// On Linux, you can configure your system to run WASI programs using a binfmt directive. Under -/// systemd, write the below to `/etc/binfmt.d/wasi.conf`, with `/usr/bin/wasmtime` optionally -/// replaced with the path to your WASI runtime of choice: -/// -/// ```plain -/// :wasi:M::\x00asm::/usr/bin/wasmtime: -/// ``` -pub fn detect_wasi_runability() -> Result { - let progdir = tempdir()?; - let prog = progdir.path().join("miniwasi.wasm"); - - { - let mut progfile = File::create(&prog)?; - progfile.write_all(WASI_PROGRAM)?; - - #[cfg(unix)] - progfile.set_permissions(Permissions::from_mode(0o777))?; - } - - match Command::new(prog).output() { - Ok(out) => Ok(out.status.success() && out.stdout.is_empty() && out.stderr.is_empty()), - Err(_) => Ok(false), - } -} diff --git a/crates/detect-wasi/src/miniwasi.wasm b/crates/detect-wasi/src/miniwasi.wasm deleted file mode 100755 index 56dcdc6a..00000000 Binary files a/crates/detect-wasi/src/miniwasi.wasm and /dev/null differ diff --git a/crates/detect-wasi/src/miniwasi.wast b/crates/detect-wasi/src/miniwasi.wast deleted file mode 100644 index 0a2b05fa..00000000 --- a/crates/detect-wasi/src/miniwasi.wast +++ /dev/null @@ -1,10 +0,0 @@ -(module - (import "wasi_snapshot_preview1" "proc_exit" (func $exit (param i32))) - (memory $0 0) - (export "memory" (memory $0)) - (export "_start" (func $0)) - (func $0 - (call $exit (i32.const 0)) - (unreachable) - ) -) diff --git a/crates/fs-lock/CHANGELOG.md b/crates/fs-lock/CHANGELOG.md deleted file mode 100644 index b9213f27..00000000 --- a/crates/fs-lock/CHANGELOG.md +++ /dev/null @@ -1,49 +0,0 @@ -# Changelog -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - -## [Unreleased] - -## [0.1.10](https://github.com/cargo-bins/cargo-binstall/compare/fs-lock-v0.1.9...fs-lock-v0.1.10) - 2025-03-19 - -### Fixed - -- actually check if lock was acquired ([#2091](https://github.com/cargo-bins/cargo-binstall/pull/2091)) - -## [0.1.9](https://github.com/cargo-bins/cargo-binstall/compare/fs-lock-v0.1.8...fs-lock-v0.1.9) - 2025-03-07 - -### Other - -- *(deps)* bump the deps group with 3 updates ([#2072](https://github.com/cargo-bins/cargo-binstall/pull/2072)) - -## [0.1.8](https://github.com/cargo-bins/cargo-binstall/compare/fs-lock-v0.1.7...fs-lock-v0.1.8) - 2025-02-22 - -### Other - -- Log when FileLock::drop fails to unlock file ([#2064](https://github.com/cargo-bins/cargo-binstall/pull/2064)) -- Fix fs-lock error on nightly ([#2059](https://github.com/cargo-bins/cargo-binstall/pull/2059)) - -## [0.1.7](https://github.com/cargo-bins/cargo-binstall/compare/fs-lock-v0.1.6...fs-lock-v0.1.7) - 2024-12-07 - -### Other - -- *(deps)* bump the deps group with 3 updates ([#1993](https://github.com/cargo-bins/cargo-binstall/pull/1993)) - -## [0.1.6](https://github.com/cargo-bins/cargo-binstall/compare/fs-lock-v0.1.5...fs-lock-v0.1.6) - 2024-11-05 - -### Other - -- *(deps)* bump the deps group with 3 updates ([#1954](https://github.com/cargo-bins/cargo-binstall/pull/1954)) - -## [0.1.5](https://github.com/cargo-bins/cargo-binstall/compare/fs-lock-v0.1.4...fs-lock-v0.1.5) - 2024-10-12 - -### Other - -- *(deps)* bump fs4 from 0.9.1 to 0.10.0 in the deps group ([#1929](https://github.com/cargo-bins/cargo-binstall/pull/1929)) - -## [0.1.4](https://github.com/cargo-bins/cargo-binstall/compare/fs-lock-v0.1.3...fs-lock-v0.1.4) - 2024-08-04 - -### Other -- *(deps)* bump the deps group across 1 directory with 2 updates ([#1859](https://github.com/cargo-bins/cargo-binstall/pull/1859)) diff --git a/crates/fs-lock/Cargo.toml b/crates/fs-lock/Cargo.toml deleted file mode 100644 index 2c0e8711..00000000 --- a/crates/fs-lock/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "fs-lock" -description = "Locked files that can be used like normal File" -repository = "https://github.com/cargo-bins/cargo-binstall" -documentation = "https://docs.rs/fs-lock" -version = "0.1.10" -rust-version = "1.61.0" -authors = ["Jiahao XU "] -edition = "2021" -license = "Apache-2.0 OR MIT" - -[dependencies] -fs4 = "0.13.0" -tracing = { version = "0.1", optional = true } diff --git a/crates/fs-lock/LICENSE-APACHE b/crates/fs-lock/LICENSE-APACHE deleted file mode 100644 index 1b5ec8b7..00000000 --- a/crates/fs-lock/LICENSE-APACHE +++ /dev/null @@ -1,176 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS diff --git a/crates/fs-lock/LICENSE-MIT b/crates/fs-lock/LICENSE-MIT deleted file mode 100644 index 31aa7938..00000000 --- a/crates/fs-lock/LICENSE-MIT +++ /dev/null @@ -1,23 +0,0 @@ -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/crates/fs-lock/src/lib.rs b/crates/fs-lock/src/lib.rs deleted file mode 100644 index 311c72d7..00000000 --- a/crates/fs-lock/src/lib.rs +++ /dev/null @@ -1,166 +0,0 @@ -//! Locked files with the same API as normal [`File`]s. -//! -//! These use the same mechanisms as, and are interoperable with, Cargo. - -use std::{ - fs::File, - io::{self, IoSlice, IoSliceMut, SeekFrom}, - ops, - path::Path, -}; - -use fs4::fs_std::FileExt; - -/// A locked file. -#[derive(Debug)] -pub struct FileLock(File, #[cfg(feature = "tracing")] Option>); - -impl FileLock { - #[cfg(not(feature = "tracing"))] - fn new(file: File) -> Self { - Self(file) - } - - #[cfg(feature = "tracing")] - fn new(file: File) -> Self { - Self(file, None) - } - - /// Take an exclusive lock on a [`File`]. - /// - /// Note that this operation is blocking, and should not be called in async contexts. - pub fn new_exclusive(file: File) -> io::Result { - FileExt::lock_exclusive(&file)?; - - Ok(Self::new(file)) - } - - /// Try to take an exclusive lock on a [`File`]. - /// - /// On success returns [`Self`]. On error the original [`File`] and optionally - /// an [`io::Error`] if the the failure was caused by anything other than - /// the lock being taken already. - /// - /// Note that this operation is blocking, and should not be called in async contexts. - pub fn new_try_exclusive(file: File) -> Result)> { - match FileExt::try_lock_exclusive(&file) { - Ok(true) => Ok(Self::new(file)), - Ok(false) => Err((file, None)), - Err(e) if e.raw_os_error() == fs4::lock_contended_error().raw_os_error() => { - Err((file, None)) - } - Err(e) => Err((file, Some(e))), - } - } - - /// Take a shared lock on a [`File`]. - /// - /// Note that this operation is blocking, and should not be called in async contexts. - pub fn new_shared(file: File) -> io::Result { - FileExt::lock_shared(&file)?; - - Ok(Self::new(file)) - } - - /// Try to take a shared lock on a [`File`]. - /// - /// On success returns [`Self`]. On error the original [`File`] and optionally - /// an [`io::Error`] if the the failure was caused by anything other than - /// the lock being taken already. - /// - /// Note that this operation is blocking, and should not be called in async contexts. - pub fn new_try_shared(file: File) -> Result)> { - match FileExt::try_lock_shared(&file) { - Ok(true) => Ok(Self::new(file)), - Ok(false) => Err((file, None)), - Err(e) if e.raw_os_error() == fs4::lock_contended_error().raw_os_error() => { - Err((file, None)) - } - Err(e) => Err((file, Some(e))), - } - } - - /// Set path to the file for logging on unlock error, if feature tracing is enabled - pub fn set_file_path(mut self, path: impl Into>) -> Self { - #[cfg(feature = "tracing")] - { - self.1 = Some(path.into()); - } - self - } -} - -impl Drop for FileLock { - fn drop(&mut self) { - let _res = FileExt::unlock(&self.0); - #[cfg(feature = "tracing")] - if let Err(err) = _res { - use std::fmt; - - struct OptionalPath<'a>(Option<&'a Path>); - impl fmt::Display for OptionalPath<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if let Some(path) = self.0 { - fmt::Display::fmt(&path.display(), f) - } else { - Ok(()) - } - } - } - - tracing::warn!( - "Failed to unlock file{}: {err}", - OptionalPath(self.1.as_deref()), - ); - } - } -} - -impl ops::Deref for FileLock { - type Target = File; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} -impl ops::DerefMut for FileLock { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - -impl io::Write for FileLock { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.0.write(buf) - } - fn flush(&mut self) -> io::Result<()> { - self.0.flush() - } - - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - self.0.write_vectored(bufs) - } -} - -impl io::Read for FileLock { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - self.0.read(buf) - } - - fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - self.0.read_vectored(bufs) - } -} - -impl io::Seek for FileLock { - fn seek(&mut self, pos: SeekFrom) -> io::Result { - self.0.seek(pos) - } - - fn rewind(&mut self) -> io::Result<()> { - self.0.rewind() - } - fn stream_position(&mut self) -> io::Result { - self.0.stream_position() - } -} diff --git a/crates/normalize-path/Cargo.toml b/crates/normalize-path/Cargo.toml deleted file mode 100644 index 7d781312..00000000 --- a/crates/normalize-path/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "normalize-path" -description = "Like canonicalize, but without performing I/O" -repository = "https://github.com/cargo-bins/cargo-binstall" -documentation = "https://docs.rs/normalize-path" -version = "0.2.1" -rust-version = "1.61.0" -authors = ["Jiahao XU "] -edition = "2021" -license = "Apache-2.0 OR MIT" diff --git a/crates/normalize-path/LICENSE-APACHE b/crates/normalize-path/LICENSE-APACHE deleted file mode 100644 index 1b5ec8b7..00000000 --- a/crates/normalize-path/LICENSE-APACHE +++ /dev/null @@ -1,176 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS diff --git a/crates/normalize-path/LICENSE-MIT b/crates/normalize-path/LICENSE-MIT deleted file mode 100644 index 31aa7938..00000000 --- a/crates/normalize-path/LICENSE-MIT +++ /dev/null @@ -1,23 +0,0 @@ -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/crates/normalize-path/src/lib.rs b/crates/normalize-path/src/lib.rs deleted file mode 100644 index cf65d025..00000000 --- a/crates/normalize-path/src/lib.rs +++ /dev/null @@ -1,109 +0,0 @@ -//! Normalizes paths similarly to canonicalize, but without performing I/O. -//! -//! This is like Python's `os.path.normpath`. -//! -//! Initially adapted from [Cargo's implementation][cargo-paths]. -//! -//! [cargo-paths]: https://github.com/rust-lang/cargo/blob/fede83ccf973457de319ba6fa0e36ead454d2e20/src/cargo/util/paths.rs#L61 -//! -//! # Example -//! -//! ``` -//! use normalize_path::NormalizePath; -//! use std::path::Path; -//! -//! assert_eq!( -//! Path::new("/A/foo/../B/./").normalize(), -//! Path::new("/A/B") -//! ); -//! ``` - -use std::path::{Component, Path, PathBuf}; - -/// Extension trait to add `normalize_path` to std's [`Path`]. -pub trait NormalizePath { - /// Normalize a path without performing I/O. - /// - /// All redundant separator and up-level references are collapsed. - /// - /// However, this does not resolve links. - fn normalize(&self) -> PathBuf; - - /// Same as [`NormalizePath::normalize`] except that if - /// `Component::Prefix`/`Component::RootDir` is encountered, - /// or if the path points outside of current dir, returns `None`. - fn try_normalize(&self) -> Option; - - /// Return `true` if the path is normalized. - /// - /// # Quirk - /// - /// If the path does not start with `./` but contains `./` in the middle, - /// then this function might returns `true`. - fn is_normalized(&self) -> bool; -} - -impl NormalizePath for Path { - fn normalize(&self) -> PathBuf { - let mut components = self.components().peekable(); - let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek() { - let buf = PathBuf::from(c.as_os_str()); - components.next(); - buf - } else { - PathBuf::new() - }; - - for component in components { - match component { - Component::Prefix(..) => unreachable!(), - Component::RootDir => { - ret.push(component.as_os_str()); - } - Component::CurDir => {} - Component::ParentDir => { - ret.pop(); - } - Component::Normal(c) => { - ret.push(c); - } - } - } - - ret - } - - fn try_normalize(&self) -> Option { - let mut ret = PathBuf::new(); - - for component in self.components() { - match component { - Component::Prefix(..) | Component::RootDir => return None, - Component::CurDir => {} - Component::ParentDir => { - if !ret.pop() { - return None; - } - } - Component::Normal(c) => { - ret.push(c); - } - } - } - - Some(ret) - } - - fn is_normalized(&self) -> bool { - for component in self.components() { - match component { - Component::CurDir | Component::ParentDir => { - return false; - } - _ => continue, - } - } - - true - } -} diff --git a/e2e-tests/continue-on-failure.sh b/e2e-tests/continue-on-failure.sh deleted file mode 100755 index 44080313..00000000 --- a/e2e-tests/continue-on-failure.sh +++ /dev/null @@ -1,58 +0,0 @@ -#!/bin/bash - -set -euxo pipefail - -unset CARGO_INSTALL_ROOT - -CARGO_HOME=$(mktemp -d 2>/dev/null || mktemp -d -t 'cargo-home') -export CARGO_HOME -othertmpdir=$(mktemp -d 2>/dev/null || mktemp -d -t 'cargo-test') -export PATH="$CARGO_HOME/bin:$othertmpdir/bin:$PATH" - -mkdir -p "$othertmpdir/bin" -# Copy it to bin to test use of env var `CARGO` -cp "./$1" "$othertmpdir/bin/" - - -## Test --continue-on-failure -set +e -cargo binstall --no-confirm --continue-on-failure cargo-watch@8.4.0 non-existent-clippy -exit_code="$?" - -set -e - -if [ "$exit_code" != 76 ]; then - echo "Expected exit code 76, but actual exit code $exit_code" - exit 1 -fi - - -cargo_watch_version="$(cargo watch -V)" -echo "$cargo_watch_version" - -[ "$cargo_watch_version" = "cargo-watch 8.4.0" ] - - -## Test that it is no-op when only one crate is passed -set +e -cargo binstall --no-confirm --continue-on-failure non-existent-clippy -exit_code="$?" - -set -e - -if [ "$exit_code" != 76 ]; then - echo "Expected exit code 76, but actual exit code $exit_code" - exit 1 -fi - -# Test if both crates are invalid -set +e -cargo binstall --no-confirm --continue-on-failure non-existent-clippy non-existent-clippy2 -exit_code="$?" - -set -e - -if [ "$exit_code" != 76 ]; then - echo "Expected exit code 76, but actual exit code $exit_code" - exit 1 -fi diff --git a/e2e-tests/fake-cargo/cargo b/e2e-tests/fake-cargo/cargo deleted file mode 100755 index da30ddf7..00000000 --- a/e2e-tests/fake-cargo/cargo +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash - -echo Always returns 1 to prevent use of "cargo-build" -exit 1 diff --git a/e2e-tests/git.sh b/e2e-tests/git.sh deleted file mode 100644 index 0811f88b..00000000 --- a/e2e-tests/git.sh +++ /dev/null @@ -1,74 +0,0 @@ -#!/bin/bash - -set -euxo pipefail - -test_cargo_binstall_install() { - # Test that the installed binaries can be run - cargo binstall --help >/dev/null - - cargo_binstall_version="$(cargo binstall -V)" - echo "$cargo_binstall_version" - - [ "$cargo_binstall_version" = "cargo-binstall 0.12.0" ] -} - -unset CARGO_INSTALL_ROOT - -CARGO_HOME="$(mktemp -d 2>/dev/null || mktemp -d -t 'cargo-home')" -export CARGO_HOME -export PATH="$CARGO_HOME/bin:$PATH" - -GIT="$(mktemp -d 2>/dev/null || mktemp -d -t 'git')" -if [ "$OSTYPE" = "cygwin" ] || [ "$OSTYPE" = "msys" ]; then - # Convert it to windows path so `--git "file://$GIT"` would work - # on windows. - GIT="$(cygpath -w "$GIT")" -fi - -git init "$GIT" -cp manifests/github-test-Cargo.toml "$GIT/Cargo.toml" -( - cd "$GIT" - git config user.email 'test@example.com' - git config user.name 'test' - git add Cargo.toml - git commit -m "Add Cargo.toml" -) - -# Install binaries using `--git` -"./$1" binstall --force --git "file://$GIT" --no-confirm cargo-binstall - -test_cargo_binstall_install - -cp -r manifests/workspace/* "$GIT" -( - cd "$GIT" - git add . - git commit -m 'Update to workspace' -) -COMMIT_HASH="$(cd "$GIT" && git rev-parse HEAD)" - -if [ "$OSTYPE" = "cygwin" ] || [ "$OSTYPE" = "msys" ]; then - source="(git+file:///$(cygpath -m "$GIT")#$COMMIT_HASH)" -else - source="(git+file://$GIT#$COMMIT_HASH)" -fi - -# Install cargo-binstall using `--git` -"./$1" binstall --force --git "file://$GIT" --no-confirm cargo-binstall - -test_cargo_binstall_install - -cat "$CARGO_HOME/.crates.toml" -grep -F "cargo-binstall 0.12.0 $source" <"$CARGO_HOME/.crates.toml" - -# Install cargo-watch using `--git` -"./$1" binstall --force --git "file://$GIT" --no-confirm cargo-watch - -cargo_watch_version="$(cargo watch -V)" -echo "$cargo_watch_version" - -[ "$cargo_watch_version" = "cargo-watch 8.4.0" ] - -cat "$CARGO_HOME/.crates.toml" -grep -F "cargo-watch 8.4.0 $source" <"$CARGO_HOME/.crates.toml" diff --git a/e2e-tests/live.sh b/e2e-tests/live.sh deleted file mode 100755 index 359ba4da..00000000 --- a/e2e-tests/live.sh +++ /dev/null @@ -1,68 +0,0 @@ -#!/bin/bash - -set -euxo pipefail - -unset CARGO_INSTALL_ROOT - -# - `b3sum@<=1.3.3` would test `fetch_crate_cratesio_version_matched` ability -# to find versions matching <= 1.3.3 -# - `cargo-quickinstall` would test `fetch_crate_cratesio_version_matched` ability -# to find latest stable version. -# - `git-mob-tool tests the using of using a binary name (`git-mob`) different -# from the package name. -crates="b3sum@<=1.3.3 cargo-release@0.24.9 cargo-binstall@0.20.1 cargo-watch@8.4.0 miniserve@0.23.0 sccache@0.3.3 cargo-quickinstall jj-cli@0.18.0 git-mob-tool@1.6.1" - -CARGO_HOME=$(mktemp -d 2>/dev/null || mktemp -d -t 'cargo-home') -export CARGO_HOME -othertmpdir=$(mktemp -d 2>/dev/null || mktemp -d -t 'cargo-test') -export PATH="$CARGO_HOME/bin:$othertmpdir/bin:$PATH" - -mkdir -p "$othertmpdir/bin" -# Copy it to bin to test use of env var `CARGO` -cp "./$1" "$othertmpdir/bin/" - -# Install binaries using cargo-binstall -# shellcheck disable=SC2086 -cargo binstall --no-confirm $crates - -rm -r "$othertmpdir" - -# Test that the installed binaries can be run -b3sum_version="$(b3sum --version)" -echo "$b3sum_version" - -[ "$b3sum_version" = "b3sum 1.3.3" ] - -cargo_release_version="$(cargo-release release --version)" -echo "$cargo_release_version" - -[ "$cargo_release_version" = "cargo-release 0.24.9" ] - -cargo binstall --help >/dev/null - -cargo_binstall_version="$(cargo-binstall -V)" -echo "cargo-binstall version $cargo_binstall_version" - -[ "$cargo_binstall_version" = "0.20.1" ] - -cargo_watch_version="$(cargo watch -V)" -echo "$cargo_watch_version" - -[ "$cargo_watch_version" = "cargo-watch 8.4.0" ] - -miniserve_version="$(miniserve -V)" -echo "$miniserve_version" - -[ "$miniserve_version" = "miniserve 0.23.0" ] - -cargo-quickinstall -V - -jj_version="$(jj --version)" -echo "$jj_version" - -[ "$jj_version" = "jj 0.18.0-9fb5307b7886e390c02817af7c31b403f0279144" ] - -git_mob_version="$(git-mob --version)" -echo "$git_mob_version" - -[ "$git_mob_version" = "git-mob-tool 1.6.1" ] diff --git a/e2e-tests/manifest-path.sh b/e2e-tests/manifest-path.sh deleted file mode 100755 index 35f2a1b7..00000000 --- a/e2e-tests/manifest-path.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash - -set -euxo pipefail - -unset CARGO_INSTALL_ROOT - -CARGO_HOME=$(mktemp -d 2>/dev/null || mktemp -d -t 'cargo-home') -export CARGO_HOME -export PATH="$CARGO_HOME/bin:$PATH" - -# Install binaries using `--manifest-path` -# Also test default github template -"./$1" binstall --force --manifest-path "manifests/github-test-Cargo.toml" --no-confirm cargo-binstall - -# Test that the installed binaries can be run -cargo binstall --help >/dev/null - -cargo_binstall_version="$(cargo binstall -V)" -echo "$cargo_binstall_version" - -[ "$cargo_binstall_version" = "cargo-binstall 0.12.0" ] - -cat "$CARGO_HOME/.crates.toml" -grep -F "cargo-binstall 0.12.0 (path+file://manifests/github-test-Cargo.toml)" <"$CARGO_HOME/.crates.toml" diff --git a/e2e-tests/manifests/bitbucket-test-Cargo.toml b/e2e-tests/manifests/bitbucket-test-Cargo.toml deleted file mode 100644 index 4b91c06f..00000000 --- a/e2e-tests/manifests/bitbucket-test-Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -name = "cargo-binstall" -description = "Rust binary package installer for CI integration" -repository = "https://bitbucket.org/nobodyxusdcdc/hello-world" -version = "0.12.0" -rust-version = "1.61.0" -authors = ["ryan "] -edition = "2021" -license = "GPL-3.0" - -[[bin]] -name = "cargo-binstall" -path = "src/main.rs" diff --git a/e2e-tests/manifests/github-test-Cargo.toml b/e2e-tests/manifests/github-test-Cargo.toml deleted file mode 100644 index aa3a0898..00000000 --- a/e2e-tests/manifests/github-test-Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -name = "cargo-binstall" -description = "Rust binary package installer for CI integration" -repository = "https://github.com/cargo-bins/cargo-binstall" -version = "0.12.0" -rust-version = "1.61.0" -authors = ["ryan "] -edition = "2021" -license = "GPL-3.0" - -[[bin]] -name = "cargo-binstall" -path = "src/main.rs" diff --git a/e2e-tests/manifests/github-test-Cargo2.toml b/e2e-tests/manifests/github-test-Cargo2.toml deleted file mode 100644 index 8732c248..00000000 --- a/e2e-tests/manifests/github-test-Cargo2.toml +++ /dev/null @@ -1,16 +0,0 @@ -[package] -name = "cargo-binstall" -description = "Rust binary package installer for CI integration" -repository = "https://github.com/cargo-bins/cargo-binstall.git" -version = "0.12.0" -rust-version = "1.61.0" -authors = ["ryan "] -edition = "2021" -license = "GPL-3.0" - -[package.metadata.binstall] -bin-dir = "{ bin }{ binary-ext }" - -[[bin]] -name = "cargo-binstall" -path = "src/main.rs" diff --git a/e2e-tests/manifests/gitlab-test-Cargo.toml b/e2e-tests/manifests/gitlab-test-Cargo.toml deleted file mode 100644 index 5d8ae7f9..00000000 --- a/e2e-tests/manifests/gitlab-test-Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -name = "cargo-binstall" -description = "Rust binary package installer for CI integration" -repository = "https://gitlab.kitware.com/NobodyXu/hello-world.git" -version = "0.2.0" -rust-version = "1.61.0" -authors = ["ryan "] -edition = "2021" -license = "GPL-3.0" - -[[bin]] -name = "cargo-binstall" -path = "src/main.rs" diff --git a/e2e-tests/manifests/private-github-repo-test-Cargo.toml b/e2e-tests/manifests/private-github-repo-test-Cargo.toml deleted file mode 100644 index 38e3ea76..00000000 --- a/e2e-tests/manifests/private-github-repo-test-Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -name = "cargo-binstall" -description = "Rust binary package installer for CI integration" -repository = "https://github.com/cargo-bins/private-repo-for-testing.git" -version = "0.12.0" -rust-version = "1.61.0" -authors = ["ryan "] -edition = "2021" -license = "GPL-3.0" - -[[bin]] -name = "cargo-binstall" -path = "src/main.rs" diff --git a/e2e-tests/manifests/signing-Cargo.toml b/e2e-tests/manifests/signing-Cargo.toml deleted file mode 100644 index 962416ad..00000000 --- a/e2e-tests/manifests/signing-Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "signing-test" -description = "Rust binary package installer for CI integration" -version = "0.1.0" -authors = ["ryan "] -edition = "2021" -license = "GPL-3.0" - -[[bin]] -name = "signing-test" -path = "src/main.rs" - -[package.metadata.binstall] -pkg-url = "https://localhost:4443/signing-test.tar" -pkg-fmt = "tar" - -[package.metadata.binstall.signing] -algorithm = "minisign" -pubkey = "RWRnmBcLmQbXVcEPWo2OOKMI36kki4GiI7gcBgIaPLwvxe14Wtxm9acX" diff --git a/e2e-tests/manifests/strategies-test-Cargo.toml b/e2e-tests/manifests/strategies-test-Cargo.toml deleted file mode 100644 index f4235a84..00000000 --- a/e2e-tests/manifests/strategies-test-Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "cargo-update" -repository = "https://github.com/nabijaczleweli/cargo-update" -version = "11.1.2" - -[[bin]] -name = "cargo-install-update" -path = "src/main.rs" -test = false -doc = false - -[[bin]] -name = "cargo-install-update-config" -path = "src/main-config.rs" -test = false -doc = false - -[package.metadata.binstall] -disabled-strategies = ["quick-install", "compile"] diff --git a/e2e-tests/manifests/strategies-test-Cargo2.toml b/e2e-tests/manifests/strategies-test-Cargo2.toml deleted file mode 100644 index aef2811e..00000000 --- a/e2e-tests/manifests/strategies-test-Cargo2.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "cargo-update" -repository = "https://github.com/nabijaczleweli/cargo-update" -version = "11.1.2" - -[[bin]] -name = "cargo-install-update" -path = "src/main.rs" -test = false -doc = false - -[[bin]] -name = "cargo-install-update-config" -path = "src/main-config.rs" -test = false -doc = false - -[package.metadata.binstall] -disabled-strategies = ["quick-install"] diff --git a/e2e-tests/manifests/strategies-test-override-Cargo.toml b/e2e-tests/manifests/strategies-test-override-Cargo.toml deleted file mode 100644 index 97127671..00000000 --- a/e2e-tests/manifests/strategies-test-override-Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -name = "cargo-quickinstall" -repository = "https://github.com/cargo-bins/cargo-quickinstall" -version = "0.2.10" - -[[bin]] -name = "cargo-quickinstall" -path = "src/main.rs" -test = false -doc = false - -[package.metadata.binstall] -disabled-strategies = ["crate-meta-data", "quick-install", "compile"] diff --git a/e2e-tests/manifests/workspace/Cargo.toml b/e2e-tests/manifests/workspace/Cargo.toml deleted file mode 100644 index 6e98755f..00000000 --- a/e2e-tests/manifests/workspace/Cargo.toml +++ /dev/null @@ -1,3 +0,0 @@ -[workspace] -members = ["crates/*/*/*/*/*/*", "b/*/*/*/*"] -exclude = ["b/c/d/e/*"] diff --git a/e2e-tests/manifests/workspace/b/c/d/e/f/Cargo.toml b/e2e-tests/manifests/workspace/b/c/d/e/f/Cargo.toml deleted file mode 100644 index 176f7044..00000000 --- a/e2e-tests/manifests/workspace/b/c/d/e/f/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -name = "cargo-binstall2" -description = "Rust binary package installer for CI integration" -repository = "https://bitbucket.org/nobodyxusdcdc/hello-world" -version = "0.0.0" -rust-version = "1.61.0" -authors = ["ryan "] -edition = "2021" -license = "GPL-3.0" - -[[bin]] -name = "cargo-binstall" -path = "src/main.rs" diff --git a/e2e-tests/manifests/workspace/crates/a/b/c/d/e/cargo-binstall/Cargo.toml b/e2e-tests/manifests/workspace/crates/a/b/c/d/e/cargo-binstall/Cargo.toml deleted file mode 100644 index d5435856..00000000 --- a/e2e-tests/manifests/workspace/crates/a/b/c/d/e/cargo-binstall/Cargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[package] -name = "cargo-binstall" -version = "0.12.0" -repository = "https://github.com/cargo-bins/cargo-binstall" - -[[bin]] -name = "cargo-binstall" -path = "src/main.rs" diff --git a/e2e-tests/manifests/workspace/crates/a/b/c/d/e/cargo-watch/Cargo.toml b/e2e-tests/manifests/workspace/crates/a/b/c/d/e/cargo-watch/Cargo.toml deleted file mode 100644 index 71d35971..00000000 --- a/e2e-tests/manifests/workspace/crates/a/b/c/d/e/cargo-watch/Cargo.toml +++ /dev/null @@ -1,7 +0,0 @@ -[package] -name = "cargo-watch" -version = "8.4.0" -repository = "https://github.com/watchexec/cargo-watch" - -[[bin]] -name = "cargo-watch" diff --git a/e2e-tests/manifests/workspace/crates/a/b/c/d/e/cargo-watch/src/main.rs b/e2e-tests/manifests/workspace/crates/a/b/c/d/e/cargo-watch/src/main.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/e2e-tests/no-track.sh b/e2e-tests/no-track.sh deleted file mode 100644 index 28f1bd74..00000000 --- a/e2e-tests/no-track.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/bash - -set -euxo pipefail - -unset CARGO_INSTALL_ROOT - -CARGO_HOME=$(mktemp -d 2>/dev/null || mktemp -d -t 'cargo-home') -export CARGO_HOME -export PATH="$CARGO_HOME/bin:$PATH" - -"./$1" binstall -y cargo-binstall@0.20.1 -cargo-binstall --help >/dev/null - -set +e - -"./$1" binstall -y --no-track cargo-binstall@0.20.1 -exit_code="$?" - -set -e - -if [ "$exit_code" != 88 ]; then - echo "Expected exit code 88 BinFile Error, but actual exit code $exit_code" - exit 1 -fi - - -"./$1" binstall -y --no-track --force cargo-binstall@0.20.1 -cargo-binstall --help >/dev/null diff --git a/e2e-tests/other-repos.sh b/e2e-tests/other-repos.sh deleted file mode 100755 index 917a21aa..00000000 --- a/e2e-tests/other-repos.sh +++ /dev/null @@ -1,50 +0,0 @@ -#!/bin/bash - -set -euxo pipefail - -unset CARGO_INSTALL_ROOT - -CARGO_HOME=$(mktemp -d 2>/dev/null || mktemp -d -t 'cargo-home') -export CARGO_HOME -export PATH="$CARGO_HOME/bin:$PATH" - -# Test default GitLab pkg-url templates -#"./$1" binstall \ -# --force \ -# --manifest-path "manifests/gitlab-test-Cargo.toml" \ -# --no-confirm \ -# --disable-strategies compile \ -# cargo-binstall - -# Test default BitBucket pkg-url templates -"./$1" binstall \ - --force \ - --manifest-path "manifests/bitbucket-test-Cargo.toml" \ - --no-confirm \ - --disable-strategies compile \ - cargo-binstall - -# Test that the installed binaries can be run -cargo binstall --help >/dev/null - -cargo_binstall_version="$(cargo binstall -V)" -echo "$cargo_binstall_version" - -[ "$cargo_binstall_version" = "cargo-binstall 0.12.0" ] - -# Test default Github pkg-url templates, -# with bin-dir provided -"./$1" binstall \ - --force \ - --manifest-path "manifests/github-test-Cargo2.toml" \ - --no-confirm \ - --disable-strategies compile \ - cargo-binstall - -# Test that the installed binaries can be run -cargo binstall --help >/dev/null - -cargo_binstall_version="$(cargo binstall -V)" -echo "$cargo_binstall_version" - -[ "$cargo_binstall_version" = "cargo-binstall 0.12.0" ] diff --git a/e2e-tests/private-github-repo.sh b/e2e-tests/private-github-repo.sh deleted file mode 100755 index e0050101..00000000 --- a/e2e-tests/private-github-repo.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash - -set -euxo pipefail - -unset CARGO_INSTALL_ROOT - -CARGO_HOME=$(mktemp -d 2>/dev/null || mktemp -d -t 'cargo-home') -export CARGO_HOME -export PATH="$CARGO_HOME/bin:$PATH" - -# Install binaries using `--manifest-path` -# Also test default github template -"./$1" binstall --force --manifest-path "manifests/private-github-repo-test-Cargo.toml" --no-confirm cargo-binstall --strategies crate-meta-data - -# Test that the installed binaries can be run -cargo binstall --help >/dev/null - -cargo_binstall_version="$(cargo binstall -V)" -echo "$cargo_binstall_version" - -[ "$cargo_binstall_version" = "cargo-binstall 0.12.0" ] diff --git a/e2e-tests/registries.sh b/e2e-tests/registries.sh deleted file mode 100644 index 7be7ffaf..00000000 --- a/e2e-tests/registries.sh +++ /dev/null @@ -1,69 +0,0 @@ -#!/bin/bash - -set -euxo pipefail - -test_cargo_binstall_install() { - # Test that the installed binaries can be run - cargo binstall --help >/dev/null - - cargo_binstall_version="$(cargo binstall -V)" - echo "$cargo_binstall_version" - - [ "$cargo_binstall_version" = "cargo-binstall 0.12.0" ] -} - -unset CARGO_INSTALL_ROOT - -CARGO_HOME="$(mktemp -d 2>/dev/null || mktemp -d -t 'cargo-home')" -export CARGO_HOME -export PATH="$CARGO_HOME/bin:$PATH" - -# Testing conflicts of `--index` and `--registry` -set +e - -"./$1" binstall --index 'sparse+https://index.crates.io/' --registry t1 cargo-binstall -exit_code="$?" - -set -e - -if [ "$exit_code" != 2 ]; then - echo "Expected exit code 2, but actual exit code $exit_code" - exit 1 -fi - -cat >"$CARGO_HOME/config.toml" << EOF -[registries] -t1 = { index = "https://github.com/rust-lang/crates.io-index" } -t2 = { index = "sparse+https://index.crates.io/" } - -[registry] -default = "t1" -EOF - -# Install binaries using default registry in config -"./$1" binstall --force -y cargo-binstall@0.12.0 - -grep -F "cargo-binstall 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" <"$CARGO_HOME/.crates.toml" - -test_cargo_binstall_install - -# Install binaries using registry t2 in config -"./$1" binstall --force --registry t2 -y cargo-binstall@0.12.0 - -grep -F "cargo-binstall 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" <"$CARGO_HOME/.crates.toml" - -test_cargo_binstall_install - -# Install binaries using registry t3 in env -CARGO_REGISTRIES_t3_INDEX='sparse+https://index.crates.io/' "./$1" binstall --force --registry t3 -y cargo-binstall@0.12.0 - -grep -F "cargo-binstall 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" <"$CARGO_HOME/.crates.toml" - -test_cargo_binstall_install - -# Install binaries using index directly -"./$1" binstall --force --index 'sparse+https://index.crates.io/' -y cargo-binstall@0.12.0 - -grep -F "cargo-binstall 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" <"$CARGO_HOME/.crates.toml" - -test_cargo_binstall_install diff --git a/e2e-tests/self-install.sh b/e2e-tests/self-install.sh deleted file mode 100644 index e00f3538..00000000 --- a/e2e-tests/self-install.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/bash - -set -euxo pipefail - -unset CARGO_INSTALL_ROOT - -CARGO_HOME=$(mktemp -d 2>/dev/null || mktemp -d -t 'cargo-home') -export CARGO_HOME -export PATH="$CARGO_HOME/bin:$PATH" - -"./$1" --self-install - -cargo binstall --help -cargo install --list diff --git a/e2e-tests/self-upgrade-no-symlink.sh b/e2e-tests/self-upgrade-no-symlink.sh deleted file mode 100644 index d00cca88..00000000 --- a/e2e-tests/self-upgrade-no-symlink.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/bash - -set -euxo pipefail - -unset CARGO_INSTALL_ROOT - -CARGO_HOME=$(mktemp -d 2>/dev/null || mktemp -d -t 'cargo-home') -export CARGO_HOME -export PATH="$CARGO_HOME/bin:$PATH" - -# first boostrap-install into the CARGO_HOME -mkdir -p "$CARGO_HOME/bin" -cp "./$1" "$CARGO_HOME/bin" - -# now we're running the CARGO_HOME/bin/cargo-binstall (via cargo): - -# self update replacing no-symlinks with no-symlinks -cargo binstall --no-confirm --no-symlinks --force cargo-binstall@0.20.1 - -# self update replacing no-symlinks with symlinks -cp "./$1" "$CARGO_HOME/bin" - -cargo binstall --no-confirm --force cargo-binstall@0.20.1 - -# self update replacing symlinks with symlinks -ln -snf "$(pwd)/cargo-binstall" "$CARGO_HOME/bin/cargo-binstall" - -cargo binstall --no-confirm --force cargo-binstall@0.20.1 - -# self update replacing symlinks with no-symlinks -ln -snf "$(pwd)/cargo-binstall" "$CARGO_HOME/bin/cargo-binstall" - -cargo binstall --no-confirm --force --no-symlinks cargo-binstall@0.20.1 diff --git a/e2e-tests/signing.sh b/e2e-tests/signing.sh deleted file mode 100755 index 08915c82..00000000 --- a/e2e-tests/signing.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/bash - -set -euxo pipefail - -unset CARGO_INSTALL_ROOT - -CARGO_HOME=$(mktemp -d 2>/dev/null || mktemp -d -t 'cargo-home') -export CARGO_HOME -export PATH="$CARGO_HOME/bin:$PATH" - -echo Generate tls cert - -CERT_DIR=$(mktemp -d 2>/dev/null || mktemp -d -t 'cert-dir') -export CERT_DIR - -openssl req -newkey rsa:4096 -x509 -sha256 -days 1 -nodes -out "$CERT_DIR/"ca.pem -keyout "$CERT_DIR/"ca.key -subj '//C=UT/CN=ca.localhost' -openssl req -new -newkey rsa:4096 -sha256 -nodes -out "$CERT_DIR/"server.csr -keyout "$CERT_DIR/"server.key -subj '//C=UT/CN=localhost' -openssl x509 -req -in "$CERT_DIR/"server.csr -CA "$CERT_DIR/"ca.pem -CAkey "$CERT_DIR/"ca.key -CAcreateserial -out "$CERT_DIR/"server.pem -days 1 -sha256 -extfile signing/server.ext - -python3 signing/server.py & -server_pid=$! -trap 'kill $server_pid' ERR INT TERM - -export BINSTALL_HTTPS_ROOT_CERTS="$CERT_DIR/ca.pem" - -signing/wait-for-server.sh - -"./$1" binstall --force --manifest-path manifests/signing-Cargo.toml --no-confirm signing-test -"./$1" binstall --force --manifest-path manifests/signing-Cargo.toml --no-confirm --only-signed signing-test -"./$1" binstall --force --manifest-path manifests/signing-Cargo.toml --no-confirm --skip-signatures signing-test - -# from quick-install -"./$1" binstall --force --strategies quick-install --no-confirm --only-signed --target x86_64-unknown-linux-musl zellij@0.38.2 - -kill $server_pid || true diff --git a/e2e-tests/signing/minisign.key b/e2e-tests/signing/minisign.key deleted file mode 100644 index 5716be67..00000000 --- a/e2e-tests/signing/minisign.key +++ /dev/null @@ -1,2 +0,0 @@ -untrusted comment: minisign encrypted secret key -RWQAAEIyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZ5gXC5kG11Wu99VVpToebb+yc0MOw4cbWzxSHyOxoSTu6kBrK09z/MEPWo2OOKMI36kki4GiI7gcBgIaPLwvxe14Wtxm9acXAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= diff --git a/e2e-tests/signing/minisign.pub b/e2e-tests/signing/minisign.pub deleted file mode 100644 index 0baa41c7..00000000 --- a/e2e-tests/signing/minisign.pub +++ /dev/null @@ -1,2 +0,0 @@ -untrusted comment: minisign public key 55D706990B179867 -RWRnmBcLmQbXVcEPWo2OOKMI36kki4GiI7gcBgIaPLwvxe14Wtxm9acX diff --git a/e2e-tests/signing/server.ext b/e2e-tests/signing/server.ext deleted file mode 100644 index 0bba95d3..00000000 --- a/e2e-tests/signing/server.ext +++ /dev/null @@ -1,6 +0,0 @@ -authorityKeyIdentifier=keyid,issuer -basicConstraints=CA:FALSE -keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment -subjectAltName = @alt_names -[alt_names] -DNS.1 = localhost diff --git a/e2e-tests/signing/server.py b/e2e-tests/signing/server.py deleted file mode 100644 index 79d7749a..00000000 --- a/e2e-tests/signing/server.py +++ /dev/null @@ -1,15 +0,0 @@ -import http.server -import os -import ssl -from pathlib import Path - -cert_dir = Path(os.environ["CERT_DIR"]) - -os.chdir(os.path.dirname(__file__)) - -server_address = ('', 4443) -httpd = http.server.HTTPServer(server_address, http.server.SimpleHTTPRequestHandler) -ctx = ssl.SSLContext(protocol=ssl.PROTOCOL_TLS_SERVER) -ctx.load_cert_chain(certfile=cert_dir / "server.pem", keyfile=cert_dir / "server.key") -httpd.socket = ctx.wrap_socket(httpd.socket, server_side=True) -httpd.serve_forever() diff --git a/e2e-tests/signing/signing-test.exe.nasm b/e2e-tests/signing/signing-test.exe.nasm deleted file mode 100644 index 35b23301..00000000 --- a/e2e-tests/signing/signing-test.exe.nasm +++ /dev/null @@ -1,74 +0,0 @@ -; tiny97.asm, copyright Alexander Sotirov - -BITS 32 -; -; MZ header -; The only two fields that matter are e_magic and e_lfanew - -mzhdr: - dw "MZ" ; e_magic - dw 0 ; e_cblp UNUSED - -; PE signature -pesig: - dd "PE" ; e_cp, e_crlc UNUSED ; PE signature - -; PE header -pehdr: - dw 0x014C ; e_cparhdr UNUSED ; Machine (Intel 386) - dw 1 ; e_minalloc UNUSED ; NumberOfSections - -; dd 0xC3582A6A ; e_maxalloc, e_ss UNUSED ; TimeDateStamp UNUSED - -; Entry point -start: - push byte 42 - pop eax - ret - -codesize equ $ - start - - dd 0 ; e_sp, e_csum UNUSED ; PointerToSymbolTable UNUSED - dd 0 ; e_ip, e_cs UNUSED ; NumberOfSymbols UNUSED - dw sections-opthdr ; e_lsarlc UNUSED ; SizeOfOptionalHeader - dw 0x103 ; e_ovno UNUSED ; Characteristics - -; PE optional header -; The debug directory size at offset 0x94 from here must be 0 - -filealign equ 4 -sect_align equ 4 ; must be 4 because of e_lfanew - -%define round(n, r) (((n+(r-1))/r)*r) - -opthdr: - dw 0x10B ; e_res UNUSED ; Magic (PE32) - db 8 ; MajorLinkerVersion UNUSED - db 0 ; MinorLinkerVersion UNUSED - -; PE code section -sections: - dd round(codesize, filealign) ; SizeOfCode UNUSED ; Name UNUSED - dd 0 ; e_oemid, e_oeminfo UNUSED ; SizeOfInitializedData UNUSED - dd codesize ; e_res2 UNUSED ; SizeOfUninitializedData UNUSED ; VirtualSize - dd start ; AddressOfEntryPoint ; VirtualAddress - dd codesize ; BaseOfCode UNUSED ; SizeOfRawData - dd start ; BaseOfData UNUSED ; PointerToRawData - dd 0x400000 ; ImageBase ; PointerToRelocations UNUSED - dd sect_align ; e_lfanew ; SectionAlignment ; PointerToLinenumbers UNUSED - dd filealign ; FileAlignment ; NumberOfRelocations, NumberOfLinenumbers UNUSED - dw 4 ; MajorOperatingSystemVersion UNUSED ; Characteristics UNUSED - dw 0 ; MinorOperatingSystemVersion UNUSED - dw 0 ; MajorImageVersion UNUSED - dw 0 ; MinorImageVersion UNUSED - dw 4 ; MajorSubsystemVersion - dw 0 ; MinorSubsystemVersion UNUSED - dd 0 ; Win32VersionValue UNUSED - dd round(hdrsize, sect_align)+round(codesize,sect_align) ; SizeOfImage - dd round(hdrsize, filealign) ; SizeOfHeaders - dd 0 ; CheckSum UNUSED - db 2 ; Subsystem (Win32 GUI) - -hdrsize equ $ - $$ -filesize equ $ - $$ - diff --git a/e2e-tests/signing/signing-test.tar b/e2e-tests/signing/signing-test.tar deleted file mode 100644 index de55cfef..00000000 Binary files a/e2e-tests/signing/signing-test.tar and /dev/null differ diff --git a/e2e-tests/signing/signing-test.tar.sig b/e2e-tests/signing/signing-test.tar.sig deleted file mode 100644 index 0f059432..00000000 --- a/e2e-tests/signing/signing-test.tar.sig +++ /dev/null @@ -1,4 +0,0 @@ -untrusted comment: signature from minisign secret key -RURnmBcLmQbXVVINqskhik18fjpzn1TTn7UZWPC6TuVNSZc+0CqLiNxJhBvT3aXiFHxiEwiBeQaFipsxXux06C12+rwT9Pozgwo= -trusted comment: timestamp:1693846563 file:signing-test.tar hashed -fQqqvTO6KgHSHf6/n18FQVJgO8azb1dB90jwj2YukbRfwK3QD0rNSDFBmhN73H7Pwxsz9of42OG60dfXA+ldCQ== diff --git a/e2e-tests/signing/wait-for-server.sh b/e2e-tests/signing/wait-for-server.sh deleted file mode 100755 index 00d0d25c..00000000 --- a/e2e-tests/signing/wait-for-server.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash - -set -euxo pipefail - -CERT="${BINSTALL_HTTPS_ROOT_CERTS?}" - -counter=0 - -while ! curl --cacert "$CERT" --ssl-revoke-best-effort -L https://localhost:4443/signing-test.tar | file -; do - counter=$(( counter + 1 )) - if [ "$counter" = "20" ]; then - echo Failed to connect to https server - exit 1; - fi - sleep 10 -done diff --git a/e2e-tests/strategies.sh b/e2e-tests/strategies.sh deleted file mode 100755 index 981ce188..00000000 --- a/e2e-tests/strategies.sh +++ /dev/null @@ -1,66 +0,0 @@ -#!/bin/bash - -set -euxo pipefail - -unset CARGO_INSTALL_ROOT - -CARGO_HOME=$(mktemp -d 2>/dev/null || mktemp -d -t 'cargo-home') -export CARGO_HOME -export PATH="$CARGO_HOME/bin:$PATH" - -## Test --disable-strategies -set +e - -"./$1" binstall --no-confirm --disable-strategies quick-install,compile cargo-update@11.1.2 -exit_code="$?" - -set -e - -if [ "$exit_code" != 94 ]; then - echo "Expected exit code 94, but actual exit code $exit_code" - exit 1 -fi - -## Test --strategies -set +e - -"./$1" binstall --no-confirm --strategies crate-meta-data cargo-update@11.1.2 -exit_code="$?" - -set -e - -if [ "$exit_code" != 94 ]; then - echo "Expected exit code 94, but actual exit code $exit_code" - exit 1 -fi - -## Test compile-only strategy -"./$1" binstall --no-confirm --strategies compile cargo-quickinstall@0.2.8 - -## Test Cargo.toml disable-strategies -set +e - -"./$1" binstall --no-confirm --manifest-path "manifests/strategies-test-Cargo.toml" cargo-update@11.1.2 -exit_code="$?" - -set -e - -if [ "$exit_code" != 94 ]; then - echo "Expected exit code 94, but actual exit code $exit_code" - exit 1 -fi - -set +e - -"./$1" binstall --disable-strategies compile --no-confirm --manifest-path "manifests/strategies-test-Cargo2.toml" cargo-update@11.1.2 -exit_code="$?" - -set -e - -if [ "$exit_code" != 94 ]; then - echo "Expected exit code 94, but actual exit code $exit_code" - exit 1 -fi - -## Test --strategies overriding `disabled-strategies=["compile"]` in Cargo.toml - "./$1" binstall --no-confirm --manifest-path "manifests/strategies-test-override-Cargo.toml" --strategies compile cargo-quickinstall@0.2.10 diff --git a/e2e-tests/subcrate.sh b/e2e-tests/subcrate.sh deleted file mode 100755 index 40a15ecc..00000000 --- a/e2e-tests/subcrate.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash - -set -euxo pipefail - -unset CARGO_INSTALL_ROOT - -CARGO_HOME=$(mktemp -d 2>/dev/null || mktemp -d -t 'cargo-home') -export CARGO_HOME -othertmpdir=$(mktemp -d 2>/dev/null || mktemp -d -t 'cargo-test') -export PATH="$CARGO_HOME/bin:$othertmpdir/bin:$PATH" - -mkdir -p "$othertmpdir/bin" -# Copy it to bin to test use of env var `CARGO` -cp "./$1" "$othertmpdir/bin/" - -# cargo-audit -cargo binstall --no-confirm cargo-audit@0.18.3 --strategies crate-meta-data - -cargo_audit_version="$(cargo audit --version)" -echo "$cargo_audit_version" - -[ "$cargo_audit_version" = "cargo-audit 0.18.3" ] diff --git a/e2e-tests/tls.sh b/e2e-tests/tls.sh deleted file mode 100755 index b54ab9c7..00000000 --- a/e2e-tests/tls.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash - -set -euxo pipefail - -unset CARGO_INSTALL_ROOT - -CARGO_HOME=$(mktemp -d 2>/dev/null || mktemp -d -t 'cargo-home') -export CARGO_HOME -export PATH="$CARGO_HOME/bin:$PATH" - -"./$1" binstall \ - --force \ - --min-tls-version "${2:-1.3}" \ - --no-confirm \ - cargo-binstall@0.20.1 -# Test that the installed binaries can be run -cargo binstall --help >/dev/null diff --git a/e2e-tests/uninstall.sh b/e2e-tests/uninstall.sh deleted file mode 100644 index 3cfd8cbc..00000000 --- a/e2e-tests/uninstall.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash - -set -euxo pipefail - -unset CARGO_INSTALL_ROOT - -CARGO_HOME=$(mktemp -d 2>/dev/null || mktemp -d -t 'cargo-home') -export CARGO_HOME -othertmpdir=$(mktemp -d 2>/dev/null || mktemp -d -t 'cargo-test') -export PATH="$CARGO_HOME/bin:$othertmpdir/bin:$PATH" - -mkdir -p "$othertmpdir/bin" -# Copy it to bin to test use of env var `CARGO` -cp "./$1" "$othertmpdir/bin/" - - -cargo binstall --no-confirm cargo-watch@8.4.0 -cargo uninstall cargo-watch diff --git a/e2e-tests/upgrade.sh b/e2e-tests/upgrade.sh deleted file mode 100755 index affdab73..00000000 --- a/e2e-tests/upgrade.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash - -set -euxo pipefail - -unset CARGO_INSTALL_ROOT - -CARGO_HOME=$(mktemp -d 2>/dev/null || mktemp -d -t 'cargo-home') -export CARGO_HOME -export PATH="$CARGO_HOME/bin:$PATH" - -# Test skip when installed -"./$1" binstall --no-confirm --force cargo-binstall@0.11.1 -"./$1" binstall --log-level=info --no-confirm cargo-binstall@0.11.1 | grep -q 'cargo-binstall v0.11.1 is already installed' - -## Test When 0.11.0 is installed but can be upgraded. -"./$1" binstall --no-confirm cargo-binstall@0.12.0 -"./$1" binstall --log-level=info --no-confirm cargo-binstall@0.12.0 | grep -q 'cargo-binstall v0.12.0 is already installed' -"./$1" binstall --log-level=info --no-confirm cargo-binstall@^0.12.0 | grep -q -v 'cargo-binstall v0.12.0 is already installed' diff --git a/e2e-tests/version-syntax.sh b/e2e-tests/version-syntax.sh deleted file mode 100755 index c9551c51..00000000 --- a/e2e-tests/version-syntax.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash - -set -euxo pipefail - -unset CARGO_INSTALL_ROOT - -CARGO_HOME=$(mktemp -d 2>/dev/null || mktemp -d -t 'cargo-home') -export CARGO_HOME -export PATH="$CARGO_HOME/bin:$PATH" - -# Test --version -"./$1" binstall --force --no-confirm --version 0.11.1 cargo-binstall -# Test that the installed binaries can be run -cargo binstall --help >/dev/null - -# Test "$crate_name@$version" -"./$1" binstall --force --no-confirm cargo-binstall@0.11.1 -# Test that the installed binaries can be run -cargo binstall --help >/dev/null diff --git a/install-from-binstall-release.ps1 b/install-from-binstall-release.ps1 deleted file mode 100644 index abbc8050..00000000 --- a/install-from-binstall-release.ps1 +++ /dev/null @@ -1,48 +0,0 @@ -$ErrorActionPreference = "Stop" -Set-PSDebug -Trace 1 -$tmpdir = $Env:TEMP -$BINSTALL_VERSION = $Env:BINSTALL_VERSION -if ($BINSTALL_VERSION -and $BINSTALL_VERSION -notlike 'v*') { - # prefix version with v - $BINSTALL_VERSION = "v$BINSTALL_VERSION" -} -# Fetch binaries from `[..]/releases/latest/download/[..]` if _no_ version is -# given, otherwise from `[..]/releases/download/VERSION/[..]`. Note the shifted -# location of '/download'. -$base_url = if (-not $BINSTALL_VERSION) { - "https://github.com/cargo-bins/cargo-binstall/releases/latest/download/cargo-binstall-" -} else { - "https://github.com/cargo-bins/cargo-binstall/releases/download/$BINSTALL_VERSION/cargo-binstall-" -} - -$proc_arch = [Environment]::GetEnvironmentVariable("PROCESSOR_ARCHITECTURE", [EnvironmentVariableTarget]::Machine) -if ($proc_arch -eq "AMD64") { - $arch = "x86_64" -} elseif ($proc_arch -eq "ARM64") { - $arch = "aarch64" -} else { - Write-Host "Unsupported Architecture: $type" -ForegroundColor Red - [Environment]::Exit(1) -} -$url = "$base_url$arch-pc-windows-msvc.zip" -Invoke-WebRequest $url -OutFile $tmpdir\cargo-binstall.zip -Expand-Archive -Force $tmpdir\cargo-binstall.zip $tmpdir\cargo-binstall -Write-Host "" - -$ps = Start-Process -PassThru -Wait "$tmpdir\cargo-binstall\cargo-binstall.exe" "--self-install" -if ($ps.ExitCode -ne 0) { - Invoke-Expression "$tmpdir\cargo-binstall\cargo-binstall.exe -y --force cargo-binstall" -} - -Remove-Item -Force $tmpdir\cargo-binstall.zip -Remove-Item -Recurse -Force $tmpdir\cargo-binstall -$cargo_home = if ($Env:CARGO_HOME -ne $null) { $Env:CARGO_HOME } else { "$HOME\.cargo" } -if ($Env:Path -split ";" -notcontains "$cargo_home\bin") { - if (($Env:CI -ne $null) -and ($Env:GITHUB_PATH -ne $null)) { - Add-Content -Path "$Env:GITHUB_PATH" -Value "$cargo_home\bin" - } else { - Write-Host "" - Write-Host "Your path is missing $cargo_home\bin, you might want to add it." -ForegroundColor Red - Write-Host "" - } -} diff --git a/install-from-binstall-release.sh b/install-from-binstall-release.sh deleted file mode 100755 index 83f6ca8d..00000000 --- a/install-from-binstall-release.sh +++ /dev/null @@ -1,71 +0,0 @@ -#!/bin/sh - -set -eux - -# Set pipefail if it works in a subshell, disregard if unsupported -# shellcheck disable=SC3040 -(set -o pipefail 2> /dev/null) && set -o pipefail - -case "${BINSTALL_VERSION:-}" in - "") ;; # unset - v*) ;; # already includes the `v` - *) BINSTALL_VERSION="v$BINSTALL_VERSION" ;; # Add a leading `v` -esac - -cd "$(mktemp -d)" - -# Fetch binaries from `[..]/releases/latest/download/[..]` if _no_ version is -# given, otherwise from `[..]/releases/download/VERSION/[..]`. Note the shifted -# location of '/download'. -if [ -z "${BINSTALL_VERSION:-}" ]; then - base_url="https://github.com/cargo-bins/cargo-binstall/releases/latest/download/cargo-binstall-" -else - base_url="https://github.com/cargo-bins/cargo-binstall/releases/download/${BINSTALL_VERSION}/cargo-binstall-" -fi - -os="$(uname -s)" -if [ "$os" = "Darwin" ]; then - url="${base_url}universal-apple-darwin.zip" - curl -A "Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Firefox/81.0" -LO --proto '=https' --tlsv1.2 -sSf "$url" - unzip cargo-binstall-universal-apple-darwin.zip -elif [ "$os" = "Linux" ]; then - machine="$(uname -m)" - if [ "$machine" = "armv7l" ]; then - machine="armv7" - fi - target="${machine}-unknown-linux-musl" - if [ "$machine" = "armv7" ]; then - target="${target}eabihf" - fi - - url="${base_url}${target}.tgz" - curl -A "Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Firefox/81.0" -L --proto '=https' --tlsv1.2 -sSf "$url" | tar -xvzf - -elif [ "${OS-}" = "Windows_NT" ]; then - machine="$(uname -m)" - target="${machine}-pc-windows-msvc" - url="${base_url}${target}.zip" - curl -A "Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Firefox/81.0" -LO --proto '=https' --tlsv1.2 -sSf "$url" - unzip "cargo-binstall-${target}.zip" -else - echo "Unsupported OS ${os}" - exit 1 -fi - -./cargo-binstall --self-install || ./cargo-binstall -y --force cargo-binstall - -CARGO_HOME="${CARGO_HOME:-$HOME/.cargo}" - -case ":$PATH:" in - *":$CARGO_HOME/bin:"*) ;; # Cargo home is already in path - *) needs_cargo_home=1 ;; -esac - -if [ -n "${needs_cargo_home:-}" ]; then - if [ -n "${CI:-}" ] && [ -n "${GITHUB_PATH:-}" ]; then - echo "$CARGO_HOME/bin" >> "$GITHUB_PATH" - else - echo - printf "\033[0;31mYour path is missing %s, you might want to add it.\033[0m\n" "$CARGO_HOME/bin" - echo - fi -fi diff --git a/justfile b/justfile deleted file mode 100644 index f4590818..00000000 --- a/justfile +++ /dev/null @@ -1,374 +0,0 @@ -# input variables -ci := env_var_or_default("CI", "") -for-release := env_var_or_default("JUST_FOR_RELEASE", "") -use-cross := env_var_or_default("JUST_USE_CROSS", "") -use-cargo-zigbuild := env_var_or_default("JUST_USE_CARGO_ZIGBUILD", "") -extra-build-args := env_var_or_default("JUST_EXTRA_BUILD_ARGS", "") -extra-features := env_var_or_default("JUST_EXTRA_FEATURES", "") -default-features := env_var_or_default("JUST_DEFAULT_FEATURES", "") -override-features := env_var_or_default("JUST_OVERRIDE_FEATURES", "") -glibc-version := env_var_or_default("GLIBC_VERSION", "") -use-auditable := env_var_or_default("JUST_USE_AUDITABLE", "") -timings := env_var_or_default("JUST_TIMINGS", "") -build-std := env_var_or_default("JUST_BUILD_STD", "") -enable-h3 := env_var_or_default("JUST_ENABLE_H3", "") -cargo-nextest-additional-args := env_var_or_default("CARGO_NEXTEST_ADDITIONAL_ARGS", "") - -export BINSTALL_LOG_LEVEL := if env_var_or_default("RUNNER_DEBUG", "0") == "1" { "debug" } else { "info" } -export BINSTALL_RATE_LIMIT := "30/1" - -cargo := if use-cargo-zigbuild != "" { "cargo-zigbuild" } else if use-cross != "" { "cross" } else { "cargo" } -export CARGO := cargo - -# target information -target-host := `rustc -vV | grep host: | cut -d ' ' -f 2` -target := env_var_or_default("CARGO_BUILD_TARGET", target-host) -target-os := if target =~ "-windows-" { "windows" - } else if target =~ "darwin" { "macos" - } else if target =~ "linux" { "linux" - } else { "unknown" } -target-arch := if target =~ "x86_64" { "x64" - } else if target =~ "i[56]86" { "x86" - } else if target =~ "aarch64" { "arm64" - } else if target =~ "armv7" { "arm32" - } else { "unknown" } -target-libc := if target =~ "gnu" { "gnu" - } else if target =~ "musl" { "musl" - } else { "unknown" } - -# build output location -output-ext := if target-os == "windows" { ".exe" } else { "" } -output-filename := "cargo-binstall" + output-ext -output-profile-folder := if for-release != "" { "release" } else { "debug" } -output-folder := "target" / target / output-profile-folder -output-path := output-folder / output-filename - -# which tool to use for compiling -cargo-bin := if use-auditable != "" { - "cargo-auditable auditable" -} else { - cargo -} - -# cargo compile options -cargo-profile := if for-release != "" { "release" } else { "dev" } - - -ci-or-no := if ci != "" { "ci" } else { "noci" } - -# In release builds in CI, build the std library ourselves so it uses our -# compile profile, and optimise panic messages out with immediate abort. -cargo-buildstd := if build-std != "" { - " -Z build-std=std,panic_abort -Z build-std-features=panic_immediate_abort" -} else if target == "x86_64h-apple-darwin" { - " -Z build-std=std,panic_abort -Z build-std-features=panic_immediate_abort" -} else { "" } - -# In musl release builds in CI, statically link gcclibs. -rustc-gcclibs := if (cargo-profile / ci-or-no / target-libc) == "release/ci/musl" { - if use-cargo-zigbuild != "" { "-C link-arg=-static-libgcc" } else { " -C link-arg=-lgcc -C link-arg=-static-libgcc" } -} else { "" } - -# disable default features in CI for debug builds, for speed -cargo-no-default-features := if default-features == "false" { " --no-default-features" - } else if default-features == "true" { "" - } else if (cargo-profile / ci-or-no) == "dev/ci" { " --no-default-features" - } else { "" } - -support-pkg-config := if target == target-host { - if target-os == "linux" { "true" } else { "" } -} else { "" } - -h3-features := if enable-h3 != "" { ",http3" } else { "" } -cargo-features := trim_end_match(if override-features != "" { override-features + h3-features - } else if (cargo-profile / ci-or-no) == "dev/ci" { "git,rustls,fancy-with-backtrace,zstd-thin,log_max_level_debug,zlib-rs" + (if support-pkg-config != "" { ",pkg-config" } else { "" }) + h3-features + extra-features - } else if (cargo-profile / ci-or-no) == "release/ci" { "git,static,rustls,trust-dns,fancy-no-backtrace,zstd-thin,log_release_max_level_debug,cross-lang-fat-lto,zlib-rs" + h3-features + extra-features - } else if extra-features != "" { extra-features + h3-features - } else if enable-h3 != "" { "http3" - } else { "" -}, ",") - -# it seems we can't split debuginfo for non-buildstd builds -# errors with: "Found a record with an unknown abbreviation code" -cargo-split-debuginfo := if cargo-buildstd != "" { " --config='profile.release.split-debuginfo=\"packed\"' --config=profile.release.debug=2" } else { "" } - -# MIR optimisation level (defaults to 2, bring it up to 4 for release builds) -# **DISABLED because it's buggy** -rustc-miropt := "" # if for-release != "" { " -Z mir-opt-level=4" } else { "" } - -# Use rust-lld that is bundled with rustup to speedup linking -# and support for icf=safe. -# -# -Zgcc-ld=lld uses the rust-lld that is bundled with rustup. -# -# TODO: There is ongoing effort to stabilise this and we will need to update -# this once it is merged. -# https://github.com/rust-lang/compiler-team/issues/510 -# -# If cargo-zigbuild is used, then it will provide the lld linker. -# This option is disabled on windows since it not supported. -rust-lld := "" #if use-cargo-zigbuild != "" { -#"" -#} else { -#" -C link-arg=-fuse-ld=lld" -#} - -# ICF: link-time identical code folding -# -# On windows it works out of the box and on linux it uses -# rust-lld. -rustc-icf := if for-release != "" { - if target-os == "windows" { - " -C link-arg=-Wl,--icf=safe" - } else if target-os == "linux" { - " -C link-arg=-Wl,--icf=safe" - } else { - "" - } -} else { - "" -} - -# Only enable linker-plugin-lto for release -# Also disable this on windows since it uses msvc. -# -# Temporarily disable this on linux due to mismatch llvm version -# } else if target-os == "linux" { -# "-C linker-plugin-lto " -linker-plugin-lto := if for-release == "" { - "" -} else { - "" -} - -target-glibc-ver-postfix := if glibc-version != "" { - if use-cargo-zigbuild != "" { - "." + glibc-version - } else { - "" - } -} else { - "" -} - -cargo-check-args := (" --target ") + (target) + (target-glibc-ver-postfix) + (cargo-buildstd) + (if extra-build-args != "" { " " + extra-build-args } else { "" }) + (cargo-split-debuginfo) -cargo-build-args := (if for-release != "" { " --release" } else { "" }) + (cargo-check-args) + (cargo-no-default-features) + (if cargo-features != "" { " --features " + cargo-features } else { "" }) + (if timings != "" { " --timings" } else { "" }) -export RUSTFLAGS := (linker-plugin-lto) + (rustc-gcclibs) + (rustc-miropt) + (rust-lld) + (rustc-icf) + (if enable-h3 != "" { " --cfg reqwest_unstable" } else { "" }) - - -# libblocksruntime-dev provides compiler-rt -ci-apt-deps := if target == "x86_64-unknown-linux-gnu" { "liblzma-dev libzip-dev libzstd-dev" - } else { "" } - -[linux] -ci-install-deps: - if [ -n "{{ci-apt-deps}}" ]; then sudo apt update && sudo apt install -y --no-install-recommends {{ci-apt-deps}}; fi - if [ -n "{{use-cargo-zigbuild}}" ]; then pip3 install -r zigbuild-requirements.txt; fi - -[macos] -[windows] -ci-install-deps: - -toolchain-name := if cargo-buildstd != "" { "nightly" } else { "stable" } -# x86_64h-apple-darwin does not contain pre-built libstd, instead we will -# install rust-src and use build-std to build it. -target-name := if target == "x86_64h-apple-darwin" { "" } else { target } -default-components := if cargo-buildstd != "" { "rust-src" } else { "" } - -toolchain components=default-components: - rustup toolchain install {{toolchain-name}} {{ if components != "" { "--component " + components } else { "" } }} --no-self-update --profile minimal {{ if target-name != "" { "--target " + target-name } else { "" } }} - rustup override set {{toolchain-name}} - -print-env: - @echo "env RUSTFLAGS='$RUSTFLAGS', CARGO='$CARGO'" - -print-rustflags: - @echo "$RUSTFLAGS" - -build: print-env - {{cargo-bin}} build {{cargo-build-args}} - -check: print-env - {{cargo-bin}} check {{cargo-build-args}} --profile check-only - {{cargo-bin}} check -p binstalk-downloader --no-default-features --profile check-only - {{cargo-bin}} check -p cargo-binstall --no-default-features --features rustls {{cargo-check-args}} --profile check-only - cargo-hack hack check -p binstalk-downloader \ - --feature-powerset \ - --include-features default,json \ - --profile check-only \ - {{cargo-check-args}} - -get-output file outdir=".": - test -d "{{outdir}}" || mkdir -p {{outdir}} - cp -r {{ output-folder / file }} {{outdir}}/{{ file_name(file) }} - -ls -l {{outdir}}/{{ file_name(file) }} - -get-binary outdir=".": (get-output output-filename outdir) - -chmod +x {{ outdir / output-filename }} - -e2e-test file *arguments: (get-binary "e2e-tests") - cd e2e-tests && env -u RUSTFLAGS -u CARGO_BUILD_TARGET bash {{file}}.sh {{output-filename}} {{arguments}} - -e2e-test-live: (e2e-test "live") -e2e-test-subcrate: (e2e-test "subcrate") -e2e-test-manifest-path: (e2e-test "manifest-path") -e2e-test-other-repos: (e2e-test "other-repos") -e2e-test-strategies: (e2e-test "strategies") -e2e-test-version-syntax: (e2e-test "version-syntax") -e2e-test-upgrade: (e2e-test "upgrade") -e2e-test-self-upgrade-no-symlink: (e2e-test "self-upgrade-no-symlink") -e2e-test-uninstall: (e2e-test "uninstall") -e2e-test-no-track: (e2e-test "no-track") -e2e-test-git: (e2e-test "git") -e2e-test-registries: (e2e-test "registries") -e2e-test-signing: (e2e-test "signing") -e2e-test-continue-on-failure: (e2e-test "continue-on-failure") -e2e-test-private-github-repo: (e2e-test "private-github-repo") -e2e-test-self-install: (e2e-test "self-install") - -# WinTLS (Windows in CI) does not have TLS 1.3 support -[windows] -e2e-test-tls: (e2e-test "tls" "1.2") -[linux] -[macos] -e2e-test-tls: (e2e-test "tls" "1.2") (e2e-test "tls" "1.3") - -e2e-tests: e2e-test-live e2e-test-manifest-path e2e-test-git e2e-test-other-repos e2e-test-strategies e2e-test-version-syntax e2e-test-upgrade e2e-test-tls e2e-test-self-upgrade-no-symlink e2e-test-uninstall e2e-test-subcrate e2e-test-no-track e2e-test-registries e2e-test-signing e2e-test-continue-on-failure e2e-test-private-github-repo e2e-test-self-install - -unit-tests: print-env - cargo test --no-run --target {{target}} - cargo nextest run --target {{target}} --no-tests=pass {{cargo-nextest-additional-args}} - cargo test --doc --target {{target}} - -test: unit-tests build e2e-tests - -clippy: print-env - {{cargo-bin}} clippy --no-deps -- -D clippy::all - -doc: print-env - cargo doc --no-deps --workspace - -fmt: print-env - cargo fmt --all -- --check - -fmt-check: fmt - -lint: clippy fmt-check doc - -# Rm dev-dependencies for `cargo-check` and clippy to speedup compilation. -# This is a workaround for the cargo nightly option `-Z avoid-dev-deps` -avoid-dev-deps: - for crate in ./crates/*; do \ - sed 's/\[dev-dependencies\]/[workaround-avoid-dev-deps]/g' "$crate/Cargo.toml" >"$crate/Cargo.toml.tmp"; \ - mv "$crate/Cargo.toml.tmp" "$crate/Cargo.toml" \ - ; done - -package-dir: - rm -rf packages/prep - mkdir -p packages/prep - cp crates/bin/LICENSE packages/prep - cp README.md packages/prep - -cp minisign.pub packages/prep - -[macos] -package-prepare: build package-dir - just get-binary packages/prep - -just get-output cargo-binstall.dSYM packages/prep - - just get-output detect-wasi{{output-ext}} packages/prep - -just get-output detect-wasi.dSYM packages/prep - - just get-output detect-targets{{output-ext}} packages/prep - -just get-output detect-targets.dSYM packages/prep - -# when https://github.com/rust-lang/cargo/pull/11384 lands, we can use -# -just get-output cargo_binstall.dwp packages/prep -# underscored dwp name needs to remain for debuggers to find the file properly -[linux] -package-prepare: build package-dir - just get-binary packages/prep - -cp {{output-folder}}/deps/cargo_binstall-*.dwp packages/prep/cargo_binstall.dwp - - just get-output detect-wasi packages/prep - -cp {{output-folder}}/deps/detect_wasi-*.dwp packages/prep/detect_wasi.dwp - - just get-output detect-targets packages/prep - -cp {{output-folder}}/deps/detect_target-*.dwp packages/prep/detect_target.dwp - -# underscored pdb name needs to remain for debuggers to find the file properly -# read from deps because sometimes cargo doesn't copy the pdb to the output folder -[windows] -package-prepare: build package-dir - just get-binary packages/prep - -just get-output deps/cargo_binstall.pdb packages/prep - - just get-output detect-wasi.exe packages/prep - -just get-output deps/detect_wasi.pdb packages/prep - - just get-output detect-targets.exe packages/prep - -just get-output deps/detect_target.pdb packages/prep - -# we don't get dSYM bundles for universal binaries; unsure if it's even a thing -[macos] -lipo-prepare: package-dir - just target=aarch64-apple-darwin build get-binary packages/prep/arm64 - just target=x86_64-apple-darwin build get-binary packages/prep/x64 - just target=x86_64h-apple-darwin build get-binary packages/prep/x64h - - just target=aarch64-apple-darwin get-binary packages/prep/arm64 - just target=x86_64-apple-darwin get-binary packages/prep/x64 - just target=x86_64h-apple-darwin get-binary packages/prep/x64h - lipo -create -output packages/prep/{{output-filename}} packages/prep/{arm64,x64,x64h}/{{output-filename}} - - just target=aarch64-apple-darwin get-output detect-wasi{{output-ext}} packages/prep/arm64 - just target=x86_64-apple-darwin get-output detect-wasi{{output-ext}} packages/prep/x64 - just target=x86_64h-apple-darwin get-output detect-wasi{{output-ext}} packages/prep/x64h - lipo -create -output packages/prep/detect-wasi{{output-ext}} packages/prep/{arm64,x64,x64h}/detect-wasi{{output-ext}} - - just target=aarch64-apple-darwin get-output detect-targets{{output-ext}} packages/prep/arm64 - just target=x86_64-apple-darwin get-output detect-targets{{output-ext}} packages/prep/x64 - just target=x86_64h-apple-darwin get-output detect-targets{{output-ext}} packages/prep/x64h - lipo -create -output packages/prep/detect-targets{{output-ext}} packages/prep/{arm64,x64,x64h}/detect-targets{{output-ext}} - - rm -rf packages/prep/{arm64,x64,x64h} - - -[linux] -package: package-prepare - cd packages/prep && tar cv {{output-filename}} | gzip -9 > "../cargo-binstall-{{target}}.tgz" - cd packages/prep && tar cv * | gzip -9 > "../cargo-binstall-{{target}}.full.tgz" - -[macos] -package: package-prepare - cd packages/prep && zip -r -9 "../cargo-binstall-{{target}}.zip" {{output-filename}} - cd packages/prep && zip -r -9 "../cargo-binstall-{{target}}.full.zip" * - -[windows] -package: package-prepare - cd packages/prep && 7z a -mx9 "../cargo-binstall-{{target}}.zip" {{output-filename}} - cd packages/prep && 7z a -mx9 "../cargo-binstall-{{target}}.full.zip" * - -[macos] -package-lipo: lipo-prepare - cd packages/prep && zip -r -9 "../cargo-binstall-universal-apple-darwin.zip" {{output-filename}} - cd packages/prep && zip -r -9 "../cargo-binstall-universal-apple-darwin.full.zip" * - -# assuming x64 and arm64 packages are already built, extract and lipo them -[macos] -repackage-lipo: package-dir - set -euxo pipefail - - mkdir -p packages/prep/{arm64,x64,x64h} - cd packages/prep/x64 && unzip -o "../../cargo-binstall-x86_64-apple-darwin.full.zip" - cd packages/prep/x64h && unzip -o "../../cargo-binstall-x86_64h-apple-darwin.full.zip" - cd packages/prep/arm64 && unzip -o "../../cargo-binstall-aarch64-apple-darwin.full.zip" - - lipo -create -output packages/prep/{{output-filename}} packages/prep/{arm64,x64,x64h}/{{output-filename}} - lipo -create -output packages/prep/detect-wasi packages/prep/{arm64,x64,x64h}/detect-wasi - lipo -create -output packages/prep/detect-targets packages/prep/{arm64,x64,x64h}/detect-targets - - ./packages/prep/{{output-filename}} -vV - - rm -rf packages/prep/{arm64,x64,x64h} - cd packages/prep && zip -9 "../cargo-binstall-universal-apple-darwin.zip" {{output-filename}} - cd packages/prep && zip -9 "../cargo-binstall-universal-apple-darwin.full.zip" * diff --git a/release-plz.toml b/release-plz.toml deleted file mode 100644 index 37bb12f8..00000000 --- a/release-plz.toml +++ /dev/null @@ -1,7 +0,0 @@ -[workspace] -git_release_latest = false # don't set release as latest release - -[[package]] -name = "cargo-binstall" -release = false # don't process this package - diff --git a/rust-toolchain.toml b/rust-toolchain.toml deleted file mode 100644 index 05e6ca1b..00000000 --- a/rust-toolchain.toml +++ /dev/null @@ -1,4 +0,0 @@ -[toolchain] -channel = "stable" -profile = "minimal" -components = ["rustfmt", "clippy"] diff --git a/src/drivers.rs b/src/drivers.rs new file mode 100644 index 00000000..9270de59 --- /dev/null +++ b/src/drivers.rs @@ -0,0 +1,118 @@ + +use std::time::Duration; +use std::path::{Path, PathBuf}; + +use log::{debug}; +use anyhow::{Context, anyhow}; +use semver::{Version, VersionReq}; + +use crates_io_api::AsyncClient; + +use crate::PkgFmt; +use crate::helpers::*; + +fn find_version<'a, V: Iterator>(requirement: &str, version_iter: V) -> Result { + // Parse version requirement + let version_req = VersionReq::parse(requirement)?; + + // Filter for matching versions + let mut filtered: Vec<_> = version_iter.filter(|v| { + // Remove leading `v` for git tags + let ver_str = match v.strip_prefix("s") { + Some(v) => v, + None => v, + }; + + // Parse out version + let ver = match Version::parse(ver_str) { + Ok(sv) => sv, + Err(_) => return false, + }; + + debug!("Version: {:?}", ver); + + // Filter by version match + version_req.matches(&ver) + }).collect(); + + // Sort by highest matching version + filtered.sort_by(|a, b| { + let a = Version::parse(a).unwrap(); + let b = Version::parse(b).unwrap(); + + b.partial_cmp(&a).unwrap() + }); + + debug!("Filtered: {:?}", filtered); + + // Return highest version + match filtered.get(0) { + Some(v) => Ok(v.to_string()), + None => Err(anyhow!("No matching version for requirement: '{}'", version_req)) + } +} + +/// Fetch a crate by name and version from crates.io +pub async fn fetch_crate_cratesio(name: &str, version_req: &str, temp_dir: &Path) -> Result { + + // Fetch / update index + debug!("Updating crates.io index"); + let index = crates_index::Index::new_cargo_default(); + index.retrieve_or_update()?; + + // Lookup crate in index + debug!("Looking up crate information"); + let base_info = match index.crate_(name) { + Some(i) => i, + None => { + return Err(anyhow::anyhow!("Error fetching information for crate {}", name)); + } + }; + + // Locate matching version + let version_iter = base_info.versions().iter().map(|v| v.version() ); + let version_name = find_version(version_req, version_iter)?; + + // Build crates.io api client + let api_client = AsyncClient::new("cargo-binstall (https://github.com/ryankurte/cargo-binstall)", Duration::from_millis(100))?; + + // Fetch online crate information + let crate_info = api_client.get_crate(name.as_ref()).await + .context("Error fetching crate information")?; + + // Fetch information for the filtered version + let version = match crate_info.versions.iter().find(|v| v.num == version_name) { + Some(v) => v, + None => { + return Err(anyhow::anyhow!("No information found for crate: '{}' version: '{}'", + name, version_name)); + } + }; + + debug!("Found information for crate version: '{}'", version.num); + + // Download crate to temporary dir (crates.io or git?) + let crate_url = format!("https://crates.io/{}", version.dl_path); + let tgz_path = temp_dir.join(format!("{}.tgz", name)); + + debug!("Fetching crate from: {}", crate_url); + + // Download crate + download(&crate_url, &tgz_path).await?; + + // Decompress downloaded tgz + debug!("Decompressing crate archive"); + extract(&tgz_path, PkgFmt::Tgz, &temp_dir)?; + let crate_path = temp_dir.join(format!("{}-{}", name, version_name)); + + // Return crate directory + Ok(crate_path) +} + +/// Fetch a crate by name and version from github +/// TODO: implement this +pub async fn fetch_crate_gh_releases(_name: &str, _version: Option<&str>, _temp_dir: &Path) -> Result { + + unimplemented!(); +} + diff --git a/src/helpers.rs b/src/helpers.rs new file mode 100644 index 00000000..dce5c9f3 --- /dev/null +++ b/src/helpers.rs @@ -0,0 +1,151 @@ + +use std::path::{Path, PathBuf}; + +use log::{debug, info, error}; + +use cargo_toml::{Manifest}; +use flate2::read::GzDecoder; +use tar::Archive; +use xz2::read::XzDecoder; +use zip::read::ZipArchive; + +use crate::{Meta}; + +use super::PkgFmt; + +/// Load binstall metadata from the crate `Cargo.toml` at the provided path +pub fn load_manifest_path>(manifest_path: P) -> Result, anyhow::Error> { + debug!("Reading manifest: {}", manifest_path.as_ref().display()); + + // Load and parse manifest (this checks file system for binary output names) + let manifest = Manifest::::from_path_with_metadata(manifest_path)?; + + // Return metadata + Ok(manifest) +} + +/// Download a file from the provided URL to the provided path +pub async fn download>(url: &str, path: P) -> Result<(), anyhow::Error> { + + debug!("Downloading from: '{}'", url); + + let resp = reqwest::get(url).await?; + + if !resp.status().is_success() { + error!("Download error: {}", resp.status()); + return Err(anyhow::anyhow!(resp.status())); + } + + let bytes = resp.bytes().await?; + + debug!("Download OK, writing to file: '{:?}'", path.as_ref()); + + std::fs::write(&path, bytes)?; + + Ok(()) +} + +/// Extract files from the specified source onto the specified path +pub fn extract, P: AsRef>(source: S, fmt: PkgFmt, path: P) -> Result<(), anyhow::Error> { + match fmt { + PkgFmt::Tar => { + // Extract to install dir + debug!("Extracting from tar archive '{:?}' to `{:?}`", source.as_ref(), path.as_ref()); + + let dat = std::fs::File::open(source)?; + let mut tar = Archive::new(dat); + + tar.unpack(path)?; + }, + PkgFmt::Tgz => { + // Extract to install dir + debug!("Decompressing from tgz archive '{:?}' to `{:?}`", source.as_ref(), path.as_ref()); + + let dat = std::fs::File::open(source)?; + let tar = GzDecoder::new(dat); + let mut tgz = Archive::new(tar); + + tgz.unpack(path)?; + }, + PkgFmt::Txz => { + // Extract to install dir + debug!("Decompressing from txz archive '{:?}' to `{:?}`", source.as_ref(), path.as_ref()); + + let dat = std::fs::File::open(source)?; + let tar = XzDecoder::new(dat); + let mut txz = Archive::new(tar); + + txz.unpack(path)?; + }, + PkgFmt::Zip => { + // Extract to install dir + debug!("Decompressing from zip archive '{:?}' to `{:?}`", source.as_ref(), path.as_ref()); + + let dat = std::fs::File::open(source)?; + let mut zip = ZipArchive::new(dat)?; + + zip.extract(path)?; + }, + PkgFmt::Bin => { + debug!("Copying binary '{:?}' to `{:?}`", source.as_ref(), path.as_ref()); + // Copy to install dir + std::fs::copy(source, path)?; + }, + }; + + Ok(()) +} + +/// Fetch install path from environment +/// roughly follows https://doc.rust-lang.org/cargo/commands/cargo-install.html#description +pub fn get_install_path>(install_path: Option

) -> Option { + // Command line override first first + if let Some(p) = install_path { + return Some(PathBuf::from(p.as_ref())) + } + + // Environmental variables + if let Ok(p) = std::env::var("CARGO_INSTALL_ROOT") { + debug!("using CARGO_INSTALL_ROOT ({})", p); + let b = PathBuf::from(p); + return Some(b.join("bin")); + } + if let Ok(p) = std::env::var("CARGO_HOME") { + debug!("using CARGO_HOME ({})", p); + let b = PathBuf::from(p); + return Some(b.join("bin")); + } + + // Standard $HOME/.cargo/bin + if let Some(d) = dirs::home_dir() { + let d = d.join(".cargo/bin"); + if d.exists() { + debug!("using $HOME/.cargo/bin"); + + return Some(d); + } + } + + // Local executable dir if no cargo is found + if let Some(d) = dirs::executable_dir() { + debug!("Fallback to {}", d.display()); + return Some(d.into()); + } + + None +} + +pub fn confirm() -> Result { + info!("Do you wish to continue? yes/no"); + + let mut input = String::new(); + std::io::stdin().read_line(&mut input)?; + + match input.as_str().trim() { + "yes" => Ok(true), + "no" => Ok(false), + _ => { + Err(anyhow::anyhow!("Valid options are 'yes', 'no', please try again")) + } + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 00000000..682e60c5 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,211 @@ + +use std::collections::HashMap; + +use serde::{Serialize, Deserialize}; +use strum_macros::{Display, EnumString, EnumVariantNames}; +use tinytemplate::TinyTemplate; + + +pub mod helpers; +pub use helpers::*; + +pub mod drivers; +pub use drivers::*; + + +/// Compiled target triple, used as default for binary fetching +pub const TARGET: &'static str = env!("TARGET"); + +/// Default package path template (may be overridden in package Cargo.toml) +pub const DEFAULT_PKG_URL: &'static str = "{ repo }/releases/download/v{ version }/{ name }-{ target }-v{ version }.{ format }"; + +/// Default binary name template (may be overridden in package Cargo.toml) +pub const DEFAULT_BIN_PATH: &'static str = "{ name }-{ target }-v{ version }/{ bin }{ format }"; + + +/// Binary format enumeration +#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)] +#[derive(Display, EnumString, EnumVariantNames)] +#[strum(serialize_all = "snake_case")] +#[serde(rename_all = "snake_case")] +pub enum PkgFmt { + /// Download format is TAR (uncompressed) + Tar, + /// Download format is TGZ (TAR + GZip) + Tgz, + /// Download format is TAR + XZ + Txz, + /// Download format is Zip + Zip, + /// Download format is raw / binary + Bin, +} + +impl Default for PkgFmt { + fn default() -> Self { + Self::Tgz + } +} + +/// `binstall` metadata container +/// +/// Required to nest metadata under `package.metadata.binstall` +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(rename_all = "kebab-case")] +pub struct Meta { + pub binstall: Option, +} + +/// Metadata for binary installation use. +/// +/// Exposed via `[package.metadata]` in `Cargo.toml` +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "kebab-case", default)] +pub struct PkgMeta { + /// URL template for package downloads + pub pkg_url: String, + + /// Format for package downloads + pub pkg_fmt: PkgFmt, + + /// Path template for binary files in packages + pub bin_dir: String, + + /// Public key for package verification (base64 encoded) + pub pub_key: Option, + + /// Target specific overrides + pub overrides: HashMap, +} + +impl Default for PkgMeta { + fn default() -> Self { + Self { + pkg_url: DEFAULT_PKG_URL.to_string(), + pkg_fmt: PkgFmt::default(), + bin_dir: DEFAULT_BIN_PATH.to_string(), + pub_key: None, + overrides: HashMap::new(), + } + } +} + +impl PkgMeta { + /// Merge configuration overrides into object + pub fn merge(&mut self, pkg_override: &PkgOverride) { + if let Some(o) = &pkg_override.pkg_url { + self.pkg_url = o.clone(); + } + if let Some(o) = &pkg_override.pkg_fmt { + self.pkg_fmt = *o; + } + if let Some(o) = &pkg_override.bin_dir { + self.bin_dir = o.clone(); + } + } +} + +/// Target specific overrides for binary installation +/// +/// Exposed via `[package.metadata.TARGET]` in `Cargo.toml` +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "kebab-case", default)] +pub struct PkgOverride { + /// URL template override for package downloads + pub pkg_url: Option, + + /// Format override for package downloads + pub pkg_fmt: Option, + + /// Path template override for binary files in packages + pub bin_dir: Option, +} + +impl Default for PkgOverride { + fn default() -> Self { + Self { + pkg_url: None, + pkg_fmt: None, + bin_dir: None, + } + } +} + + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "kebab-case")] +pub struct BinMeta { + /// Binary name + pub name: String, + /// Binary template path (within package) + pub path: String, +} + +/// Template for constructing download paths +#[derive(Clone, Debug, Serialize)] +pub struct Context { + pub name: String, + pub repo: Option, + pub target: String, + pub version: String, + pub format: String, + pub bin: Option, +} + +impl Context { + /// Render the context into the provided template + pub fn render(&self, template: &str) -> Result { + // Create template instance + let mut tt = TinyTemplate::new(); + + // Add template to instance + tt.add_template("path", &template)?; + + // Render output + let rendered = tt.render("path", self)?; + + Ok(rendered) + } +} + +#[cfg(test)] +mod test { + use crate::{load_manifest_path}; + + use cargo_toml::Product; + + fn init() { + let _ = env_logger::builder().is_test(true).try_init(); + } + + #[test] + fn parse_meta() { + init(); + + let mut manifest_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap(); + manifest_dir.push_str("/Cargo.toml"); + + let manifest = load_manifest_path(&manifest_dir).expect("Error parsing metadata"); + let package = manifest.package.unwrap(); + let meta = package.metadata.map(|m| m.binstall ).flatten().unwrap(); + + assert_eq!(&package.name, "cargo-binstall"); + + assert_eq!( + &meta.pkg_url, + "{ repo }/releases/download/v{ version }/{ name }-{ target }.tgz" + ); + + assert_eq!( + manifest.bin.as_slice(), + &[ + Product{ + name: Some("cargo-binstall".to_string()), + path: Some("src/main.rs".to_string()), + edition: Some(cargo_toml::Edition::E2018), + ..Default::default() + }, + ], + ); + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 00000000..e232eee6 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,260 @@ +use std::path::{PathBuf}; + +use log::{debug, info, warn, error, LevelFilter}; +use simplelog::{TermLogger, ConfigBuilder, TerminalMode}; + +use structopt::StructOpt; + +use tempdir::TempDir; + +use cargo_binstall::*; + + +#[derive(Debug, StructOpt)] +struct Options { + /// Package name or URL for installation + /// This must be either a crates.io package name or github or gitlab url + #[structopt()] + name: String, + + /// Filter for package version to install + #[structopt(long, default_value = "*")] + version: String, + + /// Override binary target, ignoring compiled version + #[structopt(long, default_value = TARGET)] + target: String, + + /// Override install path for downloaded binary. + /// Defaults to `$HOME/.cargo/bin` + #[structopt(long)] + install_path: Option, + + /// Disable symlinking / versioned updates + #[structopt(long)] + no_symlinks: bool, + + /// Dry run, fetch and show changes without installing binaries + #[structopt(long)] + dry_run: bool, + + /// Disable interactive mode / confirmation + #[structopt(long)] + no_confirm: bool, + + /// Do not cleanup temporary files on success + #[structopt(long)] + no_cleanup: bool, + + /// Override manifest source. + /// This skips searching crates.io for a manifest and uses + /// the specified path directly, useful for debugging and + /// when adding `binstall` support. + #[structopt(long)] + manifest_path: Option, + + /// Utility log level + #[structopt(long, default_value = "info")] + log_level: LevelFilter, +} + + +#[tokio::main] +async fn main() -> Result<(), anyhow::Error> { + + // Filter extraneous arg when invoked by cargo + // `cargo run -- --help` gives ["target/debug/cargo-binstall", "--help"] + // `cargo binstall --help` gives ["/home/ryan/.cargo/bin/cargo-binstall", "binstall", "--help"] + let mut args: Vec = std::env::args().collect(); + if args.len() > 1 && args[1] == "binstall" { + args.remove(1); + } + + // Load options + let opts = Options::from_iter(args.iter()); + + // Setup logging + let mut log_config = ConfigBuilder::new(); + log_config.add_filter_ignore("hyper".to_string()); + log_config.add_filter_ignore("reqwest".to_string()); + log_config.set_location_level(LevelFilter::Off); + TermLogger::init(opts.log_level, log_config.build(), TerminalMode::Mixed).unwrap(); + + // Create a temporary directory for downloads etc. + let temp_dir = TempDir::new("cargo-binstall")?; + + info!("Installing package: '{}'", opts.name); + + // Fetch crate via crates.io, git, or use a local manifest path + // TODO: work out which of these to do based on `opts.name` + // TODO: support git-based fetches (whole repo name rather than just crate name) + let manifest_path = match opts.manifest_path.clone() { + Some(p) => p, + None => fetch_crate_cratesio(&opts.name, &opts.version, temp_dir.path()).await?, + }; + + debug!("Reading manifest: {}", manifest_path.display()); + let manifest = load_manifest_path(manifest_path.join("Cargo.toml"))?; + let package = manifest.package.unwrap(); + + let (mut meta, binaries) = ( + package.metadata.map(|m| m.binstall ).flatten().unwrap_or(PkgMeta::default()), + manifest.bin, + ); + + // Merge any overrides + if let Some(o) = meta.overrides.remove(&opts.target) { + meta.merge(&o); + } + + debug!("Found metadata: {:?}", meta); + + // Generate context for URL interpolation + let ctx = Context { + name: opts.name.clone(), + repo: package.repository, + target: opts.target.clone(), + version: package.version.clone(), + format: meta.pkg_fmt.to_string(), + bin: None, + }; + + debug!("Using context: {:?}", ctx); + + // Interpolate version / target / etc. + let rendered = ctx.render(&meta.pkg_url)?; + + // Compute install directory + let install_path = match get_install_path(opts.install_path.as_deref()) { + Some(p) => p, + None => { + error!("No viable install path found of specified, try `--install-path`"); + return Err(anyhow::anyhow!("No install path found or specified")); + } + }; + + debug!("Using install path: {}", install_path.display()); + + info!("Downloading package from: '{}'", rendered); + + // Download package + let pkg_path = temp_dir.path().join(format!("pkg-{}.{}", opts.name, meta.pkg_fmt)); + download(&rendered, pkg_path.to_str().unwrap()).await?; + + #[cfg(incomplete)] + { + // Fetch and check package signature if available + if let Some(pub_key) = meta.as_ref().map(|m| m.pub_key.clone() ).flatten() { + debug!("Found public key: {}", pub_key); + + // Generate signature file URL + let mut sig_ctx = ctx.clone(); + sig_ctx.format = "sig".to_string(); + let sig_url = sig_ctx.render(&pkg_url)?; + + debug!("Fetching signature file: {}", sig_url); + + // Download signature file + let sig_path = temp_dir.path().join(format!("{}.sig", pkg_name)); + download(&sig_url, &sig_path).await?; + + // TODO: do the signature check + unimplemented!() + + } else { + warn!("No public key found, package signature could not be validated"); + } + } + + // Extract files + let bin_path = temp_dir.path().join(format!("bin-{}", opts.name)); + extract(&pkg_path, meta.pkg_fmt, &bin_path)?; + + // Bypass cleanup if disabled + if opts.no_cleanup { + let _ = temp_dir.into_path(); + } + + if binaries.len() == 0 { + error!("No binaries specified (or inferred from file system)"); + return Err(anyhow::anyhow!("No binaries specified (or inferred from file system)")); + } + + // List files to be installed + // based on those found via Cargo.toml + let bin_files = binaries.iter().map(|p| { + // Fetch binary base name + let base_name = p.name.clone().unwrap(); + + // Generate binary path via interpolation + let mut bin_ctx = ctx.clone(); + bin_ctx.bin = Some(base_name.clone()); + + // Append .exe to windows binaries + bin_ctx.format = match &opts.target.clone().contains("windows") { + true => ".exe".to_string(), + false => "".to_string(), + }; + + // Generate install paths + // Source path is the download dir + the generated binary path + let source_file_path = bin_ctx.render(&meta.bin_dir)?; + let source = bin_path.join(&source_file_path); + + // Destination path is the install dir + base-name-version{.format} + let dest_file_path = bin_ctx.render("{ bin }-v{ version }{ format }")?; + let dest = install_path.join(dest_file_path); + + // Link at install dir + base name + let link = install_path.join(&base_name); + + Ok((base_name, source, dest, link)) + }).collect::, anyhow::Error>>()?; + + // Prompt user for confirmation + info!("This will install the following binaries:"); + for (name, source, dest, _link) in &bin_files { + info!(" - {} ({} -> {})", name, source.file_name().unwrap().to_string_lossy(), dest.display()); + } + + if !opts.no_symlinks { + info!("And create (or update) the following symlinks:"); + for (name, _source, dest, link) in &bin_files { + info!(" - {} ({} -> {})", name, dest.display(), link.display()); + } + } + + if !opts.no_confirm && !confirm()? { + warn!("Installation cancelled"); + return Ok(()) + } + + info!("Installing binaries..."); + + // Install binaries + for (_name, source, dest, _link) in &bin_files { + // TODO: check if file already exists + std::fs::copy(source, dest)?; + } + + // Generate symlinks + if !opts.no_symlinks { + for (_name, _source, dest, link) in &bin_files { + // Remove existing symlink + // TODO: check if existing symlink is correct + if link.exists() { + std::fs::remove_file(&link)?; + } + + #[cfg(target_family = "unix")] + std::os::unix::fs::symlink(dest, link)?; + #[cfg(target_family = "windows")] + std::os::windows::fs::symlink_file(dest, link)?; + } + } + + info!("Installation complete!"); + + Ok(()) +} + diff --git a/zigbuild-requirements.txt b/zigbuild-requirements.txt deleted file mode 100644 index 41ba0c5e..00000000 --- a/zigbuild-requirements.txt +++ /dev/null @@ -1,5 +0,0 @@ -###### Requirements without Version Specifiers ###### -cargo-zigbuild - -###### Requirements with Version Specifiers ###### -ziglang < 0.11 # zig 0.11 causes link failure in our CI