feat(chore): add a lint ratchet for shadcn rules (#51014)

## Problem

shadcn lint rules has been soft landed in #50676 and are now on as
warnings in every app, but nothing stops a PR from adding new violations

linear: FE-4473

## Solution

- moved the ratchet script and its tests from `apps/studio/scripts` to
`packages/eslint-config-supabase` so every app runs one copy
- added a shared rule list,
`packages/eslint-config-supabase/ratchet-rules.json` with the shadcn
rules
- www, docs, design-system, ui-library and learn get `lint-ratchet.yml`
with one job per changed app (triggered by the app, `packages/**` or the
lockfile) + a weekly `lint-ratchet-decrease.yml` (as for studio ratchet)
- package tests run in `eslint-config-supabase-tests.yml`

<!--
## Preview links

If relevant, include links to changed pages for easy review access.

Copy the preview base URL from the Vercel bot comment on this PR. Use
the following table as an example template.

| Site | Live | Preview | Search for |
| -------------- |
-------------------------------------------------------------------------
|
------------------------------------------------------------------------------------------------------------
| ----------------------------- |
| WWW | [/blog/your-post](https://supabase.com/blog/your-post) |
[/blog/your-post](https://zone-www-dot-com-git-branch-name-supabase.vercel.app/blog/your-post)
| unique phrase from the change |
| Docs |
[/docs/guides/your-page](https://supabase.com/docs/guides/your-page) |
[/docs/guides/your-page](https://docs-git-branch-name-supabase.vercel.app/docs/guides/your-page)
| unique phrase from the change |
| Studio | [/dashboard](https://supabase.com/dashboard) |
[/dashboard](https://studio-git-branch-name-supabase.vercel.app/dashboard)
| unique phrase from the change |
| Design system | [/design-system](https://supabase.com/design-system) |
[/design-system](https://design-system-git-branch-name-supabase.vercel.app/design-system)
| unique phrase from the change |
| UI library | [/library](https://supabase.com/library) |
[/library](https://ui-library-git-branch-name-supabase.vercel.app/library)
| unique phrase from the change |
| Knowledge base |
[/kb/guides/your-page](https://supabase.com/kb/guides/your-page) |
[/kb/guides/your-page](https://kb-git-branch-name-supabase.vercel.app/kb/guides/your-page)
| unique phrase from the change |
-->

<!-- ## Additional context

Optionally add any other context or screenshots.

-->

## Review instructions

1. run `pnpm --filter ./apps/www run lint:ratchet`
2. add `p-[13px]` to a `className` in any www component and run it
again. it fails with `shadcn/no-arbitrary-values` and the file name with
`(+1)`
3. revert change
4. run `pnpm --filter eslint-config-supabase test` and see 6 tests pass
5. in ci, check `Ratchet studio lint checks` and the `ratchet (<app>)`
jobs for the apps this pr touches

## Checklist

Check all before review:

- [x] I have read
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
- [x] If I wrote a new docs topic or edited an existing topic, I used
the `/write-the-docs` or `/edit-the-docs` skill, which applies the docs
[style
guide](https://github.com/supabase/supabase/tree/master/apps/docs/style-guide)


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Developer Improvements**
* Expanded automated lint checks to cover additional apps and shared
package changes.
* Added checks for arbitrary Tailwind values, unknown classes, and raw
colors across supported apps.
* Added automated baseline updates that can open or update a pull
request when lint counts change.
* Added tests for the lint configuration and support for combining
multiple rule files.
* Updated Studio lint notifications to exclude Shadcn rules with
zero-baseline counts.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Anthony Lio authored and GitHub committed 2026-10-02 14:13:59 +03:00
1 parent 73260dfaba
commit b13d6c2878
23 files changed
+1021 -20

No files matched your search

@@ -0,0 +1,43 @@
name: ESLint Config Tests
on:
pull_request:
branches: ['master']
paths:
- 'packages/eslint-config-supabase/**/*'
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
permissions:
contents: read
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
sparse-checkout: |
packages/eslint-config-supabase
patches
- uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
name: Install pnpm
with:
run_install: false
- name: Use Node.js
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: '.nvmrc'
cache: 'pnpm'
- name: Install deps
run: pnpm install --frozen-lockfile
- name: Run tests
run: pnpm --filter eslint-config-supabase run test
+109
View File
@@ -0,0 +1,109 @@
name: Decrease lint ratchet baselines
on:
schedule:
- cron: '0 0 * * SUN'
workflow_dispatch:
permissions:
contents: write
pull-requests: write
jobs:
decrease-baselines:
runs-on: blacksmith-4vcpu-ubuntu-2404
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
sparse-checkout: |
apps/www
apps/docs
apps/design-system
apps/ui-library
apps/learn
packages
patches
- uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
name: Install pnpm
with:
run_install: false
- name: Use Node.js
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: '.nvmrc'
cache: 'pnpm'
- name: Install deps
run: pnpm install --frozen-lockfile
- name: Generate token
id: app-token
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
with:
client-id: ${{ vars.GH_AUTOFIX_APP_CLIENT_ID }}
private-key: ${{ secrets.GH_AUTOFIX_PRIVATE_KEY }}
permission-contents: write
permission-pull-requests: write
- name: Decrease ESLint ratchet baselines and open PR
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
run: |
set -eo pipefail
DEFAULT_BRANCH=${DEFAULT_BRANCH:-master}
BRANCH="bot/decrease-lint-ratchet-baselines"
git fetch origin "$DEFAULT_BRANCH" --depth=1
if git ls-remote --exit-code --heads origin "$BRANCH" > /dev/null 2>&1; then
git fetch origin "$BRANCH":"$BRANCH" --depth=1
git switch "$BRANCH"
git reset --hard "origin/$DEFAULT_BRANCH"
else
git switch --create "$BRANCH" "origin/$DEFAULT_BRANCH"
fi
# Keep going past an app that regressed so the others still get decreased;
# the job fails at the end if any app did.
failed=""
APPS="www docs design-system ui-library learn"
for app in $APPS; do
pnpm --filter "./apps/$app" run lint:ratchet --decrease-baselines || failed="$failed $app"
done
if git diff --quiet; then
echo "No baseline updates detected."
if [ -n "$failed" ]; then
echo "::error title=Ratchet regressions::New violations on $DEFAULT_BRANCH in:$failed"
exit 1
fi
exit 0
fi
git config user.name 'github-actions[bot]'
git config user.email 'github-actions[bot]@users.noreply.github.com'
for app in $APPS; do git add "apps/$app/.github/eslint-rule-baselines.json"; done
git commit --message "chore: decrease lint ratchet baselines"
git -c credential.helper= push --force "https://x-access-token:${GH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" "HEAD:${BRANCH}"
pr_url=$(gh pr list --state open --head "$BRANCH" --json url --jq '.[0].url // ""' 2>/dev/null || echo "")
if [ -z "$pr_url" ]; then
gh pr create \
--title "[bot] Decrease lint ratchet baselines" \
--body "Automated weekly decrease of lint ratchet baselines." \
--base "$DEFAULT_BRANCH" \
--head "$BRANCH"
else
gh pr comment "$pr_url" --body "Updated lint ratchet baselines with the latest weekly decreases."
fi
if [ -n "$failed" ]; then
echo "::error title=Ratchet regressions::New violations on $DEFAULT_BRANCH in:$failed"
exit 1
fi
+85
View File
@@ -0,0 +1,85 @@
name: Ratchet lint checks
on:
pull_request:
branches:
- master
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
permissions:
contents: read
pull-requests: read
jobs:
changes:
runs-on: ubuntu-latest
outputs:
apps: ${{ steps.filter.outputs.changes }}
steps:
- uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
id: filter
with:
filters: |
shared: &shared
- 'packages/**'
- 'pnpm-lock.yaml'
www:
- *shared
- 'apps/www/**'
docs:
- *shared
- 'apps/docs/**'
design-system:
- *shared
- 'apps/design-system/**'
ui-library:
- *shared
- 'apps/ui-library/**'
learn:
- *shared
- 'apps/learn/**'
ratchet:
needs: changes
if: ${{ needs.changes.outputs.apps != '[]' }}
# Uses larger hosted runner as it significantly decreases build times
runs-on: blacksmith-4vcpu-ubuntu-2404
strategy:
fail-fast: false
matrix:
app: ${{ fromJSON(needs.changes.outputs.apps) }}
exclude:
- app: shared
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
sparse-checkout: |
apps/${{ matrix.app }}
packages
patches
- uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
name: Install pnpm
with:
run_install: false
- name: Use Node.js
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: '.nvmrc'
cache: 'pnpm'
- name: Install deps
run: pnpm install --frozen-lockfile
# pnpm runs the script from the app directory, which is where the
# baselines were generated, so per-file keys line up.
- name: Run ratchet script
env:
APP: ${{ matrix.app }}
run: pnpm --filter "./apps/$APP" run lint:ratchet
@@ -103,7 +103,7 @@ jobs:
run: |
set -euo pipefail
zero_rules=$(jq -r '.rules | to_entries | map(select(.value == 0) | .key) | join(", ")' apps/studio/.github/eslint-rule-baselines.json)
zero_rules=$(jq -r '.rules | to_entries | map(select(.value == 0 and (.key | startswith("shadcn/") | not)) | .key) | join(", ")' apps/studio/.github/eslint-rule-baselines.json)
if [ -z "$zero_rules" ]; then
echo "No rules dropped to a baseline of 0; nothing to notify."
@@ -6,6 +6,8 @@ on:
- master
paths:
- 'apps/studio/**'
- 'packages/**'
- 'pnpm-lock.yaml'
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}