name: "[Quality] Code Analysis" permissions: contents: read on: pull_request: branches: ["*"] push: branches: ["main"] merge_group: concurrency: group: "${{ github.workflow }}-${{ github.ref }}" cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} env: FORCE_COLOR: 3 SKIP_ENV_VALIDATION: true TURBO_API: ${{ secrets.TURBO_API }} TURBO_TEAM: ${{ secrets.TURBO_TEAM }} TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }} TURBO_REMOTE_CACHE_SIGNATURE_KEY: ${{ secrets.TURBO_REMOTE_CACHE_SIGNATURE_KEY }} TURBO_SCM_BASE: ${{ github.event.pull_request.base.sha || github.event.before || 'HEAD~1' }} TURBO_SCM_HEAD: ${{ github.sha }} jobs: lint-format-autofix: if: github.event_name == 'pull_request' runs-on: ubuntu-latest timeout-minutes: 5 permissions: contents: write steps: - uses: actions/checkout@v6 with: fetch-depth: 0 ref: ${{ github.head_ref }} - name: Setup uses: ./tooling/github/setup - name: Get changed files id: changed run: | git diff --name-only --diff-filter=ACMR origin/${{ github.base_ref }}...HEAD -- '*.ts' '*.tsx' '*.js' '*.jsx' > /tmp/changed-files echo "count=$(wc -l < /tmp/changed-files | tr -d '[:space:]')" >> $GITHUB_OUTPUT - name: Auto-fix lint + format if: steps.changed.outputs.count != '0' run: | xargs -r -a /tmp/changed-files pnpm exec oxlint --fix || true xargs -r -a /tmp/changed-files pnpm exec oxfmt --write - name: Commit fixes uses: stefanzweifel/git-auto-commit-action@v7 with: commit_message: "style: auto-fix lint and format" lint: runs-on: ubuntu-latest timeout-minutes: 10 steps: - uses: actions/checkout@v6 with: fetch-depth: 0 - name: Setup uses: ./tooling/github/setup - name: Copy env shell: bash run: cp .env.example .env - name: Lint run: pnpm turbo lint --affected && pnpm lint:ws format: runs-on: ubuntu-latest timeout-minutes: 10 steps: - uses: actions/checkout@v6 with: fetch-depth: 0 - name: Setup uses: ./tooling/github/setup - name: Format run: pnpm turbo format --affected --continue typecheck: runs-on: ubuntu-latest timeout-minutes: 10 steps: - uses: actions/checkout@v6 with: fetch-depth: 0 - name: Setup uses: ./tooling/github/setup - name: Typecheck run: pnpm turbo typecheck --affected test: runs-on: ubuntu-latest timeout-minutes: 10 steps: - uses: actions/checkout@v6 - name: Setup uses: ./tooling/github/setup - name: Test run: pnpm test - name: "Report Coverage" # Set if: always() to also generate the report if tests are failing # Only works if you set `reportOnFailure: true` in your vite config as specified above if: always() uses: davelosert/vitest-coverage-report-action@v2 e2e: runs-on: ubuntu-latest timeout-minutes: 15 steps: - uses: actions/checkout@v6 - name: Setup uses: ./tooling/github/setup - name: Set up Docker Buildx uses: docker/setup-buildx-action@v4 - name: Build docker image id: build-docker-image uses: docker/build-push-action@v7 with: platforms: linux/amd64 context: . push: false load: true tags: homarr-e2e network: host cache-from: type=gha cache-to: type=gha,mode=max env: SKIP_ENV_VALIDATION: true - name: Install playwright browsers run: pnpm exec playwright install chromium - name: Run E2E Tests shell: bash run: pnpm test:e2e build: runs-on: ubuntu-latest timeout-minutes: 10 steps: - uses: actions/checkout@v6 with: fetch-depth: 0 - name: Setup uses: ./tooling/github/setup - name: Copy env shell: bash run: cp .env.example .env - name: Build run: pnpm turbo build --affected memory-regression: runs-on: ubuntu-latest timeout-minutes: 15 steps: - uses: actions/checkout@v6 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v4 - name: Build Docker image uses: docker/build-push-action@v7 with: platforms: linux/amd64 context: . push: false load: true tags: homarr-memory-test network: host cache-from: type=gha cache-to: type=gha,mode=max env: SKIP_ENV_VALIDATION: true - name: Start container and measure memory run: | docker run -d --name homarr-mem-test -p 17575:7575 homarr-memory-test # Wait for container to be healthy (up to 60s) for i in $(seq 1 30); do if curl -sf http://localhost:17575/api/health 2>/dev/null; then echo "Container healthy after $((i * 2))s" break fi sleep 2 done # Warm up: hit the dashboard page a few times for i in $(seq 1 5); do curl -sf http://localhost:17575/ > /dev/null 2>&1 || true sleep 2 done # Measure RSS after warmup MEM_KB=$(docker stats --no-stream --format '{{.MemUsage}}' homarr-mem-test | sed 's| /.*||') echo "Raw memory: $MEM_KB" # Convert to numeric MB (handles GiB and MiB suffixes) MEM_NUM=$(echo "$MEM_KB" | awk '{ if ($0 ~ /GiB/) { gsub(/GiB/,""); print $1 * 1024 } else if ($0 ~ /MiB/) { gsub(/MiB/,""); print $1 } else if ($0 ~ /KiB/) { gsub(/KiB/,""); print $1 / 1024 } else { print $1 / 1048576 } }') echo "Memory usage: ${MEM_NUM} MB" MAX_MB=700 if awk "BEGIN {exit !($MEM_NUM > $MAX_MB)}"; then echo "ERROR: Memory usage ${MEM_NUM} MB exceeds ${MAX_MB} MB threshold" docker logs homarr-mem-test --tail 50 exit 1 fi echo "Memory check passed: ${MEM_NUM} MB <= ${MAX_MB} MB"