diff --git a/.github/merge-queue-ci-skipper/action.yml b/.github/merge-queue-ci-skipper/action.yml new file mode 100644 index 000000000..3a2474a6d --- /dev/null +++ b/.github/merge-queue-ci-skipper/action.yml @@ -0,0 +1,171 @@ +# MIT License +# +# Copyright (c) 2024 CARIAD SE +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +name: 'Merge Queue CI Check Skipper' +description: 'Outputs `skip-check` as `true` if this is running as part of merge queue checks and the same checks have already been executed in the PR itself.' +inputs: + secret: + description: 'Optional GitHub Secret that can access branch protection rules using the administration:read permission' + required: false +outputs: + skip-check: + description: 'Skip Check (boolean)' + value: ${{ steps.passed-checks.outputs.can-skip-checks }} +runs: + using: 'composite' + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Extract PR Number and Commit ID + id: extract-pr-info + uses: actions/github-script@v7 + with: + script: | + const githubRef = process.env.GITHUB_REF; + const regex = /^refs\/heads\/gh-readonly-queue\/([a-zA-Z0-9.\-_\/]+)\/pr-(\d+)-([a-f0-9]+)$/; + + if (regex.test(githubRef)) { + const [, targetBranchName, prNumber, commitId] = githubRef.match(regex); + core.setOutput('targetBranchName', targetBranchName); + core.setOutput('prNumber', prNumber); + core.setOutput('commitId', commitId); + } else { + console.log(`GITHUB_REF is not a merge queue ref, setting CAN_SKIP_CHECKS to false: ${githubRef}`); + core.exportVariable('CAN_SKIP_CHECKS', 'false'); + } + + - name: Print PR Number and Target Commit ID + if: env.CAN_SKIP_CHECKS != 'false' + shell: bash + run: | + echo "Target Branch Name: ${{ steps.extract-pr-info.outputs.targetBranchName }}" + echo "PR Number: ${{ steps.extract-pr-info.outputs.prNumber }}" + echo "Target Commit ID: ${{ steps.extract-pr-info.outputs.commitId }}" + + - name: Check if merge queue entry was enqueued as head of the queue + if: env.CAN_SKIP_CHECKS != 'false' + shell: bash + run: | + targetBranchHead=$(git rev-parse origin/${{ steps.extract-pr-info.outputs.targetBranchName }}) + if [[ "$targetBranchHead" != "${{ steps.extract-pr-info.outputs.commitId }}" ]]; then + echo "'${{ steps.extract-pr-info.outputs.targetBranchName }}' branch commit ID does not match PR commit ID. This Merge Queue run was not head of the queue when it was enqueued. Setting CAN_SKIP_CHECKS to false." + echo "CAN_SKIP_CHECKS=false" >> "$GITHUB_ENV" + else + echo "This merge queue entry is targeting '${{ steps.extract-pr-info.outputs.targetBranchName }}' directly." + fi + + - name: Get PR Branch + id: get-pr-branch + if: env.CAN_SKIP_CHECKS != 'false' + shell: bash + env: + GH_TOKEN: ${{ github.token }} + run: | + prNumber=${{ steps.extract-pr-info.outputs.prNumber }} + branchName=$(gh pr view ${prNumber} --json headRefName -q '.headRefName') + echo "prBranch=$branchName" >> "$GITHUB_OUTPUT" + + - name: Print PR Branch + if: env.CAN_SKIP_CHECKS != 'false' + shell: bash + run: | + echo "PR Branch: ${{ steps.get-pr-branch.outputs.prBranch }}" + + - name: Check if PR branch contains the Merge Queue target commit ID + if: env.CAN_SKIP_CHECKS != 'false' + shell: bash + run: | + # Get the branch name from previous steps + branch_name="origin/${{ steps.get-pr-branch.outputs.prBranch }}" + commit_id="${{ steps.extract-pr-info.outputs.commitId }}" + + # Check if the branch history contains the commit + if git branch -r --contains "$commit_id" | grep -q "$branch_name"; then + echo "Branch '$branch_name' contains commit '$commit_id'. It is up to date with ${{ steps.extract-pr-info.outputs.targetBranchName }}." + else + echo "Branch '$branch_name' does not contain commit '$commit_id'. It is outdated. Setting CAN_SKIP_CHECKS to false." + echo "CAN_SKIP_CHECKS=false" >> "$GITHUB_ENV" + fi + + - name: Compare PR Branch with Current Branch + if: env.CAN_SKIP_CHECKS != 'false' + shell: bash + run: | + if git diff --quiet "origin/${{ steps.get-pr-branch.outputs.prBranch }}"; then + echo "No differences found. PR branch is identical with this merge queue branch." + else + echo "Differences detected. PR branch has been updated after PR was added to merge queue. Setting CAN_SKIP_CHECKS to false." + echo "CAN_SKIP_CHECKS=false" >> "$GITHUB_ENV" + fi + + - name: Compute/publish skip result + id: passed-checks + uses: actions/github-script@v7 + env: + SECRET: ${{ inputs.secret }} + with: + github-token: ${{ inputs.secret != '' && inputs.secret || github.token }} + script: | + if (process.env.CAN_SKIP_CHECKS == "false") { + console.log("Setting CAN_SKIP_CHECKS to false"); + core.setOutput("can-skip-checks", false); + return; + } + + const secretProvided = !!process.env.SECRET; + if (!secretProvided) { + console.log("secret input not set, assuming all checks have passed. Ensure 'Require status checks to pass before merging' is enabled, or provide a secret with administration:read."); + core.setOutput("can-skip-checks", true); + return; + } + + const { data: branchProtection } = await github.rest.repos.getBranchProtection({ + owner: context.repo.owner, + repo: context.repo.repo, + branch: "${{ steps.extract-pr-info.outputs.targetBranchName }}", + }); + const requiredCheckNames = branchProtection.required_status_checks.contexts; + console.log(`requiredCheckNames = ${requiredCheckNames}`); + + const { data: checks } = await github.rest.checks.listForRef({ + owner: context.repo.owner, + repo: context.repo.repo, + ref: "refs/heads/${{ steps.get-pr-branch.outputs.prBranch }}", + }); + + console.log(`checks.check_runs = ${checks.check_runs.map(check => `${check.status},${check.conclusion},${check.name};`)}`); + const nonSuccessfulChecks = checks.check_runs.filter(check => check.status !== "completed" || check.conclusion !== "success"); + const nonSuccessfulCheckNames = nonSuccessfulChecks.map(check => check.name); + console.log(`nonSuccessfulCheckNames = ${nonSuccessfulCheckNames}`); + + const missingChecks = requiredCheckNames.filter(checkName => nonSuccessfulCheckNames.includes(checkName)); + + if (missingChecks.length > 0) { + console.log(`Required checks not passed, cannot skip merge queue checks. Setting CAN_SKIP_CHECKS to false. Missing checks: ${missingChecks.join(', ')}`); + core.setOutput("can-skip-checks", false); + } else { + console.log("No missing checks. Setting CAN_SKIP_CHECKS to true."); + core.setOutput("can-skip-checks", true); + } diff --git a/.github/workflows/api-client-release.yml b/.github/workflows/api-client-release.yml new file mode 100644 index 000000000..9f777e913 --- /dev/null +++ b/.github/workflows/api-client-release.yml @@ -0,0 +1,125 @@ +name: API client release + +on: + push: + branches: [main] + paths: + - .github/workflows/api-client-release.yml + - packages/api-client/** + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: false + +permissions: + contents: read + id-token: write + +jobs: + release: + if: github.repository_owner == 'modrinth' && github.ref == 'refs/heads/main' + # npm Trusted Publishing requires a GitHub-hosted runner. + runs-on: ubuntu-latest + env: + FORCE_COLOR: 3 + PACKAGE_DIR: packages/api-client + PACKAGE_NAME: '@modrinth/api-client' + BUMP_TYPE: minor + steps: + - name: Check out code + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 0 + + - name: Check for api-client changes + id: changes + run: | + if [ "${{ github.event.before }}" = "0000000000000000000000000000000000000000" ]; then + echo "changed=true" >> "$GITHUB_OUTPUT" + exit 0 + fi + + if git diff --quiet "${{ github.event.before }}" "$GITHUB_SHA" -- "$PACKAGE_DIR"; then + echo "changed=false" >> "$GITHUB_OUTPUT" + else + echo "changed=true" >> "$GITHUB_OUTPUT" + fi + + - name: Setup Node + if: steps.changes.outputs.changed == 'true' + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + with: + node-version-file: .nvmrc + registry-url: https://registry.npmjs.org + + - name: Enable Corepack + if: steps.changes.outputs.changed == 'true' + run: corepack enable + + - name: Get pnpm store path + if: steps.changes.outputs.changed == 'true' + id: pnpm-store + run: echo "store-path=$(pnpm store path --silent)" >> "$GITHUB_OUTPUT" + + - name: Restore pnpm cache + if: steps.changes.outputs.changed == 'true' + uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + with: + path: ${{ steps.pnpm-store.outputs.store-path }} + key: pnpm-cache-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + pnpm-cache- + + - name: Install dependencies + if: steps.changes.outputs.changed == 'true' + run: pnpm install --frozen-lockfile --filter @modrinth/api-client... + + - name: Resolve release version + if: steps.changes.outputs.changed == 'true' + id: version + run: | + CURRENT_VERSION_JSON="$(npm view "${PACKAGE_NAME}" version --json)" + CURRENT_VERSION="$( + jq -nr \ + --argjson version "$CURRENT_VERSION_JSON" \ + 'if ($version | type) == "array" then $version[-1] else $version end' + )" + + NEXT_VERSION="$( + jq -nr \ + --arg version "$CURRENT_VERSION" \ + --arg bump "$BUMP_TYPE" ' + def semver: + capture("^(?[0-9]+)\\.(?[0-9]+)\\.(?[0-9]+)$") + | with_entries(.value |= tonumber); + + ($version | semver) as $current + | if $bump == "major" then "\($current.major + 1).0.0" + elif $bump == "minor" then "\($current.major).\($current.minor + 1).0" + elif $bump == "patch" then "\($current.major).\($current.minor).\($current.patch + 1)" + else error("Unsupported bump type: \($bump)") + end + ' + )" + + PACKAGE_JSON="$(mktemp)" + jq --tab --arg version "$NEXT_VERSION" '.version = $version' "$PACKAGE_DIR/package.json" > "$PACKAGE_JSON" + mv "$PACKAGE_JSON" "$PACKAGE_DIR/package.json" + + echo "current_version=$CURRENT_VERSION" >> "$GITHUB_OUTPUT" + echo "published_version=$CURRENT_VERSION" >> "$GITHUB_OUTPUT" + echo "version=$NEXT_VERSION" >> "$GITHUB_OUTPUT" + + - name: Build api-client + if: steps.changes.outputs.changed == 'true' + run: pnpm --filter @modrinth/api-client build + + - name: Check package contents + if: steps.changes.outputs.changed == 'true' + working-directory: packages/api-client + run: pnpm pack --dry-run + + - name: Publish api-client + if: steps.changes.outputs.changed == 'true' + working-directory: packages/api-client + run: pnpm publish --access public --provenance --no-git-checks diff --git a/.github/workflows/cancel-pr-workflow-on-merge.yml b/.github/workflows/cancel-pr-workflow-on-merge.yml new file mode 100644 index 000000000..3f3eec12d --- /dev/null +++ b/.github/workflows/cancel-pr-workflow-on-merge.yml @@ -0,0 +1,22 @@ +name: Cancel PR Workflows on Merge + +on: + pull_request_target: + types: + - closed + +permissions: + actions: write + +jobs: + cancel: + if: github.event.pull_request.merged == true + runs-on: ubuntu-latest + steps: + - name: Cancel Previous Runs + uses: styfle/cancel-workflow-action@85880fa0301c86cca9da44039ee3bb12d3bedbfa # 0.12.1 + with: + workflow_id: all + access_token: ${{ secrets.GITHUB_TOKEN }} + ignore_sha: true + pr_number: ${{ github.event.pull_request.number }} diff --git a/.github/workflows/changelog-comment.yml b/.github/workflows/changelog-comment.yml index b68e72c86..79937c7a3 100644 --- a/.github/workflows/changelog-comment.yml +++ b/.github/workflows/changelog-comment.yml @@ -16,8 +16,8 @@ jobs: runs-on: ubuntu-latest steps: - - name: 💬 Post or update changelog comment - uses: actions/github-script@v7 + - name: Post or update changelog comment + uses: actions/github-script@d746ffe35508b1917358783b479e04febd2b8f71 # v9.0.0 with: github-token: ${{ secrets.CROWDIN_GH_TOKEN }} script: | diff --git a/.github/workflows/check-generic.yml b/.github/workflows/check-generic.yml index 03e3dc649..a22c8c44a 100644 --- a/.github/workflows/check-generic.yml +++ b/.github/workflows/check-generic.yml @@ -2,7 +2,7 @@ on: pull_request: push: branches: - - master + - main env: CARGO_TERM_COLOR: always @@ -12,15 +12,15 @@ jobs: typos: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: crate-ci/typos@v1.43.1 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: crate-ci/typos@6ac2ebd1b93eade61faf7e12688ad87a073fea59 # v1.46.0 # see tombi: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: taiki-e/install-action@v2 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: taiki-e/install-action@b5fddbb5361bce8a06fb168c9d403a6cc552b084 # v2.75.29 with: tool: tombi - run: tombi lint diff --git a/.github/workflows/check-rust.yml b/.github/workflows/check-rust.yml index 97b08c255..3a516c20b 100644 --- a/.github/workflows/check-rust.yml +++ b/.github/workflows/check-rust.yml @@ -2,7 +2,7 @@ on: pull_request: push: branches: - - master + - main env: CARGO_TERM_COLOR: always @@ -12,8 +12,8 @@ jobs: shear: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@stable - - uses: cargo-bins/cargo-binstall@main + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable + - uses: cargo-bins/cargo-binstall@dc19f1e48450eefe5a29b8da6c6b00a87d730b37 # v1.18.1 - run: cargo binstall --no-confirm cargo-shear - run: cargo shear diff --git a/.github/workflows/daedalus-docker.yml b/.github/workflows/daedalus-docker.yml index ad6661134..3f11eb4ed 100644 --- a/.github/workflows/daedalus-docker.yml +++ b/.github/workflows/daedalus-docker.yml @@ -3,33 +3,133 @@ name: daedalus-docker-build on: push: branches: - - '**' + - 'main' paths: - .github/workflows/daedalus-docker.yml - 'apps/daedalus_client/**' - 'packages/daedalus/**' + - Cargo.toml + - Cargo.lock pull_request: types: [opened, synchronize] paths: - .github/workflows/daedalus-docker.yml - 'apps/daedalus_client/**' - 'packages/daedalus/**' + - Cargo.toml + - Cargo.lock merge_group: types: [checks_requested] +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.ref != 'refs/heads/main' && github.ref != 'refs/heads/prod' }} + jobs: - docker: + skip-if-clean: + name: Skip if merge_queue produces no diff runs-on: ubuntu-latest + outputs: + skip: ${{ steps.check.outputs.skip }} + internal: ${{ steps.check-internal.outputs.internal }} + if: ${{ always() }} steps: - - name: 📥 Check out code - uses: actions/checkout@v4 + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - name: 🧰 Set up Docker Buildx - uses: docker/setup-buildx-action@v2 + - name: Check if workflow runs on an internal branch + id: check-internal + env: + EVENT_NAME: ${{ github.event_name }} + HEAD_REPO: ${{ github.event.pull_request.head.repo.full_name }} + REPO: ${{ github.repository }} + run: | + if [ "$EVENT_NAME" != "pull_request" ] || [ "$HEAD_REPO" = "$REPO" ]; then + echo "internal=true" >> $GITHUB_OUTPUT + else + echo "internal=false" >> $GITHUB_OUTPUT + fi - - name: ⚙️ Generate Docker image metadata - id: docker_meta - uses: docker/metadata-action@v5 + - name: Merge Queue CI Check Skipper + id: merge-queue-ci-skipper + uses: ./.github/merge-queue-ci-skipper + with: + secret: ${{ secrets.GH_ACCESS_TOKEN }} + + - name: Check merge_group synthetic commit + id: check + run: | + # PR mode: never skip + if [ "${{ github.event_name }}" != "merge_group" ]; then + echo "skip=false" >> $GITHUB_OUTPUT + exit 0 + fi + + if [ "${{ steps.merge-queue-ci-skipper.outputs.skip-check }}" = "true" ]; then + echo "skip=true" >> $GITHUB_OUTPUT + else + echo "skip=false" >> $GITHUB_OUTPUT + fi + + docker: + runs-on: ${{ needs.skip-if-clean.outputs.internal == 'true' && 'blacksmith-4vcpu-ubuntu-2404' || 'ubuntu-latest' }} + env: + SCCACHE_DIR: ${{ needs.skip-if-clean.outputs.internal == 'true' && '/mnt/sccache' || '' }} + SCCACHE_CACHE_SIZE: ${{ needs.skip-if-clean.outputs.internal == 'true' && '10G' || '' }} + SCCACHE_MULTILEVEL_CHAIN: ${{ needs.skip-if-clean.outputs.internal == 'true' && 'disk,s3' || '' }} + SCCACHE_S3_KEY_PREFIX: ${{ needs.skip-if-clean.outputs.internal == 'true' && format('{0}/', github.repository) || '' }} + SCCACHE_BUCKET: ${{ secrets.SCCACHE_BUCKET }} + SCCACHE_REGION: ${{ secrets.SCCACHE_REGION }} + SCCACHE_ENDPOINT: ${{ secrets.SCCACHE_ENDPOINT }} + AWS_ACCESS_KEY_ID: ${{ secrets.SCCACHE_S3_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.SCCACHE_S3_SECRET_ACCESS_KEY }} + RUSTC_WRAPPER: ${{ needs.skip-if-clean.outputs.internal == 'true' && 'sccache' || '' }} + needs: [skip-if-clean] + if: ${{ needs.skip-if-clean.outputs.skip != 'true' }} + steps: + - name: Check out code + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Setup Rust toolchain + uses: actions-rust-lang/setup-rust-toolchain@2b1f5e9b395427c92ee4e3331786ca3c37afe2d7 # v1.16.0 + with: + rustflags: '' + cache: false + + - name: Cache Cargo registry and index + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + ~/.cargo/bin + key: ${{ runner.os }}-${{ runner.arch }}-cargo-${{ hashFiles('**/Cargo.lock') }} + + - name: Mount sccache disk cache + if: needs.skip-if-clean.outputs.internal == 'true' + uses: useblacksmith/stickydisk@13af8883542ca949a717e70fef89d15edbb29d88 # v1.2.0 + with: + key: ${{ github.repository }}-daedalus-sccache + path: /mnt/sccache + + - name: Setup sccache + if: needs.skip-if-clean.outputs.internal == 'true' + uses: mozilla-actions/sccache-action@9e7fa8a12102821edf02ca5dbea1acd0f89a2696 # v0.0.10 + + - name: Build daedalus_client + run: cargo build --release --package daedalus_client + + - name: Stage Docker context + run: | + mkdir -p apps/daedalus_client/docker-stage + cp target/release/daedalus_client apps/daedalus_client/docker-stage/daedalus_client + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 + + - name: Generate Docker image metadata + id: docker-meta + uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0 env: DOCKER_METADATA_ANNOTATIONS_LEVELS: manifest,index with: @@ -43,20 +143,19 @@ jobs: org.opencontainers.image.description=Modrinth game metadata query client org.opencontainers.image.licenses=MIT - - name: 🔑 Login to GitHub Packages - uses: docker/login-action@v3 + - name: Login to GitHub Packages + uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - name: 🔨 Build and push - uses: docker/build-push-action@v6 + - name: Build and push + uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0 with: + context: ./apps/daedalus_client/docker-stage file: ./apps/daedalus_client/Dockerfile push: ${{ github.event_name != 'pull_request' }} - tags: ${{ steps.docker_meta.outputs.tags }} - labels: ${{ steps.docker_meta.outputs.labels }} - annotations: ${{ steps.docker_meta.outputs.annotations }} - cache-from: type=registry,ref=ghcr.io/modrinth/daedalus:main - cache-to: type=inline + tags: ${{ steps.docker-meta.outputs.tags }} + labels: ${{ steps.docker-meta.outputs.labels }} + annotations: ${{ steps.docker-meta.outputs.annotations }} diff --git a/.github/workflows/daedalus-run.yml b/.github/workflows/daedalus-run.yml index 56fbd9f82..5060693e6 100644 --- a/.github/workflows/daedalus-run.yml +++ b/.github/workflows/daedalus-run.yml @@ -12,10 +12,10 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Log in to GitHub Container Registry - uses: docker/login-action@v2 + uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 with: registry: ghcr.io username: ${{ github.actor }} diff --git a/.github/workflows/frontend-deploy.yml b/.github/workflows/frontend-deploy.yml index cf6f8eff6..df9b93d2a 100644 --- a/.github/workflows/frontend-deploy.yml +++ b/.github/workflows/frontend-deploy.yml @@ -21,16 +21,20 @@ on: type: string description: 'The environment to deploy to (staging-preview or production-preview)' +concurrency: + group: ${{ github.workflow }}-${{ github.ref }}-${{ inputs.environment || 'push' }} + cancel-in-progress: ${{ github.ref != 'refs/heads/main' && github.ref != 'refs/heads/prod' }} + jobs: deploy: - runs-on: ubuntu-latest + runs-on: blacksmith-2vcpu-ubuntu-2404 permissions: contents: read deployments: write pull-requests: write steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 @@ -63,14 +67,25 @@ jobs: echo "url=https://modrinth.com" >> $GITHUB_OUTPUT fi - - name: Setup pnpm - uses: pnpm/action-setup@v4 - - name: Setup Node - uses: actions/setup-node@v4 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version-file: .nvmrc - cache: pnpm + + - name: Enable Corepack + run: corepack enable + + - name: Get pnpm store path + id: pnpm-store + run: echo "store-path=$(pnpm store path --silent)" >> $GITHUB_OUTPUT + + - name: Restore pnpm cache + uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + with: + path: ${{ steps.pnpm-store.outputs.store-path }} + key: pnpm-cache-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + pnpm-cache- - name: Inject build variables working-directory: ./apps/frontend @@ -99,7 +114,7 @@ jobs: SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} - name: Create Sentry release and upload sourcemaps - uses: getsentry/action-release@v3 + uses: getsentry/action-release@5657c9e888b4e2cc85f4d29143ea4131fde4a73a # v3.6.0 env: SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} SENTRY_ORG: modrinth @@ -111,7 +126,7 @@ jobs: - name: Deploy Cloudflare Worker id: wrangler - uses: cloudflare/wrangler-action@v3 + uses: cloudflare/wrangler-action@9acf94ace14e7dc412b076f2c5c20b8ce93c79cd # v3.15.0 with: apiToken: ${{ secrets.CF_API_TOKEN }} accountId: ${{ secrets.CF_ACCOUNT_ID }} @@ -137,7 +152,7 @@ jobs: - name: Upload deployment URL if: ${{ inputs.environment != '' }} - uses: actions/upload-artifact@v6 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: deployment-url-${{ inputs.environment }} path: deployment-url-${{ inputs.environment }}.txt diff --git a/.github/workflows/frontend-preview.yml b/.github/workflows/frontend-preview.yml index d7ba1ad4b..16b3e98ed 100644 --- a/.github/workflows/frontend-preview.yml +++ b/.github/workflows/frontend-preview.yml @@ -16,6 +16,9 @@ jobs: if: github.repository_owner == 'modrinth' && github.event.pull_request.head.repo.full_name == github.repository uses: ./.github/workflows/frontend-deploy.yml secrets: inherit + concurrency: + group: ${{ github.workflow }}-${{ github.ref }}-${{ matrix.environment }} + cancel-in-progress: true strategy: matrix: environment: [staging-preview, production-preview] @@ -24,22 +27,36 @@ jobs: deploy-storybook: if: github.repository_owner == 'modrinth' && github.event.pull_request.head.repo.full_name == github.repository - runs-on: ubuntu-latest + runs-on: blacksmith-2vcpu-ubuntu-2404 + concurrency: + group: ${{ github.workflow }}-${{ github.ref }}-storybook + cancel-in-progress: true permissions: contents: read deployments: write steps: - name: Checkout code - uses: actions/checkout@v4 - - - name: Setup pnpm - uses: pnpm/action-setup@v4 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Setup Node - uses: actions/setup-node@v4 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version-file: .nvmrc - cache: pnpm + + - name: Enable Corepack + run: corepack enable + + - name: Get pnpm store path + id: pnpm-store + run: echo "store-path=$(pnpm store path --silent)" >> $GITHUB_OUTPUT + + - name: Restore pnpm cache + uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + with: + path: ${{ steps.pnpm-store.outputs.store-path }} + key: pnpm-cache-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + pnpm-cache- - name: Install dependencies working-directory: ./packages/ui @@ -54,7 +71,7 @@ jobs: run: echo "sha_short=${GITHUB_SHA::8}" >> $GITHUB_OUTPUT - name: Deploy Storybook preview - uses: cloudflare/wrangler-action@v3 + uses: cloudflare/wrangler-action@9acf94ace14e7dc412b076f2c5c20b8ce93c79cd # v3.15.0 with: apiToken: ${{ secrets.CF_API_TOKEN }} accountId: ${{ secrets.CF_ACCOUNT_ID }} @@ -69,7 +86,7 @@ jobs: needs: [deploy, deploy-storybook] steps: - name: Download deployment URLs - uses: actions/download-artifact@v7 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: pattern: deployment-url-* merge-multiple: true @@ -89,7 +106,7 @@ jobs: - name: Find comment if: github.event_name == 'pull_request' - uses: peter-evans/find-comment@v3 + uses: peter-evans/find-comment@b30e6a3c0ed37e7c023ccd3f1db5c6c0b0c23aad # v4.0.0 id: fc with: token: ${{ secrets.CROWDIN_GH_TOKEN }} @@ -98,7 +115,7 @@ jobs: - name: Comment deploy URL on PR if: github.event_name == 'pull_request' - uses: peter-evans/create-or-update-comment@v5 + uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9 # v5.0.0 with: token: ${{ secrets.CROWDIN_GH_TOKEN }} issue-number: ${{ github.event.pull_request.number }} diff --git a/.github/workflows/i18n-pull.yml b/.github/workflows/i18n-pull.yml index 530dec45b..4ea0e1952 100644 --- a/.github/workflows/i18n-pull.yml +++ b/.github/workflows/i18n-pull.yml @@ -51,14 +51,14 @@ jobs: CROWDIN_GH_TOKEN_DEFINED: ${{ secrets.CROWDIN_GH_TOKEN != '' }} - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: ref: ${{ github.ref }} token: ${{ secrets.CROWDIN_GH_TOKEN }} - name: Configure Git author id: git-author - uses: MarcoIeni/git-config@v0.1 + uses: MarcoIeni/git-config@59144859caf016f8b817a2ac9b051578729173c4 # v0.1.2 env: GITHUB_TOKEN: ${{ secrets.CROWDIN_GH_TOKEN }} @@ -79,7 +79,7 @@ jobs: echo "safe_branch_name=$SAFE_BRANCH_NAME" >> "$GITHUB_OUTPUT" - name: Download translations from Crowdin - uses: crowdin/github-action@v2 + uses: crowdin/github-action@8868a33591d21088edfc398968173a3b98d51706 # v2.16.2 with: upload_sources: false upload_translations: false @@ -96,7 +96,7 @@ jobs: run: sudo chown -R $USER:$USER . - name: Create Pull Request - uses: peter-evans/create-pull-request@v7 + uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1 with: title: 'New translations from Crowdin (${{ steps.branch-name.outputs.branch_name }})' body-path: .github/templates/crowdin-pr.md diff --git a/.github/workflows/i18n-push.yml b/.github/workflows/i18n-push.yml index 0e34d9cfe..9a0de81d4 100644 --- a/.github/workflows/i18n-push.yml +++ b/.github/workflows/i18n-push.yml @@ -53,7 +53,7 @@ jobs: CROWDIN_PERSONAL_TOKEN_DEFINED: ${{ secrets.CROWDIN_PERSONAL_TOKEN != '' }} - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: ref: ${{ github.ref }} @@ -68,7 +68,7 @@ jobs: echo "safe_branch_name=$SAFE_BRANCH_NAME" >> "$GITHUB_OUTPUT" - name: Upload translations to Crowdin - uses: crowdin/github-action@v1 + uses: crowdin/github-action@8868a33591d21088edfc398968173a3b98d51706 # v2.16.2 with: upload_sources: true upload_translations: false diff --git a/.github/workflows/labrinth-docker.yml b/.github/workflows/labrinth-docker.yml index 99a57f7a8..3ecc90fea 100644 --- a/.github/workflows/labrinth-docker.yml +++ b/.github/workflows/labrinth-docker.yml @@ -3,7 +3,7 @@ name: docker-build on: push: branches: - - '**' + - 'main' paths: - .github/workflows/labrinth-docker.yml - 'apps/labrinth/**' @@ -19,19 +19,122 @@ on: merge_group: types: [checks_requested] +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.ref != 'refs/heads/main' && github.ref != 'refs/heads/prod' }} + jobs: - docker: + skip-if-clean: + name: Skip if merge_queue produces no diff runs-on: ubuntu-latest + outputs: + skip: ${{ steps.check.outputs.skip }} + internal: ${{ steps.check-internal.outputs.internal }} + if: ${{ always() }} steps: - - name: 📥 Check out code - uses: actions/checkout@v4 + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - name: 🧰 Set up Docker Buildx - uses: docker/setup-buildx-action@v2 + - name: Check if workflow runs on an internal branch + id: check-internal + env: + EVENT_NAME: ${{ github.event_name }} + HEAD_REPO: ${{ github.event.pull_request.head.repo.full_name }} + REPO: ${{ github.repository }} + run: | + if [ "$EVENT_NAME" != "pull_request" ] || [ "$HEAD_REPO" = "$REPO" ]; then + echo "internal=true" >> $GITHUB_OUTPUT + else + echo "internal=false" >> $GITHUB_OUTPUT + fi - - name: ⚙️ Generate Docker image metadata - id: docker_meta - uses: docker/metadata-action@v5 + - name: Merge Queue CI Check Skipper + id: merge-queue-ci-skipper + uses: ./.github/merge-queue-ci-skipper + with: + secret: ${{ secrets.GH_ACCESS_TOKEN }} + + - name: Check merge_group synthetic commit + id: check + run: | + # PR mode: never skip + if [ "${{ github.event_name }}" != "merge_group" ]; then + echo "skip=false" >> $GITHUB_OUTPUT + exit 0 + fi + + if [ "${{ steps.merge-queue-ci-skipper.outputs.skip-check }}" = "true" ]; then + echo "skip=true" >> $GITHUB_OUTPUT + else + echo "skip=false" >> $GITHUB_OUTPUT + fi + + docker: + runs-on: ${{ needs.skip-if-clean.outputs.internal == 'true' && 'blacksmith-4vcpu-ubuntu-2404' || 'ubuntu-latest' }} + needs: [skip-if-clean] + if: ${{ needs.skip-if-clean.outputs.skip != 'true' }} + env: + SQLX_OFFLINE: 'true' + GIT_HASH: ${{ github.sha }} + SCCACHE_DIR: ${{ needs.skip-if-clean.outputs.internal == 'true' && '/mnt/sccache' || '' }} + SCCACHE_CACHE_SIZE: ${{ needs.skip-if-clean.outputs.internal == 'true' && '10G' || '' }} + SCCACHE_MULTILEVEL_CHAIN: ${{ needs.skip-if-clean.outputs.internal == 'true' && 'disk,s3' || '' }} + SCCACHE_S3_KEY_PREFIX: ${{ needs.skip-if-clean.outputs.internal == 'true' && format('{0}/', github.repository) || '' }} + SCCACHE_BUCKET: ${{ secrets.SCCACHE_BUCKET }} + SCCACHE_REGION: ${{ secrets.SCCACHE_REGION }} + SCCACHE_ENDPOINT: ${{ secrets.SCCACHE_ENDPOINT }} + AWS_ACCESS_KEY_ID: ${{ secrets.SCCACHE_S3_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.SCCACHE_S3_SECRET_ACCESS_KEY }} + RUSTC_WRAPPER: ${{ needs.skip-if-clean.outputs.internal == 'true' && 'sccache' || '' }} + steps: + - name: Check out code + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Setup Rust toolchain + uses: actions-rust-lang/setup-rust-toolchain@2b1f5e9b395427c92ee4e3331786ca3c37afe2d7 # v1.16.0 + with: + rustflags: '' + cache: false + + - name: Setup mold + uses: rui314/setup-mold@9c9c13bf4c3f1adef0cc596abc155580bcb04444 # v1 / Mold 2.41.0 + + - name: Cache Cargo registry and index + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + ~/.cargo/bin + key: ${{ runner.os }}-${{ runner.arch }}-cargo-${{ hashFiles('**/Cargo.lock') }} + + - name: Mount sccache disk cache + if: needs.skip-if-clean.outputs.internal == 'true' + uses: useblacksmith/stickydisk@13af8883542ca949a717e70fef89d15edbb29d88 # v1.2.0 + with: + key: ${{ github.repository }}-labrinth-sccache + path: /mnt/sccache + + - name: Setup sccache + if: needs.skip-if-clean.outputs.internal == 'true' + uses: mozilla-actions/sccache-action@9e7fa8a12102821edf02ca5dbea1acd0f89a2696 # v0.0.10 + + - name: Build labrinth + run: cargo build --profile release-labrinth --package labrinth + + - name: Stage Docker context + run: | + mkdir -p apps/labrinth/docker-stage + cp target/release-labrinth/labrinth apps/labrinth/docker-stage/labrinth + cp -r apps/labrinth/migrations apps/labrinth/docker-stage/migrations + cp -r apps/labrinth/assets apps/labrinth/docker-stage/assets + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 + + - name: Generate Docker image metadata + id: docker-meta + uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0 env: # GitHub Packages requires annotations metadata in at least the index descriptor to show them # up properly in its UI it seems, but it's not clear about it, because the docs refer to the @@ -49,22 +152,19 @@ jobs: org.opencontainers.image.description=Modrinth API org.opencontainers.image.licenses=AGPL-3.0-only - - name: 🔑 Login to GitHub Packages - uses: docker/login-action@v3 + - name: Login to GitHub Packages + uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - name: 🔨 Build and push - uses: docker/build-push-action@v6 + - name: Build and push + uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0 with: + context: ./apps/labrinth/docker-stage file: ./apps/labrinth/Dockerfile - push: ${{ github.event_name != 'pull_request' }} - tags: ${{ steps.docker_meta.outputs.tags }} - labels: ${{ steps.docker_meta.outputs.labels }} - annotations: ${{ steps.docker_meta.outputs.annotations }} - build-args: | - GIT_HASH=${{ fromJSON(steps.docker_meta.outputs.json).labels['org.opencontainers.image.revision'] }} - cache-from: type=registry,ref=ghcr.io/modrinth/labrinth:main - cache-to: type=inline + push: true + tags: ${{ steps.docker-meta.outputs.tags }} + labels: ${{ steps.docker-meta.outputs.labels }} + annotations: ${{ steps.docker-meta.outputs.annotations }} diff --git a/.github/workflows/prepare-pnpm-cache.yml b/.github/workflows/prepare-pnpm-cache.yml new file mode 100644 index 000000000..16c563c94 --- /dev/null +++ b/.github/workflows/prepare-pnpm-cache.yml @@ -0,0 +1,41 @@ +name: Prepare pnpm cache + +on: + push: + paths: + - .github/workflows/prepare-pnpm-cache.yml + - package.json + - pnpm-lock.yaml + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.ref != 'refs/heads/main' && github.ref != 'refs/heads/prod' }} + +jobs: + prepare: + if: github.repository_owner == 'modrinth' + runs-on: blacksmith-2vcpu-ubuntu-2404 + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Setup Node + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + with: + node-version-file: .nvmrc + + - name: Enable Corepack + run: corepack enable + + - name: Get pnpm store path + id: pnpm-store + run: echo "store-path=$(pnpm store path --silent)" >> $GITHUB_OUTPUT + + - name: Cache pnpm + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + with: + path: ${{ steps.pnpm-store.outputs.store-path }} + key: pnpm-cache-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('**/pnpm-lock.yaml') }} + + - name: Install dependencies + run: pnpm recursive install --frozen-lockfile diff --git a/.github/workflows/theseus-build.yml b/.github/workflows/theseus-build.yml index 36547186e..3f882f506 100644 --- a/.github/workflows/theseus-build.yml +++ b/.github/workflows/theseus-build.yml @@ -31,45 +31,66 @@ on: default: prod required: false +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.ref != 'refs/heads/main' && github.ref != 'refs/heads/prod' }} + jobs: build: name: Build env: VITE_STRIPE_PUBLISHABLE_KEY: pk_live_51JbFxJJygY5LJFfKLVVldb10HlLt24p421OWRsTOWc5sXYFOnFUXWieSc6HD3PHo25ktx8db1WcHr36XGFvZFVUz00V9ixrCs5 + # SCCACHE_DIR: '/mnt/sccache' + # SCCACHE_CACHE_SIZE: '10G' + # SCCACHE_MULTILEVEL_CHAIN: 'disk,s3' + SCCACHE_S3_KEY_PREFIX: '${{ github.repository }}/' + SCCACHE_BUCKET: ${{ secrets.SCCACHE_BUCKET }} + SCCACHE_REGION: ${{ secrets.SCCACHE_REGION }} + SCCACHE_ENDPOINT: ${{ secrets.SCCACHE_ENDPOINT }} + AWS_ACCESS_KEY_ID: ${{ secrets.SCCACHE_S3_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.SCCACHE_S3_SECRET_ACCESS_KEY }} + RUSTC_WRAPPER: 'sccache' strategy: fail-fast: false matrix: - platform: [macos-latest, windows-latest, ubuntu-latest] + platform: [ + blacksmith-6vcpu-macos-26, + blacksmith-8vcpu-windows-2025, # At time of writing, Windows 4 vCPU VMs don't seem to actually exist + blacksmith-4vcpu-ubuntu-2404, + ] include: - - platform: macos-latest + - platform: blacksmith-6vcpu-macos-26 artifact-target-name: universal-apple-darwin - - platform: windows-latest + - platform: blacksmith-8vcpu-windows-2025 artifact-target-name: x86_64-pc-windows-msvc - - platform: ubuntu-latest + - platform: blacksmith-4vcpu-ubuntu-2404 artifact-target-name: x86_64-unknown-linux-gnu runs-on: ${{ matrix.platform }} steps: - name: Check out code - uses: actions/checkout@v4 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 - name: Setup Rust toolchain - uses: actions-rust-lang/setup-rust-toolchain@v1 + uses: actions-rust-lang/setup-rust-toolchain@2b1f5e9b395427c92ee4e3331786ca3c37afe2d7 # v1.16.0 with: rustflags: '' - target: ${{ startsWith(matrix.platform, 'macos') && 'x86_64-apple-darwin' || '' }} + target: ${{ contains(matrix.platform, 'macos') && 'x86_64-apple-darwin' || '' }} - - name: Install pnpm - uses: pnpm/action-setup@v4 + - name: Setup sccache + uses: mozilla-actions/sccache-action@9e7fa8a12102821edf02ca5dbea1acd0f89a2696 # v0.0.10 - - name: Setup Node.js - uses: actions/setup-node@v4 + - name: Enable Corepack + run: corepack enable + + - name: Setup Node + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version-file: .nvmrc - cache: pnpm + cache: 'pnpm' - name: Generate tauri-dev.conf.json shell: bash @@ -87,18 +108,19 @@ jobs: EOF - name: Install Linux build dependencies - if: startsWith(matrix.platform, 'ubuntu') - run: | - sudo apt-get update - sudo apt-get install -yq libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev + if: contains(matrix.platform, 'ubuntu') + uses: awalsh128/cache-apt-pkgs-action@acb598e5ddbc6f68a970c5da0688d2f3a9f04d05 # v1.6.0 + with: + packages: libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev + version: v1 # cache key - name: Setup Dasel - uses: jaxxstorm/action-install-gh-release@v2.1.0 + uses: jaxxstorm/action-install-gh-release@25e24d2d23ae098373794ef1d6faecb48ee52da8 # v3.0.0 with: repo: TomWright/dasel tag: v2.8.1 extension-matching: disable - rename-to: ${{ startsWith(matrix.platform, 'windows') && 'dasel.exe' || 'dasel' }} + rename-to: ${{ contains(matrix.platform, 'windows') && 'dasel.exe' || 'dasel' }} chmod: 0755 - name: Set application version and environment @@ -115,13 +137,13 @@ jobs: cp "packages/app-lib/.env.${BUILD_ENVIRONMENT}" packages/app-lib/.env - name: Setup Turbo cache - uses: rharkor/caching-for-turbo@v1.8 + uses: rharkor/caching-for-turbo@56219402aacc0d06b650d898c222996dbc1191ec # v2.3.14 - name: Install dependencies run: pnpm install - name: Set up Windows code signing - if: startsWith(matrix.platform, 'windows') + if: contains(matrix.platform, 'windows') shell: bash run: | if [ '${{ startsWith(github.ref, 'refs/tags/v') || inputs.sign-windows-binaries }}' = 'true' ]; then @@ -132,8 +154,9 @@ jobs: - name: Build macOS app run: ${{ (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')) && 'pnpm --filter=@modrinth/app run tauri build --target universal-apple-darwin --config tauri-release.conf.json' || 'pnpm --filter=@modrinth/app run tauri build --target universal-apple-darwin --config tauri-dev.conf.json' }} - if: startsWith(matrix.platform, 'macos') + if: contains(matrix.platform, 'macos') env: + TAURI_BUNDLER_DMG_IGNORE_CI: 'true' ENABLE_CODE_SIGNING: ${{ secrets.APPLE_CERTIFICATE }} APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} @@ -146,7 +169,7 @@ jobs: - name: Build Linux app run: ${{ (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')) && 'pnpm --filter=@modrinth/app run tauri build --config tauri-release.conf.json' || 'pnpm --filter=@modrinth/app run tauri build --config tauri-dev.conf.json' }} - if: startsWith(matrix.platform, 'ubuntu') + if: contains(matrix.platform, 'ubuntu') env: TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }} TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }} @@ -158,7 +181,7 @@ jobs: $env:JAVA_HOME = "$env:JAVA_HOME_17_X64" ${{ (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')) && 'pnpm --filter=@modrinth/app run tauri build --config tauri-release.conf.json --verbose --bundles "nsis,updater"' || 'pnpm --filter=@modrinth/app run tauri build --config tauri-dev.conf.json --verbose --bundles "nsis,updater"' }} Remove-Item -Path signer-client-cert.p12 -ErrorAction SilentlyContinue - if: startsWith(matrix.platform, 'windows') + if: contains(matrix.platform, 'windows') env: TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }} TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }} @@ -167,7 +190,7 @@ jobs: DIGICERT_ONE_SIGNER_CLIENT_CERTIFICATE_PASSWORD: ${{ secrets.DIGICERT_ONE_SIGNER_CLIENT_CERTIFICATE_PASSWORD }} - name: Upload app bundles - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: App bundle (${{ matrix.artifact-target-name }}) path: | diff --git a/.github/workflows/theseus-release.yml b/.github/workflows/theseus-release.yml index 20d6fa331..c0331c33b 100644 --- a/.github/workflows/theseus-release.yml +++ b/.github/workflows/theseus-release.yml @@ -4,6 +4,10 @@ on: workflows: ['Modrinth App build'] types: [completed] +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.ref != 'refs/heads/main' && github.ref != 'refs/heads/prod' }} + jobs: release: name: Release Modrinth App @@ -11,8 +15,7 @@ jobs: github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event == 'push' && startsWith(github.event.workflow_run.head_branch, 'v') - runs-on: ubuntu-latest - + runs-on: blacksmith-4vcpu-ubuntu-2404 env: VERSION_TAG: ${{ github.event.workflow_run.head_branch }} LINUX_X64_BUNDLE_ARTIFACT_NAME: App bundle (x86_64-unknown-linux-gnu) @@ -21,10 +24,10 @@ jobs: LAUNCHER_FILES_BUCKET_BASE_URL: https://launcher-files.modrinth.com steps: - - name: 📥 Check out code - uses: actions/checkout@v4 + - name: Check out code + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - name: 🔒 Verify ref is a tag + - name: Verify ref is a tag env: GH_TOKEN: ${{ github.token }} HEAD_SHA: ${{ github.event.workflow_run.head_sha }} @@ -43,8 +46,8 @@ jobs: fi echo "Verified ${VERSION_TAG} is a tag pointing at ${HEAD_SHA}" - - name: 📥 Download Modrinth App artifacts - uses: dawidd6/action-download-artifact@v11 + - name: Download Modrinth App artifacts + uses: dawidd6/action-download-artifact@b6e2e70617bc3265edd6dab6c906732b2f1ae151 # v21 with: workflow: theseus-build.yml workflow_conclusion: success @@ -52,12 +55,12 @@ jobs: branch: ${{ env.VERSION_TAG }} use_unzip: true - - name: 📝 Extract app changelog + - name: Extract app changelog env: VERSION: ${{ env.VERSION_TAG }} run: npx --yes tsx scripts/build-theseus-release-notes.ts - - name: 🛠️ Generate version manifest + - name: Generate version manifest run: | # Reference: https://tauri.app/plugin/updater/#server-support jq -nc \ @@ -102,7 +105,7 @@ jobs: echo "Generated manifest for version ${VERSION_TAG}:" cat updates.json - - name: 📤 Upload release artifacts + - name: Upload release artifacts env: AWS_ACCESS_KEY_ID: ${{ secrets.LAUNCHER_FILES_BUCKET_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.LAUNCHER_FILES_BUCKET_SECRET_ACCESS_KEY }} @@ -137,7 +140,7 @@ jobs: aws s3 cp updates.json "s3://${AWS_BUCKET}" - - name: 🏷️ Create GitHub release + - name: Create GitHub release env: GH_TOKEN: ${{ github.token }} run: | diff --git a/.github/workflows/turbo-ci.yml b/.github/workflows/turbo-ci.yml index f416e6c09..e25448c86 100644 --- a/.github/workflows/turbo-ci.yml +++ b/.github/workflows/turbo-ci.yml @@ -8,10 +8,63 @@ on: merge_group: types: [checks_requested] +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.ref != 'refs/heads/main' && github.ref != 'refs/heads/prod' }} + jobs: + skip-if-clean: + name: Skip if merge_queue produces no diff + runs-on: ubuntu-latest + outputs: + skip: ${{ steps.check.outputs.skip }} + internal: ${{ steps.check-internal.outputs.internal }} + if: ${{ always() }} + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Check if workflow runs on an internal branch + id: check-internal + env: + EVENT_NAME: ${{ github.event_name }} + HEAD_REPO: ${{ github.event.pull_request.head.repo.full_name }} + REPO: ${{ github.repository }} + run: | + if [ "$EVENT_NAME" != "pull_request" ] || [ "$HEAD_REPO" = "$REPO" ]; then + echo "internal=true" >> $GITHUB_OUTPUT + else + echo "internal=false" >> $GITHUB_OUTPUT + fi + + - name: Merge Queue CI Check Skipper + id: merge-queue-ci-skipper + uses: ./.github/merge-queue-ci-skipper + with: + secret: ${{ secrets.GH_ACCESS_TOKEN }} + + - name: Check merge_group synthetic commit + id: check + env: + EVENT_NAME: ${{ github.event_name }} + SKIP_CHECK: ${{ steps.merge-queue-ci-skipper.outputs.skip-check }} + run: | + if [ "$EVENT_NAME" != "merge_group" ]; then + echo "skip=false" >> $GITHUB_OUTPUT + exit 0 + fi + + if [ "$SKIP_CHECK" = "true" ]; then + echo "skip=true" >> $GITHUB_OUTPUT + else + echo "skip=false" >> $GITHUB_OUTPUT + fi + build: name: Lint and Test - runs-on: ubuntu-latest + runs-on: ${{ needs.skip-if-clean.outputs.internal == 'true' && 'blacksmith-4vcpu-ubuntu-2404' || 'ubuntu-latest' }} + needs: [skip-if-clean] + if: ${{ needs.skip-if-clean.outputs.skip != 'true' }} env: # Ensure pnpm output is colored in GitHub Actions logs @@ -23,59 +76,107 @@ jobs: # since we don't want warnings to become errors # while developing) RUSTFLAGS: -Dwarnings + # sccache config (only populated for internal branches; secrets aren't + # available to forked PRs, and blacksmith stickydisk requires a + # blacksmith runner) + SCCACHE_DIR: ${{ needs.skip-if-clean.outputs.internal == 'true' && '/mnt/sccache' || '' }} + SCCACHE_CACHE_SIZE: ${{ needs.skip-if-clean.outputs.internal == 'true' && '10G' || '' }} + SCCACHE_MULTILEVEL_CHAIN: ${{ needs.skip-if-clean.outputs.internal == 'true' && 'disk,s3' || '' }} + SCCACHE_S3_KEY_PREFIX: ${{ needs.skip-if-clean.outputs.internal == 'true' && format('{0}/', github.repository) || '' }} + SCCACHE_BUCKET: ${{ secrets.SCCACHE_BUCKET }} + SCCACHE_REGION: ${{ secrets.SCCACHE_REGION }} + SCCACHE_ENDPOINT: ${{ secrets.SCCACHE_ENDPOINT }} + AWS_ACCESS_KEY_ID: ${{ secrets.SCCACHE_S3_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.SCCACHE_S3_SECRET_ACCESS_KEY }} + RUSTC_WRAPPER: ${{ needs.skip-if-clean.outputs.internal == 'true' && 'sccache' || '' }} steps: - - name: 📥 Check out code - uses: actions/checkout@v4 + - name: Check out code + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 2 - - name: 🧰 Install build dependencies - run: | - sudo apt-get update - sudo apt-get install -yq libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev + - name: Install build dependencies + uses: awalsh128/cache-apt-pkgs-action@acb598e5ddbc6f68a970c5da0688d2f3a9f04d05 # v1.6.0 + with: + packages: libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev + version: v1 # cache key - - name: 🧰 Install pnpm - uses: pnpm/action-setup@v4 - - - name: 🧰 Setup Node.js - uses: actions/setup-node@v4 + - name: Setup Node + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version-file: .nvmrc - cache: pnpm - - name: 🧰 Setup Rust toolchain - uses: actions-rust-lang/setup-rust-toolchain@v1 + - name: Enable Corepack + run: corepack enable + + - name: Get pnpm store path + id: pnpm-store + run: echo "store-path=$(pnpm store path --silent)" >> $GITHUB_OUTPUT + + - name: Restore pnpm cache + uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + with: + path: ${{ steps.pnpm-store.outputs.store-path }} + key: pnpm-cache-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + pnpm-cache- + + - name: Setup Rust toolchain + uses: actions-rust-lang/setup-rust-toolchain@2b1f5e9b395427c92ee4e3331786ca3c37afe2d7 # v1.16.0 with: rustflags: '' components: clippy, rustfmt cache: false - - name: 🧰 Setup nextest - uses: taiki-e/install-action@nextest + - name: Cache Cargo registry and index + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae #v5.0.5 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + ~/.cargo/bin + key: ${{ runner.os }}-${{ runner.arch }}-cargo-${{ hashFiles('**/Cargo.lock') }} + + - name: Mount sccache disk cache + if: needs.skip-if-clean.outputs.internal == 'true' + uses: useblacksmith/stickydisk@13af8883542ca949a717e70fef89d15edbb29d88 # v1.2.0 + with: + key: ${{ github.repository }}-turbo-sccache + path: /mnt/sccache + + - name: Setup sccache + if: needs.skip-if-clean.outputs.internal == 'true' + uses: mozilla-actions/sccache-action@9e7fa8a12102821edf02ca5dbea1acd0f89a2696 # v0.0.10 + + - name: Setup binstall + uses: cargo-bins/cargo-binstall@dc19f1e48450eefe5a29b8da6c6b00a87d730b37 # v1.18.1 + + - name: Setup nextest + run: cargo binstall --no-confirm --secure cargo-nextest@0.9.133 # cargo-binstall does not have pre-built binaries for sqlx-cli, so we fall # back to a cached cargo install - - name: 🧰 Setup cargo-sqlx - uses: taiki-e/cache-cargo-install-action@v2 + - name: Setup cargo-sqlx + uses: taiki-e/cache-cargo-install-action@f9eed3e4680f27610dc6d8c67be1b88593f7dade # v3.0.6 with: - tool: sqlx-cli + tool: sqlx-cli@0.8.6 locked: false no-default-features: true features: rustls,postgres - - name: 💨 Setup Turbo cache - uses: rharkor/caching-for-turbo@v1.8 + - name: Setup Turbo cache + uses: rharkor/caching-for-turbo@56219402aacc0d06b650d898c222996dbc1191ec # v2.3.14 - - name: 🧰 Install dependencies + - name: Install dependencies run: pnpm install - - name: ⚙️ Set app environment + - name: Set app environment working-directory: packages/app-lib run: cp .env.staging .env # check if labrinth tests will actually run (cache miss) - - name: 🔍 Check if labrinth tests need to run + - name: Check if labrinth tests need to run id: check-labrinth run: | LABRINTH_TEST_STATUS=$(pnpm turbo run test --filter=@modrinth/labrinth --dry-run=json | jq -r '.tasks[] | select(.task == "test") | .cache.status') @@ -86,21 +187,21 @@ jobs: echo "needs_services=true" >> $GITHUB_OUTPUT fi - - name: ⚙️ Start services + - name: Start services if: steps.check-labrinth.outputs.needs_services == 'true' run: docker compose up --wait - - name: ⚙️ Setup labrinth environment and database + - name: Setup labrinth environment and database if: steps.check-labrinth.outputs.needs_services == 'true' working-directory: apps/labrinth run: | cp .env.local .env sqlx database setup - - name: 🔍 Lint and test + - name: Lint and test run: pnpm run ci - - name: 🔍 Verify intl:extract has been run + - name: Verify intl:extract has been run run: | pnpm turbo run intl:extract --force git diff --exit-code --color */*/src/locales/en-US/index.json diff --git a/.npmrc b/.npmrc index 19be10eb3..babcb1fd5 100644 --- a/.npmrc +++ b/.npmrc @@ -1,2 +1,11 @@ strict-peer-dependencies=false auto-install-peers=true +public-hoist-pattern[]=prettier-plugin-* +public-hoist-pattern[]=@prettier/plugin-* +public-hoist-pattern[]=eslint +public-hoist-pattern[]=@eslint/* +public-hoist-pattern[]=eslint-plugin-* +public-hoist-pattern[]=@nuxt/eslint-config +public-hoist-pattern[]=typescript-eslint +public-hoist-pattern[]=vue-eslint-parser +public-hoist-pattern[]=globals diff --git a/.nvmrc b/.nvmrc index ba331903d..5bf4400f2 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -20.19.2 +24.15.0 diff --git a/Cargo.lock b/Cargo.lock index 1abbf338b..212ab4860 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3059,29 +3059,6 @@ dependencies = [ "serde", ] -[[package]] -name = "elasticsearch" -version = "9.1.0-alpha.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12bb303aa6e1d28c0c86b6fbfe484fd0fd3f512629aeed1ac4f6b85f81d9834a" -dependencies = [ - "base64 0.22.1", - "bytes", - "dyn-clone", - "flate2", - "lazy_static", - "parking_lot", - "percent-encoding", - "reqwest 0.12.24", - "rustc_version", - "serde", - "serde_json", - "serde_with", - "tokio", - "url", - "void", -] - [[package]] name = "elliptic-curve" version = "0.13.8" @@ -5275,7 +5252,6 @@ dependencies = [ "dotenv-build", "dotenvy", "either", - "elasticsearch", "eyre", "futures", "futures-util", diff --git a/Cargo.toml b/Cargo.toml index 57e076b19..a7a9dbe04 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -77,7 +77,6 @@ dotenv-build = "0.1.1" dotenvy = "0.15.7" dunce = "1.0.5" either = "1.15.0" -elasticsearch = "9.1.0-alpha.1" encoding_rs = "0.8.35" enumset = "1.1.10" eyre = "0.6.12" @@ -279,10 +278,11 @@ opt-level = "s" # Optimize for binary size strip = true # Remove debug symbols lto = true # Enables link to optimizations panic = "abort" # Strip expensive panic clean-up logic -codegen-units = 1 # Compile crates one after another so the compiler can optimize better # Specific profile for labrinth production builds [profile.release-labrinth] inherits = "release" +opt-level = 2 strip = false # Keep debug symbols for Sentry +lto = "thin" # Enable LTO but keep compile times reasonable panic = "unwind" # Don't exit the whole app on panic in production diff --git a/README.md b/README.md index bfba90f1d..17edb3e0a 100644 --- a/README.md +++ b/README.md @@ -15,10 +15,10 @@ If you're not a developer and you've stumbled upon this repository, you can acce ## Development -This repository contains two primary packages. For detailed development information, please refer to their respective READMEs: +This repository contains two primary packages. For detailed development information, please refer to their respective guides: -- [Web Interface](apps/frontend/README.md) -- [Desktop App](apps/app/README.md) +- [Website frontend](https://docs.modrinth.com/contributing/knossos/) +- [Desktop app](https://docs.modrinth.com/contributing/theseus/) ## Modrinth Plus diff --git a/apps/app-frontend/package.json b/apps/app-frontend/package.json index 5a889cd37..b0572572b 100644 --- a/apps/app-frontend/package.json +++ b/apps/app-frontend/package.json @@ -20,7 +20,7 @@ "@modrinth/utils": "workspace:*", "@sentry/vue": "^8.27.0", "@sfirew/minecraft-motd-parser": "^1.1.6", - "@tanstack/vue-query": "^5.90.7", + "@tanstack/vue-query": "5.90.7", "@tauri-apps/api": "^2.5.0", "@tauri-apps/plugin-dialog": "^2.2.1", "@tauri-apps/plugin-fs": "^2.4.5", diff --git a/apps/app-frontend/src/App.vue b/apps/app-frontend/src/App.vue index a4363e25c..48fa91d88 100644 --- a/apps/app-frontend/src/App.vue +++ b/apps/app-frontend/src/App.vue @@ -2,6 +2,7 @@ import { Intercom, shutdown as shutdownIntercom } from '@intercom/messenger-js-sdk' import { AuthFeature, + ModrinthApiError, NodeAuthFeature, nodeAuthState, PanelVersionFeature, @@ -52,9 +53,10 @@ import { providePageContext, providePopupNotificationManager, useDebugLogger, + useFormatBytes, useVIntl, } from '@modrinth/ui' -import { formatBytes, renderString } from '@modrinth/utils' +import { renderString } from '@modrinth/utils' import { useQuery, useQueryClient } from '@tanstack/vue-query' import { getVersion } from '@tauri-apps/api/app' import { invoke } from '@tauri-apps/api/core' @@ -99,6 +101,7 @@ import { command_listener, warning_listener } from '@/helpers/events.js' import { cancelLogin, get as getCreds, login, logout } from '@/helpers/mr_auth.ts' import { create_profile_and_install_from_file } from '@/helpers/pack' import { list } from '@/helpers/profile.js' +import { mergeUrlQuery, parseModrinthLink } from '@/helpers/project-links.ts' import { get as getSettings, set as setSettings } from '@/helpers/settings.ts' import { get_opening_command, initialize_state } from '@/helpers/state' import { @@ -108,6 +111,7 @@ import { getUpdateSize, isDev, isNetworkMetered, + setRestartAfterPendingUpdate, } from '@/helpers/utils.js' import i18n from '@/i18n.config' import { createContentInstall, provideContentInstall } from '@/providers/content-install' @@ -148,8 +152,9 @@ const popupNotificationManager = new AppPopupNotificationManager() providePopupNotificationManager(popupNotificationManager) const { addPopupNotification } = popupNotificationManager +const appVersion = getVersion() const tauriApiClient = new TauriModrinthClient({ - userAgent: `modrinth/theseus/${getVersion()} (support@modrinth.com)`, + userAgent: async () => `modrinth/theseus/${await appVersion} (support@modrinth.com)`, labrinthBaseUrl: config.labrinthBaseUrl, archonBaseUrl: config.archonBaseUrl, features: [ @@ -263,6 +268,8 @@ onUnmounted(async () => { }) const { formatMessage } = useVIntl() +const formatBytes = useFormatBytes() + const messages = defineMessages({ updateInstalledToastTitle: { id: 'app.update.complete-toast.title', @@ -925,6 +932,7 @@ async function checkUpdates() { { label: formatMessage(updatePopupMessages.changelog), action: () => openUrl('https://modrinth.com/news/changelog?filter=app'), + keepOpen: true, }, ], }) @@ -1007,6 +1015,7 @@ async function downloadUpdate(versionToDownload) { { label: formatMessage(updatePopupMessages.changelog), action: () => openUrl('https://modrinth.com/news/changelog?filter=app'), + keepOpen: true, }, ], }) @@ -1022,11 +1031,40 @@ async function downloadUpdate(versionToDownload) { async function installUpdate() { restarting.value = true + try { + await setRestartAfterPendingUpdate(true) + } catch (e) { + restarting.value = false + handleError(e) + return + } setTimeout(async () => { await handleClose() }, 250) } +async function openModrinthProjectLinkInApp(parsed) { + const { slug, pathSuffix, url } = parsed + const loadToken = loading.begin() + try { + const { id } = await tauriApiClient.labrinth.projects_v2.check(slug) + const query = mergeUrlQuery(route.query, url) + await router.push({ + path: `/project/${id}${pathSuffix}`, + query, + hash: url.hash || undefined, + }) + } catch (err) { + if (err instanceof ModrinthApiError && err.statusCode === 404) { + openUrl(url.href) + } else { + handleError(err) + } + } finally { + loading.end(loadToken) + } +} + function handleClick(e) { let target = e.target while (target != null) { @@ -1039,7 +1077,12 @@ function handleClick(e) { !target.href.startsWith('https://tauri.localhost') && !target.href.startsWith('http://tauri.localhost') ) { - openUrl(target.href) + const parsed = parseModrinthLink(target.href) + if (target.target !== '_blank' && parsed) { + void openModrinthProjectLinkInApp(parsed) + } else { + openUrl(target.href) + } } e.preventDefault() break @@ -1180,7 +1223,7 @@ provideAppUpdateDownloadProgress(appUpdateDownload)
@@ -1388,7 +1431,7 @@ provideAppUpdateDownloadProgress(appUpdateDownload)

