mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-13 23:11:40 +00:00
* fix(ci): make every remaining CI checkout shallow PR #35982 only covered the lint and budget-ratchet jobs, so secret-scan kept spending minutes fetching every branch inside its 5 minute timeout and PRs kept getting cancelled. The UI lint and UI unit jobs carried the same fetch-depth 0 checkout secret-scan now checks out at depth 1, runs the hardcoded-secret pytest without building the project environment, and lets the ggshield step deepen history itself when a key is configured. UI lint resolves the merge base through the API instead of local history. UI unit tests compute the changed files the same way and feed them to vitest related, because vitest --changed does a three-dot diff that silently selects zero tests on a shallow clone The daily branch creation workflows also did full checkouts, then failed every run since persist-credentials: false left git push with no credentials. They now create the ref through the GitHub API without a checkout at all * fix(ci): feed deleted UI files into vitest related selection vitest --changed fed git's full change list to the related filter, deletions included, so a deletion-only dashboard PR still selected the tests importing the removed files. Keep that behavior by dropping the diff filter and existence guard; vitest resolves nonexistent paths fine and --passWithNoTests covers the nothing-related case
103 lines
4.2 KiB
YAML
103 lines
4.2 KiB
YAML
name: UI Lint
|
|
permissions:
|
|
contents: read
|
|
|
|
on:
|
|
pull_request:
|
|
branches:
|
|
- main
|
|
- litellm_internal_staging
|
|
- litellm_oss_staging
|
|
- "litellm_**"
|
|
|
|
jobs:
|
|
frontend-lint:
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 8
|
|
defaults:
|
|
run:
|
|
working-directory: ui/litellm-dashboard
|
|
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
|
with:
|
|
fetch-depth: 1
|
|
persist-credentials: false
|
|
|
|
- name: Collect changed files
|
|
id: changed
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
BASE_SHA: ${{ github.event.pull_request.base.sha }}
|
|
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
|
|
run: |
|
|
# base.sha is the base branch tip from when the PR was opened, while
|
|
# actions/checkout leaves HEAD on a merge of the PR into the *current*
|
|
# base tip. "$BASE_SHA"...HEAD therefore spans every base-branch commit
|
|
# landed since, so a PR that touches no UI file still gets linted
|
|
# against hundreds of other people's files. Diff the PR head against its
|
|
# own merge base instead, which is exactly what this PR changed.
|
|
merge_base=$(gh api "repos/${{ github.repository }}/compare/${BASE_SHA}...${HEAD_SHA}?per_page=1" --jq '.merge_base_commit.sha')
|
|
test -n "$merge_base"
|
|
git fetch --no-tags --depth=1 origin "$merge_base" "$HEAD_SHA"
|
|
: > "$RUNNER_TEMP/prettier_files.txt"
|
|
: > "$RUNNER_TEMP/eslint_files.txt"
|
|
while IFS= read -r f; do
|
|
[ -f "$f" ] || continue
|
|
case "$f" in
|
|
*.js | *.jsx | *.ts | *.tsx | *.mjs | *.cjs)
|
|
printf '%s\n' "$f" >> "$RUNNER_TEMP/prettier_files.txt"
|
|
printf '%s\n' "$f" >> "$RUNNER_TEMP/eslint_files.txt" ;;
|
|
*.json | *.css | *.scss | *.md | *.mdx | *.yml | *.yaml | *.html)
|
|
printf '%s\n' "$f" >> "$RUNNER_TEMP/prettier_files.txt" ;;
|
|
esac
|
|
done < <(git diff --name-only --diff-filter=ACMR --relative "$merge_base" "$HEAD_SHA" -- .)
|
|
if [ -s "$RUNNER_TEMP/prettier_files.txt" ] || [ -s "$RUNNER_TEMP/eslint_files.txt" ]; then
|
|
echo "has_files=true" >> "$GITHUB_OUTPUT"
|
|
else
|
|
echo "has_files=false" >> "$GITHUB_OUTPUT"
|
|
echo "No lintable UI files changed in this PR; nothing to check."
|
|
fi
|
|
|
|
- name: Setup Node.js
|
|
if: steps.changed.outputs.has_files == 'true'
|
|
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
|
|
with:
|
|
node-version-file: ui/litellm-dashboard/.nvmrc
|
|
cache: "npm"
|
|
cache-dependency-path: ui/litellm-dashboard/package-lock.json
|
|
|
|
- name: Install dependencies
|
|
if: steps.changed.outputs.has_files == 'true'
|
|
run: npm ci
|
|
|
|
- name: Lint changed files (prettier + eslint)
|
|
if: steps.changed.outputs.has_files == 'true'
|
|
run: |
|
|
prettier_files=()
|
|
eslint_files=()
|
|
while IFS= read -r f; do prettier_files+=("$f"); done < "$RUNNER_TEMP/prettier_files.txt"
|
|
while IFS= read -r f; do eslint_files+=("$f"); done < "$RUNNER_TEMP/eslint_files.txt"
|
|
status=0
|
|
if [ ${#prettier_files[@]} -gt 0 ]; then
|
|
echo "::group::Prettier (${#prettier_files[@]} files)"
|
|
npx prettier --check "${prettier_files[@]}" || { status=1; echo "::error::Unformatted files. Fix with: npm run format"; }
|
|
echo "::endgroup::"
|
|
fi
|
|
if [ ${#eslint_files[@]} -gt 0 ]; then
|
|
echo "::group::ESLint (${#eslint_files[@]} files)"
|
|
npx eslint --no-warn-ignored --pass-on-unpruned-suppressions "${eslint_files[@]}" || status=1
|
|
echo "::endgroup::"
|
|
fi
|
|
exit $status
|
|
|
|
- name: Check lint budgets
|
|
if: ${{ !cancelled() && steps.changed.outputs.has_files == 'true' }}
|
|
run: |
|
|
npx eslint . -f json -o "$RUNNER_TEMP/lint-report.json" || true
|
|
node scripts/check-lint-budgets.mjs "$RUNNER_TEMP/lint-report.json" eslint-budgets.json
|
|
|
|
- name: Check for dead code (knip)
|
|
if: ${{ !cancelled() && steps.changed.outputs.has_files == 'true' }}
|
|
run: npm run knip:ci
|