diff --git a/.claude/hooks/sonar-secrets/build-scripts/pretool-secrets.sh b/.claude/hooks/sonar-secrets/build-scripts/pretool-secrets.sh new file mode 100755 index 000000000..24f537c2d --- /dev/null +++ b/.claude/hooks/sonar-secrets/build-scripts/pretool-secrets.sh @@ -0,0 +1,5 @@ +#!/bin/bash +if ! command -v sonar &> /dev/null; then + exit 0 +fi +sonar hook claude-pre-tool-use diff --git a/.claude/hooks/sonar-secrets/build-scripts/prompt-secrets.sh b/.claude/hooks/sonar-secrets/build-scripts/prompt-secrets.sh new file mode 100755 index 000000000..2bb89ce29 --- /dev/null +++ b/.claude/hooks/sonar-secrets/build-scripts/prompt-secrets.sh @@ -0,0 +1,5 @@ +#!/bin/bash +if ! command -v sonar &> /dev/null; then + exit 0 +fi +sonar hook claude-prompt-submit diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 000000000..1034f511b --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,40 @@ +{ + "hooks": { + "PostToolUse": [ + { + "hooks": [ + { + "command": "\"${CLAUDE_PROJECT_DIR}/scripts/agent-quality-hook.sh\"", + "timeout": 60, + "type": "command" + } + ], + "matcher": "Edit|Write|MultiEdit" + } + ], + "PreToolUse": [ + { + "hooks": [ + { + "command": "\"${CLAUDE_PROJECT_DIR}/.claude/hooks/sonar-secrets/build-scripts/pretool-secrets.sh\"", + "timeout": 60, + "type": "command" + } + ], + "matcher": "Read" + } + ], + "UserPromptSubmit": [ + { + "hooks": [ + { + "command": "\"${CLAUDE_PROJECT_DIR}/.claude/hooks/sonar-secrets/build-scripts/prompt-secrets.sh\"", + "timeout": 60, + "type": "command" + } + ], + "matcher": "*" + } + ] + } +} diff --git a/.codex/config.toml b/.codex/config.toml new file mode 100644 index 000000000..8c9547ab6 --- /dev/null +++ b/.codex/config.toml @@ -0,0 +1,3 @@ +[mcp_servers.sonarqube] +command = "sonar" +args = [ "run", "mcp", "--project", "Heretek-Games_drop" ] diff --git a/.codex/hooks.json b/.codex/hooks.json new file mode 100644 index 000000000..430c831f4 --- /dev/null +++ b/.codex/hooks.json @@ -0,0 +1,16 @@ +{ + "hooks": { + "UserPromptSubmit": [ + { + "hooks": [ + { + "command": "'.codex/hooks/sonar-secrets/build-scripts/prompt-secrets.sh'", + "timeout": 60, + "type": "command" + } + ], + "matcher": "*" + } + ] + } +} diff --git a/.codex/hooks/sonar-secrets/build-scripts/prompt-secrets.sh b/.codex/hooks/sonar-secrets/build-scripts/prompt-secrets.sh new file mode 100755 index 000000000..50516a199 --- /dev/null +++ b/.codex/hooks/sonar-secrets/build-scripts/prompt-secrets.sh @@ -0,0 +1,5 @@ +#!/bin/bash +if ! command -v sonar &> /dev/null; then + exit 0 +fi +sonar hook codex-prompt-submit diff --git a/.cursor/hooks.json b/.cursor/hooks.json new file mode 100644 index 000000000..d1d62f983 --- /dev/null +++ b/.cursor/hooks.json @@ -0,0 +1,29 @@ +{ + "hooks": { + "beforeReadFile": [ + { + "command": "'.cursor/hooks/sonar-secrets/build-scripts/before-read-file-secrets.sh'", + "failClosed": false, + "matcher": "Read|TabRead", + "timeout": 60 + } + ], + "beforeSubmitPrompt": [ + { + "command": "'.cursor/hooks/sonar-secrets/build-scripts/prompt-secrets.sh'", + "failClosed": false, + "matcher": "UserPromptSubmit", + "timeout": 60 + } + ], + "preToolUse": [ + { + "command": "'.cursor/hooks/sonar-secrets/build-scripts/pre-tool-use-secrets.sh'", + "failClosed": false, + "matcher": "Read", + "timeout": 60 + } + ] + }, + "version": 1 +} diff --git a/.cursor/hooks/sonar-secrets/build-scripts/before-read-file-secrets.sh b/.cursor/hooks/sonar-secrets/build-scripts/before-read-file-secrets.sh new file mode 100755 index 000000000..5d5f0fd65 --- /dev/null +++ b/.cursor/hooks/sonar-secrets/build-scripts/before-read-file-secrets.sh @@ -0,0 +1,5 @@ +#!/bin/bash +if ! command -v sonar &> /dev/null; then + exit 0 +fi +sonar hook cursor-pre-file-read diff --git a/.cursor/hooks/sonar-secrets/build-scripts/pre-tool-use-secrets.sh b/.cursor/hooks/sonar-secrets/build-scripts/pre-tool-use-secrets.sh new file mode 100755 index 000000000..5eae39313 --- /dev/null +++ b/.cursor/hooks/sonar-secrets/build-scripts/pre-tool-use-secrets.sh @@ -0,0 +1,5 @@ +#!/bin/bash +if ! command -v sonar &> /dev/null; then + exit 0 +fi +sonar hook cursor-pre-tool-use diff --git a/.cursor/hooks/sonar-secrets/build-scripts/prompt-secrets.sh b/.cursor/hooks/sonar-secrets/build-scripts/prompt-secrets.sh new file mode 100755 index 000000000..21876194e --- /dev/null +++ b/.cursor/hooks/sonar-secrets/build-scripts/prompt-secrets.sh @@ -0,0 +1,5 @@ +#!/bin/bash +if ! command -v sonar &> /dev/null; then + exit 0 +fi +sonar hook cursor-prompt-submit diff --git a/.cursor/mcp.json b/.cursor/mcp.json new file mode 100644 index 000000000..fd6f45b1f --- /dev/null +++ b/.cursor/mcp.json @@ -0,0 +1,8 @@ +{ + "mcpServers": { + "sonarqube": { + "args": ["run", "mcp", "--project", "Heretek-Games_drop"], + "command": "sonar" + } + } +} diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 000000000..402757901 --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,2 @@ +# Repository-wide Prettier formatting pass +ab351bf800feba2e5658c9758fe3f5682a501497 diff --git a/.github/workflows/analysis.yml b/.github/workflows/analysis.yml new file mode 100644 index 000000000..cfe54f680 --- /dev/null +++ b/.github/workflows/analysis.yml @@ -0,0 +1,228 @@ +name: Static analysis + +on: + push: + branches: [develop] + pull_request: + branches: [develop] + schedule: + - cron: "17 5 * * 1" # weekly Monday 05:17 UTC + workflow_dispatch: + +permissions: + contents: read + +jobs: + actionlint: + name: actionlint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # actions/checkout@v7 + with: + persist-credentials: false + - name: Lint workflows + run: docker run --rm -v "$PWD:/mnt:ro" -w /mnt rhysd/actionlint:1.7.12 -color + + zizmor: + name: zizmor (Actions security) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # actions/checkout@v7 + with: + persist-credentials: false + - name: Install zizmor + run: pipx install zizmor==1.30.1 + - name: Audit workflows + run: zizmor --format plain .github/workflows + + shellcheck: + name: shellcheck + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # actions/checkout@v7 + with: + persist-credentials: false + - name: Shellcheck scripts + run: | + mapfile -t files < <(git ls-files '*.sh') + if [ "${#files[@]}" -eq 0 ]; then + echo "No shell scripts found." + exit 0 + fi + shellcheck "${files[@]}" + + hadolint: + name: hadolint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # actions/checkout@v7 + with: + persist-credentials: false + - name: Lint Dockerfile + run: docker run --rm -i hadolint/hadolint:v2.15.1 < Dockerfile + + pnpm-audit: + name: pnpm audit + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # actions/checkout@v7 + with: + persist-credentials: false + - uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # pnpm/action-setup@v6 + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # actions/setup-node@v7 + with: + node-version: lts/* + cache: "pnpm" + - name: Install dependencies + run: pnpm install --ignore-scripts + - name: Audit + run: pnpm audit --audit-level high + + govulncheck: + name: govulncheck + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # actions/checkout@v7 + with: + persist-credentials: false + - uses: golang/govulncheck-action@032d45514ae346b1db93c04b0c90b841c370344f # v1.1.0 + with: + repo-checkout: false + go-version-file: backend/go.work + work-dir: backend + go-package: ./core/... + cache: false + + cargo-machete: + name: cargo-machete (unused Rust deps) + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + crate: + - cli + - torrential + - desktop/src-tauri + - libraries/droplet + - libraries/droplet_types + - libraries/libarchive + - libraries/native_model + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # actions/checkout@v7 + with: + persist-credentials: false + - uses: dtolnay/rust-toolchain@7c8d7d138f5c09cef361f8214cf96882cd029cdb # dtolnay/rust-toolchain@nightly + - uses: taiki-e/install-action@4b8992af23a2be34fd58f8d9b50d3f4939ad1891 # taiki-e/install-action@cargo-machete + with: + tool: cargo-machete + - name: Find unused dependencies + working-directory: ${{ matrix.crate }} + run: cargo machete + + desktop: + name: desktop frontend (typecheck + knip) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # actions/checkout@v7 + with: + persist-credentials: false + - uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # pnpm/action-setup@v6 + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # actions/setup-node@v7 + with: + node-version: lts/* + cache: "pnpm" + - name: Install root dependencies + run: pnpm install --ignore-scripts + - name: Install desktop/main dependencies + run: pnpm -C desktop/main install --ignore-scripts + - name: Typecheck desktop/main + run: pnpm -C desktop/main run typecheck + - name: Knip report (desktop/main) + if: always() + run: pnpm exec knip --directory desktop/main --reporter json --no-exit-code > desktop-knip-report.json + - name: Upload knip report + if: always() + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # actions/upload-artifact@v7 + with: + name: desktop-knip-report + path: desktop-knip-report.json + + frontend-lint: + name: frontend lint (${{ matrix.target }}) + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + target: + - desktop/main + - libraries/base + - sites/promo + - sites/docs + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # actions/checkout@v7 + with: + persist-credentials: false + - uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # pnpm/action-setup@v6 + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # actions/setup-node@v7 + with: + node-version: lts/* + cache: "pnpm" + - name: Install root dependencies + run: pnpm install --ignore-scripts + - name: Install desktop/main dependencies + if: matrix.target == 'desktop/main' + run: pnpm -C desktop/main install --ignore-scripts + - name: Lint + run: pnpm -C ${{ matrix.target }} run lint + + commitlint: + name: commitlint + if: github.event_name == 'pull_request' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # actions/checkout@v7 + with: + fetch-depth: 0 + persist-credentials: false + - uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # pnpm/action-setup@v6 + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # actions/setup-node@v7 + with: + node-version: lts/* + cache: "pnpm" + - name: Install dependencies + run: pnpm install --ignore-scripts + - name: Lint commit messages + env: + BASE_REF: ${{ github.base_ref }} + run: >- + pnpm exec commitlint + --from "origin/$BASE_REF" + --to HEAD + + ruff: + name: ruff (Python dev tools) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # actions/checkout@v7 + with: + persist-credentials: false + - name: Install ruff + run: pipx install ruff==0.16.7 + - name: Check + working-directory: server + run: | + ruff check dev-tools + ruff format --check dev-tools + + typos: + name: typos + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # actions/checkout@v7 + with: + persist-credentials: false + - uses: taiki-e/install-action@4b8992af23a2be34fd58f8d9b50d3f4939ad1891 # taiki-e/install-action@typos + with: + tool: typos@1.50.1 + - name: Check spelling + run: typos diff --git a/.github/workflows/client-alpha.yml b/.github/workflows/client-alpha.yml new file mode 100644 index 000000000..a6d2b9041 --- /dev/null +++ b/.github/workflows/client-alpha.yml @@ -0,0 +1,233 @@ +name: "Build and publish desktop (Alpha)" + +on: + push: + branches: + - develop + paths: + - "desktop/**" + - "distribution/debian/**" + - "distribution/flatpak/**" + - "distribution/rpm/**" + - "scripts/distro/**" + - ".github/workflows/client-alpha.yml" + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +# Least privilege by default; jobs that publish override it. +permissions: + contents: read + +jobs: + build-and-publish-alpha: + name: Build and publish Alpha .deb + runs-on: ubuntu-22.04 + permissions: + contents: write + outputs: + alpha_version: ${{ steps.version.outputs.alpha_version }} + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # actions/checkout@v7 + with: + token: ${{ secrets.GITHUB_TOKEN }} + fetch-depth: 0 + persist-credentials: false + + - name: setup pnpm + uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # pnpm/action-setup@v6 + with: + run_install: false + + - name: setup node + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # actions/setup-node@v7 + with: + node-version: lts/* + cache: pnpm + + - name: install Rust nightly + uses: dtolnay/rust-toolchain@7c8d7d138f5c09cef361f8214cf96882cd029cdb # dtolnay/rust-toolchain@nightly + + - name: Rust cache + uses: swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # swatinem/rust-cache@v2 + with: + workspaces: "./desktop/src-tauri -> target" + + - name: install dependencies + run: | + sudo apt-get update + sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf xdg-utils + + - name: compute alpha version + id: version + run: | + BASE_VERSION=$(jq -r .version desktop/src-tauri/tauri.conf.json) + SHORT_SHA=$(echo "${{ github.sha }}" | cut -c1-7) + ALPHA_VERSION="${BASE_VERSION}-alpha.${{ github.run_number }}.${SHORT_SHA}" + echo "alpha_version=${ALPHA_VERSION}" >> "$GITHUB_OUTPUT" + echo "Building Alpha version: ${ALPHA_VERSION}" + + # Update version in tauri.conf.json so the binary and deb are stamped with the alpha version + jq --arg v "$ALPHA_VERSION" '.version = $v' desktop/src-tauri/tauri.conf.json > desktop/src-tauri/tauri.conf.json.tmp + mv desktop/src-tauri/tauri.conf.json.tmp desktop/src-tauri/tauri.conf.json + + - name: install frontend dependencies + run: pnpm install + + - name: build Tauri debian bundle + run: | + pnpm --filter drop-app exec tauri build --bundles deb + + - name: stage deb asset + id: deb + env: + ALPHA_VERSION: ${{ steps.version.outputs.alpha_version }} + run: | + DEB_PATH=$(find desktop/src-tauri/target/release/bundle/deb -name "*.deb" | head -n1) + [ -f "$DEB_PATH" ] || { echo "Debian package not found" >&2; exit 1; } + TARGET_NAME="Drop.Desktop.Client_${ALPHA_VERSION}_amd64.deb" + cp "$DEB_PATH" "$TARGET_NAME" + echo "deb_path=${TARGET_NAME}" >> "$GITHUB_OUTPUT" + echo "deb_name=${TARGET_NAME}" >> "$GITHUB_OUTPUT" + ls -la "$TARGET_NAME" + + - name: upload alpha deb artifact + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # actions/upload-artifact@v7 + with: + name: drop-alpha-deb + path: ${{ steps.deb.outputs.deb_path }} + if-no-files-found: error + retention-days: 7 + + - name: publish to rolling 'alpha' GitHub release + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + ALPHA_VERSION: ${{ steps.version.outputs.alpha_version }} + DEB_PATH: ${{ steps.deb.outputs.deb_path }} + run: | + tag="alpha" + title="Drop Alpha (${ALPHA_VERSION})" + notes="Automated alpha build from commit ${{ github.sha }} on develop." + + if ! gh release view "$tag" >/dev/null 2>&1; then + echo "Creating new release $tag" + gh release create "$tag" "$DEB_PATH" \ + --title "$title" --notes "$notes" --prerelease --target "${{ github.sha }}" + else + echo "Updating existing release $tag" + gh release edit "$tag" --title "$title" --notes "$notes" --prerelease --target "${{ github.sha }}" + gh release upload "$tag" "$DEB_PATH" --clobber + fi + + - name: Trigger FlatPark Alpha Release Webhook + uses: flatpark/publish-action@b67fca1b80b095541bc011967e0a35292bed14d8 # v1 + continue-on-error: true # Non-blocking until PR #344 is merged on FlatPark + with: + app-id: org.droposs.client.Alpha + tag: alpha + + ppa-alpha: + name: Publish Alpha to Ubuntu PPA + needs: [build-and-publish-alpha] + runs-on: ubuntu-22.04 + permissions: + contents: read + env: + PPA_ALPHA: ${{ vars.PPA_ALPHA }} + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # actions/checkout@v7 + with: + persist-credentials: false + + - name: download alpha deb + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # actions/download-artifact@v8 + with: + name: drop-alpha-deb + path: dist/deb + + - name: prepare PPA source + id: ppa + env: + ALPHA_VERSION: ${{ needs.build-and-publish-alpha.outputs.alpha_version }} + run: | + set -euo pipefail + deb="$(find dist/deb -maxdepth 1 -name '*_amd64.deb' -print -quit)" + [ -n "$deb" ] || { echo "no alpha deb artifact found" >&2; exit 1; } + scripts/distro/prepare-ppa-source.sh \ + --channel alpha \ + --version "$ALPHA_VERSION" \ + --deb "$deb" \ + --outdir dist/ppa >> "$GITHUB_OUTPUT" + + - name: publish to Launchpad PPA + uses: yuezk/publish-ppa-package@3e4da8d50b48950e8a84cd4302f031a17b5bfd27 # v2.0.1 + with: + repository: ${{ env.PPA_ALPHA }} + series: "jammy noble" + revision: "0" + include_orig: auto + debmake_arguments: "-y" + new_version_template: "{VERSION}-{REVISION}~ubuntu{SERIES_VERSION}.1" + tarball: ${{ steps.ppa.outputs.tarball }} + debian_dir: ${{ steps.ppa.outputs.debian_dir }} + deb_email: contact@heretek.games + deb_fullname: Heretek Games + gpg_private_key: ${{ secrets.PPA_GPG_PRIVATE_KEY }} + gpg_passphrase: ${{ secrets.PPA_GPG_PASSPHRASE }} + + copr-alpha: + name: Publish Alpha to Fedora COPR + needs: [build-and-publish-alpha] + runs-on: ubuntu-22.04 + permissions: + contents: read + env: + COPR_ALPHA: ${{ vars.COPR_ALPHA }} + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # actions/checkout@v7 + with: + persist-credentials: false + + - name: download alpha deb + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # actions/download-artifact@v8 + with: + name: drop-alpha-deb + path: dist/deb + + - name: extract client binary + run: | + set -euo pipefail + deb="$(find dist/deb -maxdepth 1 -name '*_amd64.deb' -print -quit)" + [ -n "$deb" ] || { echo "no alpha deb artifact found" >&2; exit 1; } + mkdir -p dist/bin + dpkg-deb --fsys-tarfile "$deb" | tar -xO --wildcards '*usr/bin/drop-app' > dist/bin/drop-app + chmod +x dist/bin/drop-app + + - name: build SRPM and submit to COPR + env: + COPR_CONFIG_B64: ${{ secrets.COPR_API_TOKEN_CONFIG }} + ALPHA_VERSION: ${{ needs.build-and-publish-alpha.outputs.alpha_version }} + run: | + set -euo pipefail + : "${COPR_ALPHA:?COPR_ALPHA repository variable is not set}" + : "${COPR_CONFIG_B64:?COPR_API_TOKEN_CONFIG secret is not set}" + docker run --rm \ + -v "$PWD:/work" -w /work \ + -e ALPHA_VERSION \ + -e COPR_ALPHA \ + -e COPR_CONFIG_B64 \ + fedora:41 bash -euo pipefail -c ' + dnf install -y --setopt=install_weak_deps=False rpm-build copr-cli tar gzip findutils >/dev/null + mkdir -p /root/.config + printf "%s" "$COPR_CONFIG_B64" | base64 -d > /root/.config/copr + chmod 600 /root/.config/copr + scripts/distro/build-rpm-srpm.sh \ + --channel alpha \ + --version "$ALPHA_VERSION" \ + --binary /work/dist/bin/drop-app \ + --outdir /work/dist/rpm + srpm="$(find /work/dist/rpm -maxdepth 1 -name "*.src.rpm" -print -quit)" + copr-cli build "$COPR_ALPHA" "$srpm" + ' diff --git a/.github/workflows/client-release.yml b/.github/workflows/client-release.yml index 6dc41a2db..6d4ee1bf8 100644 --- a/.github/workflows/client-release.yml +++ b/.github/workflows/client-release.yml @@ -15,10 +15,18 @@ on: # This workflow will trigger on each push to the `release` branch to create or update a GitHub release, build your app, and upload the artifacts to the release. +# Least privilege for every job; publish-tauri overrides with contents: write. +permissions: + contents: read + jobs: publish-tauri: permissions: contents: write + env: + # Read at job scope so the macOS steps can gate on presence without + # referencing `secrets` in a step-level `if` (not an allowed context). + APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} strategy: fail-fast: false matrix: @@ -36,32 +44,28 @@ jobs: runs-on: ${{ matrix.platform }} steps: - - uses: actions/checkout@v7 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # actions/checkout@v7 with: token: ${{ secrets.GITHUB_TOKEN }} + persist-credentials: false - name: setup pnpm - uses: pnpm/action-setup@v6 + uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # pnpm/action-setup@v6 with: run_install: false - name: setup node - uses: actions/setup-node@v7 + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # actions/setup-node@v7 with: node-version: lts/* - cache: pnpm + package-manager-cache: false - name: install Rust nightly - uses: dtolnay/rust-toolchain@nightly + uses: dtolnay/rust-toolchain@7c8d7d138f5c09cef361f8214cf96882cd029cdb # dtolnay/rust-toolchain@nightly with: # Those targets are only used on macos runners so it's in an `if` to slightly speed up windows and linux builds. targets: ${{ matrix.platform == 'macos-14' && 'aarch64-apple-darwin,x86_64-apple-darwin' || '' }} - - name: Rust cache - uses: swatinem/rust-cache@v2 - with: - workspaces: "./desktop/src-tauri -> target" - - name: install dependencies (ubuntu only) if: matrix.platform == 'ubuntu-22.04' || matrix.platform == 'ubuntu-22.04-arm' # This must match the platform value defined above. run: | @@ -70,13 +74,12 @@ jobs: # webkitgtk 4.0 is for Tauri v1 - webkitgtk 4.1 is for Tauri v2. - name: Import Apple Developer Certificate - if: matrix.platform == 'macos-14' + if: matrix.platform == 'macos-14' && env.APPLE_CERTIFICATE != '' env: - APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }} run: | - echo $APPLE_CERTIFICATE | base64 --decode > certificate.p12 + echo "$APPLE_CERTIFICATE" | base64 --decode > certificate.p12 security create-keychain -p "$KEYCHAIN_PASSWORD" build.keychain security default-keychain -s build.keychain security unlock-keychain -p "$KEYCHAIN_PASSWORD" build.keychain @@ -85,11 +88,16 @@ jobs: # Add build.keychain to the user keychain search list so that codesign # (invoked later by tauri-action WITHOUT an explicit --keychain) can # resolve the signing identity from it. + # shellcheck disable=SC2046 security list-keychains -d user -s build.keychain $(security list-keychains -d user | tr -d '"') echo "Created keychain" - curl https://droposs.org/drop.der --output drop.der + # SECURITY: this fetches a code-signing trust anchor from a third-party + # domain at build time. Vendor this certificate into the repo (or pin + # its SHA-256) so a compromise of droposs.org cannot inject a trusted + # root into the signing keychain. + curl --fail --silent --show-error https://droposs.org/drop.der --output drop.der # swiftc libs/appletrust/add-certificate.swift # ./add-certificate drop.der @@ -108,17 +116,17 @@ jobs: security find-identity -v -p codesigning build.keychain - name: Verify Certificate - if: matrix.platform == 'macos-14' + if: matrix.platform == 'macos-14' && env.APPLE_CERTIFICATE != '' run: | CERT_INFO=$(security find-identity -v -p codesigning build.keychain | grep "Drop OSS") CERT_ID=$(echo "$CERT_INFO" | awk -F'"' '{print $2}') - echo "CERT_ID=$CERT_ID" >> $GITHUB_ENV + echo "CERT_ID=$CERT_ID" >> "$GITHUB_ENV" echo "Certificate imported. Using identity: $CERT_ID" - name: install frontend dependencies run: pnpm install # change this to npm, pnpm or bun depending on which one you use. - - uses: tauri-apps/tauri-action@v0 + - uses: tauri-apps/tauri-action@84b9d35b5fc46c1e45415bdb6144030364f7ebc5 # tauri-apps/tauri-action@v0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Do NOT set APPLE_CERTIFICATE / APPLE_CERTIFICATE_PASSWORD here. Doing so @@ -137,3 +145,173 @@ jobs: prerelease: true args: ${{ matrix.args }} projectPath: "./desktop" + + winget-publish: + name: Publish to WinGet + runs-on: windows-latest + needs: [publish-tauri] + if: startsWith(github.ref, 'refs/tags/v') || (github.event_name == 'release' && github.event.action == 'published') + permissions: + contents: read + # Note: the token must be job-scoped because `secrets` is not an allowed + # context in a step-level `if`; the step itself is gated below. + env: + WINGET_TOKEN: ${{ secrets.WINGET_TOKEN }} + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # actions/checkout@v7 + with: + persist-credentials: false + - name: Submit package to Windows Package Manager Community Repository + if: env.WINGET_TOKEN != '' + uses: vedantmgoyal2009/winget-releaser@4ffc7888bffd451b357355dc214d43bb9f23917e # v2 + with: + identifier: HeretekGames.Drop + token: ${{ secrets.WINGET_TOKEN }} + installers-regex: '.*-setup\.exe$' + max-retries: 3 + + flatpark-publish: + name: Publish to FlatPark + runs-on: ubuntu-latest + needs: [publish-tauri] + if: startsWith(github.ref, 'refs/tags/v') || (github.event_name == 'release' && github.event.action == 'published') + steps: + - name: Trigger FlatPark Release Webhook + uses: flatpark/publish-action@b67fca1b80b095541bc011967e0a35292bed14d8 # v1 + continue-on-error: true # Non-blocking until PR #343 is merged on FlatPark + with: + app-id: org.droposs.client + + ppa-stable: + name: Publish stable to Ubuntu PPA + runs-on: ubuntu-22.04 + needs: [publish-tauri] + if: startsWith(github.ref, 'refs/tags/v') || (github.event_name == 'release' && github.event.action == 'published') + permissions: + contents: read + env: + PPA_STABLE: ${{ vars.PPA_STABLE }} + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # actions/checkout@v7 + with: + persist-credentials: false + + - name: resolve release tag + id: version + env: + RELEASE_TAG: ${{ github.event.release.tag_name || inputs.tagName || github.ref_name }} + run: | + set -euo pipefail + : "${RELEASE_TAG:?unable to resolve release tag}" + echo "tag=${RELEASE_TAG}" >> "$GITHUB_OUTPUT" + echo "version=${RELEASE_TAG#v}" >> "$GITHUB_OUTPUT" + + - name: download stable deb from the GitHub release + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TAG: ${{ steps.version.outputs.tag }} + run: | + set -euo pipefail + mkdir -p dist/deb + gh release download "$TAG" --pattern '*_amd64.deb' --dir dist/deb --clobber + ls -la dist/deb + + - name: prepare PPA source + id: ppa + env: + VERSION: ${{ steps.version.outputs.version }} + run: | + set -euo pipefail + deb="$(find dist/deb -maxdepth 1 -name '*_amd64.deb' -print -quit)" + [ -n "$deb" ] || { echo "no stable deb asset found" >&2; exit 1; } + scripts/distro/prepare-ppa-source.sh \ + --channel stable \ + --version "$VERSION" \ + --deb "$deb" \ + --outdir dist/ppa >> "$GITHUB_OUTPUT" + + - name: publish to Launchpad PPA + uses: yuezk/publish-ppa-package@3e4da8d50b48950e8a84cd4302f031a17b5bfd27 # v2.0.1 + with: + repository: ${{ env.PPA_STABLE }} + series: "jammy noble" + revision: "1" + include_orig: auto + debmake_arguments: "-y" + new_version_template: "{VERSION}-{REVISION}~ubuntu{SERIES_VERSION}.1" + tarball: ${{ steps.ppa.outputs.tarball }} + debian_dir: ${{ steps.ppa.outputs.debian_dir }} + deb_email: contact@heretek.games + deb_fullname: Heretek Games + gpg_private_key: ${{ secrets.PPA_GPG_PRIVATE_KEY }} + gpg_passphrase: ${{ secrets.PPA_GPG_PASSPHRASE }} + + copr-stable: + name: Publish stable to Fedora COPR + runs-on: ubuntu-22.04 + needs: [publish-tauri] + if: startsWith(github.ref, 'refs/tags/v') || (github.event_name == 'release' && github.event.action == 'published') + permissions: + contents: read + env: + COPR_STABLE: ${{ vars.COPR_STABLE }} + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # actions/checkout@v7 + with: + persist-credentials: false + + - name: resolve release tag + id: version + env: + RELEASE_TAG: ${{ github.event.release.tag_name || inputs.tagName || github.ref_name }} + run: | + set -euo pipefail + : "${RELEASE_TAG:?unable to resolve release tag}" + echo "tag=${RELEASE_TAG}" >> "$GITHUB_OUTPUT" + echo "version=${RELEASE_TAG#v}" >> "$GITHUB_OUTPUT" + + - name: download stable deb from the GitHub release + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TAG: ${{ steps.version.outputs.tag }} + run: | + set -euo pipefail + mkdir -p dist/deb + gh release download "$TAG" --pattern '*_amd64.deb' --dir dist/deb --clobber + ls -la dist/deb + + - name: extract client binary + run: | + set -euo pipefail + deb="$(find dist/deb -maxdepth 1 -name '*_amd64.deb' -print -quit)" + [ -n "$deb" ] || { echo "no stable deb asset found" >&2; exit 1; } + mkdir -p dist/bin + dpkg-deb --fsys-tarfile "$deb" | tar -xO --wildcards '*usr/bin/drop-app' > dist/bin/drop-app + chmod +x dist/bin/drop-app + + - name: build SRPM and submit to COPR + env: + COPR_CONFIG_B64: ${{ secrets.COPR_API_TOKEN_CONFIG }} + VERSION: ${{ steps.version.outputs.version }} + run: | + set -euo pipefail + : "${COPR_STABLE:?COPR_STABLE repository variable is not set}" + : "${COPR_CONFIG_B64:?COPR_API_TOKEN_CONFIG secret is not set}" + docker run --rm \ + -v "$PWD:/work" -w /work \ + -e VERSION \ + -e COPR_STABLE \ + -e COPR_CONFIG_B64 \ + fedora:41 bash -euo pipefail -c ' + dnf install -y --setopt=install_weak_deps=False rpm-build copr-cli tar gzip findutils >/dev/null + mkdir -p /root/.config + printf "%s" "$COPR_CONFIG_B64" | base64 -d > /root/.config/copr + chmod 600 /root/.config/copr + scripts/distro/build-rpm-srpm.sh \ + --channel stable \ + --version "$VERSION" \ + --binary /work/dist/bin/drop-app \ + --outdir /work/dist/rpm + srpm="$(find /work/dist/rpm -maxdepth 1 -name "*.src.rpm" -print -quit)" + copr-cli build "$COPR_STABLE" "$srpm" + ' diff --git a/.github/workflows/droplet-ci.yml b/.github/workflows/droplet-ci.yml index 673925d01..49ec9091b 100644 --- a/.github/workflows/droplet-ci.yml +++ b/.github/workflows/droplet-ci.yml @@ -17,6 +17,9 @@ on: - ".github/workflows/droplet-ci.yml" workflow_dispatch: +permissions: + contents: read + env: CARGO_TERM_COLOR: always @@ -29,15 +32,17 @@ jobs: working-directory: libraries/droplet steps: - name: Checkout repository - uses: actions/checkout@v7 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # actions/checkout@v7 + with: + persist-credentials: false - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@nightly + uses: dtolnay/rust-toolchain@7c8d7d138f5c09cef361f8214cf96882cd029cdb # dtolnay/rust-toolchain@nightly with: components: rustfmt, clippy - name: Rust cache - uses: swatinem/rust-cache@v2 + uses: swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # swatinem/rust-cache@v2 with: workspaces: "./libraries/droplet -> target" diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index 4dd073818..a778ddc90 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -18,8 +18,6 @@ on: # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages permissions: contents: read - pages: write - id-token: write # Allow only one concurrent deployment per the "pages" group, skipping runs queued # between the in-progress run and the latest queued one. cancel-in-progress defaults @@ -31,15 +29,17 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v7 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # actions/checkout@v7 + with: + persist-credentials: false - name: Install pnpm - uses: pnpm/action-setup@v6 + uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # pnpm/action-setup@v6 with: run_install: false - name: Install Node.js - uses: actions/setup-node@v7 + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # actions/setup-node@v7 with: node-version: "22" cache: "pnpm" @@ -52,10 +52,10 @@ jobs: - name: Setup Pages id: setup_pages - uses: actions/configure-pages@v6 + uses: actions/configure-pages@45bfe0192ca1faeb007ade9deae92b16b8254a0d # actions/configure-pages@v6 - name: Restore cache - uses: actions/cache@v6 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # actions/cache@v6 with: path: | sites/promo/.next/cache @@ -84,11 +84,14 @@ jobs: cp -r sites/docs/dist/. sites/promo/out/docs/ - name: Upload artifact - uses: actions/upload-pages-artifact@v3 + uses: actions/upload-pages-artifact@56afc609e74202658d3ffba0e8f6dda462b719fa # actions/upload-pages-artifact@v3 with: path: sites/promo/out deploy: + permissions: + pages: write + id-token: write environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} @@ -97,4 +100,4 @@ jobs: steps: - name: Deploy to GitHub Pages id: deployment - uses: actions/deploy-pages@v4 + uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # actions/deploy-pages@v4 diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml new file mode 100644 index 000000000..90324c8ca --- /dev/null +++ b/.github/workflows/security.yml @@ -0,0 +1,111 @@ +name: Security + +on: + push: + branches: [develop] + pull_request: + branches: [develop] + schedule: + # Weekly Monday 06:06 UTC — catches new advisories between PRs. + - cron: "6 6 * * 1" + workflow_dispatch: + +permissions: + contents: read + +jobs: + gitleaks: + name: Secret scan + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # actions/checkout@v4 + with: + fetch-depth: 0 # full history for leak detection + persist-credentials: false + # Direct CLI invocation instead of gitleaks/gitleaks-action, which + # requires a paid license key for organization repos. + - name: Install gitleaks + run: | + curl -sSfL --proto '=https' --tlsv1.2 -o gitleaks.tar.gz \ + https://github.com/gitleaks/gitleaks/releases/download/v8.30.1/gitleaks_8.30.1_linux_x64.tar.gz + echo "551f6fc83ea457d62a0d98237cbad105af8d557003051f41f3e7ca7b3f2470eb gitleaks.tar.gz" | sha256sum -c - + tar -xzf gitleaks.tar.gz -C /usr/local/bin gitleaks + - name: Run gitleaks + run: gitleaks detect --redact --verbose + + cargo-audit: + name: Rust advisory audit + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + crate: + - cli + - torrential + - desktop/src-tauri + - libraries/droplet + - libraries/droplet_types + - libraries/libarchive + - libraries/native_model + steps: + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # actions/checkout@v4 + with: + persist-credentials: false + - uses: dtolnay/rust-toolchain@7c8d7d138f5c09cef361f8214cf96882cd029cdb # dtolnay/rust-toolchain@nightly + - uses: taiki-e/install-action@4b8992af23a2be34fd58f8d9b50d3f4939ad1891 # taiki-e/install-action@cargo-audit + - name: Audit ${{ matrix.crate }} + # Some vendored crates gitignore their Cargo.lock; generate one on the + # fly so `cargo audit` has a manifest to scan. + run: | + if [ ! -f Cargo.lock ]; then cargo generate-lockfile; fi + cargo audit --file Cargo.lock + working-directory: ${{ matrix.crate }} + + cargo-deny: + name: Rust dependency policy + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + crate: + - cli + - torrential + - desktop/src-tauri + - libraries/droplet + - libraries/droplet_types + - libraries/libarchive + - libraries/native_model + steps: + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # actions/checkout@v4 + with: + persist-credentials: false + - uses: dtolnay/rust-toolchain@7c8d7d138f5c09cef361f8214cf96882cd029cdb # dtolnay/rust-toolchain@nightly + - uses: taiki-e/install-action@4b8992af23a2be34fd58f8d9b50d3f4939ad1891 # taiki-e/install-action@cargo-audit + with: + tool: cargo-deny + - name: Check advisories, licenses, bans and sources (${{ matrix.crate }}) + # cargo-deny discovers the repository-level deny.toml by walking up from + # the crate directory. Vendored crates gitignore their Cargo.lock; + # generate one on the fly so the dependency graph can be resolved. + run: | + if [ ! -f Cargo.lock ]; then cargo generate-lockfile; fi + cargo deny check + working-directory: ${{ matrix.crate }} + + golangci: + name: Go lint (backend) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # actions/checkout@v4 + with: + fetch-depth: 0 # needed for --new-from-rev against origin/develop + persist-credentials: false + - uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # actions/setup-go@v5 + with: + go-version-file: backend/go.work + - uses: golangci/golangci-lint-action@4afd733a84b1f43292c63897423277bb7f4313a9 # golangci/golangci-lint-action@v8 + with: + working-directory: backend + # Rollout mode: only report issues on changed lines. Remove once the + # existing debt (2 issues in core/database.go) is cleaned up. + args: --new-from-rev=origin/develop ./core/... diff --git a/.github/workflows/semgrep-scheduled.yml b/.github/workflows/semgrep-scheduled.yml new file mode 100644 index 000000000..790cf06f4 --- /dev/null +++ b/.github/workflows/semgrep-scheduled.yml @@ -0,0 +1,30 @@ +name: Semgrep (scheduled) + +# Deep security scan on a weekly schedule — deliberately not wired into git +# hooks or push/PR CI, where its runtime would be too disruptive. +on: + schedule: + - cron: "30 5 * * 1" # weekly Monday 05:30 UTC + workflow_dispatch: + +permissions: + contents: read + security-events: write + +jobs: + semgrep: + runs-on: ubuntu-latest + container: + # Pinned by tag + manifest digest; bump both together. + image: semgrep/semgrep:latest@sha256:34ab619bf1391a24bfda3f05debd0d8a6ce3093c2d5f9d39cfc00f83c1397823 + steps: + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # actions/checkout@v4 + with: + persist-credentials: false + - name: Scan + run: semgrep scan --config p/default --sarif --output semgrep.sarif + - name: Upload SARIF + if: always() + uses: github/codeql-action/upload-sarif@6d786de4d6f3531a740e445b53a42b622bbbace8 # github/codeql-action/upload-sarif@v3 + with: + sarif_file: semgrep.sarif diff --git a/.github/workflows/server-ci.yml b/.github/workflows/server-ci.yml index 6015847ea..18368afb0 100644 --- a/.github/workflows/server-ci.yml +++ b/.github/workflows/server-ci.yml @@ -29,13 +29,15 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out the repo - uses: actions/checkout@v7 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # actions/checkout@v7 + with: + persist-credentials: false - name: Install pnpm - uses: pnpm/action-setup@v6 + uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # pnpm/action-setup@v6 - name: Setup Node.js environment - uses: actions/setup-node@v7 + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # actions/setup-node@v7 with: node-version: lts/* cache: "pnpm" @@ -47,18 +49,45 @@ jobs: working-directory: server run: pnpm run typecheck + test: + name: Test + runs-on: ubuntu-latest + steps: + - name: Check out the repo + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # actions/checkout@v7 + with: + persist-credentials: false + + - name: Install pnpm + uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # pnpm/action-setup@v6 + + - name: Setup Node.js environment + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # actions/setup-node@v7 + with: + node-version: lts/* + cache: "pnpm" + + - name: Install dependencies + run: pnpm install + + - name: Test + working-directory: server + run: pnpm run test + lint: name: Lint runs-on: ubuntu-latest steps: - name: Check out the repo - uses: actions/checkout@v7 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # actions/checkout@v7 + with: + persist-credentials: false - name: Install pnpm - uses: pnpm/action-setup@v6 + uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # pnpm/action-setup@v6 - name: Setup Node.js environment - uses: actions/setup-node@v7 + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # actions/setup-node@v7 with: node-version: lts/* cache: "pnpm" @@ -69,3 +98,34 @@ jobs: - name: Lint working-directory: server run: pnpm run lint + + knip: + name: Dependency & export report (non-blocking) + runs-on: ubuntu-latest + continue-on-error: true # report-only rollout phase; remove once triaged + steps: + - name: Check out the repo + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # actions/checkout@v7 + with: + persist-credentials: false + + - name: Install pnpm + uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # pnpm/action-setup@v6 + + - name: Setup Node.js environment + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # actions/setup-node@v7 + with: + node-version: lts/* + cache: "pnpm" + + - name: Install dependencies + run: pnpm install + + - name: Knip report + run: pnpm exec knip --reporter json > knip-report.json || true + + - name: Upload knip report + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # actions/upload-artifact@v7 + with: + name: knip-report + path: knip-report.json diff --git a/.github/workflows/server-release.yml b/.github/workflows/server-release.yml index 8fff61c2b..732bc0c3c 100644 --- a/.github/workflows/server-release.yml +++ b/.github/workflows/server-release.yml @@ -9,7 +9,7 @@ on: - cron: "0 2 * * *" # run at 2 AM UTC env: - REGISTRY_IMAGE: ghcr.io/drop-oss/drop + REGISTRY_IMAGE: ghcr.io/heretek-games/drop jobs: build: @@ -25,47 +25,50 @@ jobs: permissions: packages: write contents: read + outputs: + today: ${{ steps.get_final_ver.outputs.today }} steps: - name: Check out the repo - uses: actions/checkout@v7 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # actions/checkout@v7 with: fetch-depth: 3 # fix for when this gets triggered by tag fetch-tags: true ref: ${{ github.ref }} token: ${{ secrets.GITHUB_TOKEN }} + persist-credentials: false - name: Prepare run: | platform=${{ matrix.platform }} - echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV + echo "PLATFORM_PAIR=${platform//\//-}" >> "$GITHUB_ENV" - name: Docker meta id: meta - uses: docker/metadata-action@v6 + uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # docker/metadata-action@v6 with: images: ${{ env.REGISTRY_IMAGE }} - name: Login to Docker Hub - uses: docker/login-action@v4 + uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # docker/login-action@v4 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Set up QEMU - uses: docker/setup-qemu-action@v4 + uses: docker/setup-qemu-action@1f40c72289eff860ee54a304f1438e3cff362e0a # docker/setup-qemu-action@v4 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v4 + uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # docker/setup-buildx-action@v4 - name: Determine final version id: get_final_ver run: | - BASE_VER=v$(jq -r '.version' package.json) + BASE_VER=v$(jq -r '.version' server/package.json) TODAY=$(date +'%Y.%m.%d') echo "Today will be: $TODAY" - echo "today=$TODAY" >> $GITHUB_OUTPUT + echo "today=$TODAY" >> "$GITHUB_OUTPUT" if [[ "${{ github.event_name }}" == "release" ]]; then FINAL_VER="$BASE_VER" @@ -74,11 +77,11 @@ jobs: fi echo "Drop's release tag will be: $FINAL_VER" - echo "final_ver=$FINAL_VER" >> $GITHUB_OUTPUT + echo "final_ver=$FINAL_VER" >> "$GITHUB_OUTPUT" - name: Build and push by digest id: build - uses: docker/build-push-action@v7 + uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # docker/build-push-action@v7 with: platforms: ${{ matrix.platform }} labels: ${{ steps.meta.outputs.labels }} @@ -91,13 +94,15 @@ jobs: BUILD_GIT_REF=${{ github.sha }} - name: Export digest + env: + DIGEST: ${{ steps.build.outputs.digest }} run: | mkdir -p ${{ runner.temp }}/digests - digest="${{ steps.build.outputs.digest }}" + digest="$DIGEST" touch "${{ runner.temp }}/digests/${digest#sha256:}" - name: Upload digest - uses: actions/upload-artifact@v7 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # actions/upload-artifact@v7 with: name: digests-${{ env.PLATFORM_PAIR }} path: ${{ runner.temp }}/digests/* @@ -113,30 +118,30 @@ jobs: contents: read steps: - name: Download digests - uses: actions/download-artifact@v8 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # actions/download-artifact@v8 with: path: ${{ runner.temp }}/digests pattern: digests-* merge-multiple: true - name: Login to Docker Hub - uses: docker/login-action@v4 + uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # docker/login-action@v4 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v4 + uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # docker/setup-buildx-action@v4 - name: Extract metadata (tags, labels) for Docker id: meta - uses: docker/metadata-action@v6 + uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # docker/metadata-action@v6 with: images: | - ghcr.io/drop-OSS/drop + ${{ env.REGISTRY_IMAGE }} tags: | type=schedule,pattern=nightly - type=schedule,pattern=nightly.${{ steps.get_final_ver.outputs.today }} + type=schedule,pattern=nightly.${{ needs.build.outputs.today }} type=semver,pattern=v{{version}} type=semver,pattern=v{{major}}.{{minor}} type=semver,pattern=v{{major}} @@ -149,9 +154,14 @@ jobs: - name: Create manifest list and push working-directory: ${{ runner.temp }}/digests run: | + # shellcheck disable=SC2046 + # Command substitution is intentionally split into separate arguments. docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ $(printf '${{ env.REGISTRY_IMAGE }}@sha256:%s ' *) - name: Inspect image + env: + REGISTRY_IMAGE: ${{ env.REGISTRY_IMAGE }} + VERSION: ${{ steps.meta.outputs.version }} run: | - docker buildx imagetools inspect ${{ env.REGISTRY_IMAGE }}:${{ steps.meta.outputs.version }} + docker buildx imagetools inspect "$REGISTRY_IMAGE:$VERSION" diff --git a/.github/workflows/torrential-ci.yml b/.github/workflows/torrential-ci.yml index 1535cf977..a24d0521d 100644 --- a/.github/workflows/torrential-ci.yml +++ b/.github/workflows/torrential-ci.yml @@ -34,13 +34,17 @@ jobs: working-directory: torrential steps: - name: Checkout repository - uses: actions/checkout@v7 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # actions/checkout@v7 + with: + persist-credentials: false - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@nightly + uses: dtolnay/rust-toolchain@7c8d7d138f5c09cef361f8214cf96882cd029cdb # dtolnay/rust-toolchain@nightly + with: + components: clippy, rustfmt - name: Rust cache - uses: swatinem/rust-cache@v2 + uses: swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # swatinem/rust-cache@v2 with: workspaces: "./torrential -> target" @@ -50,8 +54,14 @@ jobs: sudo apt-get update sudo apt-get install -y libarchive-dev + - name: Check formatting + run: cargo fmt --all -- --check + + - name: Run Clippy (lint) + run: cargo clippy --all-targets --all-features -- -D warnings + - name: Build - run: cargo build --all-targets --verbose + run: cargo build --all-targets --locked --verbose - name: Run tests - run: cargo test --verbose + run: cargo test --locked --verbose diff --git a/.gitignore b/.gitignore index 16875b0e5..6eb0143d0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,8 @@ dist/ node_modules/ +mitm_mcp_traffic.db +/sonarcloud-drop2-issues.json +/scripts/sonar-fix/ .pnpm-store/ +__pycache__/ +*.pyc diff --git a/.gitleaksignore b/.gitleaksignore new file mode 100644 index 000000000..4b4989996 --- /dev/null +++ b/.gitleaksignore @@ -0,0 +1,5 @@ +# Revoked Twitch OAuth token, committed in 405754ab and removed/revoked in +# 2cf8b72c ("accidently commited secret from testing lol (revoked)"). +# History is not rewritten; this entry silences the dead credential. +405754abd5e505ae4e57c72a27cb84267f6d57c2:server/server/internal/metadata/igdb.ts:generic-api-key:150 +2755aa472bee4946a6d611602ab4acfa64a29209:server/internal/metadata/igdb.ts:generic-api-key:150 diff --git a/.mcp.json b/.mcp.json new file mode 100644 index 000000000..fd6f45b1f --- /dev/null +++ b/.mcp.json @@ -0,0 +1,8 @@ +{ + "mcpServers": { + "sonarqube": { + "args": ["run", "mcp", "--project", "Heretek-Games_drop"], + "command": "sonar" + } + } +} diff --git a/.mise.toml b/.mise.toml new file mode 100644 index 000000000..ee8904939 --- /dev/null +++ b/.mise.toml @@ -0,0 +1,26 @@ +# Toolchain pins for local development and git hooks. +# Install: mise install +# Docs: https://mise.jdx.dev +# +# Versions of the lint/security tools match the pins used in CI workflows so +# local hooks and CI agree. Runtime pins mirror the versions in backend/go.work, +# package.json and server/package.json. +[tools] +node = "22" +pnpm = "10.33.0" +python = "3.14" +go = "1.26.6" +# rustfmt/clippy are required by scripts/clippy-changed.sh. +rust = { version = "nightly", components = "rustfmt,clippy" } + +shellcheck = "0.11.0" # hooks + analysis CI (shellcheck) +shfmt = "3.14.1" +hadolint = "2.15.1" # analysis CI (hadolint) +actionlint = "1.7.12" # analysis CI (actionlint) +ruff = "0.16.7" # server/dev-tools Python +gitleaks = "8.30.1" # security CI (gitleaks) +typos = "1.50.1" + +[tasks.install] +description = "Install toolchain + workspace dependencies" +run = "mise install && pnpm install && pnpm -C desktop/main install" diff --git a/.opencode/plugin/quality.js b/.opencode/plugin/quality.js new file mode 100644 index 000000000..539bf4c96 --- /dev/null +++ b/.opencode/plugin/quality.js @@ -0,0 +1,28 @@ +// Formats and auto-fixes files right after opencode edits them, using the same +// shared script as the Claude/Cursor hooks. Best-effort: failures never block. +export const QualityPlugin = async ({ $, directory }) => { + const root = directory ?? process.cwd(); + return { + "tool.execute.after": async (input, output) => { + const tool = input?.tool; + if (tool !== "edit" && tool !== "write") return; + + const file = + output?.args?.filePath ?? + input?.args?.filePath ?? + output?.args?.path ?? + input?.args?.path; + if (typeof file !== "string" || file.length === 0) return; + + try { + await $`bash ${root}/scripts/agent-quality.sh ${file}` + .quiet() + .nothrow(); + } catch { + // Formatting is advisory; never surface a failure to the model. + } + }, + }; +}; + +export default QualityPlugin; diff --git a/.opencode/skills/quality-gates/SKILL.md b/.opencode/skills/quality-gates/SKILL.md new file mode 100644 index 000000000..0bd01827d --- /dev/null +++ b/.opencode/skills/quality-gates/SKILL.md @@ -0,0 +1,46 @@ +--- +name: quality-gates +description: Use before committing or when asked to "run the gates", "check everything", or verify a change compiles/lints/tests. Lists the exact local commands for each Drop workspace and the hook behaviour. +--- + +# Local quality gates + +Run from the repository root unless noted. + +## Fast (staged-file hooks) + +```sh +pnpm exec lefthook run pre-commit --all-files +``` + +Runs prettier, eslint (server), ast-grep, shellcheck, hadolint, actionlint, ruff, typos and gitleaks. Tools come from mise; install with `mise install`. + +## Before pushing + +```sh +pnpm exec lefthook run pre-push --all-files +``` + +Runs server typecheck + lint, desktop/base/promo/docs lint, `scripts/clippy-changed.sh` (cargo fmt + clippy), golangci-lint and a report-only knip. + +## Per-workspace commands + +```sh +pnpm -C server run lint && pnpm -C server run typecheck && pnpm -C server run test +pnpm -C desktop/main run lint && pnpm -C desktop/main run typecheck +pnpm -C libraries/base run lint +pnpm -C sites/promo run lint +pnpm -C sites/docs run lint +``` + +## Single edited file + +```sh +scripts/agent-quality.sh +``` + +Formats and auto-fixes one file (prettier, workspace eslint --fix, ruff, shfmt, cargo fmt, gofmt). The opencode plugin and the Claude/Cursor PostToolUse hooks call it automatically after edits. + +## SonarCloud + +Remote analysis runs on push; see the `sonar-triage` skill for the CLI workflow. diff --git a/.opencode/skills/sonar-triage/SKILL.md b/.opencode/skills/sonar-triage/SKILL.md new file mode 100644 index 000000000..e82173230 --- /dev/null +++ b/.opencode/skills/sonar-triage/SKILL.md @@ -0,0 +1,51 @@ +--- +name: sonar-triage +description: Use when triaging SonarCloud findings for this repository (quality gate red, new issues, "fix sonar", "sonar list issues", "quality gate", or when a push triggers SonarCloud analysis). Covers authenticating the sonar CLI, listing issues, and the fix/NOSONAR/accept decision policy. +--- + +# SonarCloud triage for Heretek-Games/drop + +## Project facts + +- Project key: `Heretek-Games_drop` (org `heretek-games`), default branch `develop`. +- Analysis is **automatic** on push; the remote analysis is authoritative. +- The quality gate enforces the **last 30 days** of new code (new-code ratings A, new duplicated lines density < 3 %, hotspots reviewed 100 %). +- Project exclusions: `server/prisma/migrations/**` (generated/historical SQL) and vendored `libraries/{libarchive,native_model}/**/Cargo.toml`. +- Local rule parity lives in `eslint.config.shared.mjs` (sonarjs, regexp, unicorn, vuejs-a11y) plus ruff (Python) and shellcheck/hadolint/actionlint hooks. +- The root `knip` check is report-only; migrations and vendored crates are excluded. + +## Authenticate the CLI + +Ensure the `sonar` CLI is on your `PATH` (Homebrew, `mise`, or your package +manager), then source your token and confirm it works: + +```sh +. "$HOME/.config/sonar/env" # personal token; often sourced by ~/.bashrc +sonar auth status +``` + +## Triage loop + +1. Fetch open issues, grouped by rule: + + ```sh + sonar list issues -p Heretek-Games_drop --format json > /tmp/sonar.json + python3 -c "import json,collections;d=json.load(open('/tmp/sonar.json'));print(collections.Counter(i['rule'] for i in d['issues']))" + ``` + +2. `sonar quality-gate status -p Heretek-Games_drop` for the gate verdict. +3. For each rule, decide: + - **Fix** when the issue is real (behaviour, safety, accessibility, complexity above 15). + - **NOSONAR** when the analyzer cannot see a sanitizer, only as a trailing comment on the exact flagged line, with a short justification. + - **Accept** when it is intentional (e.g. tracked TODO markers) via `sonar api post /api/issues/do_transition` with `{"issue":"","transition":"accept","comment":"reason"}`. +4. Verify locally before pushing: + - `pnpm run lint` (all workspaces) + - `pnpm -C server run typecheck && pnpm -C server run test` + - `pnpm -C desktop/main run typecheck` +5. Push; the next automatic analysis should show the issues resolved. Never mark a fix as done without the analysis or a local command confirming it. + +## Policy notes + +- Do not edit `server/prisma/migrations/**` to silence SQL findings; scope excludes it instead. +- Prefer fixing the root cause over suppressions; every NOSONAR needs a one-line reason. +- Accepted findings must carry a rationale comment in SonarCloud. diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 000000000..30581eb7d --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,5 @@ +{ + "jsonRecursiveSort": true, + "jsonSortOrder": "{\"/.*/\": \"lexical\"}", + "plugins": ["prettier-plugin-sort-json"] +} diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 000000000..a259305fd --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,426 @@ +# AGENTS.md — Drop contributor & AI agent guide + +**Drop** is an open-source, self-hosted game distribution and multiplayer +platform maintained by [Heretek Games](https://github.com/Heretek-Games/drop) +(transitioned from `Drop-OSS/drop` and `Heretek-AI/drop`). + +This is the **canonical** guide for AI agents (opencode, Claude Code, Antigravity, +Gemini CLI) and human developers working on **`drop` core**. `CLAUDE.md` and `GEMINI.md` are thin, +tool-specific wrappers that point back here — update this file first. + +> [!NOTE] +> **Multi-Repo Workspace**: `drop` is the core platform within the [Heretek Games](https://github.com/Heretek-Games) workspace (`~/Projects/Heretek-Games/`). +> Specialized subsystems live in dedicated sister repositories to keep `drop` lean and upstream-friendly with `Drop-OSS/drop`: +> +> - **GSE Multiplayer Engine**: [`drop-gse`](https://github.com/Heretek-Games/drop-gse) (`~/Projects/Heretek-Games/drop-gse`) +> - **ZeroTier Mesh Provider**: [`drop-zerotier`](https://github.com/Heretek-Games/drop-zerotier) (`~/Projects/Heretek-Games/drop-zerotier`) +> - **Plugin SDK & CLI**: [`drop-plugin-sdk`](https://github.com/Heretek-Games/drop-plugin-sdk) (`~/Projects/Heretek-Games/drop-plugin-sdk`) +> - **GameBox Metadata Index**: [`drop-gamebox`](https://github.com/Heretek-Games/drop-gamebox) (`~/Projects/Heretek-Games/drop-gamebox`) +> - **Seedbox / qBittorrent**: [`drop-seedbox`](https://github.com/Heretek-Games/drop-seedbox) (`~/Projects/Heretek-Games/drop-seedbox`) +> - **Federation & Peering**: [`drop-federation`](https://github.com/Heretek-Games/drop-federation) (`~/Projects/Heretek-Games/drop-federation`) +> +> Core `drop` changes should focus on general improvements, upstream compatibility, and the generic Plugin SPI. + +--- + +## 1. Monorepo layout + +| Directory | Stack | Description | +| :----------------------- | :---------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------- | +| **`server/`** | Nuxt 3, Nitro, Vue 3, TypeScript, Prisma 7.10, Tailwind, Protobuf | Web app, REST API, WebSocket pub/sub, library manager, plugin SPI, Game Distribution pipeline. | +| **`backend/`** | Go (`backend/go.work`, module `core/`) | Background game management and sync services. | +| **`desktop/`** | Tauri v2 (`drop-app`), Node build scripts | Desktop client orchestration and packaging. | +| **`desktop/main/`** | Nuxt 4 (`view`) | Desktop client UI. **Separate pnpm workspace** (own `pnpm-workspace.yaml`). | +| **`desktop/src-tauri/`** | Rust (cargo workspace) | Tauri commands, process manager, launch interceptors, native pipeline runner. `Cargo.lock` is committed. | +| **`cli/`** | Rust | `downpour` CLI for interacting with Drop instances. | +| **`torrential/`** | Rust (standalone cargo workspace) | Depot HTTP server that serves chunks from library backends; spawned by the server. | +| **`libraries/`** | Rust & TypeScript | `droplet` (manifest gen + read backends), `droplet_types`, `libarchive`, `native_model`; TS UI layer in `libraries/base/`. | +| **`distribution/`** | YAML / XML / Desktop | Packaging manifests: WinGet (`winget/`), Flatpak (`flatpak/`). | +| **`sites/`** | Astro / site tooling | `sites/docs` (`docs-next`), `sites/promo` (`radiant`). | +| **`scripts/`** | Shell | `clippy-changed.sh` (pre-push Rust lint), helpers. | + +pnpm workspace roots: root (`package.json`) covers `server/`, +`libraries/base/`, `sites/*`, and `desktop/`. **`desktop/main/` is NOT part of +the root workspace** — use `pnpm -C desktop/main ...`. + +--- + +## 2. Key architectural systems + +### 2.1 Game Distribution Pipeline & declarative recipes (server) + +Source: `server/server/internal/library/pipeline/` + +- **`classifier.ts`** — detects `DistributionType`: `SceneRelease`, `GogInstaller`, + `FitGirlRepack`, `KaOsRepack`, `DodiRepack`, `ArchiveBundle`, `LoosePortable`, + `PatchUpdate`, `Unknown`. +- **`executable-scorer.ts`** — ranks candidate game binaries, filtering crash + handlers, redistributables, uninstallers and verifiers. +- **`recipe-generator.ts`** — emits a declarative `PipelineRecipe` of + `PipelineStep`s (`extract_rar`, `extract_iso`, `extract_archive`, + `innoextract`, `apply_crack`, `cleanup`, `run_command`) plus standalone + `drop-pipeline-setup.bat` / `.sh` fallbacks. +- `library/index.ts` `importVersion()` embeds the recipe at + `GameVersion.dropletManifest.recipe` and auto-adds a setup command when a + release needs one. The desktop client executes the recipe natively. +- Param names matter and are consumed by the Rust runner: `extract_rar` uses + `input`, `extract_archive` uses `archiveFile`, `extract_iso` uses + `sourceGlob`. Keep server and `process/src/pipeline.rs` in sync. +- Standalone Scene ISOs emit only `extract_iso` (no redundant `extract_rar`). + +### 2.2 Desktop native pipeline runner (Track 1) + +Source: `desktop/src-tauri/process/src/pipeline.rs`, +`desktop/src-tauri/src/process.rs`, `desktop/main/components/GameSetupModal.vue` + +- `prepare_pipeline(game_id)` resolves the recipe + install dir synchronously so + IPC can validate before spawning; `run_prepared_pipeline` executes it. +- Emits `pipeline_progress` and `pipeline_completed` Tauri events; IPC commands + are `start_pipeline_setup`, `cancel_pipeline_setup`, `reclaim_pipeline_space`. +- `GameSetupModal.vue` shows step progress, a live log, cancellation and + post-setup archive reclamation. The library page falls back to the legacy + setup command when a version has no recipe; generated setup scripts are + materialized from the recipe before running, and a missing script reports an + actionable error instead of spawning a nonexistent file. +- Tools (`7z`, `innoextract`) are discovered on PATH / bundled + `/tools//`; missing tools produce an actionable error. + +### 2.3 Admin bulk auto-import (Track 2) + +- Model `DiscoveredGame` (`server/prisma/models/discovery.prisma`) persists + scan results and decisions (`Pending` / `Imported` / `Ignored`). +- Task groups `import:discover` (`registry/discover-games.ts`) and `import:bulk`; + `libraryManager.discoverUnimportedGames()` and + `importUnimportedVersionsForGame()` do the work. Bulk import filters + already-imported versions (retries are idempotent) and only marks a discovery + `Imported` when at least one version was actually imported. +- APIs: `GET/POST/PATCH /api/v1/admin/import/discover`, + `POST /api/v1/admin/import/bulk`; UI at `pages/admin/import/bulk.vue`. + +### 2.4 Depot chunk cache (Track 3) + +Source: `torrential/src/downloads/cache.rs` (wired in `serve.rs`, `state.rs`, `main.rs`) + +- Opt-in read-through cache keyed by the **SHA-256 plaintext checksum** + (`ChunkData::checksum`). Disabled unless `CHUNK_CACHE_DIR` is set; + `CHUNK_CACHE_MAX_BYTES` bounds the LRU (default 20 GiB). +- Caches plaintext (pre-AES) bytes, is best-effort (a cache failure never fails + a request), uses single-flight fills and atomic `.partial` → rename. Cache + keys must be 64-char SHA-256 hex; plaintext is hashed during the fill and + refused unless it matches the key, and in-flight fills are bounded by the + configured budget. +- Documented optional volume/env in `server/deploy-template/compose.yml` and + `sites/docs/src/content/docs/admin/quickstart.md`; the cache stores + **unencrypted game data at rest**. +- The RPC listener (`server/src/server/mod.rs`) authenticates its local peer + with a spawn-time shared secret: `TORRENTIAL_RPC_SECRET` (64 hex chars) is + set by the server for the child it spawns and is required by torrential + (fail-closed). An operator running torrential out-of-process must set the + same value on both sides. The `WaitMap` leak was replaced by a removable + response map, and committed + in-flight cache bytes now share one budget. +- `/invalidate` and `/api/v1/depot/manifest.json` accept an optional + `TORRENTIAL_HTTP_TOKEN` (`Authorization: Bearer ` or + `x-torrential-token`); when unset those routes stay open for back-compat. + Chunk content (`/api/v1/depot/content/...`) can additionally require the same + token by setting `TORRENTIAL_REQUIRE_CHUNK_AUTH=true`; it is off by default so + anonymous local depots keep working, and enabling it without configuring + `TORRENTIAL_HTTP_TOKEN` fails closed (every chunk request is rejected). + +### 2.5 Tauri high-DPI window architecture + +- Use a single `WebviewWindowBuilder::new(&handle, "main", WebviewUrl::App("main".into()))` + in `desktop/src-tauri/src/lib.rs`. +- **Do NOT** use decoupled child webviews (`WindowBuilder` + `add_child(WebviewBuilder)`); + they desynchronize physical/logical bounds on Windows 4K at 200–300% scaling. +- Webview label `"main"` must match `desktop/src-tauri/capabilities/default.json`. + +### 2.6 Plugin platform (Track P) + +Source: `server/server/internal/plugins/` + +- `PluginManager` (`manager.ts`) owns the lifecycle: dynamic routing + `/api/v1/plugins/[pluginId]/...`, an event bus, the WebSocket gateway + (`api/v1/plugins/ws.get.ts`), and discovery of external bundles from + `${dataDir}/plugins/*/drop-plugin.json`. It is dependency-injectable + (`dataDir`, `storageFactory`, `authResolver`, `registryPath`) and resolves + Drop's runtime config lazily, so it imports outside Nuxt and is unit-testable. +- **Contract**: `PLUGIN_API_VERSION` (`types.ts`); `metadata.apiVersion` is + required and a missing or mismatched version is rejected + (`PluginApiVersionError`). +- **Capabilities** are fail-closed (declaring none denies all): `routes`, + `storage`, `events`, `network`, `websocket`. Undeclared use throws + `PluginCapabilityError` (routes/events at call time, storage via a guarded + wrapper, `network` gates `ctx.fetch`, `websocket` gates + `ctx.registerWebSocket`). `trust: "trusted"` (in-process) is the only tier; + `"sandboxed"` is rejected until an isolated runtime exists. +- **Auth**: plugin REST routes and the WS gateway resolve browser sessions, + `Bearer` API tokens, and the desktop client `JWT ` scheme + (`internal/plugins/auth.ts`). Sensitive WS channels require an authenticated + peer and cross-site upgrades (Origin mismatch) are rejected. +- **Storage**: one directory per plugin under `/plugins//`, with + `metadata.storageVersion` driving `migrateStorage` (recorded in `schema.json`). +- **Bundles**: `/plugins//{drop-plugin.json,index.js}`. Optional + `checksum` (SHA-256 of the entry) and `signature` (HMAC-SHA256 under + `DROP_PLUGIN_SIGNING_KEY`); `DROP_PLUGIN_REQUIRE_SIGNATURE=true` refuses + unsigned bundles. A `PluginRegistry` (`DROP_PLUGIN_REGISTRY`) allow-lists ids + and pins version/checksum. Sign with `server/dev-tools/sign-plugin.mjs`. +- Admin install/remove: `POST /api/v1/plugins/install`, + `DELETE /api/v1/plugins//bundle`, plus the Settings → Plugins UI + (`desktop/main/pages/settings/plugins.vue`). Builtins cannot be removed. +- Tests run via `pnpm --filter drop run test` (`server/dev-tools/run-tests.mjs`, + jiti + `~` alias). `hello-world` is the minimal reference plugin. + +### 2.7 Modular plugin architecture & externalized subsystems + +Drop core maintains a strictly decoupled, upstream-friendly architecture. +Domain-specific features are implemented as external plugins or standalone +sister repositories: + +- **Multiplayer & Emulation (`drop-gse`)**: Goldberg Steam Emulator patching, + anti-cheat verification, and P2P room lifecycle are externalized in + [`Heretek-Games/drop-gse`](https://github.com/Heretek-Games/drop-gse). +- **Mesh Networking (`drop-zerotier`)**: ZeroTier / ZTNET room mesh coordination + and daemon management are externalized in + [`Heretek-Games/drop-zerotier`](https://github.com/Heretek-Games/drop-zerotier). +- **Plugin SPI & SDK (`drop-plugin-sdk`)**: Type definitions, CLI tooling, and + runtime test suites for external plugins live in + [`Heretek-Games/drop-plugin-sdk`](https://github.com/Heretek-Games/drop-plugin-sdk). + +Core `drop` exposes generic hook points: + +- `LaunchInterceptor` trait (`process/src/interceptor.rs`) for native launch + lifecycle extension (`pre_launch`, `on_running`, `post_exit`). +- Generic Plugin SPI (`server/server/internal/plugins/`) for backend extensions, + dynamic REST routes, authenticated WebSocket events, and isolated storage. + +### 2.9 Desktop UI conventions + +- Vue discriminated unions (`GameStatus`) must be narrowed with a typed + computed (e.g. `installedData = computed(() => status.value?.type === "Installed" ? status.value : undefined)`) + before accessing `install_type`. +- `ModalTemplate`/`LoadingButton` live in `libraries/base/` and are auto-imported. +- Plugin settings UI: `desktop/main/pages/settings/plugins.vue`. + +### 2.10 Desktop distribution channels + +The Tauri identifier `org.droposs.client` (alpha: `org.droposs.client.Alpha`) is +the single reverse-DNS ID used by every packaged `.desktop`, AppStream and +Flatpak manifest. Packaging templates live in `distribution/debian/` and +`distribution/rpm/`, with helpers in `scripts/distro/`. + +- **Stable** is published manually on a `v*` tag/release by + `client-release.yml` (`ppa-stable`, `copr-stable`); **alpha** publishes on + every qualifying `develop` push via `client-alpha.yml` (`ppa-alpha`, + `copr-alpha`). amd64, Ubuntu jammy/noble, Fedora 43/44/45. +- Alpha versions map from semver `X.Y.Z-alpha.N.sha` (Tauri requires semver) to + the tilde form `X.Y.Z~alpha.N+sha` so alphas sort below stable in apt/dnf. +- `prepare-ppa-source.sh` builds the tarball + generated `debian/` consumed by + the pinned `yuezk/publish-ppa-package` action for Launchpad; + `build-rpm-srpm.sh` builds an SRPM in a Fedora container for COPR (submitted + with `copr-cli`). See `distribution/README.md` for one-time setup, the + required repository secrets/variables and channel names. + +--- + +## 3. Toolchain + +| Tool | Version / requirement | +| :----------- | :--------------------------------------------------------------- | +| **Node.js** | `>=22.16.0` (server `engines`) | +| **pnpm** | 10+ | +| **Rust** | `cargo +nightly` (workspace features: `nonpoison_mutex`, etc.) | +| **Go** | 1.26 (`backend/go.work`) | +| **Prisma** | 7.10.0 (pinned); multi-file schema under `server/prisma/models/` | +| **Lefthook** | 2.x | + +> `desktop/src-tauri/Cargo.toml` sets `[profile.dev.package.tokio] opt-level = 1` +> to avoid an upstream nightly ICE while compiling Tokio in debug. + +> Local development: `mise install` provisions the pinned runtimes above plus +> the lint/security tools (see `.mise.toml`). + +--- + +## 4. Quality gates + +| Layer | When | What | +| :------------------ | :--------- | :-------------------------------------------------------------------------------------------------------------------------------- | +| Editor hooks | every edit | format-on-edit (advisory) | +| lefthook pre-commit | commit | prettier + eslint --fix (staged), ast-grep scan, gitleaks | +| lefthook pre-push | push | server typecheck, `clippy-changed.sh` (Rust), golangci-lint, knip report | +| GitHub Actions | PR/push | typecheck/lint/clippy, gitleaks history, cargo-audit ×7 crates, cargo-deny, golangci-lint | +| GitHub Actions | PR/push | `server-ci` test job; `ztnet-e2e` (GSE mesh, path-filtered, needs Docker) | +| GitHub Actions | PR/push | `analysis` (blocking): actionlint, zizmor, shellcheck, hadolint, pnpm audit, govulncheck, cargo-machete, desktop typecheck + knip | +| GitHub Actions | weekly | semgrep deep scan → Code Scanning | + +Hooks are early feedback; **CI is the authority**. If a hook fails, read the +output and fix the root cause. The documented escape hatches exist but must not +be used to hide a failure: + +```sh +git commit --no-verify # skip pre-commit once +git push --no-verify # skip pre-push once +LEFTHOOK=0 git commit # same via env var +``` + +### Rollout status (flip these when clean) + +- **knip**: report-only (hook `|| true`, CI `--no-exit-code`). Root is now + clean of unused files/dependencies/unlisted deps; remaining items are runtime + false positives (unused devDeps, the `./torrential` spawn, `desktop/main` + contract types) plus unused/duplicate exports. `desktop/main` (separate + workspace) is analyzed with `desktop/main/knip.json` via + `pnpm exec knip --directory desktop/main`. +- **analysis**: blocking (promoted in `2d34bc0b` once the pinned-tool baseline + was clean). +- **golangci-lint**: `--new-from-rev=origin/develop` (new issues only). Baseline: + 2 legacy issues in `core/database.go`. +- **ast-grep**: rules at `severity: warning`; promote per-rule after cleanup. +- **`torrential` clippy**: `cargo clippy --all-targets --all-features -- -D warnings` + is clean. Generated rust-protobuf output is built into `OUT_DIR` by `build.rs` + and included through `src/proto/mod.rs`, which applies + `#[allow(clippy::all, clippy::pedantic)]`, so generated code never trips the + crate lints. `torrential-ci.yml` runs fmt + clippy + build + test. +- **Desktop frontend typecheck**: `desktop/main` is a separate pnpm workspace, + but its `typecheck` is a blocking step in the analysis `desktop` job; keep + `pnpm -C desktop/main run typecheck` clean. + +--- + +## 5. Essential commands + +```sh +# Install / generate +pnpm install # root workspace deps + Prisma client +cd server && pnpm exec prisma generate # after schema changes + +# Server +pnpm --filter drop run typecheck +pnpm --filter drop run lint +pnpm --filter drop run test +# ^ runs every server `*.test.ts` through `server/dev-tools/run-tests.mjs` +# (jiti + the `~` alias). Plain `node --test` fails on the ESM/alias setup. + +# Desktop frontend (separate workspace; not gated) +pnpm -C desktop/main install +pnpm -C desktop/main run typecheck + +# Rust — desktop client (needs Tauri system libs) +cargo +nightly check --manifest-path desktop/src-tauri/Cargo.toml -p process +cargo +nightly check --manifest-path desktop/src-tauri/Cargo.toml -p drop-app +cargo +nightly check --manifest-path desktop/src-tauri/Cargo.toml --tests -p process + +# Rust — torrential (standalone workspace; needs libarchive-devel) +cd torrential && cargo +nightly build --all-targets && cargo +nightly test + +# Go +cd backend && golangci-lint run --new-from-rev=origin/develop ./core/... + +# Hooks / structural lint +pnpm exec lefthook run pre-commit --all-files +pnpm exec lefthook run pre-push --all-files +pnpm exec ast-grep scan [paths] +pnpm exec knip --reporter compact +``` + +Native binaries not installable via pnpm: + +- `gitleaks` — `brew install gitleaks` +- `cargo-audit` — `cargo install cargo-audit` +- `cargo-deny` — `cargo install cargo-deny` (policy config at `deny.toml`; + run from a crate dir, e.g. `cd torrential && cargo deny check`) +- `golangci-lint` — `go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latest` + (must be built with the same Go version as `backend/go.work`) +- `libarchive-devel` — required to build `torrential` (Fedora) / `libarchive-dev` (Debian) + +--- + +## 6. Environment quirks + +- **`cargo` may not be on `PATH`** for git hooks / non-login shells; rustup lives + at `~/.cargo/bin`. Export it before cargo/hook commands if needed. +- **Headless desktop `cargo check`**: `desktop/src-tauri` links GTK/WebKit. + On a box without the real dev libs, `pkg-config` shims under + `~/.local/lib/pkgconfig` plus `PKG_CONFIG_PATH="$HOME/.local/lib/pkgconfig"` + allow `cargo check` (type-check only). **`cargo test` for desktop still needs + real GTK/WebKit `.so` files to link**, so prefer `cargo check --tests`. +- **`libdbus-sys`** is a Linux-only target dependency (vendored) in + `database/Cargo.toml`; it lets `keyring` build without system `dbus-1.pc`. + Do not make it unconditional — it doesn't build on Windows/macOS. +- **Prisma multi-file schema**: `prisma.config.ts` sets the schema to the + `prisma/` folder; models live in `prisma/models/*.prisma`. Add a migration + under `prisma/migrations/_/migration.sql`; it applies on next + server start via `prisma migrate deploy`. +- Do not commit `server/prisma/client/` (generated, gitignored). + +--- + +## 7. Conventions + +- **Commits**: [Conventional Commits](https://www.conventionalcommits.org/) + (`feat(desktop): ...`, `fix(server): ...`). One logical change per commit. +- **Rust**: nightly toolchain; keep clippy clean for touched crates where the + baseline allows. Prefer `unwrap_or_else`/explicit errors over `unwrap`/`expect` + in new code (`unwrap_used`/`expect_used` are warn-level lints). +- **Formatting**: Prettier config at the repo root (JS/TS/Vue/YAML/MD); `gofmt`/ + `goimports` for Go. +- **Go**: keep modules inside `backend/` consistent with `go.work`. +- **Secrets**: never commit tokens/keys. Use `.gitleaksignore` fingerprints only + for historical mock fixtures. +- **Blame hygiene**: record formatting-only passes in `.git-blame-ignore-revs`. + +## 8. Keeping the board green + +**Toolchain** — `mise install` (`.mise.toml`) pins node/pnpm/python/go/rust plus +`shellcheck`, `shfmt`, `hadolint`, `actionlint`, `ruff`, `gitleaks` and `typos`. +Git hooks resolve tools through `scripts/run-tool.sh`, which fails with an +actionable install hint instead of silently skipping a check. + +**Git hooks** (`lefthook.yml`, installed by `pnpm install`): + +- pre-commit: prettier, server eslint, ast-grep, shellcheck, hadolint, + actionlint, ruff (check + format), typos and gitleaks. +- commit-msg: commitlint (Conventional Commits). +- pre-push: server typecheck + lint, per-workspace frontend lint, cargo fmt + + clippy on changed crates, golangci-lint, report-only knip and an optional + local `sonar analyze --base origin/develop` (skipped without + `~/.config/sonar/env`). + +**Lint rules** — `eslint.config.shared.mjs` holds the curated local equivalents +of the SonarCloud rules (sonarjs, regexp, unicorn, vuejs-accessibility) for +every workspace; `server/ruff.toml` covers Python under `server/dev-tools`. + +**SonarCloud** — project `Heretek-Games_drop`, automatic analysis on push; the +gate enforces the last 30 days of new code. `server/prisma/migrations/**` and +vendored `libraries/{libarchive,native_model}/**/Cargo.toml` are excluded. +Triage policy: fix the root cause, else a justified `NOSONAR` on the exact line, +else accept with a rationale comment via the API. `knip` stays report-only until +its unused/duplicate exports are triaged. + +**Agents** — opencode formats edited files via `.opencode/plugin/quality.js` +and documents the workflows in `.opencode/skills/`; Claude Code uses the +PostToolUse hook in `.claude/settings.json`; Cursor and Codex get Sonar secrets +scanning plus the SonarQube MCP server (`.mcp.json`, `.cursor/mcp.json`, +`.codex/config.toml`). The shared per-file formatter is +`scripts/agent-quality.sh`. Restart opencode after config changes. + + + +# SonarQube secrets scanning for files protocol + +Before reading any file in this workspace, when the deterministic scanner is +available (`command -v sonar`), scan it for secrets: + +```bash +sonar analyze secrets +``` + +If the `sonar` CLI is not installed, skip this step; the git hooks and CI still +run gitleaks. If the command reports that the file contains a secret, **do not +read the file**. Instead: + +1. Inform the user that the file appears to contain a secret or credential and that reading it would expose the value in chat history, logs, and any downstream telemetry. +2. Advise them to rotate the leaked credential at its source of truth and remove it from the file. +3. Do not proceed with the original request until the secret has been removed. + + diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 000000000..262fbe5a7 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,74 @@ +# Drop — Claude Code guide + +Self-hosted game distribution platform (polyglot monorepo). + +> **Canonical guide: read [`AGENTS.md`](./AGENTS.md)** — it covers the monorepo +> layout, every key subsystem (Game Distribution pipeline & recipes, desktop +> native pipeline runner, admin bulk auto-import, depot chunk cache, plugin SPI, +> launch interceptors, Tauri window model), toolchain, commands, environment +> quirks and conventions. This file only keeps Claude-specific operational notes. +> +> Note: `drop` is part of the [Heretek Games](https://github.com/Heretek-Games) +> workspace. External plugins (`drop-gse`, `drop-zerotier`, `drop-plugin-sdk`, +> `drop-gamebox`, `drop-seedbox`, `drop-federation`) live in sister repos. + +## Quick orientation + +- `server/` — Nuxt 3 + Vue 3 (TypeScript, Prisma 7.10, Tailwind, buf/protobuf) +- `backend/` — Go (`go.work`, module `core/`) +- `desktop/src-tauri/` — Rust cargo workspace (Tauri v2 + process/pipeline crates) +- `desktop/main/` — Nuxt `view` UI, **separate pnpm workspace** +- `torrential/` — standalone Rust depot/chunk HTTP server +- `cli/` — Rust `downpour`; `libraries/` — `droplet`, `droplet_types`, `libarchive`, `native_model`, `base/` +- Root pnpm workspace: `server/`, `libraries/base/`, `sites/*`, `desktop/` + +## Quality gates — what runs when + +| Layer | When | What | +| :------------------ | :--------- | :------------------------------------------------------------------------------------ | +| Claude Code hooks | every edit | format-on-edit (advisory) | +| lefthook pre-commit | commit | prettier + eslint --fix on staged files, ast-grep scan, gitleaks | +| lefthook pre-push | push | server typecheck, clippy (changed crates), golangci-lint, knip report | +| GitHub Actions | PR/push | typecheck/lint/clippy + gitleaks history scan + cargo-audit ×7 crates + golangci-lint | +| GitHub Actions | weekly | semgrep deep scan → Code Scanning | + +Hooks are early feedback; **CI is the authority**. Never disable a hook to make a +failure go away — read the output and fix it. Known red-at-baseline gate: +`torrential` clippy (~227 pre-existing errors, mostly generated `src/proto/version.rs`) +— torrential CI only builds + tests. + +### Escape hatches + +```sh +git commit --no-verify # skip pre-commit once +git push --no-verify # skip pre-push once +LEFTHOOK=0 git commit # same via env var +``` + +## Commands + +```sh +pnpm exec lefthook run pre-commit --all-files # dry-run all pre-commit checks +pnpm exec lefthook run pre-push --all-files # dry-run all pre-push checks +pnpm --filter drop run typecheck # server +node_modules/.bin/jiti server/server/internal/library/pipeline/__tests__/pipeline.test.ts +pnpm exec ast-grep scan [paths] # structural lint (sgconfig.yml) +pnpm exec knip --reporter compact # unused deps/exports/files report +cd backend && golangci-lint run ./core/... # Go lint (.golangci.yml in backend/) +gitleaks protect --staged # secret scan staged changes +``` + +Native binaries NOT installable via pnpm: + +- `gitleaks` — `brew install gitleaks` +- `cargo-audit` — `cargo install cargo-audit` +- `golangci-lint` — `go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latest` + (must be built with the same Go version as `backend/go.work` or typechecking fails) + +## Conventions + +- **Commits**: Conventional Commits (`feat(desktop): ...`, `fix(server): ...`), + one logical change per commit. +- Rust: nightly; prefer explicit errors over `unwrap`/`expect` in new code. +- Formatting: Prettier at the repo root (JS/TS/Vue/YAML/MD); `gofmt`/`goimports` for Go. +- Never commit secrets; never commit `server/prisma/client/`. diff --git a/Dockerfile b/Dockerfile index ecc594e2e..6fe9ea57a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -26,14 +26,15 @@ RUN pnpm install --frozen-lockfile --ignore-scripts FROM rustlang/rust:nightly-bookworm-slim AS torrential-build ## libarchive-dev + pkg-config let libarchive3-sys link libarchive dynamically (glibc). ## protobuf-compiler is kept for parity (torrential's build.rs uses a vendored protoc). +# hadolint ignore=DL3008 RUN apt-get update && apt-get install -y --no-install-recommends \ - pkg-config \ libarchive-dev \ + pkg-config \ protobuf-compiler \ && rm -rf /var/lib/apt/lists/* WORKDIR /build COPY . . -RUN cargo build --release --manifest-path ./torrential/Cargo.toml +RUN cargo build --release --locked --manifest-path ./torrential/Cargo.toml ### BUILD APP FROM base AS build-system @@ -42,6 +43,7 @@ ENV NODE_ENV=production ENV NUXT_TELEMETRY_DISABLED=1 ## add git so drop can determine its git ref at build +# hadolint ignore=DL3008 RUN apt-get update && apt-get install -y --no-install-recommends git \ && rm -rf /var/lib/apt/lists/* @@ -69,25 +71,26 @@ ENV NUXT_TELEMETRY_DISABLED=1 # the cwd for `torrential`, and a directory there is spawned as ./torrential and # fails with EACCES. With it gone, resolution falls through to the `torrential` # binary installed on PATH (/usr/bin/torrential) below. -RUN rm -rf /app/torrential - -# RUN --mount=type=cache,target=/root/.yarn YARN_CACHE_FOLDER=/root/.yarn yarn add --network-timeout 1000000 --no-lockfile --ignore-scripts prisma@6.11.1 -## runtime deps: -## - libarchive13: torrential now links libarchive dynamically (glibc build) -## - p7zip-full: provides the 7z CLI -## - nginx: front-end proxy -## - openssl + ca-certificates: required by Prisma's query engine on Debian -## pnpm itself is provided by corepack (enabled in the base stage) -RUN apt-get update && apt-get install -y --no-install-recommends \ +# +# runtime deps: +# - libarchive13: torrential now links libarchive dynamically (glibc build) +# - p7zip-full: provides the 7z CLI +# - nginx: front-end proxy +# - openssl + ca-certificates: required by Prisma's query engine on Debian +# pnpm itself is provided by corepack (enabled in the base stage). +# Install prisma globally and initialise it to download all required files. +# hadolint ignore=DL3008 +RUN rm -rf /app/torrential \ + && apt-get update \ + && apt-get install -y --no-install-recommends \ + ca-certificates \ libarchive13 \ - p7zip-full \ nginx \ openssl \ - ca-certificates \ - && rm -rf /var/lib/apt/lists/* -RUN pnpm install prisma@7.7.0 --global -# init prisma to download all required files -RUN pnpm prisma init + p7zip-full \ + && rm -rf /var/lib/apt/lists/* \ + && pnpm install prisma@7.10.0 --global --ignore-scripts \ + && pnpm prisma init COPY --from=build-system /app/server/prisma.config.ts ./ COPY --from=build-system /app/server/.output ./app @@ -96,6 +99,18 @@ COPY --from=build-system /app/server/build ./startup COPY --from=build-system /app/server/build/nginx.conf /nginx.conf COPY --from=torrential-build /build/torrential/target/release/torrential /usr/bin/ +# Run as an unprivileged user. Drop (port 4000) and nginx (port 3000) both bind +# unprivileged ports. Operators using bind-mounted /data or /library must make +# the mount writable by uid 10001, or run the container with a matching --user. +RUN groupadd --system --gid 10001 drop \ + && useradd --system --uid 10001 --gid drop --home-dir /app --shell /usr/sbin/nologin drop \ + && mkdir -p /pnpm /data /library \ + && chown -R drop:drop /app /pnpm /data /library + +ENV HOME="/app" +# Numeric id so hadolint can verify it; matches the `drop` user created above. +USER 10001:10001 + ENV LIBRARY="/library" ENV DATA="/data" ENV NGINX_CONFIG="/nginx.conf" diff --git a/GEMINI.md b/GEMINI.md new file mode 100644 index 000000000..2a5ef7ff1 --- /dev/null +++ b/GEMINI.md @@ -0,0 +1,65 @@ +# Drop — Gemini / Antigravity guide + +**Drop** is an open-source, self-hosted game distribution and multiplayer +platform maintained by [Heretek Games](https://github.com/Heretek-Games/drop). + +> **Canonical guide: read [`AGENTS.md`](./AGENTS.md)** — it is the source of +> truth for the monorepo layout, architecture, quality gates, toolchain, +> commands, environment quirks and conventions. This file is a thin wrapper for +> Gemini CLI / Google Antigravity sessions; update `AGENTS.md` first and keep +> this in sync. +> +> Note: `drop` is part of the [Heretek Games](https://github.com/Heretek-Games) +> workspace. External plugins (`drop-gse`, `drop-zerotier`, `drop-plugin-sdk`, +> `drop-gamebox`, `drop-seedbox`, `drop-federation`) live in sister repos. + +## Orientation + +Polyglot monorepo: + +- `server/` — Nuxt 3 + Vue 3 (TypeScript, Prisma 7.10, Tailwind, buf/protobuf): + REST API, WebSocket pub/sub, library manager, plugin SPI, Game Distribution + pipeline & declarative recipes. +- `backend/` — Go (`go.work`, module `core/`). +- `desktop/src-tauri/` — Rust cargo workspace: Tauri commands, process manager, + launch interceptors, native pipeline runner (`process/src/pipeline.rs`). +- `desktop/main/` — Nuxt 4 `view` UI (separate pnpm workspace). +- `torrential/` — standalone Rust depot/chunk HTTP server (with the opt-in + content-addressed chunk cache). +- `cli/` — Rust `downpour`; `libraries/` — `droplet`, `droplet_types`, + `libarchive`, `native_model`, plus the `base/` TS UI layer. +- `distribution/` (WinGet/Flatpak), `sites/` (docs, promo). + +## Systems to be aware of + +See `AGENTS.md` §2 for detail. Highlights relevant to recent work: + +- **Game Distribution pipeline & recipes** — `server/server/internal/library/pipeline/` + classifies releases and emits `PipelineRecipe`s embedded at + `GameVersion.dropletManifest.recipe`. +- **Desktop native pipeline runner** — `process/src/pipeline.rs` + + `GameSetupModal.vue`; emits `pipeline_progress` / `pipeline_completed`, + IPC `start_pipeline_setup` / `cancel_pipeline_setup` / `reclaim_pipeline_space`. +- **Admin bulk auto-import** — `DiscoveredGame` model, `import:discover` / + `import:bulk` tasks, `pages/admin/import/bulk.vue`. +- **Depot chunk cache** — `torrential/src/downloads/cache.rs`, opt-in via + `CHUNK_CACHE_DIR` / `CHUNK_CACHE_MAX_BYTES`. +- **Podman Quadlet deployment** — `server/deploy-template/quadlet/` provides + systemd-native units for Drop and ZTNET mesh coordination. + +## Working notes for Gemini / Antigravity + +- **Follow `AGENTS.md` for commands and gates.** Prefer `cargo +nightly`, + `pnpm --filter drop run typecheck`, and `jiti` for server `node:test` files + (no test runner is wired). +- **Never bypass hooks to hide a failure.** `git push --no-verify` is documented + only for the known `torrential` clippy baseline (generated `src/proto/version.rs` + - `build.rs`); torrential CI intentionally does not run clippy. +- **`desktop/main` is a separate pnpm workspace** — use `pnpm -C desktop/main ...`. +- **Headless desktop checks**: `cargo check` needs GTK/WebKit pkg-config shims + (`PKG_CONFIG_PATH="$HOME/.local/lib/pkgconfig"`); `cargo test` for the desktop + crate still requires real GTK/WebKit libraries to link. +- **Prisma**: multi-file schema under `server/prisma/models/`; add migrations + under `server/prisma/migrations/_/migration.sql`; run + `pnpm exec prisma generate` after schema changes. +- **Commits**: Conventional Commits, one logical change per commit. diff --git a/README.md b/README.md index 74af78cf6..783ce5a2c 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,10 @@ Drop is an open-source game distribution platform, similar to GameVault or Steam 2. Drop is secure. The nature of Drop means an instance can never be accessible without authentication. In line with #1, Drop also supports a huge variety of authentication mechanisms, from username/password to SSO. 3. Drop is user-friendly. The interface is designed to be clean and simple to use, with advanced features available to users who want them. +## Roadmap & Architecture + +See our [Roadmap](./ROADMAP.md) for our long-term architectural blueprint and vision for building an open-source, federated alternative to Steam. + ## Deployment See our documentation on how to [deploy Drop](https://droposs.org/docs/admin/quickstart) for more information. diff --git a/ROADMAP.md b/ROADMAP.md new file mode 100644 index 000000000..4f06caab0 --- /dev/null +++ b/ROADMAP.md @@ -0,0 +1,113 @@ +# Drop Project Roadmap — The Open-Source Steam Alternative + +Drop is building a modern, open-source, self-hosted, and federated alternative to Steam and proprietary game distribution platforms. This document outlines our long-term architectural pillars, core systems, and phased development roadmap. + +--- + +## 1. Vision & Architectural Pillars + +Steam dominates PC gaming not just because it is a store, but because it is an integrated operating environment for games. To provide a complete, DRM-free, community-governed alternative, Drop systematically addresses each layer of that ecosystem: + +| Steam Layer | Drop Replacement | Architectural Foundation | Tracking Issue | +| :----------------------------- | :---------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------ | :----------------------------------------------------- | +| **SteamPipe & SteamCMD** | **Droplet + Torrential + Downpour** | Chunked content-addressed storage, LAN P2P streaming, binary delta updates, and the `downpour push` developer release CLI. | [#17](https://github.com/Heretek-Games/drop/issues/17) | +| **Steam Client & Big Picture** | **Drop Desktop & Deck UI** | Tauri v2 + Nuxt 4 client with a dedicated 10-foot gamepad interface, on-screen keyboard, and power management hooks. | [#18](https://github.com/Heretek-Games/drop/issues/18) | +| **Steam Input** | **Drop Input** | Low-level cross-platform controller remapping (`evdev`/`uinput` on Linux, ViGEm on Windows), gyro aiming, and community layouts. | [#18](https://github.com/Heretek-Games/drop/issues/18) | +| **GameOverlayRenderer** | **Drop In-Game Overlay** | Lightweight Vulkan layer (`VK_LAYER_DROP_overlay`) and DXGI hook providing in-game FPS, chat, friends, and screenshots (`Shift+Tab`). | [#18](https://github.com/Heretek-Games/drop/issues/18) | +| **Steamworks SDK** | **Dropworks SDK** | Open C/Rust/C#/GDScript game integration SDK with zero-config WebRTC NAT traversal, lobbies, cloud saves, and achievements. | [#19](https://github.com/Heretek-Games/drop/issues/19) | +| **Steam Community & Friends** | **Drop Social & Hubs** | Federated presence, party invites, cross-instance chat (WebSockets/Matrix), user guides, and verified playtime reviews. | [#20](https://github.com/Heretek-Games/drop/issues/20) | +| **Steam Workshop** | **Drop Workshop** | Open `mod.json` package format, 1-click subscription/installation, automatic updates, and load order management. | [#20](https://github.com/Heretek-Games/drop/issues/20) | +| **Steam Store (30% cut)** | **Federated Storefront** | Open Depot Protocol (ODP), catalog syndication, 0–5% platform fee, and offline-verifiable Ed25519 license receipts. | [#21](https://github.com/Heretek-Games/drop/issues/21) | + +> **Master Epic Tracking Issue:** [#22 — Drop as the Open-Source Steam Alternative](https://github.com/Heretek-Games/drop/issues/22) + +--- + +## 2. Phased Roadmap + +### Phase 1: High-Performance Content Delivery & Developer Publishing ([#17](https://github.com/Heretek-Games/drop/issues/17)) + +- **Local LAN P2P Chunk Sharing**: + - Automatic peer discovery via mDNS on local subnets. + - P2P chunk transfer over HTTP at multi-gigabit LAN speeds, reducing WAN bandwidth during LAN parties and multi-device homes. (QUIC is a deferred/optional transport: only the `ChunkFetcher` contract exists today, and HTTP is the implemented transport.) +- **`downpour push` Developer Tooling**: + - Headless CLI for game developers to upload builds to target depots and manage release branches (`main`, `beta`, `nightly`). + - Rolling hash chunking, Zstandard compression, and binary deltas. +- **Shader Pre-caching & Runtime Depots**: + - Automated compilation and distribution of DXVK/VKD3D pipeline caches to eliminate shader compilation stutter on Linux/Steam Deck. + +### Phase 2: Universal Runtime, Controller Mapper & In-Game Overlay ([#18](https://github.com/Heretek-Games/drop/issues/18)) + +- **Drop Big Picture / Handheld Mode**: + - 10-foot controller-first UI with focus management and virtual keyboard for Steam Deck, ASUS ROG Ally, and living-room setups. + - Battery awareness and clean sleep/suspend/resume handling. +- **"Drop Input" Virtual Controller Subsystem**: + - Native gamepad translation, custom deadzones, gyro-to-mouse mapping, and shareable per-game input configurations. +- **"Drop In-Game Overlay"**: + - Non-intrusive Vulkan/DirectX graphics overlay hook providing real-time FPS/frametime telemetry, friends chat, screenshot capture, and controller rebinding. + +### Phase 3: "Dropworks" Game SDK & Zero-Config Multiplayer ([#19](https://github.com/Heretek-Games/drop/issues/19)) + +- **Native Dropworks SDK**: + - Clean, idiomatic client libraries for C, Rust, C# (.NET/Unity), and GDScript (Godot). + - High-level APIs for authentication, matchmaking lobbies, encrypted P2P messaging, and cloud save sync. +- **Zero-Config NAT Traversal & Userspace Mesh**: + - Embedded WebRTC DataChannels and ICE/STUN/TURN negotiation, eliminating mandatory external daemons (`zerotier-cli`). + - Seamless evolution of `drop-gse` for legacy games and Dropworks for new indie titles. +- **Verifiable Achievements & Global Leaderboards**: + - Server-side stats tracking, time-stamped milestone unlocks, and anti-tamper challenge verification. + +### Phase 4: Social Graph, Community Hubs & The Drop Workshop ([#20](https://github.com/Heretek-Games/drop/issues/20)) + +- **Federated Friends & Rich Presence**: + - Real-time status ("In Main Menu", "In Level 4 - 3/4 Players [Join]"). + - Seamless in-game invites and cross-instance messaging. +- **Community Hubs & Verified Playtime Reviews**: + - Dedicated game discussion spaces, user screenshot showcases, and reviews displaying playtime verified by the Drop client. +- **The Drop Workshop**: + - Standardized mod manifest (`mod.json`) and repository engine. + - 1-click mod subscriptions, dependency resolution, version locking, and conflict detection. + +### Phase 5: Catalog Federation, Indie Commerce & Decentralized Store ([#21](https://github.com/Heretek-Games/drop/issues/21)) + +- **Open Depot Protocol (ODP) & Federation**: + - Decentralized catalog syndication allowing home servers to subscribe to verified community and indie repositories. +- **Fair Developer Commerce**: + - Direct developer monetization via Stripe, PayPal, and open crypto (BTCPay). + - Low or zero platform take-rate (0–5%) directly returning revenue to creators. + - Cryptographically signed (Ed25519) ownership receipts with zero intrusive DRM. +- **Universal Multi-Store Aggregator**: + - Import existing libraries from Steam, GOG, Epic, and itch.io into Drop, establishing Drop as the singular daily-driver gaming hub. + +--- + +## 3. Long-term goal tracks (Heretek fork) + +Beyond the phased roadmap above, the following long-term tracks are tracked as +epics. They are complementary to the subsystem issues (#6–#15) and the phase +epics (#17–#21). + +| Track | Sister Repository | Milestone | Summary | +| :--------------------------------------------------- | :-------------------------------------------------------------------------------- | :-------- | :----------------------------------------------------------------------------------------------------- | +| **Plugin SDK & Extensibility** | [Heretek-Games/drop-plugin-sdk](https://github.com/Heretek-Games/drop-plugin-sdk) | M1 | Official `@droposs/plugin-sdk`, `drop-plugin` build/sign toolchain, and plugin runtime contracts. | +| **Shared Game Metadata & Hash Database ("GameBox")** | [Heretek-Games/drop-gamebox](https://github.com/Heretek-Games/drop-gamebox) | M2 | A stash-box-style fingerprint index: hash a game folder/executable and get back identity and metadata. | +| **Seedbox / qBittorrent Remote Depots** | [Heretek-Games/drop-seedbox](https://github.com/Heretek-Games/drop-seedbox) | M3 | qBittorrent integration and streaming remote depots, extending the Droplet/Torrential content system. | +| **Friends Federation & Instance Peering** | [Heretek-Games/drop-federation](https://github.com/Heretek-Games/drop-federation) | M4 | Syncthing-style instance identity and optional peering over an optional port forward or VPN. | +| **GSE Multiplayer Engine** | [Heretek-Games/drop-gse](https://github.com/Heretek-Games/drop-gse) | M1/M3 | Goldberg Steam Emulator patcher, fail-closed anti-cheat checks, and room orchestration. | +| **ZeroTier & Mesh Orchestration** | [Heretek-Games/drop-zerotier](https://github.com/Heretek-Games/drop-zerotier) | M1/M3 | ZeroTier One and ZTNET virtual network controller, Quadlet templates, and daemon manager. | + +The common thread: all six are developed and maintained in **dedicated sister repositories** as external plugins and modular extensions, keeping the core Drop distribution lean and directly compatible with upstream `Drop-OSS/drop`. + +For the comprehensive issue-by-issue categorization of all tracker items into Core vs. Plugins, see [`docs/implementation/plugin-boundary-plan.md`](./docs/implementation/plugin-boundary-plan.md). + +--- + +## 4. Contributing + +We welcome contributions across all languages and layers of the stack: + +- **Rust**: `torrential` (depots & chunk cache), `cli` (`downpour`), `desktop/src-tauri` (`process`, `gse-engine`, runtime). +- **TypeScript / Vue / Nuxt**: `server` (REST/WS API, library manager, plugins) and `desktop/main` (client UI). +- **Go**: `backend/core/` (background services). + +Please consult [`AGENTS.md`](./AGENTS.md) for architectural conventions, toolchain requirements, and pre-push quality gates. diff --git a/_typos.toml b/_typos.toml new file mode 100644 index 000000000..0150b24c4 --- /dev/null +++ b/_typos.toml @@ -0,0 +1,24 @@ +# Project-specific words that typos would otherwise flag, and files it should +# not scan (binary fixtures). +[files] +extend-exclude = [ + "sites/promo/public/key.gpg", + "server/i18n/locales/**", + # Historical changelog quotes old commit messages verbatim. + "desktop/changelog.md", + # Historical Prisma migrations are immutable (checksummed) and generated + # build output is not source. + "server/prisma/migrations/**", + "server/.output/**", + "**/target/**", + "**/dist/**", + "**/.nuxt/**", +] + +[default.extend-words] +# Auth "mechanism" (AuthMec enum) shorthand used across the server. +Mek = "Mek" +# Rust enum variant name; "unparseable" is intentional. +Unparseable = "Unparseable" +# Steam API field name. +packageid = "packageid" diff --git a/backend/.golangci.yml b/backend/.golangci.yml new file mode 100644 index 000000000..00adaef68 --- /dev/null +++ b/backend/.golangci.yml @@ -0,0 +1,35 @@ +# golangci-lint v2 configuration. +# Run: cd backend && golangci-lint run +# CI: golangci-lint-action@v8 with --new-from-rev=origin/develop (rollout mode). +version: "2" + +run: + timeout: 5m + +linters: + default: none + enable: + - gosec # security-focused linter + - errcheck + - govet + - staticcheck + - ineffassign + - unused + - gocognit # cognitive complexity (Sonar S3776 equivalent) + - gocyclo + - misspell + settings: + gocognit: + min-complexity: 15 + gocyclo: + min-complexity: 15 + exclusions: + rules: + - path: _test\.go + linters: + - gosec # test fixtures routinely trip G201/G404 etc. + +formatters: + enable: + - gofmt + - goimports diff --git a/backend/core/go.mod b/backend/core/go.mod index ae1311a4a..f0339298e 100644 --- a/backend/core/go.mod +++ b/backend/core/go.mod @@ -1,11 +1,11 @@ module drop/core -go 1.26.1 +go 1.26.6 require github.com/jackc/pgx/v5 v5.9.2 require ( github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect - golang.org/x/text v0.29.0 // indirect + golang.org/x/text v0.39.0 // indirect ) diff --git a/backend/core/go.sum b/backend/core/go.sum index f5b2410e9..b565b5e04 100644 --- a/backend/core/go.sum +++ b/backend/core/go.sum @@ -16,10 +16,10 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= -golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= -golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= -golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= +golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM= +golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/text v0.39.0 h1:UbZz4pLOvn600D6Oh6GGEI6VAmndrEBLv8/6BEXzyus= +golang.org/x/text v0.39.0/go.mod h1:3UwRclnC2g0TU9x8PZiyfOajCd1zaUNHF9cvqcQZ+ZM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/backend/go.mod b/backend/go.mod index 003669675..b689b4a33 100644 --- a/backend/go.mod +++ b/backend/go.mod @@ -1,5 +1,5 @@ module drop -go 1.26.1 +go 1.26.6 require github.com/gorilla/mux v1.8.1 diff --git a/backend/go.work b/backend/go.work index f1da35088..b78275c86 100644 --- a/backend/go.work +++ b/backend/go.work @@ -1,3 +1,3 @@ -go 1.26.1 +go 1.26.6 use ./core diff --git a/backend/main.go b/backend/main.go index d5d0c8562..e92d4bd59 100644 --- a/backend/main.go +++ b/backend/main.go @@ -16,7 +16,7 @@ func routingMiddleware(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { url := *r.URL url.Path = strings.TrimSuffix(r.URL.Path, "/") - r.URL = &url + r.URL = &url // NOSONAR: no outbound request is made; URL is only rewritten for the router h.ServeHTTP(w, r) }) diff --git a/cli/Cargo.lock b/cli/Cargo.lock index 093b9014b..ef25aea81 100644 --- a/cli/Cargo.lock +++ b/cli/Cargo.lock @@ -2,35 +2,20 @@ # It is not intended for manual editing. version = 4 -[[package]] -name = "addr2line" -version = "0.25.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" - [[package]] name = "android_system_properties" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +checksum = "ae221649c9976a6f6c56ae1facf410f3ddb33cc661c4b7b61020a912d4237fbc" dependencies = [ "libc", ] [[package]] name = "anstream" -version = "0.6.21" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" +checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" dependencies = [ "anstyle", "anstyle-parse", @@ -43,15 +28,15 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" [[package]] name = "anstyle-parse" -version = "0.2.7" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" dependencies = [ "utf8parse", ] @@ -78,9 +63,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.100" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" +checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470" [[package]] name = "asn1-rs" @@ -100,9 +85,9 @@ dependencies = [ [[package]] name = "asn1-rs" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56624a96882bb8c26d61312ae18cb45868e5a9992ea73c58e45c3101e56a1e60" +checksum = "b7f43a50ac4fdca5df8e885c21b835997f0a1cdee65494a6847694a98652d9d8" dependencies = [ "asn1-rs-derive 0.6.0", "asn1-rs-impl", @@ -110,7 +95,7 @@ dependencies = [ "nom", "num-traits", "rusticata-macros", - "thiserror 2.0.17", + "thiserror 2.0.20", "time", ] @@ -120,10 +105,10 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "965c2d33e53cb6b267e148a4cb0760bc01f4904c1cd4bb4002a085bb016d1490" dependencies = [ - "proc-macro2 1.0.103", - "quote 1.0.43", - "syn 2.0.114", - "synstructure 0.13.2", + "proc-macro2 1.0.107", + "quote 1.0.47", + "syn 2.0.119", + "synstructure", ] [[package]] @@ -132,10 +117,10 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c" dependencies = [ - "proc-macro2 1.0.103", - "quote 1.0.43", - "syn 2.0.114", - "synstructure 0.13.2", + "proc-macro2 1.0.107", + "quote 1.0.47", + "syn 2.0.119", + "synstructure", ] [[package]] @@ -144,20 +129,30 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" dependencies = [ - "proc-macro2 1.0.103", - "quote 1.0.43", - "syn 2.0.114", + "proc-macro2 1.0.107", + "quote 1.0.47", + "syn 2.0.119", ] [[package]] name = "async-trait" -version = "0.1.89" +version = "0.1.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82f6aeea286b8eb4dd3431a1be1b59d290ace00f5bfd8e2a159bc2a05e2c1667" +dependencies = [ + "proc-macro2 1.0.107", + "quote 1.0.47", + "syn 3.0.4", +] + +[[package]] +name = "asyncband" +version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +checksum = "94a214ba60d6231afd0e805e3c27c45a1626d9debaa5a5061c45a1ea1b2f1ed0" dependencies = [ - "proc-macro2 1.0.103", - "quote 1.0.43", - "syn 2.0.114", + "hashbrown 0.17.1", + "slab", ] [[package]] @@ -168,15 +163,15 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "autocfg" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" [[package]] name = "aws-lc-rs" -version = "1.15.2" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a88aab2464f1f25453baa7a07c84c5b7684e274054ba06817f382357f77a288" +checksum = "ce2b2dcc879c3bae0d371e77c99f2238400ef24ec001394befa67b6e543add9e" dependencies = [ "aws-lc-sys", "zeroize", @@ -184,14 +179,15 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.35.0" +version = "0.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b45afffdee1e7c9126814751f88dddc747f41d91da16c9551a0f1e8a11e788a1" +checksum = "f09fae7be8bb3174e05c6afdb34199e6dc0c7c04ba9fa237b1967adfbde27483" dependencies = [ "cc", "cmake", "dunce", "fs_extra", + "pkg-config", ] [[package]] @@ -206,31 +202,28 @@ dependencies = [ ] [[package]] -name = "backtrace" -version = "0.3.76" +name = "base64" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" -dependencies = [ - "addr2line", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", - "windows-link", -] +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64" -version = "0.22.1" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5" [[package]] name = "bitflags" -version = "2.10.0" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da" [[package]] name = "block-buffer" @@ -241,23 +234,32 @@ dependencies = [ "generic-array", ] +[[package]] +name = "block-buffer" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2f6c7dbe95a6ed67ad9f18e57daf93a2f034c524b99fd2b76d18fdfeb6660aa" +dependencies = [ + "hybrid-array", +] + [[package]] name = "bumpalo" -version = "3.19.0" +version = "3.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" [[package]] name = "bytes" -version = "1.11.0" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" +checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" [[package]] name = "cc" -version = "1.2.49" +version = "1.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90583009037521a116abf44494efecd645ba48b6622457080f080b85544e2215" +checksum = "0ad534f4357a5264cce5019c989cf66a4f0dc4e0d1b1d15f8aacec0ff7360273" dependencies = [ "find-msvc-tools", "jobserver", @@ -265,12 +267,6 @@ dependencies = [ "shlex", ] -[[package]] -name = "cesu8" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" - [[package]] name = "cfg-if" version = "1.0.4" @@ -279,15 +275,26 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "cfg_aliases" -version = "0.2.1" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f079e83a288787bcd14a6aea84cee5c87a67c5a3e660c30f557a3d24761b3527" + +[[package]] +name = "chacha20" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +checksum = "d524456ba66e72eb8b115ff89e01e497f8e6d11d78b70b1aa13c0fbd97540a81" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", + "rand_core 0.10.1", +] [[package]] name = "chrono" -version = "0.4.43" +version = "0.4.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118" +checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" dependencies = [ "iana-time-zone", "js-sys", @@ -298,9 +305,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.54" +version = "4.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6e6ff9dcd79cff5cd969a17a545d79e84ab086e444102a591e288a8aa3ce394" +checksum = "473c7e07f409a8d772161724aa8db6a765a2532a70f9667eeb7b49d3d02fbdca" dependencies = [ "clap_builder", "clap_derive", @@ -308,9 +315,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.54" +version = "4.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa42cf4d2b7a41bc8f663a7cab4031ebafa1bf3875705bfaf8466dc60ab52c00" +checksum = "7b48fea5a88e9ae728a2dcbedbfc0e730f7d60da42e1cb049a83c9fb8b789889" dependencies = [ "anstream", "anstyle", @@ -320,36 +327,42 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.49" +version = "4.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" +checksum = "d012d2b9d65aca7f18f4d9878a045bc17899bba951561ba5ec3c2ba1eed9a061" dependencies = [ "heck", - "proc-macro2 1.0.103", - "quote 1.0.43", - "syn 2.0.114", + "proc-macro2 1.0.107", + "quote 1.0.47", + "syn 3.0.4", ] [[package]] name = "clap_lex" -version = "0.7.6" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" [[package]] name = "cmake" -version = "0.1.57" +version = "0.1.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75443c44cd6b379beb8c5b45d85d0773baf31cce901fe7bb252f4eff3008ef7d" +checksum = "c0f78a02292a74a88ac736019ab962ece0bc380e3f977bf72e376c5d78ff0678" dependencies = [ "cc", ] +[[package]] +name = "cmov" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c9ea0ac24bc397ab3c98583a3c9ba74fa56b09a4449bbe172b9b1ddb016027a" + [[package]] name = "colorchoice" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" +checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" [[package]] name = "colored" @@ -358,14 +371,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c" dependencies = [ "lazy_static", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "combine" -version = "4.6.7" +version = "4.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +checksum = "cfc320937d09e6de266b31b9afb480f197d7a861be86be7cb2ea7e5d1bfffc5e" dependencies = [ "bytes", "memchr", @@ -373,22 +386,21 @@ dependencies = [ [[package]] name = "console" -version = "0.16.2" +version = "0.16.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03e45a4a8926227e4197636ba97a9fc9b00477e9f4bd711395687c5f0734bec4" +checksum = "4fe5f465a4f6fee88fad41b85d990f84c835335e85b5d9e6e63e0d06d28cba7c" dependencies = [ "encode_unicode", "libc", - "once_cell", "unicode-width", "windows-sys 0.61.2", ] [[package]] name = "const-oid" -version = "0.9.6" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" +checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" [[package]] name = "const-random" @@ -405,21 +417,11 @@ version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" dependencies = [ - "getrandom 0.2.16", + "getrandom 0.2.17", "once_cell", "tiny-keccak", ] -[[package]] -name = "core-foundation" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "core-foundation" version = "0.10.1" @@ -446,12 +448,22 @@ dependencies = [ ] [[package]] -name = "crc32c" -version = "0.6.8" +name = "cpufeatures" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a47af21622d091a8f0fb295b88bc886ac74efcc613efc19f5d0b21de5c89e47" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" dependencies = [ - "rustc_version", + "libc", +] + +[[package]] +name = "crc-fast" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e75b2483e97a5a7da73ac68a05b629f9c53cff58d8ed1c77866079e18b00dba5" +dependencies = [ + "digest 0.10.7", + "spin", ] [[package]] @@ -470,11 +482,70 @@ dependencies = [ "typenum", ] +[[package]] +name = "crypto-common" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" +dependencies = [ + "hybrid-array", +] + +[[package]] +name = "ctor" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "914a755b7c2d4af2bdcff7ce1739e2db9a1b81a9b07123d8015786ae03c0980d" +dependencies = [ + "link-section", + "linktime-proc-macro", +] + +[[package]] +name = "ctutils" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5515a3834141de9eafb9717ad39eea8247b5674e6066c404e8c4b365d2a29e" +dependencies = [ + "cmov", +] + [[package]] name = "data-encoding" -version = "2.10.0" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4583a4551df46e2792f82ceeac45e850d2e2d5debba0b91f102385cda5b11f06" + +[[package]] +name = "defmt" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2953bfe4f93bbd20cc71198842756f77d161884c99ebbabc41d80231ded88d1" +dependencies = [ + "bitflags 1.3.2", + "defmt-macros", +] + +[[package]] +name = "defmt-macros" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bad9c72e7ca2137e0dc3813245a0d282fd6daad32fd800af018306a9169b5fe8" +dependencies = [ + "defmt-parser", + "proc-macro2 1.0.107", + "quote 1.0.47", + "syn 2.0.119", +] + +[[package]] +name = "defmt-parser" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" +checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e" +dependencies = [ + "thiserror 2.0.20", +] [[package]] name = "der-parser" @@ -496,7 +567,7 @@ version = "10.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07da5016415d5a3c4dd39b11ed26f915f52fc4e0dc197d87908bc916e51bc1a6" dependencies = [ - "asn1-rs 0.7.1", + "asn1-rs 0.7.2", "displaydoc", "nom", "num-bigint", @@ -506,12 +577,9 @@ dependencies = [ [[package]] name = "deranged" -version = "0.5.5" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" -dependencies = [ - "powerfmt", -] +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" [[package]] name = "dialoguer" @@ -531,10 +599,20 @@ version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer", + "block-buffer 0.10.4", + "crypto-common 0.1.7", +] + +[[package]] +name = "digest" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" +dependencies = [ + "block-buffer 0.12.1", "const-oid", - "crypto-common", - "subtle", + "crypto-common 0.2.2", + "ctutils", ] [[package]] @@ -560,13 +638,13 @@ dependencies = [ [[package]] name = "displaydoc" -version = "0.2.5" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8" dependencies = [ - "proc-macro2 1.0.103", - "quote 1.0.43", - "syn 2.0.114", + "proc-macro2 1.0.107", + "quote 1.0.47", + "syn 3.0.4", ] [[package]] @@ -586,7 +664,6 @@ dependencies = [ "async-trait", "chrono", "clap", - "console", "dialoguer", "dirs", "droplet-rs", @@ -595,14 +672,15 @@ dependencies = [ "indicatif", "log", "opendal", - "rand 0.9.3", - "reqwest 0.13.1", + "rand 0.9.5", "serde", "serde_json", + "sha2 0.10.9", + "tempfile", "tokio", "tokio-util", - "url", - "webbrowser", + "walkdir", + "zstd", ] [[package]] @@ -612,7 +690,6 @@ dependencies = [ "anyhow", "async-trait", "droplet_types", - "dyn-clone", "futures", "getrandom 0.3.4", "hex", @@ -620,10 +697,7 @@ dependencies = [ "libarchive-drop", "rcgen", "ring", - "serde", - "serde_json", - "sha2", - "speedometer", + "sha2 0.10.9", "test-generator", "time", "tokio", @@ -644,33 +718,12 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" -[[package]] -name = "dyn-clone" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" - [[package]] name = "encode_unicode" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" -[[package]] -name = "encoding_rs" -version = "0.8.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "equivalent" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" - [[package]] name = "errno" version = "0.3.14" @@ -681,33 +734,11 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "failure" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86" -dependencies = [ - "backtrace", - "failure_derive", -] - -[[package]] -name = "failure_derive" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4" -dependencies = [ - "proc-macro2 1.0.103", - "quote 1.0.43", - "syn 1.0.109", - "synstructure 0.12.6", -] - [[package]] name = "fastrand" -version = "2.3.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223" [[package]] name = "fern" @@ -721,15 +752,9 @@ dependencies = [ [[package]] name = "find-msvc-tools" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" - -[[package]] -name = "fnv" -version = "1.0.7" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +checksum = "d45db016d36b838f563236e9193d0ee6ce38f3f68b6c94e914b4929c96bbb890" [[package]] name = "form_urlencoded" @@ -748,9 +773,9 @@ checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" [[package]] name = "futures" -version = "0.3.31" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +checksum = "9a31d2a3fbaaeb2af2368bbdd904aa8e812d3c04a1ee10d3171f52d556e5d0a3" dependencies = [ "futures-channel", "futures-core", @@ -763,9 +788,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.31" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +checksum = "b1f9e3d69d39e4862ffed03ed071a76f9a13ba1d9109d355b0f0aa6b15e393c4" dependencies = [ "futures-core", "futures-sink", @@ -773,15 +798,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.31" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" +checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e" [[package]] name = "futures-executor" -version = "0.3.31" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +checksum = "031b47cf1a3c6cc8bc2fc76cd437f521619387907d469316e7c0bc278f1f5432" dependencies = [ "futures-core", "futures-task", @@ -790,38 +815,38 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.31" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" +checksum = "53c0fa8157de1303bfffdaa1cc2a673bfffb60102f76b0ef4441659124373fed" [[package]] name = "futures-macro" -version = "0.3.31" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +checksum = "9fb9654ba8355388abeb8dcb4fc62f511300867002afc858860463bdd9fe0c44" dependencies = [ - "proc-macro2 1.0.103", - "quote 1.0.43", - "syn 2.0.114", + "proc-macro2 1.0.107", + "quote 1.0.47", + "syn 3.0.4", ] [[package]] name = "futures-sink" -version = "0.3.31" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" +checksum = "1944426bf7d03f1d14f708785e4b33efd750b36d48a157b836b3efc15ede8e1d" [[package]] name = "futures-task" -version = "0.3.31" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" +checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd" [[package]] name = "futures-util" -version = "0.3.31" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc" dependencies = [ "futures-channel", "futures-core", @@ -831,7 +856,6 @@ dependencies = [ "futures-task", "memchr", "pin-project-lite", - "pin-utils", "slab", ] @@ -847,9 +871,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" dependencies = [ "cfg-if", "js-sys", @@ -865,24 +889,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", - "js-sys", "libc", - "r-efi", + "r-efi 5.3.0", "wasip2", - "wasm-bindgen", ] [[package]] -name = "gimli" -version = "0.32.3" +name = "getrandom" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "r-efi 6.0.0", + "rand_core 0.10.1", + "wasm-bindgen", +] [[package]] name = "glob" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" +checksum = "e4eba85ea1d0a966a983acd07deee566e67395d2d96b6fb39e62b5a833f1eb0b" [[package]] name = "gloo-timers" @@ -896,25 +926,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "h2" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54" -dependencies = [ - "atomic-waker", - "bytes", - "fnv", - "futures-core", - "futures-sink", - "http", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", -] - [[package]] name = "hashbrown" version = "0.14.5" @@ -923,9 +934,9 @@ checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "hashbrown" -version = "0.16.1" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" [[package]] name = "heck" @@ -941,27 +952,18 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "hmac" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" -dependencies = [ - "digest", -] - -[[package]] -name = "home" -version = "0.5.12" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d" +checksum = "6303bc9732ae41b04cb554b844a762b4115a61bfaa81e3e83050991eeb56863f" dependencies = [ - "windows-sys 0.61.2", + "digest 0.11.3", ] [[package]] name = "http" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +checksum = "918d3568bebf352712bc2ef3d46a8bcf1a75b373be6539de198e9105cbbf9ce0" dependencies = [ "bytes", "itoa", @@ -969,9 +971,9 @@ dependencies = [ [[package]] name = "http-body" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +checksum = "ca2a8f2913ee65f60facd6a5905613afaa448497a0230cc41ce022d93290bc2c" dependencies = [ "bytes", "http", @@ -979,9 +981,9 @@ dependencies = [ [[package]] name = "http-body-util" -version = "0.1.3" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +checksum = "23169fe34a5fbcdd3f3862e78fb9b6fccd5f02a6dc6f732547005d45631ce71c" dependencies = [ "bytes", "futures-core", @@ -1005,23 +1007,30 @@ dependencies = [ "libm", ] +[[package]] +name = "hybrid-array" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "707114b52a152fa7bdb290cd7cd5912d9467273b6d74e21b8d81aca1f8533f6b" +dependencies = [ + "typenum", +] + [[package]] name = "hyper" -version = "1.8.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" +checksum = "d22053281f852e11534f5198498373cbb59295120a20771d90f7ed1897490a72" dependencies = [ "atomic-waker", "bytes", "futures-channel", "futures-core", - "h2", "http", "http-body", "httparse", "itoa", "pin-project-lite", - "pin-utils", "smallvec", "tokio", "want", @@ -1029,31 +1038,28 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.27.7" +version = "0.27.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" +checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f" dependencies = [ "http", "hyper", "hyper-util", "rustls", - "rustls-pki-types", "tokio", "tokio-rustls", "tower-service", - "webpki-roots", ] [[package]] name = "hyper-util" -version = "0.1.19" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "727805d60e7938b76b826a6ef209eb70eaa1812794f9424d4a4e2d740662df5f" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" dependencies = [ - "base64", + "base64 0.22.1", "bytes", "futures-channel", - "futures-core", "futures-util", "http", "http-body", @@ -1063,18 +1069,16 @@ dependencies = [ "percent-encoding", "pin-project-lite", "socket2", - "system-configuration", "tokio", "tower-service", "tracing", - "windows-registry", ] [[package]] name = "iana-time-zone" -version = "0.1.64" +version = "0.1.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -1096,12 +1100,13 @@ dependencies = [ [[package]] name = "icu_collections" -version = "2.1.1" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" +checksum = "fa68d21081c4a05d5a901a1c62add574c77048b6a1c67be3b50ce0b60d4ca513" dependencies = [ "displaydoc", "potential_utf", + "utf8_iter", "yoke", "zerofrom", "zerovec", @@ -1109,9 +1114,9 @@ dependencies = [ [[package]] name = "icu_locale_core" -version = "2.1.1" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" +checksum = "d56e28588da92eee5c3201a6eff33fabdd49b62269c8938d4ff050ce4d900deb" dependencies = [ "displaydoc", "litemap", @@ -1122,9 +1127,9 @@ dependencies = [ [[package]] name = "icu_normalizer" -version = "2.1.1" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" +checksum = "12f9cf5f235641ed274641dd81c3f28d870e276763d0797aeeab72317b1c646f" dependencies = [ "icu_collections", "icu_normalizer_data", @@ -1136,16 +1141,17 @@ dependencies = [ [[package]] name = "icu_normalizer_data" -version = "2.1.1" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" +checksum = "1563da1ed3e0b3bf3d74c9b85917ac9c56464d2f57242270c09c9e752f8021a0" [[package]] name = "icu_properties" -version = "2.1.2" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" +checksum = "7e7ca276ad3145661a65914e6daf131ca5120cd3dcee8f8f3214b8875184a148" dependencies = [ + "displaydoc", "icu_collections", "icu_locale_core", "icu_properties_data", @@ -1156,15 +1162,15 @@ dependencies = [ [[package]] name = "icu_properties_data" -version = "2.1.2" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" +checksum = "e590f038c1464a96894fd6d10127e90a8be4509f56ff7ecef851b15cee0b7caa" [[package]] name = "icu_provider" -version = "2.1.1" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" +checksum = "d27bbb9d3abbefac45d55f647c9de1d44aafcd1186eb91879afef17c396c3e73" dependencies = [ "displaydoc", "icu_locale_core", @@ -1188,29 +1194,19 @@ dependencies = [ [[package]] name = "idna_adapter" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" dependencies = [ "icu_normalizer", "icu_properties", ] -[[package]] -name = "indexmap" -version = "2.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" -dependencies = [ - "equivalent", - "hashbrown 0.16.1", -] - [[package]] name = "indicatif" -version = "0.18.3" +version = "0.18.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9375e112e4b463ec1b1c6c011953545c65a30164fbab5b581df32b3abf0dcb88" +checksum = "9433806cd6b4ec1aba79c021c7e4c58fb4c3b9977c085062e611ac929998fb0c" dependencies = [ "console", "portable-atomic", @@ -1221,19 +1217,9 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" - -[[package]] -name = "iri-string" -version = "0.7.10" +version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c91338f0783edbd6195decb37bae672fd3b165faffb89bf7b9e6942f8b1a731a" -dependencies = [ - "memchr", - "serde", -] +checksum = "6a756c3fac73139e83f14c2d742155dd2b78d3ee56597b419a0579b7bdd6dd78" [[package]] name = "is_terminal_polyfill" @@ -1243,41 +1229,55 @@ checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" [[package]] name = "itoa" -version = "1.0.15" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "jiff" -version = "0.2.18" +version = "0.2.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e67e8da4c49d6d9909fe03361f9b620f58898859f5c7aded68351e85e71ecf50" +checksum = "668b7183bd07af9a4885f5c35b0cc5c83c4607a913c16b7e17291832910d2dcc" dependencies = [ + "defmt", + "jiff-core", "jiff-static", "jiff-tzdb-platform", + "js-sys", "log", "portable-atomic", "portable-atomic-util", "serde_core", - "windows-sys 0.61.2", + "wasm-bindgen", + "windows-link", +] + +[[package]] +name = "jiff-core" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7feca88439efe53da3754500c1851dedf3cb36c524dd5cf8225cc0794de95d09" +dependencies = [ + "defmt", ] [[package]] name = "jiff-static" -version = "0.2.18" +version = "0.2.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0c84ee7f197eca9a86c6fd6cb771e55eb991632f15f2bc3ca6ec838929e6e78" +checksum = "3a69dcb3a21cfb32ce1cd056169337ca284af0766dd766e7878819b251a49204" dependencies = [ - "proc-macro2 1.0.103", - "quote 1.0.43", - "syn 2.0.114", + "jiff-core", + "proc-macro2 1.0.107", + "quote 1.0.47", + "syn 2.0.119", ] [[package]] name = "jiff-tzdb" -version = "0.1.5" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68971ebff725b9e2ca27a601c5eb38a4c5d64422c4cbab0c535f248087eda5c2" +checksum = "142bd39932ad231f10513df9ab62661fead8719872150b7ad02a2df79f4e141e" [[package]] name = "jiff-tzdb-platform" @@ -1290,43 +1290,71 @@ dependencies = [ [[package]] name = "jni" -version = "0.21.1" +version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498" dependencies = [ - "cesu8", "cfg-if", "combine", + "jni-macros", "jni-sys", "log", - "thiserror 1.0.69", + "simd_cesu8", + "thiserror 2.0.20", "walkdir", - "windows-sys 0.45.0", + "windows-link", +] + +[[package]] +name = "jni-macros" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3" +dependencies = [ + "proc-macro2 1.0.107", + "quote 1.0.47", + "rustc_version", + "simd_cesu8", + "syn 2.0.119", ] [[package]] name = "jni-sys" -version = "0.3.0" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2" +dependencies = [ + "jni-sys-macros", +] + +[[package]] +name = "jni-sys-macros" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" +checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" +dependencies = [ + "quote 1.0.47", + "syn 2.0.119", +] [[package]] name = "jobserver" -version = "0.1.34" +version = "0.1.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +checksum = "1c00acbd29eabad4a2392fa0e921c874934dbbf4194312ad20f04a0ed67a3cb3" dependencies = [ - "getrandom 0.3.4", + "getrandom 0.4.3", "libc", ] [[package]] name = "js-sys" -version = "0.3.83" +version = "0.3.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" +checksum = "0e0c1080212aad755ea003d18543e8768dd432c48819efd73a7bf1e39b7a5a3a" dependencies = [ - "once_cell", + "cfg-if", + "futures-util", "wasm-bindgen", ] @@ -1356,9 +1384,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.178" +version = "0.2.189" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" +checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" [[package]] name = "libm" @@ -1368,31 +1396,42 @@ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "libredox" -version = "0.1.12" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616" +checksum = "28d0a00925a9f930d679b6789b721e3a7f9ed110f41b86d2497caa780c3a070a" dependencies = [ - "bitflags", "libc", ] +[[package]] +name = "link-section" +version = "0.19.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39c29a617ce3df32c08497bdc1ab6e2376e0b17948ac166a2fbe5977c5954cd9" + +[[package]] +name = "linktime-proc-macro" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e57c38c1e860fd37c604281cdfb1dd2216977fd76a50f85ba2f388ef3219616" + [[package]] name = "linux-raw-sys" -version = "0.11.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" [[package]] name = "litemap" -version = "0.8.1" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" +checksum = "47d9d19d1d6efa0109d2f65ff4c85cddd50bd572e5a00127ab10987290bcefae" [[package]] name = "log" -version = "0.4.29" +version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" +checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6" [[package]] name = "lru-slab" @@ -1402,25 +1441,29 @@ checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" [[package]] name = "md-5" -version = "0.10.6" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +checksum = "69b6441f590336821bb897fb28fc622898ccceb1d6cea3fde5ea86b090c4de98" dependencies = [ "cfg-if", - "digest", + "digest 0.11.3", ] [[package]] -name = "memchr" -version = "2.7.6" +name = "mea" +version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" +checksum = "c709842c4ce65cb91e2666ad5319dfc1efc3af0d34f02075eddca9000d9f8afb" +dependencies = [ + "hashbrown 0.17.1", + "slab", +] [[package]] -name = "mime" -version = "0.3.17" +name = "memchr" +version = "2.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" [[package]] name = "minimal-lexical" @@ -1428,32 +1471,17 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" -[[package]] -name = "miniz_oxide" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" -dependencies = [ - "adler2", -] - [[package]] name = "mio" -version = "1.1.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" +checksum = "30d65c71f1ce40ab09135ce117d742b9f8a19ff91a41a8b57ed50bc2de59c427" dependencies = [ "libc", "wasi", "windows-sys 0.61.2", ] -[[package]] -name = "ndk-context" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" - [[package]] name = "nom" version = "7.1.3" @@ -1466,9 +1494,9 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.4.6" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +checksum = "c89e69e7e0f03bea5ef08013795c25018e101932225a656383bd384495ecc367" dependencies = [ "num-integer", "num-traits", @@ -1476,15 +1504,15 @@ dependencies = [ [[package]] name = "num-conv" -version = "0.2.0" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" +checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" [[package]] name = "num-integer" -version = "0.1.46" +version = "0.1.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +checksum = "7ce2d95d4b3734dc35aa2f45e1aa22cd416814592a4f9d9205e11affd5b8e10b" dependencies = [ "num-traits", ] @@ -1498,40 +1526,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "objc2" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c2599ce0ec54857b29ce62166b0ed9b4f6f1a70ccc9a71165b6154caca8c05" -dependencies = [ - "objc2-encode", -] - -[[package]] -name = "objc2-encode" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" - -[[package]] -name = "objc2-foundation" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" -dependencies = [ - "bitflags", - "objc2", -] - -[[package]] -name = "object" -version = "0.37.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" -dependencies = [ - "memchr", -] - [[package]] name = "oid-registry" version = "0.7.1" @@ -1547,14 +1541,14 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "12f40cff3dde1b6087cc5d5f5d4d65712f34016a03ed60e9c08dcc392736b5b7" dependencies = [ - "asn1-rs 0.7.1", + "asn1-rs 0.7.2", ] [[package]] name = "once_cell" -version = "1.21.3" +version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" [[package]] name = "once_cell_polyfill" @@ -1564,38 +1558,129 @@ checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" [[package]] name = "opendal" -version = "0.55.0" +version = "0.58.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d075ab8a203a6ab4bc1bce0a4b9fe486a72bf8b939037f4b78d95386384bc80a" +checksum = "33dbff14cc9bb085224256d6a81289d2f3202e85b06f408d42534b42162a4231" +dependencies = [ + "ctor", + "opendal-core", + "opendal-http-transport-reqwest", + "opendal-layer-concurrent-limit", + "opendal-layer-logging", + "opendal-layer-retry", + "opendal-layer-timeout", + "opendal-service-s3", +] + +[[package]] +name = "opendal-core" +version = "0.58.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48dbcef97d3eb7591db2c18d5cae95c836bcce07359b98d98dd6f4e861eb77b7" dependencies = [ "anyhow", - "backon", - "base64", + "asyncband", + "base64 0.23.1", "bytes", - "crc32c", "futures", - "getrandom 0.2.16", "http", - "http-body", "jiff", "log", "md-5", "percent-encoding", - "quick-xml 0.38.4", - "reqsign", - "reqwest 0.12.28", + "quick-xml", + "reqsign-core", "serde", "serde_json", "tokio", "url", "uuid", + "web-time", +] + +[[package]] +name = "opendal-http-transport-reqwest" +version = "0.58.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85663452ea32bbc17e8f79ab29788c846d116ec7de31451be9c787e462dcb36c" +dependencies = [ + "bytes", + "futures", + "http", + "http-body", + "opendal-core", + "reqwest", +] + +[[package]] +name = "opendal-layer-concurrent-limit" +version = "0.58.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03f9e144b5228d741c3763ade8711d9b72e5fb6d998e779f2d7a09da0b5a3eba" +dependencies = [ + "asyncband", + "futures", + "http", + "opendal-core", +] + +[[package]] +name = "opendal-layer-logging" +version = "0.58.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2de17c61cd32e9d8d7d8efb91795e714dbccbafbc3c4e219e1542f4d6324161" +dependencies = [ + "log", + "opendal-core", +] + +[[package]] +name = "opendal-layer-retry" +version = "0.58.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e94db301964a25366090484d61e6da16d5979cc8faf02c3e12210dc74fafed38" +dependencies = [ + "backon", + "log", + "opendal-core", +] + +[[package]] +name = "opendal-layer-timeout" +version = "0.58.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08956ddda07465449bfd48825f4f0f25e0351278ac974eaa659895d9d74f2c80" +dependencies = [ + "opendal-core", + "tokio", +] + +[[package]] +name = "opendal-service-s3" +version = "0.58.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c64335f9f24ccb62ac36f1d976342b48611a75ba61979813a4f78a4ebd94de42" +dependencies = [ + "base64 0.23.1", + "bytes", + "crc-fast", + "http", + "log", + "md-5", + "opendal-core", + "quick-xml", + "reqsign-aws-v4", + "reqsign-core", + "reqsign-file-read-tokio", + "serde", + "url", ] [[package]] name = "openssl-probe" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f50d9b3dabb09ecd771ad0aa242ca6894994c130308ca3d7684634df8037391" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" [[package]] name = "option-ext" @@ -1619,7 +1704,7 @@ version = "3.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d30c53c26bc5b31a98cd02d20f25a7c8567146caf63ed593a9d87b2775291be" dependencies = [ - "base64", + "base64 0.22.1", "serde_core", ] @@ -1631,42 +1716,36 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "pin-project-lite" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" - -[[package]] -name = "pin-utils" -version = "0.1.0" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" [[package]] name = "pkg-config" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" +checksum = "f6b464fbc74e149a392436b17d523f769e057cb6877f6a5c4618bc6f11800548" [[package]] name = "portable-atomic" -version = "1.11.1" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" +checksum = "05c8b63e8d9609db387f0324918f81d68fe27748f084ef092fb35954d0539a85" [[package]] name = "portable-atomic-util" -version = "0.2.4" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" +checksum = "c2a106d1259c23fac8e543272398ae0e3c0b8d33c88ed73d0cc71b0f1d902618" dependencies = [ "portable-atomic", ] [[package]] name = "potential_utf" -version = "0.1.4" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +checksum = "d83eb9bc6d8e5cf568e7a1101d60ee05e81ed50ea106026f3d18deeb046d7661" dependencies = [ "zerovec", ] @@ -1692,33 +1771,23 @@ version = "0.4.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" dependencies = [ - "unicode-xid 0.1.0", + "unicode-xid", ] [[package]] name = "proc-macro2" -version = "1.0.103" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" dependencies = [ "unicode-ident", ] [[package]] name = "quick-xml" -version = "0.37.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb" -dependencies = [ - "memchr", - "serde", -] - -[[package]] -name = "quick-xml" -version = "0.38.4" +version = "0.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b66c2058c55a409d601666cffe35f04333cf1013010882cec174a7467cd4e21c" +checksum = "e660451e55124f798a69a5af3f49ccfbefbd41910eefd25caf2393e1f3473ec1" dependencies = [ "memchr", "serde", @@ -1726,9 +1795,9 @@ dependencies = [ [[package]] name = "quinn" -version = "0.11.9" +version = "0.11.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" +checksum = "0c1a41e437b6bbd489372cd4971de128e85c855f56c57f283d20ff016cf7c0a8" dependencies = [ "bytes", "cfg_aliases", @@ -1738,7 +1807,7 @@ dependencies = [ "rustc-hash", "rustls", "socket2", - "thiserror 2.0.17", + "thiserror 2.0.20", "tokio", "tracing", "web-time", @@ -1746,21 +1815,22 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.11.13" +version = "0.11.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" +checksum = "04759210543be93709136e28212294a659ef5001836ff4eab4d663e4529bba83" dependencies = [ "aws-lc-rs", "bytes", - "getrandom 0.3.4", + "getrandom 0.4.3", "lru-slab", - "rand 0.9.3", + "rand 0.10.2", + "rand_pcg", "ring", "rustc-hash", "rustls", "rustls-pki-types", "slab", - "thiserror 2.0.17", + "thiserror 2.0.20", "tinyvec", "tracing", "web-time", @@ -1768,16 +1838,16 @@ dependencies = [ [[package]] name = "quinn-udp" -version = "0.5.14" +version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" +checksum = "35a133f956daabe89a61a685c2649f13d82d5aa4bd5d12d1277e1072a21c0694" dependencies = [ "cfg_aliases", "libc", "once_cell", "socket2", "tracing", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -1791,11 +1861,11 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.43" +version = "1.0.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc74d9a594b72ae6656596548f56f667211f8a97b3d4c3d467150794690dc40a" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" dependencies = [ - "proc-macro2 1.0.103", + "proc-macro2 1.0.107", ] [[package]] @@ -1805,34 +1875,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] -name = "rand" -version = "0.8.5" +name = "r-efi" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" [[package]] name = "rand" -version = "0.9.3" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ec095654a25171c2124e9e3393a930bddbffdc939556c914957a4c3e0a87166" +checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41" dependencies = [ - "rand_chacha 0.9.0", - "rand_core 0.9.3", + "rand_chacha", + "rand_core 0.9.5", ] [[package]] -name = "rand_chacha" -version = "0.3.1" +name = "rand" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +checksum = "c7f5fa3a058cd35567ef9bfa5e75732bee0f9e4c55fa90477bef2dfcdbc4be80" dependencies = [ - "ppv-lite86", - "rand_core 0.6.4", + "chacha20", + "getrandom 0.4.3", + "rand_core 0.10.1", ] [[package]] @@ -1842,25 +1908,31 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", - "rand_core 0.9.3", + "rand_core 0.9.5", ] [[package]] name = "rand_core" -version = "0.6.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" dependencies = [ - "getrandom 0.2.16", + "getrandom 0.3.4", ] [[package]] name = "rand_core" -version = "0.9.3" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" + +[[package]] +name = "rand_pcg" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +checksum = "caa0f4137e1c0a72f4c651489402276c8e8e1cf081f3b0ba156d2cbeef09e86a" dependencies = [ - "getrandom 0.3.4", + "rand_core 0.10.1", ] [[package]] @@ -1883,92 +1955,90 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" dependencies = [ - "getrandom 0.2.16", + "getrandom 0.2.17", "libredox", - "thiserror 2.0.17", + "thiserror 2.0.20", +] + +[[package]] +name = "reqsign-aws-core" +version = "3.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bac4749b7dfa7bfaccd01eb03e9dc795ed37e3f20d6f0f38e2c67ee85ad6bc86" +dependencies = [ + "bytes", + "form_urlencoded", + "hex", + "http", + "log", + "percent-encoding", + "quick-xml", + "reqsign-core", + "rust-ini", + "serde", + "serde_json", + "serde_urlencoded", + "sha1", +] + +[[package]] +name = "reqsign-aws-v4" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff250f0fd0b913fbd565e405acc553da0f13bde30bfb5403178c9d0313cdc15f" +dependencies = [ + "bytes", + "http", + "log", + "quick-xml", + "reqsign-aws-core", + "reqsign-core", + "serde", ] [[package]] -name = "reqsign" -version = "0.16.5" +name = "reqsign-core" +version = "3.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43451dbf3590a7590684c25fb8d12ecdcc90ed3ac123433e500447c7d77ed701" +checksum = "ff052daffb0599681c50f85c59e7236438976efe991ab864edd9f3b235501a0f" dependencies = [ "anyhow", - "async-trait", - "base64", - "chrono", - "form_urlencoded", - "getrandom 0.2.16", + "base64 0.23.1", + "bytes", + "futures", "hex", "hmac", - "home", "http", + "jiff", "log", + "mea", "percent-encoding", - "quick-xml 0.37.5", - "rand 0.8.5", - "reqwest 0.12.28", - "rust-ini", - "serde", - "serde_json", "sha1", - "sha2", - "tokio", + "sha2 0.11.0", + "windows-sys 0.61.2", ] [[package]] -name = "reqwest" -version = "0.12.28" +name = "reqsign-file-read-tokio" +version = "3.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" +checksum = "b3235df90a6bca681aa47dd86f2393d122a6d77042aa8a7c81e218cd45c5bfc0" dependencies = [ - "base64", - "bytes", - "futures-core", - "futures-util", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-rustls", - "hyper-util", - "js-sys", - "log", - "percent-encoding", - "pin-project-lite", - "quinn", - "rustls", - "rustls-pki-types", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper", + "anyhow", + "reqsign-core", "tokio", - "tokio-rustls", - "tokio-util", - "tower", - "tower-http", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "wasm-streams", - "web-sys", - "webpki-roots", ] [[package]] name = "reqwest" -version = "0.13.1" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04e9018c9d814e5f30cc16a0f03271aeab3571e609612d9fe78c1aa8d11c2f62" +checksum = "219c5811de6525e5416c7d5d53bb656d3afdbc6c5af816e0802bcfa42dbdc1c3" dependencies = [ - "base64", + "base64 0.22.1", "bytes", - "encoding_rs", "futures-core", - "h2", + "futures-util", "http", "http-body", "http-body-util", @@ -1977,24 +2047,23 @@ dependencies = [ "hyper-util", "js-sys", "log", - "mime", "percent-encoding", "pin-project-lite", "quinn", "rustls", "rustls-pki-types", "rustls-platform-verifier", - "serde", - "serde_json", "sync_wrapper", "tokio", "tokio-rustls", + "tokio-util", "tower", "tower-http", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", + "wasm-streams", "web-sys", ] @@ -2006,7 +2075,7 @@ checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", "cfg-if", - "getrandom 0.2.16", + "getrandom 0.2.17", "libc", "untrusted", "windows-sys 0.52.0", @@ -2022,17 +2091,11 @@ dependencies = [ "ordered-multimap", ] -[[package]] -name = "rustc-demangle" -version = "0.1.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d" - [[package]] name = "rustc-hash" -version = "2.1.1" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" +checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d" [[package]] name = "rustc_version" @@ -2054,11 +2117,11 @@ dependencies = [ [[package]] name = "rustix" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ - "bitflags", + "bitflags 2.13.1", "errno", "libc", "linux-raw-sys", @@ -2067,13 +2130,12 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.36" +version = "0.23.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c665f33d38cea657d9614f766881e4d510e0eda4239891eea56b4cadcf01801b" +checksum = "0283386ce02abc0151e1761d08802dfe86c173b0b494af5cbc086574e453da06" dependencies = [ "aws-lc-rs", "once_cell", - "ring", "rustls-pki-types", "rustls-webpki", "subtle", @@ -2082,9 +2144,9 @@ dependencies = [ [[package]] name = "rustls-native-certs" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" +checksum = "dab5152771c58876a2146916e53e35057e1a4dfa2b9df0f0305b07f611fdea4d" dependencies = [ "openssl-probe", "rustls-pki-types", @@ -2094,9 +2156,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.13.1" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "708c0f9d5f54ba0272468c1d306a52c495b31fa155e91bc25371e6df7996908c" +checksum = "2f4925028c7eb5d1fcdaf196971378ed9d2c1c4efc7dc5d011256f76c99c0a96" dependencies = [ "web-time", "zeroize", @@ -2104,11 +2166,11 @@ dependencies = [ [[package]] name = "rustls-platform-verifier" -version = "0.6.2" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d99feebc72bae7ab76ba994bb5e121b8d83d910ca40b36e0921f53becc41784" +checksum = "26d1e2536ce4f35f4846aa13bff16bd0ff40157cdb14cc056c7b14ba41233ba0" dependencies = [ - "core-foundation 0.10.1", + "core-foundation", "core-foundation-sys", "jni", "log", @@ -2131,9 +2193,9 @@ checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" [[package]] name = "rustls-webpki" -version = "0.103.8" +version = "0.103.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52" +checksum = "f3c3cf1d8b1e7d4927e2d154c3fcb02979afb9939629c62cd9048d4f07b60ac2" dependencies = [ "aws-lc-rs", "ring", @@ -2143,15 +2205,15 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" +checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" [[package]] name = "ryu" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" [[package]] name = "same-file" @@ -2164,21 +2226,21 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.28" +version = "0.1.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" +checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" dependencies = [ "windows-sys 0.61.2", ] [[package]] name = "security-framework" -version = "3.5.1" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" dependencies = [ - "bitflags", - "core-foundation 0.10.1", + "bitflags 2.13.1", + "core-foundation", "core-foundation-sys", "libc", "security-framework-sys", @@ -2186,9 +2248,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.15.0" +version = "2.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" dependencies = [ "core-foundation-sys", "libc", @@ -2196,15 +2258,15 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.27" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" [[package]] name = "serde" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" dependencies = [ "serde_core", "serde_derive", @@ -2212,29 +2274,29 @@ dependencies = [ [[package]] name = "serde_core" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" dependencies = [ - "proc-macro2 1.0.103", - "quote 1.0.43", - "syn 2.0.114", + "proc-macro2 1.0.107", + "quote 1.0.47", + "syn 3.0.4", ] [[package]] name = "serde_json" -version = "1.0.148" +version = "1.0.151" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3084b546a1dd6289475996f182a22aba973866ea8e8b02c51d9f46b1336a22da" +checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14" dependencies = [ "itoa", "memchr", @@ -2257,13 +2319,13 @@ dependencies = [ [[package]] name = "sha1" -version = "0.10.6" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +checksum = "aacc4cc499359472b4abe1bf11d0b12e688af9a805fa5e3016f9a386dc2d0214" dependencies = [ "cfg-if", - "cpufeatures", - "digest", + "cpufeatures 0.3.0", + "digest 0.11.3", ] [[package]] @@ -2273,8 +2335,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", - "cpufeatures", - "digest", + "cpufeatures 0.2.17", + "digest 0.10.7", +] + +[[package]] +name = "sha2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", + "digest 0.11.3", ] [[package]] @@ -2285,9 +2358,9 @@ checksum = "dc6fe69c597f9c37bfeeeeeb33da3530379845f10be461a66d16d03eca2ded77" [[package]] name = "shlex" -version = "1.3.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" [[package]] name = "signal-hook-registry" @@ -2299,36 +2372,49 @@ dependencies = [ "libc", ] +[[package]] +name = "simd_cesu8" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11031e251abf8611c80f460e19dbdeb54a66db918e49c65a7065b46ac7aec520" +dependencies = [ + "rustc_version", + "simdutf8", +] + +[[package]] +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" + [[package]] name = "slab" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" [[package]] name = "smallvec" -version = "1.15.1" +version = "1.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" [[package]] name = "socket2" -version = "0.6.1" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" +checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] -name = "speedometer" -version = "0.2.2" +name = "spin" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2789736092fa21b44baf8590acb4b360cb91f0f597bd6c1f1741ca9644c95c1e" -dependencies = [ - "failure", -] +checksum = "023a211cb3138dbc438680b32560ad89f699977624c9f8dbb95a47d5b4c07dd3" [[package]] name = "stable_deref_trait" @@ -2356,28 +2442,28 @@ checksum = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" dependencies = [ "proc-macro2 0.4.30", "quote 0.6.13", - "unicode-xid 0.1.0", + "unicode-xid", ] [[package]] name = "syn" -version = "1.0.109" +version = "2.0.119" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" dependencies = [ - "proc-macro2 1.0.103", - "quote 1.0.43", + "proc-macro2 1.0.107", + "quote 1.0.47", "unicode-ident", ] [[package]] name = "syn" -version = "2.0.114" +version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" +checksum = "e6275cddf4610d1775e6d1fe9469b2e77d0f39fd98fb7450901b821e0c53649f" dependencies = [ - "proc-macro2 1.0.103", - "quote 1.0.43", + "proc-macro2 1.0.107", + "quote 1.0.47", "unicode-ident", ] @@ -2390,58 +2476,25 @@ dependencies = [ "futures-core", ] -[[package]] -name = "synstructure" -version = "0.12.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" -dependencies = [ - "proc-macro2 1.0.103", - "quote 1.0.43", - "syn 1.0.109", - "unicode-xid 0.2.6", -] - [[package]] name = "synstructure" version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ - "proc-macro2 1.0.103", - "quote 1.0.43", - "syn 2.0.114", -] - -[[package]] -name = "system-configuration" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" -dependencies = [ - "bitflags", - "core-foundation 0.9.4", - "system-configuration-sys", -] - -[[package]] -name = "system-configuration-sys" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" -dependencies = [ - "core-foundation-sys", - "libc", + "proc-macro2 1.0.107", + "quote 1.0.47", + "syn 2.0.119", ] [[package]] name = "tempfile" -version = "3.24.0" +version = "3.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom 0.3.4", + "getrandom 0.4.3", "once_cell", "rustix", "windows-sys 0.61.2", @@ -2470,11 +2523,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.17" +version = "2.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" +checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f" dependencies = [ - "thiserror-impl 2.0.17", + "thiserror-impl 2.0.20", ] [[package]] @@ -2483,30 +2536,29 @@ version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ - "proc-macro2 1.0.103", - "quote 1.0.43", - "syn 2.0.114", + "proc-macro2 1.0.107", + "quote 1.0.47", + "syn 2.0.119", ] [[package]] name = "thiserror-impl" -version = "2.0.17" +version = "2.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" +checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af" dependencies = [ - "proc-macro2 1.0.103", - "quote 1.0.43", - "syn 2.0.114", + "proc-macro2 1.0.107", + "quote 1.0.47", + "syn 3.0.4", ] [[package]] name = "time" -version = "0.3.46" +version = "0.3.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9da98b7d9b7dad93488a84b8248efc35352b0b2657397d4167e7ad67e5d535e5" +checksum = "cdb87b95ec50ddfa440816d227a17b2ccbdda963a316a727fda0fc4334f7d134" dependencies = [ "deranged", - "itoa", "num-conv", "powerfmt", "serde_core", @@ -2516,15 +2568,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" +checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" [[package]] name = "time-macros" -version = "0.2.26" +version = "0.2.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78cc610bac2dcee56805c99642447d4c5dbde4d01f752ffea0199aee1f601dc4" +checksum = "7e689342a48d2ea927c87ea50cabf8594854bf940e9310208848d680d668ed85" dependencies = [ "num-conv", "time-core", @@ -2541,9 +2593,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.8.2" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" +checksum = "b1e27c91459209c2986af3dcf603a5a74a4368754ce37414f59acc971167f643" dependencies = [ "displaydoc", "zerovec", @@ -2551,9 +2603,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.10.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" +checksum = "bb4ebadaa0af04fab11ae01eb5f9fdb5f9c5b875506e210e71c07873528baa7f" dependencies = [ "tinyvec_macros", ] @@ -2566,9 +2618,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.48.0" +version = "1.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" +checksum = "202caea871b69668250d242070849eb495be178ed697a3e98aebce5bc81a0bed" dependencies = [ "bytes", "libc", @@ -2582,13 +2634,13 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.6.0" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" +checksum = "78773a2a397f451582ce068015985c33193cf6dea8b74d2a639fe457b2f07b0e" dependencies = [ - "proc-macro2 1.0.103", - "quote 1.0.43", - "syn 2.0.114", + "proc-macro2 1.0.107", + "quote 1.0.47", + "syn 3.0.4", ] [[package]] @@ -2603,9 +2655,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.18" +version = "0.7.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" +checksum = "494815d09bf52b5548659851081238f0ca39ff638363907596da739561c62c52" dependencies = [ "bytes", "futures-core", @@ -2617,9 +2669,9 @@ dependencies = [ [[package]] name = "tower" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" dependencies = [ "futures-core", "futures-util", @@ -2632,20 +2684,20 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.6.8" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" +checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" dependencies = [ - "bitflags", + "bitflags 2.13.1", "bytes", "futures-util", "http", "http-body", - "iri-string", "pin-project-lite", "tower", "tower-layer", "tower-service", + "url", ] [[package]] @@ -2687,15 +2739,15 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "typenum" -version = "1.19.0" +version = "1.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" [[package]] name = "unicode-ident" -version = "1.0.22" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" [[package]] name = "unicode-width" @@ -2709,12 +2761,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" -[[package]] -name = "unicode-xid" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" - [[package]] name = "unit-prefix" version = "0.5.2" @@ -2753,11 +2799,11 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.19.0" +version = "1.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a" +checksum = "f053576934f05a761a402421fbbe3d425d9366f75f978806a037b3ca481abecc" dependencies = [ - "getrandom 0.3.4", + "getrandom 0.4.3", "js-sys", "serde_core", "wasm-bindgen", @@ -2796,18 +2842,18 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasip2" -version = "1.0.1+wasi-0.2.4" +version = "1.0.4+wasi-0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" dependencies = [ "wit-bindgen", ] [[package]] name = "wasm-bindgen" -version = "0.2.106" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" +checksum = "1b70935747edd64d89de3efa29d73789b806c15798f8e7dca4d8ac356b50ce70" dependencies = [ "cfg-if", "once_cell", @@ -2818,54 +2864,51 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.56" +version = "0.4.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836d9622d604feee9e5de25ac10e3ea5f2d65b41eac0d9ce72eb5deae707ce7c" +checksum = "6b7777d5cc23d0e91404e53ce2d5e8ec7acae3026b16233dba62cd3246457950" dependencies = [ - "cfg-if", "js-sys", - "once_cell", "wasm-bindgen", - "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.106" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" +checksum = "77775f8f3f7217702089053b94958f8f54061a3f663417df76e19cbdcca29bc1" dependencies = [ - "quote 1.0.43", + "quote 1.0.47", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.106" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" +checksum = "e11d33f857dc2fb11b8bc75aee111aa9cbeb12cd9f25efd3d4c2a3dd4e235284" dependencies = [ "bumpalo", - "proc-macro2 1.0.103", - "quote 1.0.43", - "syn 2.0.114", + "proc-macro2 1.0.107", + "quote 1.0.47", + "syn 2.0.119", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.106" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" +checksum = "7ef64dbcc55df09c7e5a46182d181c2cfa3e925f3da937ea764728b4bbb9dcbf" dependencies = [ "unicode-ident", ] [[package]] name = "wasm-streams" -version = "0.4.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" +checksum = "9d1ec4f6517c9e11ae630e200b2b65d193279042e28edd4a2cda233e46670bbb" dependencies = [ "futures-util", "js-sys", @@ -2876,9 +2919,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.83" +version = "0.3.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b32828d774c412041098d182a8b38b16ea816958e07cf40eec2bc080ae137ac" +checksum = "c435338968042f4f59a557f690a253676d47ce13ceb55d70100e7facf6620a30" dependencies = [ "js-sys", "wasm-bindgen", @@ -2894,36 +2937,11 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "webbrowser" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00f1243ef785213e3a32fa0396093424a3a6ea566f9948497e5a2309261a4c97" -dependencies = [ - "core-foundation 0.10.1", - "jni", - "log", - "ndk-context", - "objc2", - "objc2-foundation", - "url", - "web-sys", -] - [[package]] name = "webpki-root-certs" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36a29fc0408b113f68cf32637857ab740edfafdf460c326cd2afaa2d84cc05dc" -dependencies = [ - "rustls-pki-types", -] - -[[package]] -name = "webpki-roots" -version = "1.0.5" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12bed680863276c63889429bfd6cab3b99943659923822de1c8a39c49e4d722c" +checksum = "b96554aa2acc8ccdb7e1c9a58a7a68dd5d13bccc69cd124cb09406db612a1c9b" dependencies = [ "rustls-pki-types", ] @@ -2956,9 +2974,9 @@ version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ - "proc-macro2 1.0.103", - "quote 1.0.43", - "syn 2.0.114", + "proc-macro2 1.0.107", + "quote 1.0.47", + "syn 2.0.119", ] [[package]] @@ -2967,9 +2985,9 @@ version = "0.59.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ - "proc-macro2 1.0.103", - "quote 1.0.43", - "syn 2.0.114", + "proc-macro2 1.0.107", + "quote 1.0.47", + "syn 2.0.119", ] [[package]] @@ -2978,17 +2996,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" -[[package]] -name = "windows-registry" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" -dependencies = [ - "windows-link", - "windows-result", - "windows-strings", -] - [[package]] name = "windows-result" version = "0.4.1" @@ -3007,31 +3014,22 @@ dependencies = [ "windows-link", ] -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.2", -] - [[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", + "windows-targets", ] [[package]] name = "windows-sys" -version = "0.60.2" +version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets 0.53.5", + "windows-targets", ] [[package]] @@ -3043,203 +3041,81 @@ dependencies = [ "windows-link", ] -[[package]] -name = "windows-targets" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - [[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", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] -[[package]] -name = "windows-targets" -version = "0.53.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" -dependencies = [ - "windows-link", - "windows_aarch64_gnullvm 0.53.1", - "windows_aarch64_msvc 0.53.1", - "windows_i686_gnu 0.53.1", - "windows_i686_gnullvm 0.53.1", - "windows_i686_msvc 0.53.1", - "windows_x86_64_gnu 0.53.1", - "windows_x86_64_gnullvm 0.53.1", - "windows_x86_64_msvc 0.53.1", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - [[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.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - [[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.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" - -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - [[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.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" - [[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.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" - -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - [[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.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - [[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.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - [[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.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - [[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.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" - [[package]] name = "wit-bindgen" -version = "0.46.0" +version = "0.57.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" [[package]] name = "writeable" -version = "0.6.2" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" +checksum = "3ad82d2a33cdc9674dc7465672f271e096168fcdbe0f799d9e6db8c5892679dc" [[package]] name = "x509-parser" @@ -3265,7 +3141,7 @@ version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4569f339c0c402346d4a75a9e39cf8dad310e287eef1ff56d4c68e5067f53460" dependencies = [ - "asn1-rs 0.7.1", + "asn1-rs 0.7.2", "data-encoding", "der-parser 10.0.0", "lazy_static", @@ -3273,7 +3149,7 @@ dependencies = [ "oid-registry 0.8.1", "ring", "rusticata-macros", - "thiserror 2.0.17", + "thiserror 2.0.20", "time", ] @@ -3288,9 +3164,9 @@ dependencies = [ [[package]] name = "yoke" -version = "0.8.1" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" +checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5" dependencies = [ "stable_deref_trait", "yoke-derive", @@ -3299,68 +3175,68 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" +checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" dependencies = [ - "proc-macro2 1.0.103", - "quote 1.0.43", - "syn 2.0.114", - "synstructure 0.13.2", + "proc-macro2 1.0.107", + "quote 1.0.47", + "syn 2.0.119", + "synstructure", ] [[package]] name = "zerocopy" -version = "0.8.31" +version = "0.8.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3" +checksum = "556764e583adb45a9f8d413c2a147fa7e8d821e48e12b14fd560b607998b75eb" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.31" +version = "0.8.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a" +checksum = "f2ab42fc20575779bd240faa45f94a74256f755c0fa9e89f0ede20d91d0cdfc1" dependencies = [ - "proc-macro2 1.0.103", - "quote 1.0.43", - "syn 2.0.114", + "proc-macro2 1.0.107", + "quote 1.0.47", + "syn 2.0.119", ] [[package]] name = "zerofrom" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" dependencies = [ - "proc-macro2 1.0.103", - "quote 1.0.43", - "syn 2.0.114", - "synstructure 0.13.2", + "proc-macro2 1.0.107", + "quote 1.0.47", + "syn 2.0.119", + "synstructure", ] [[package]] name = "zeroize" -version = "1.8.2" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" +checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" [[package]] name = "zerotrie" -version = "0.2.3" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" +checksum = "4ea269c3bd32f0a32c321907a2ae912ba6f4649bb0fc764a15627e99a7095a3f" dependencies = [ "displaydoc", "yoke", @@ -3369,9 +3245,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.5" +version = "0.11.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" +checksum = "bb0464e17806c1d976d5cba29399c7f08e516e279e2ba493f63123b5fca67dd8" dependencies = [ "yoke", "zerofrom", @@ -3380,17 +3256,45 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.11.2" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" +checksum = "34df6fc39dbd26ddc9c10e6a2984476e13acce22e64e4487636ef494369225da" dependencies = [ - "proc-macro2 1.0.103", - "quote 1.0.43", - "syn 2.0.114", + "proc-macro2 1.0.107", + "quote 1.0.47", + "syn 3.0.4", ] [[package]] name = "zmij" -version = "1.0.12" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" + +[[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.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64d80649ab6db9d9f6f9c80a40becd948eda4714a0a5ac8c4d157a32231c7882" +dependencies = [ + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.1.0+zstd.1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fc5a66a20078bf1251bde995aa2fdcc4b800c70b5d92dd2c62abc5c60f679f8" +checksum = "0ef0a8027ec3ee71300ab3bcbcd0393f434aa72b91ca6d635a39941deae8eea0" +dependencies = [ + "cc", + "pkg-config", +] diff --git a/cli/Cargo.toml b/cli/Cargo.toml index f269f5a80..9fd7a0ca8 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -2,13 +2,13 @@ name = "downpour" version = "0.1.0" edition = "2024" +license = "AGPL-3.0-only" [dependencies] anyhow = "1.0.100" async-trait = "0.1.89" chrono = "0.4.43" clap = { version = "4.5.54", features = ["derive"] } -console = "0.16.2" dialoguer = "0.12.0" dirs = "6.0.0" droplet-rs = { path = "../libraries/droplet" } @@ -16,12 +16,15 @@ fern = { version = "0.7.1", features = ["colored"] } futures = "0.3.31" indicatif = "0.18.3" log = "0.4.29" -opendal = { version = "0.55.0", features = ["services-s3"] } +opendal = { version = "0.58.2", features = ["services-s3", "services-memory"] } rand = "0.9.3" -reqwest = { version = "0.13.1", features = ["json"] } serde = { version = "1.0.228", features = ["derive"] } serde_json = "1.0.148" -tokio = { version = "1.48.0", features = ["fs", "macros"] } +sha2 = "0.10" +walkdir = "2.5" +zstd = "0.13" +tokio = { version = "1.48.0", features = ["fs", "macros", "rt"] } tokio-util = { version = "0.7.18", features = ["compat"] } -url = "2.5.8" -webbrowser = "1.0.6" + +[dev-dependencies] +tempfile = "3" diff --git a/cli/README.md b/cli/README.md index 77643fd94..6f2c804d2 100644 --- a/cli/README.md +++ b/cli/README.md @@ -1,3 +1,3 @@ # CLI (`downpour`) -The cli way to access Drop. Used for admin tasks that require local access, like uploading game content. \ No newline at end of file +The cli way to access Drop. Used for admin tasks that require local access, like uploading game content. diff --git a/cli/spec.md b/cli/spec.md index e54017505..8c63195c1 100644 --- a/cli/spec.md +++ b/cli/spec.md @@ -1,10 +1,21 @@ -# Downpour CLI spec +# Downpour CLI Specification + `downpour [command] --opts` -## Commands: -- new - creates/initalizes a depot at the endpoint. Creates manifest.json and speedtest -- connect [name] - connects to an s3 endpoint and saves the endpoint to some sort of credentials file. Name is either as provided or the hostname of the endpoint -- upload - uploads game as described before. Should fail if depot isn't initialized with new from above -- copy - copies between two depots -- mark [exists/absent] - modifies depot's manifest.json to show content exists or is absent without copying (for third party copies) -- rename - renames an endpoint [NEEDS API ROUTES - can't do yet] -- delete - delete an endpoint [NEEDS API ROUTES - can't do yet] + +Downpour is the official CLI tool for Drop administrators and game developers, providing SteamCMD-equivalent publishing workflows and depot management. + +## Core Commands + +- `connect [name]` - Connects to an S3/storage endpoint and saves credentials locally. +- `new ` - Initializes a depot at the endpoint; creates `manifest.json` and speedtest object. +- `upload ` - Legacy/simple upload of game files to a configured depot. +- `push [options]` - **(Phase 1 Developer Publishing)**: + - Generates droplet content-addressed chunk manifests with rolling checksums. + - Computes binary deltas against previous release/branch manifests to upload only changed blocks. + - Supports `--branch `, `--depot `, and `--description `. + - Automatically invokes compression (zstd) before depot transmission. +- `copy ` - Copies versions between two depots. +- `mark [exists/absent] ` - Modifies depot `manifest.json` state without copying. +- `manifest inspect ` - Validates and inspects a local droplet manifest for chunk integrity and recipe steps. +- `rename ` - Renames an endpoint (server API required). +- `delete ` - Deletes an endpoint (server API required). diff --git a/cli/src/cli.rs b/cli/src/cli.rs index 318fc457b..f19d23b5d 100644 --- a/cli/src/cli.rs +++ b/cli/src/cli.rs @@ -30,6 +30,30 @@ pub enum Commands { /// Alias of a given connection name: Option, }, + /// Publishes a local depot directory as content-addressed chunks + Push { + /// Directory to publish + #[arg(short, long, default_value_t = String::from("."))] + path: String, + /// Output directory for chunks + manifest + #[arg(short, long, default_value_t = String::from("dist"))] + out: String, + /// Upload scheme (memory, s3); when set, chunks + manifest are uploaded + #[arg(short, long)] + upload: Option, + /// Object-store key prefix for the upload + #[arg(long, default_value_t = String::new())] + prefix: String, + /// Release branch (main, beta, nightly); recorded in the manifest + #[arg(long)] + branch: Option, + /// Multi-platform depot (windows, linux, assets); recorded in the manifest + #[arg(long)] + depot: Option, + /// Previous manifest to diff against for a binary delta + #[arg(long)] + base: Option, + }, } #[derive(Args)] diff --git a/cli/src/commands/connect/config.rs b/cli/src/commands/connect/config.rs index 549109d92..ae38be70d 100644 --- a/cli/src/commands/connect/config.rs +++ b/cli/src/commands/connect/config.rs @@ -46,7 +46,7 @@ impl Config { .expect("Apparently your home directory doesn't exist") // Should probably formalise that error .join(CONFIG_DIR); if fs::exists(&save_path) - .unwrap_or_else(|_| panic!("Could not read save path {:#?}", &save_path)) + .unwrap_or_else(|_| panic!("Could not read save path {:#?}", save_path)) { serde_json::from_str(&fs::read_to_string(save_path).unwrap()).unwrap() } else { diff --git a/cli/src/commands/connect/interactive.rs b/cli/src/commands/connect/interactive.rs index 8e8231989..777ede06b 100644 --- a/cli/src/commands/connect/interactive.rs +++ b/cli/src/commands/connect/interactive.rs @@ -12,16 +12,6 @@ macro_rules! interactive_variable { }; }; } -#[macro_export] -macro_rules! interactive_optional_variable { - ($value:ident, $var:ident, $prompt:expr) => { - let $var = if let Some($var) = $value.$var { - Some($var) - } else { - $crate::commands::connect::interactive::query_optional_variable($prompt).unwrap() - }; - }; -} pub fn query_variable(prompt: impl ToString) -> dialoguer::Result where ::Err: ToString, @@ -30,18 +20,3 @@ where .with_prompt(prompt.to_string()) .interact_text() } -pub fn query_optional_variable( - prompt: impl ToString, -) -> dialoguer::Result> -where - ::Err: ToString, -{ - let input: T = Input::with_theme(&ColorfulTheme::default()) - .with_prompt(prompt.to_string()) - .allow_empty(true) - .interact_text()?; - if input.to_string().is_empty() { - return Ok(None); - } - Ok(Some(input)) -} diff --git a/cli/src/commands/connect/s3.rs b/cli/src/commands/connect/s3.rs index 5b30d1013..e87d170e0 100644 --- a/cli/src/commands/connect/s3.rs +++ b/cli/src/commands/connect/s3.rs @@ -35,7 +35,7 @@ impl Configure for S3ConfigCli { interactive_variable!(self, region, "S3 Region"); interactive_variable!(self, bucket_name, "S3 Bucket Name"); interactive_variable!(self, endpoint, "S3 Endpoint"); - if let None = name { + if name.is_none() { *name = Some(endpoint.clone()); } Ok(ConfigOption::S3(S3Config { @@ -60,7 +60,7 @@ impl OperatorBuilder for S3Config { .bucket(&self.bucket_name) .disable_config_load(); - let op: Operator = Operator::new(builder)?.finish(); + let op: Operator = Operator::new(builder)?; Ok(op) } diff --git a/cli/src/commands/mod.rs b/cli/src/commands/mod.rs index 0a9a925a4..3160ddeb0 100644 --- a/cli/src/commands/mod.rs +++ b/cli/src/commands/mod.rs @@ -1,2 +1,3 @@ pub mod connect; +pub mod push; pub mod upload; diff --git a/cli/src/commands/push/chunker.rs b/cli/src/commands/push/chunker.rs new file mode 100644 index 000000000..4eefdf29e --- /dev/null +++ b/cli/src/commands/push/chunker.rs @@ -0,0 +1,120 @@ +//! Content-defined chunking for the `downpour push` publisher. +//! +//! Uses a rolling buzhash so chunk boundaries follow file content rather than +//! fixed offsets: inserting bytes near the start of a file only re-chunks the +//! region around the edit, which keeps depot deltas small. + +/// Smallest chunk emitted (except the final chunk of a file). +pub const DEFAULT_MIN_CHUNK: usize = 64 * 1024; +/// Largest chunk emitted; forces a boundary to bound memory and blob size. +pub const DEFAULT_MAX_CHUNK: usize = 1024 * 1024; +/// Boundary mask size: average chunk target is `2^DEFAULT_AVG_BITS`. +pub const DEFAULT_AVG_BITS: u32 = 20; + +/// Rolling content-defined chunker. +pub struct Chunker { + min: usize, + max: usize, + mask: u64, + table: [u64; 256], +} + +impl Default for Chunker { + fn default() -> Self { + Self::new(DEFAULT_MIN_CHUNK, DEFAULT_MAX_CHUNK, DEFAULT_AVG_BITS) + } +} + +impl Chunker { + pub fn new(min: usize, max: usize, avg_bits: u32) -> Self { + assert!(min > 0 && min <= max, "invalid chunk bounds"); + let mut state: u64 = 0x9E37_79B9_7F4A_7C15; + let mut table = [0u64; 256]; + for entry in table.iter_mut() { + // Deterministic xorshift64 so boundaries are reproducible everywhere. + state ^= state << 13; + state ^= state >> 7; + state ^= state << 17; + *entry = state; + } + Self { + min, + max, + mask: (1u64 << avg_bits) - 1, + table, + } + } + + /// Splits `data` into content-defined chunks, returned as `(start, end)`. + pub fn split<'a>(&self, data: &'a [u8]) -> Vec<&'a [u8]> { + let mut chunks = Vec::new(); + let mut start = 0usize; + while start < data.len() { + let end = self.next_boundary(&data[start..]); + chunks.push(&data[start..start + end]); + start += end; + } + if chunks.is_empty() { + chunks.push(&data[0..0]); + } + chunks + } + + fn next_boundary(&self, data: &[u8]) -> usize { + if data.len() <= self.min { + return data.len(); + } + + let limit = data.len().min(self.max); + let mut hash: u64 = 0; + for (index, byte) in data[..limit].iter().enumerate() { + hash = hash.rotate_left(1) ^ self.table[*byte as usize]; + if index + 1 >= self.min && (hash & self.mask) == 0 { + return index + 1; + } + } + limit + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn chunks_reassemble_to_the_input() { + let data: Vec = (0..200_000u32).map(|i| (i % 251) as u8).collect(); + let chunker = Chunker::new(1024, 8192, 10); + let chunks = chunker.split(&data); + let rejoined: Vec = chunks.iter().flat_map(|c| c.iter().copied()).collect(); + assert_eq!(rejoined, data); + assert!(chunks.len() > 1, "expected multiple chunks"); + } + + #[test] + fn respects_min_and_max_bounds() { + let data: Vec = (0..100_000u32).map(|i| (i * 7 % 256) as u8).collect(); + let chunker = Chunker::new(2048, 4096, 9); + let chunks = chunker.split(&data); + for chunk in &chunks[..chunks.len().saturating_sub(1)] { + assert!(chunk.len() >= 2048, "chunk below minimum: {}", chunk.len()); + assert!(chunk.len() <= 4096, "chunk above maximum: {}", chunk.len()); + } + } + + #[test] + fn is_deterministic_for_the_same_input() { + let data: Vec = (0..50_000u32).map(|i| (i % 97) as u8).collect(); + let a = Chunker::default().split(&data); + let b = Chunker::default().split(&data); + assert_eq!(a.len(), b.len()); + assert!(a.iter().zip(&b).all(|(x, y)| x == y)); + } + + #[test] + fn empty_input_still_yields_one_empty_chunk() { + let chunks = Chunker::default().split(&[]); + assert_eq!(chunks.len(), 1); + assert!(chunks[0].is_empty()); + } +} diff --git a/cli/src/commands/push/mod.rs b/cli/src/commands/push/mod.rs new file mode 100644 index 000000000..994daf171 --- /dev/null +++ b/cli/src/commands/push/mod.rs @@ -0,0 +1,390 @@ +//! `downpour push`: publishes a local depot directory as content-addressed, +//! zstd-compressed chunks plus a JSON manifest. +//! +//! This is the local/offline half of Phase 1 content delivery (#17): it produces +//! the same chunk layout the depot server ingests, so a later S3/HTTP uploader +//! only has to stream the emitted objects. + +pub mod chunker; + +use std::fs; +use std::path::{Path, PathBuf}; + +use anyhow::{Context, Result}; +use serde::{Deserialize, Serialize}; +use sha2::{Digest, Sha256}; +use walkdir::WalkDir; + +use self::chunker::Chunker; + +fn hex_digest(bytes: &[u8]) -> String { + use std::fmt::Write as _; + let mut out = String::with_capacity(64); + for byte in Sha256::digest(bytes) { + let _ = write!(out, "{byte:02x}"); + } + out +} + +#[derive(Debug, Default, Clone)] +pub struct PushOptions { + /// Release branch the artifacts belong to (e.g. `main`, `beta`). + pub branch: Option, + /// Multi-platform depot the artifacts belong to (e.g. `windows`, `assets`). + pub depot: Option, + /// Previous manifest to diff against; chunks already present there are + /// reused instead of being written and re-uploaded. + pub base_manifest: Option, +} + +#[derive(Debug, Serialize)] +pub struct PushSummary { + pub files: usize, + pub chunks: usize, + pub bytes_in: u64, + pub bytes_out: u64, + /// Chunks whose digest was already present in `base_manifest`. + pub reused_chunks: usize, + /// Chunks newly written (and therefore uploaded). + pub new_chunks: usize, +} + +#[derive(Debug, Serialize, Deserialize)] +struct ChunkRef { + sha256: String, + original_len: usize, + compressed_len: usize, +} + +#[derive(Debug, Serialize, Deserialize)] +struct ManifestEntry { + path: String, + chunks: Vec, +} + +#[derive(Debug, Serialize, Deserialize)] +struct PushManifest { + version: u32, + chunker: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + branch: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + depot: Option, + entries: Vec, +} + +/// Read the set of chunk digests already published by a previous manifest. +fn read_base_chunks(manifest_path: &Path) -> Result> { + let bytes = fs::read(manifest_path) + .with_context(|| format!("reading base manifest {}", manifest_path.display()))?; + let manifest: PushManifest = serde_json::from_slice(&bytes).context("parsing base manifest")?; + let mut digests = std::collections::HashSet::new(); + for entry in manifest.entries { + for chunk in entry.chunks { + digests.insert(chunk.sha256); + } + } + Ok(digests) +} + +/// Chunks `path` into `out/chunks` and writes `out/manifest.json`. +#[cfg(test)] +pub fn run(path: &Path, out: &Path) -> Result { + run_with_options(path, out, &PushOptions::default()) +} + +/// Chunks `path` and writes a manifest, reusing chunks from an optional base +/// manifest so only changed content is emitted (and later uploaded). +pub fn run_with_options(path: &Path, out: &Path, options: &PushOptions) -> Result { + let chunker = Chunker::default(); + let chunk_dir = out.join("chunks"); + fs::create_dir_all(&chunk_dir).context("creating chunk output directory")?; + + let base_chunks = match &options.base_manifest { + Some(base) => read_base_chunks(base)?, + None => std::collections::HashSet::new(), + }; + + let mut entries = Vec::new(); + let mut summary = PushSummary { + files: 0, + chunks: 0, + bytes_in: 0, + bytes_out: 0, + reused_chunks: 0, + new_chunks: 0, + }; + let mut written: std::collections::HashSet = std::collections::HashSet::new(); + + for entry in WalkDir::new(path).into_iter().filter_map(|e| e.ok()) { + if !entry.file_type().is_file() { + continue; + } + let relative = entry + .path() + .strip_prefix(path) + .context("stripping root prefix")? + .to_string_lossy() + .replace('\\', "/"); + let data = fs::read(entry.path()) + .with_context(|| format!("reading {}", entry.path().display()))?; + + summary.files += 1; + summary.bytes_in += data.len() as u64; + + let mut chunks = Vec::new(); + for chunk in chunker.split(&data) { + let digest = hex_digest(chunk); + let already_published = base_chunks.contains(&digest); + let compressed = zstd::bulk::compress(chunk, 3).context("compressing chunk")?; + + if already_published { + summary.reused_chunks += 1; + } else if !written.contains(&digest) { + let chunk_path = chunk_dir.join(&digest); + fs::write(&chunk_path, &compressed).context("writing chunk")?; + written.insert(digest.clone()); + summary.new_chunks += 1; + summary.bytes_out += compressed.len() as u64; + } else { + summary.reused_chunks += 1; + } + summary.chunks += 1; + chunks.push(ChunkRef { + sha256: digest, + original_len: chunk.len(), + compressed_len: compressed.len(), + }); + } + + entries.push(ManifestEntry { + path: relative, + chunks, + }); + } + + let manifest = PushManifest { + version: 1, + chunker: "buzhash-cdc-v1".to_string(), + branch: options.branch.clone(), + depot: options.depot.clone(), + entries, + }; + let manifest_path = out.join("manifest.json"); + fs::write( + &manifest_path, + serde_json::to_vec_pretty(&manifest).context("serializing manifest")?, + ) + .context("writing manifest")?; + + Ok(summary) +} + +/// Returns the on-disk path of a content-addressed chunk. +#[allow(dead_code)] // consumed by the S3/HTTP uploader that reuses this layout +pub fn chunk_path(out: &Path, sha256: &str) -> PathBuf { + out.join("chunks").join(sha256) +} + +/// Builds an OpenDAL operator for a storage scheme. `memory` is used by tests; +/// `s3` reads credentials from the standard environment variables. +/// +/// # Errors +/// Returns an error when the scheme is unsupported or the operator cannot be +/// constructed. +pub fn build_operator(scheme: &str) -> anyhow::Result { + use anyhow::Context; + + match scheme { + "memory" => Ok(opendal::Operator::new(opendal::services::Memory::default()) + .context("creating memory operator")?), + "s3" => { + let bucket = std::env::var("DROP_S3_BUCKET") + .context("DROP_S3_BUCKET is required for the s3 scheme")?; + let mut builder = opendal::services::S3::default().bucket(&bucket); + if let Ok(region) = std::env::var("AWS_REGION") { + builder = builder.region(®ion); + } + if let Ok(endpoint) = std::env::var("AWS_ENDPOINT") { + builder = builder.endpoint(&endpoint); + } + if let Ok(key) = std::env::var("AWS_ACCESS_KEY_ID") { + builder = builder.access_key_id(&key); + } + if let Ok(secret) = std::env::var("AWS_SECRET_ACCESS_KEY") { + builder = builder.secret_access_key(&secret); + } + Ok(opendal::Operator::new(builder).context("creating s3 operator")?) + } + other => anyhow::bail!("unsupported upload scheme '{other}'"), + } +} + +/// Uploads `dir` (chunks + manifest) under `prefix` in `operator`. +/// +/// # Errors +/// Returns an error when a file cannot be read or written. +pub async fn upload_dir( + operator: &opendal::Operator, + prefix: &str, + dir: &Path, +) -> anyhow::Result { + let prefix = prefix.trim_matches('/'); + let mut uploaded = 0; + + for entry in WalkDir::new(dir).into_iter().filter_map(|e| e.ok()) { + if !entry.file_type().is_file() { + continue; + } + let relative = entry + .path() + .strip_prefix(dir) + .context("stripping upload root")? + .to_string_lossy() + .replace('\\', "/"); + let key = if prefix.is_empty() { + relative + } else { + format!("{prefix}/{relative}") + }; + let data = fs::read(entry.path()) + .with_context(|| format!("reading {}", entry.path().display()))?; + operator + .write(&key, data) + .await + .with_context(|| format!("uploading {key}"))?; + uploaded += 1; + } + + Ok(uploaded) +} + +#[cfg(test)] +mod tests { + use super::*; + use tempfile::tempdir; + + #[tokio::test] + async fn uploads_packaged_chunks_to_an_operator() { + let input = tempdir().unwrap(); + let out = tempdir().unwrap(); + fs::write(input.path().join("a.bin"), vec![3u8; 4096]).unwrap(); + + run(input.path(), out.path()).unwrap(); + let operator = build_operator("memory").unwrap(); + let uploaded = upload_dir(&operator, "depot", out.path()).await.unwrap(); + + assert!(uploaded >= 2, "expected chunks + manifest"); + assert!(operator.read("depot/manifest.json").await.is_ok()); + } + + #[test] + fn rejects_unknown_upload_schemes() { + assert!(build_operator("ftp").is_err()); + } + + #[test] + fn push_writes_manifest_and_reassembles_chunks() { + let input = tempdir().unwrap(); + let out = tempdir().unwrap(); + + let payload_a = vec![7u8; 150_000]; + let payload_b: Vec = (0..90_000u32).map(|i| (i % 13) as u8).collect(); + fs::write(input.path().join("a.bin"), &payload_a).unwrap(); + fs::create_dir(input.path().join("nested")).unwrap(); + fs::write(input.path().join("nested/b.bin"), &payload_b).unwrap(); + + let summary = run(input.path(), out.path()).unwrap(); + assert_eq!(summary.files, 2); + assert!(summary.chunks >= 2); + assert!(summary.bytes_in >= (payload_a.len() + payload_b.len()) as u64); + + let manifest: serde_json::Value = + serde_json::from_slice(&fs::read(out.path().join("manifest.json")).unwrap()).unwrap(); + let entries = manifest["entries"].as_array().unwrap(); + assert_eq!(entries.len(), 2); + + // Reassemble a.bin from its content-addressed chunks. + let a_entry = entries + .iter() + .find(|e| e["path"] == "a.bin") + .expect("a.bin entry"); + let mut rebuilt = Vec::new(); + for chunk in a_entry["chunks"].as_array().unwrap() { + let sha = chunk["sha256"].as_str().unwrap(); + let compressed = fs::read(chunk_path(out.path(), sha)).unwrap(); + let decompressed = zstd::bulk::decompress(&compressed, 8 * 1024 * 1024).unwrap(); + assert_eq!(hex_digest(&decompressed), sha); + rebuilt.extend_from_slice(&decompressed); + } + assert_eq!(rebuilt, payload_a); + } + + #[test] + fn push_reuses_chunks_from_a_base_manifest() { + let input = tempdir().unwrap(); + let base = tempdir().unwrap(); + let out = tempdir().unwrap(); + + let original = vec![11u8; 200_000]; + fs::write(input.path().join("data.bin"), &original).unwrap(); + let first = run(input.path(), base.path()).unwrap(); + assert!(first.new_chunks >= 1); + + // Unchanged content: every chunk should be reused, nothing rewritten. + let unchanged = run_with_options( + input.path(), + out.path(), + &PushOptions { + base_manifest: Some(base.path().join("manifest.json")), + ..Default::default() + }, + ) + .unwrap(); + assert_eq!(unchanged.new_chunks, 0); + assert!(unchanged.reused_chunks >= 1); + assert_eq!(unchanged.bytes_out, 0); + + // Appended content: the unchanged prefix is reused, the tail is new. + let mut modified = original.clone(); + modified.extend_from_slice(&[99u8; 5_000]); + fs::write(input.path().join("data.bin"), &modified).unwrap(); + let delta = run_with_options( + input.path(), + out.path(), + &PushOptions { + base_manifest: Some(base.path().join("manifest.json")), + ..Default::default() + }, + ) + .unwrap(); + assert!( + delta.reused_chunks >= 1, + "unchanged prefix should be reused" + ); + assert!(delta.new_chunks >= 1, "appended content should be new"); + } + + #[test] + fn push_records_branch_and_depot_in_the_manifest() { + let input = tempdir().unwrap(); + let out = tempdir().unwrap(); + fs::write(input.path().join("a.bin"), vec![1u8; 100]).unwrap(); + + run_with_options( + input.path(), + out.path(), + &PushOptions { + branch: Some("beta".to_string()), + depot: Some("windows".to_string()), + base_manifest: None, + }, + ) + .unwrap(); + + let manifest: serde_json::Value = + serde_json::from_slice(&fs::read(out.path().join("manifest.json")).unwrap()).unwrap(); + assert_eq!(manifest["branch"], "beta"); + assert_eq!(manifest["depot"], "windows"); + } +} diff --git a/cli/src/commands/upload/interface.rs b/cli/src/commands/upload/interface.rs index dab985567..c327b9c6b 100644 --- a/cli/src/commands/upload/interface.rs +++ b/cli/src/commands/upload/interface.rs @@ -26,18 +26,17 @@ pub async fn upload( info!("Uploading chunks"); - let v2_manifest = generate_v2_manifest( + generate_v2_manifest( Path::new(&path), ClosureFactory::new( async move |id: String| { info!("Uploading chunk id {id}"); - let writer = operator + operator .writer(&format!("{game_id}/{version_id}/{id}")) .await .unwrap() .into_futures_async_write() - .compat_write(); - writer + .compat_write() }, |writer: Compat| async { writer.into_inner().close().await.unwrap(); diff --git a/cli/src/main.rs b/cli/src/main.rs index 91cc05e78..3ab72572d 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -28,6 +28,46 @@ async fn main() -> anyhow::Result<()> { let info = info.interactive_configure(); upload::interface::upload(&info, config, &name).await?; } + Commands::Push { + path, + out, + upload, + prefix, + branch, + depot, + base, + } => { + let options = crate::commands::push::PushOptions { + branch, + depot, + base_manifest: base.map(std::path::PathBuf::from), + }; + let summary = crate::commands::push::run_with_options( + std::path::Path::new(&path), + std::path::Path::new(&out), + &options, + )?; + println!( + "pushed {} file(s), {} chunk(s) ({} new, {} reused), {} -> {} bytes", + summary.files, + summary.chunks, + summary.new_chunks, + summary.reused_chunks, + summary.bytes_in, + summary.bytes_out + ); + + if let Some(scheme) = upload { + let operator = crate::commands::push::build_operator(&scheme)?; + let uploaded = crate::commands::push::upload_dir( + &operator, + &prefix, + std::path::Path::new(&out), + ) + .await?; + println!("uploaded {uploaded} object(s) via '{scheme}'"); + } + } }; Ok(()) diff --git a/commitlint.config.mjs b/commitlint.config.mjs new file mode 100644 index 000000000..b29b5ae80 --- /dev/null +++ b/commitlint.config.mjs @@ -0,0 +1,3 @@ +export default { + extends: ["@commitlint/config-conventional"], +}; diff --git a/deny.toml b/deny.toml new file mode 100644 index 000000000..b2fba4d7e --- /dev/null +++ b/deny.toml @@ -0,0 +1,60 @@ +# cargo-deny configuration (https://embarkstudios.github.io/cargo-deny/). +# Run from a crate directory, e.g. `cd torrential && cargo deny check`. + +[graph] +all-features = true + +[advisories] +version = 2 +# Only flag unmaintained crates that a workspace directly depends on. The +# transitive unmaintained stack (async-std, instant, unic-*, proc-macro-error, +# GTK-era crates, ...) comes from Tauri/Linux bindings and can only be retired +# upstream; `cargo audit` still reports it weekly in security.yml. Any +# first-party dependency that goes unmaintained must be replaced rather than +# ignored. +unmaintained = "workspace" +# Fail on known vulnerabilities. Add scoped ignores here with a comment and a +# tracking issue if a transitive advisory cannot be resolved immediately. +ignore = [ + # Unmaintained (not vulnerable) advisory for bincode 1.3.3/2.0.1, a direct + # dependency of the vendored `native_model` crate for its versioned + # serialization ABI. No safe upgrade exists and swapping the format would + # break stored/consumed models; revisit when `native_model` migrates. + { id = "RUSTSEC-2025-0141", reason = "bincode is a direct dependency of the vendored native_model crate" }, +] + +[licenses] +version = 2 +# Permissive/inbound licenses accepted for the dependency graph. The workspace +# itself is AGPL-3.0-only. +allow = [ + "0BSD", + "AGPL-3.0-only", + "Apache-2.0", + "Apache-2.0 WITH LLVM-exception", + "BSD-2-Clause", + "BSD-3-Clause", + "BSL-1.0", + "CC0-1.0", + "CDLA-Permissive-2.0", + "ISC", + "MIT", + "MIT-0", + "MPL-2.0", + "OpenSSL", + "Unicode-3.0", + "Unicode-DFS-2016", + "Unlicense", + "Zlib", + "bzip2-1.0.6", +] + +[bans] +multiple-versions = "warn" +# The workspace intentionally uses "*" for a few crates (tokio, bytes). +wildcards = "allow" + +[sources] +unknown-registry = "deny" +unknown-git = "deny" +allow-registry = ["https://github.com/rust-lang/crates.io-index"] diff --git a/desktop/DEBUG.md b/desktop/DEBUG.md index 2a4478f36..f84ed785b 100644 --- a/desktop/DEBUG.md +++ b/desktop/DEBUG.md @@ -1,6 +1,7 @@ # How to create Flamegraph Run this in `src-tauri`: + ``` WEBKIT_DISABLE_DMABUF_RENDERER=1 CARGO_PROFILE_RELEASE_DEBUG=true cargo flamegraph --release ``` @@ -8,6 +9,7 @@ WEBKIT_DISABLE_DMABUF_RENDERER=1 CARGO_PROFILE_RELEASE_DEBUG=true cargo flamegra You can leave out `WEBKIT_DISABLE_DMABUF_RENDERER=1` if you're not on NVIDIA/Linux And then run this in the root dir: + ``` yarn dev --port 1432 ``` diff --git a/desktop/README.md b/desktop/README.md index 87a26cc3b..111effa15 100644 --- a/desktop/README.md +++ b/desktop/README.md @@ -1,3 +1,3 @@ # Desktop -The official desktop client for Drop. \ No newline at end of file +The official desktop client for Drop. diff --git a/desktop/build.mjs b/desktop/build.mjs index aeb341d11..fc679ab64 100644 --- a/desktop/build.mjs +++ b/desktop/build.mjs @@ -1,6 +1,6 @@ -import fs from "fs"; -import process from "process"; -import childProcess from "child_process"; +import fs from "node:fs"; +import process from "node:process"; +import childProcess from "node:child_process"; import createLogger from "pino"; const OUTPUT = "./.output"; @@ -45,4 +45,4 @@ for (const view of views) { fs.cpSync(`./${view}/.output/public`, `${OUTPUT}/${view}`, { recursive: true, }); -} \ No newline at end of file +} diff --git a/desktop/changelog.md b/desktop/changelog.md index 691c4601f..bc1d9e1a1 100644 --- a/desktop/changelog.md +++ b/desktop/changelog.md @@ -1,8 +1,7 @@ - - ## Release 0.2.0-beta ### Fixes + - Re-enabled killing games #005bab2 - fixed queue manipulation and waiting for downloads #01260f0 - fix logic error in detecting dir #04368ff @@ -49,8 +48,8 @@ - update readme instructions #f0c47d8 - Adding usize to completed_contexts_lock instead of &usize #f508186 - ### Features + - Game kill tauri command #01e6162 - add debug page #02f8591 - Add signout functionality (#16) #0a0d9d6 @@ -113,9 +112,9 @@ - add note about more install dirs #f4ac1c8 - Added rolling progress window #fd30b3e - ### Other Changes -- quexeky + +- quexeky - Convert DATA_ROOT_DIR to Mutex #00b7179 - Converting DB access to a trait #01b092c - Updated changelog #022330b @@ -284,12 +283,12 @@ - initial commit #f6cd7c3 - Update .gitlab-ci.yml #fc6bab9 - _changelog generated by_ [go-conventional-commits](https://github.com/joselitofilho/go-conventional-commits) ## Release 0.1.0-beta ### Fixes + - fixed queue manipulation and waiting for downloads #01260f0 - fix logic error in detecting dir #04368ff - absolute executable invoke #17759c4 @@ -307,8 +306,8 @@ _changelog generated by_ [go-conventional-commits](https://github.com/joselitofi - fix scrollbars on edge webview #f09605a - update readme instructions #f0c47d8 - ### Features + - queue and library UIs #0a20139 - add pre-launch log to file #17f8d76 - Added option to change root directory #1aa52c0 @@ -343,9 +342,9 @@ _changelog generated by_ [go-conventional-commits](https://github.com/joselitofi - Generic function to set download state #f10d92d - Convert DownloadThreadControlFlag to AtomicBool #f25bfed - ### Other Changes -- quexeky + +- quexeky - Convert DATA_ROOT_DIR to Mutex #00b7179 - Converting DB access to a trait #01b092c - Scoping changes and removing qualifications #046ba64 @@ -459,5 +458,4 @@ _changelog generated by_ [go-conventional-commits](https://github.com/joselitofi - fixes and patches for merged changes #f6476bc - initial commit #f6cd7c3 - _changelog generated by_ [go-conventional-commits](https://github.com/joselitofilho/go-conventional-commits) diff --git a/desktop/libs/appletrust/add-certificate.swift b/desktop/libs/appletrust/add-certificate.swift index 8ed6601a0..c55ebac34 100644 --- a/desktop/libs/appletrust/add-certificate.swift +++ b/desktop/libs/appletrust/add-certificate.swift @@ -32,14 +32,13 @@ func saveCertificateToKeyChain(_ certificate: SecCertificate, certificateLabel: throw SecurityError.generalError } - var status = SecTrustSettingsSetTrustSettings(certificate, SecTrustSettingsDomain.admin, nil) + SecTrustSettingsSetTrustSettings(certificate, SecTrustSettingsDomain.admin, nil) } func getCertificateFromString(stringData: String) throws -> SecCertificate { - if let data = NSData(base64Encoded: stringData, options: NSData.Base64DecodingOptions.ignoreUnknownCharacters) { - if let certificate = SecCertificateCreateWithData(kCFAllocatorDefault, data) { - return certificate - } + if let data = NSData(base64Encoded: stringData, options: NSData.Base64DecodingOptions.ignoreUnknownCharacters), + let certificate = SecCertificateCreateWithData(kCFAllocatorDefault, data) { + return certificate } throw SecurityError.generalError } diff --git a/desktop/main/app.vue b/desktop/main/app.vue index 0820cd871..de916c9db 100644 --- a/desktop/main/app.vue +++ b/desktop/main/app.vue @@ -1,8 +1,12 @@ @@ -15,10 +19,12 @@ import { initialNavigation, setupHooks, } from "./composables/state-navigation.js"; +import { useAchievementToasts } from "./composables/useAchievementToasts.js"; import { listen } from "@tauri-apps/api/event"; import type { AppState } from "./types.js"; -const router = useRouter(); +const { toasts: achievementToasts, dismiss: dismissAchievementToast } = + useAchievementToasts(); const state = useAppState(); diff --git a/desktop/main/components/AchievementToast.vue b/desktop/main/components/AchievementToast.vue new file mode 100644 index 000000000..fa134c0a2 --- /dev/null +++ b/desktop/main/components/AchievementToast.vue @@ -0,0 +1,46 @@ + + + diff --git a/desktop/main/components/DefaultProtonButton.vue b/desktop/main/components/DefaultProtonButton.vue index dda1f0f08..c770c8216 100644 --- a/desktop/main/components/DefaultProtonButton.vue +++ b/desktop/main/components/DefaultProtonButton.vue @@ -19,9 +19,10 @@ async function setDefault() { + + diff --git a/server/pages/admin/settings/tokens.vue b/server/pages/admin/settings/tokens.vue index 4e9aab8c7..9467a765c 100644 --- a/server/pages/admin/settings/tokens.vue +++ b/server/pages/admin/settings/tokens.vue @@ -116,6 +116,7 @@ class="relative whitespace-nowrap py-4 pl-3 pr-4 text-right text-sm font-medium sm:pr-6" >

{{ $t("users.admin.groups.createGroup") }}

-
-