Playing as

- +
@@ -1577,11 +1620,15 @@ provideAppUpdateDownloadProgress(appUpdateDownload) .app-grid-navbar { grid-area: nav; + position: relative; + z-index: 2; } .app-grid-statusbar { grid-area: status; padding-right: var(--window-controls-width, 0px); + position: relative; + z-index: 2; } [data-tauri-drag-region-exclude] { @@ -1649,6 +1696,12 @@ provideAppUpdateDownloadProgress(appUpdateDownload) &.app-contents::before { box-shadow: none; } + + *, + :deep(*) { + box-shadow: none !important; + --tw-drop-shadow:; + } } .app-sidebar::before { @@ -1667,10 +1720,11 @@ provideAppUpdateDownloadProgress(appUpdateDownload) height: 100%; overflow: auto; overflow-x: hidden; + scrollbar-gutter: stable; } .app-contents::before { - z-index: 1; + z-index: 30; content: ''; position: fixed; left: var(--left-bar-width); diff --git a/apps/app-frontend/src/components/RowDisplay.vue b/apps/app-frontend/src/components/RowDisplay.vue index 174d3e1ae..8e3a4cc27 100644 --- a/apps/app-frontend/src/components/RowDisplay.vue +++ b/apps/app-frontend/src/components/RowDisplay.vue @@ -149,7 +149,7 @@ const handleOptionsClick = async (args) => { break case 'edit': await router.push({ - path: `/instance/${encodeURIComponent(args.item.path)}/`, + path: `/instance/${encodeURIComponent(args.item.path)}`, }) break case 'duplicate': diff --git a/apps/app-frontend/src/components/ui/AccountsCard.vue b/apps/app-frontend/src/components/ui/AccountsCard.vue index 493ce451e..7d01d2e67 100644 --- a/apps/app-frontend/src/components/ui/AccountsCard.vue +++ b/apps/app-frontend/src/components/ui/AccountsCard.vue @@ -1,81 +1,107 @@ - - - diff --git a/apps/app-frontend/src/components/ui/ErrorModal.vue b/apps/app-frontend/src/components/ui/ErrorModal.vue index 1eade7702..6ae482c77 100644 --- a/apps/app-frontend/src/components/ui/ErrorModal.vue +++ b/apps/app-frontend/src/components/ui/ErrorModal.vue @@ -61,7 +61,7 @@ defineExpose({ errorType.value = 'directory_move' supportLink.value = 'https://support.modrinth.com' - if (errorVal.message.includes('directory is not writeable')) { + if (errorVal.message.includes('directory is not writable')) { metadata.value.readOnly = true } diff --git a/apps/app-frontend/src/components/ui/ExportModal.vue b/apps/app-frontend/src/components/ui/ExportModal.vue index fa605f320..d856a9022 100644 --- a/apps/app-frontend/src/components/ui/ExportModal.vue +++ b/apps/app-frontend/src/components/ui/ExportModal.vue @@ -1,7 +1,8 @@ diff --git a/apps/app-frontend/src/components/ui/install_flow/IncompatibilityWarningModal.vue b/apps/app-frontend/src/components/ui/install_flow/IncompatibilityWarningModal.vue index dd04ae88b..cfcd48329 100644 --- a/apps/app-frontend/src/components/ui/install_flow/IncompatibilityWarningModal.vue +++ b/apps/app-frontend/src/components/ui/install_flow/IncompatibilityWarningModal.vue @@ -34,10 +34,14 @@
- - + + + + + +
@@ -45,7 +49,13 @@ - - - - diff --git a/apps/app-frontend/src/components/ui/modal/ConfirmDeleteInstanceModal.vue b/apps/app-frontend/src/components/ui/modal/ConfirmDeleteInstanceModal.vue index a2ee2a05c..d6c869ba1 100644 --- a/apps/app-frontend/src/components/ui/modal/ConfirmDeleteInstanceModal.vue +++ b/apps/app-frontend/src/components/ui/modal/ConfirmDeleteInstanceModal.vue @@ -7,7 +7,7 @@ diff --git a/apps/frontend/src/components/ui/banner/PreviewBanner.vue b/apps/frontend/src/components/ui/banner/PreviewBanner.vue index 141f65649..7fc253caf 100644 --- a/apps/frontend/src/components/ui/banner/PreviewBanner.vue +++ b/apps/frontend/src/components/ui/banner/PreviewBanner.vue @@ -13,6 +13,7 @@ import { const { formatMessage } = useVIntl() const flags = useFeatureFlags() const config = useRuntimeConfig() +const route = useRoute() const messages = defineMessages({ title: { @@ -21,7 +22,7 @@ const messages = defineMessages({ }, description: { id: 'layout.banner.preview.description', - defaultMessage: `If you meant to access the official Modrinth website, visit https://modrinth.com. This preview deploy is used by Modrinth staff for testing purposes. It was built using {owner}/{branch} @ {commit}.`, + defaultMessage: `If you meant to access the official Modrinth website, visit {url}. This preview deploy is used by Modrinth staff for testing purposes. It was built using {owner}/{branch} @ {commit}.`, }, }) @@ -29,10 +30,12 @@ function hidePreviewBanner() { flags.value.hidePreviewBanner = true saveFeatureFlags() } + +const url = computed(() => `https://modrinth.com${route.fullPath}`)