22
33on :
44 pull_request :
5- branches : [ master, develop ]
5+ branches : [master, develop]
66 push :
7- branches : [ master, develop ]
7+ branches : [master, develop]
88
99permissions :
1010 contents : read
@@ -23,43 +23,42 @@ jobs:
2323 with :
2424 fetch-depth : 0
2525
26- - name : Setup Node
27- uses : actions/setup-node@v6
28- with :
29- node-version : 20
30-
26+ # pnpm must be set up BEFORE setup-node so `cache: pnpm` can find it
3127 - name : Setup pnpm
3228 uses : pnpm/action-setup@v6
3329
34- - name : Get pnpm store directory
35- shell : bash
36- run : |
37- echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
38-
39- - name : Setup pnpm cache
40- uses : actions/cache@v5
30+ - name : Setup Node
31+ uses : actions/setup-node@v6
4132 with :
42- path : ${{ env.STORE_PATH }}
43- key : ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
44- restore-keys : |
45- ${{ runner.os }}-pnpm-store-
33+ node-version : 24
34+ cache : pnpm
4635
4736 - run : pnpm install --frozen-lockfile
4837
49- - name : Lint
50- run : |
51- if pnpm run | grep -q "^ *lint *"; then pnpm run lint; else echo "no lint script"; fi
38+ # Build once; lint/typecheck/test/size reuse the Turbo cache (no rebuild)
39+ - name : Build
40+ run : pnpm run build
41+
42+ - name : Lint (oxlint fast pass)
43+ run : pnpm run lint:fast
44+
45+ - name : Lint (ESLint)
46+ run : pnpm run lint
5247
5348 - name : Typecheck
5449 run : pnpm run typecheck
5550
56- - name : Install Playwright Browsers
57- run : npx playwright install --with-deps
51+ - name : Dead code / unused deps (knip)
52+ run : pnpm run knip
53+
54+ - name : Package correctness (publint + attw)
55+ run : pnpm run lint:package
5856
5957 - name : Test
60- run : |
61- if pnpm run | grep -q "^ *test *"; then pnpm test -- --coverage; else echo "no test script"; fi
62- pnpm --filter @scrolloop/react test:e2e
58+ run : pnpm test -- --coverage
59+
60+ - name : Bundle size budgets (size-limit)
61+ run : pnpm run size
6362
6463 - name : Report Coverage
6564 if : success() && github.event_name == 'pull_request'
@@ -68,52 +67,86 @@ jobs:
6867 script : |
6968 const fs = require('fs');
7069 const path = require('path');
71-
70+
7271 const packagesDir = 'packages';
7372 if (!fs.existsSync(packagesDir)) return;
74-
73+
7574 const dirs = fs.readdirSync(packagesDir);
76-
75+
7776 const validPackages = dirs.filter(dir => {
7877 const summaryPath = path.join(packagesDir, dir, 'coverage', 'coverage-summary.json');
7978 return fs.existsSync(summaryPath);
8079 });
81-
80+
8281 if (validPackages.length === 0) return;
83-
82+
8483 let message = '## 📊 Test Coverage Report (vitest) \n\n';
85-
84+
8685 message += '| Package | Statements | Branches | Functions | Lines |\n';
8786 message += '| :--- | :--- | :--- | :--- | :--- |\n';
88-
87+
8988 const metrics = ['statements', 'branches', 'functions', 'lines'];
90-
89+
9190 for (const pkg of validPackages) {
9291 const summaryPath = path.join(packagesDir, pkg, 'coverage', 'coverage-summary.json');
9392 const summary = JSON.parse(fs.readFileSync(summaryPath, 'utf8'));
9493 const total = summary.total;
95-
94+
9695 message += `| **@scrolloop/${pkg}** |`;
97-
96+
9897 for (const metric of metrics) {
9998 const data = total[metric];
10099 message += ` ${data.covered}/${data.total} (${data.pct}%) |`;
101100 }
102101 message += '\n';
103102 }
104-
103+
105104 await github.rest.issues.createComment({
106105 issue_number: context.issue.number,
107106 owner: context.repo.owner,
108107 repo: context.repo.repo,
109108 body: message
110109 });
111110
112- - name : Build
113- run : pnpm run build
111+ e2e :
112+ runs-on : ubuntu-latest
113+ steps :
114+ - uses : actions/checkout@v5
115+ with :
116+ fetch-depth : 0
117+
118+ - name : Setup pnpm
119+ uses : pnpm/action-setup@v6
120+
121+ - name : Setup Node
122+ uses : actions/setup-node@v6
123+ with :
124+ node-version : 24
125+ cache : pnpm
126+
127+ - run : pnpm install --frozen-lockfile
128+ - run : pnpm run build
129+
130+ - name : Cache Playwright browsers
131+ id : pw-cache
132+ uses : actions/cache@v4
133+ with :
134+ path : ~/.cache/ms-playwright
135+ key : ${{ runner.os }}-playwright-${{ hashFiles('**/pnpm-lock.yaml') }}
136+
137+ - name : Install Playwright (browsers + system deps)
138+ if : steps.pw-cache.outputs.cache-hit != 'true'
139+ run : pnpm --filter @scrolloop/react exec playwright install --with-deps
140+
141+ - name : Install Playwright system deps (cached browsers)
142+ if : steps.pw-cache.outputs.cache-hit == 'true'
143+ run : pnpm --filter @scrolloop/react exec playwright install-deps
144+
145+ - name : E2E
146+ run : pnpm --filter @scrolloop/react test:e2e
114147
115148 publish :
116- needs : ci
149+ needs : [ci, e2e]
117150 if : github.event_name == 'push' && startsWith(github.ref, 'refs/heads/master')
118151 runs-on : ubuntu-latest
119152 permissions :
@@ -124,27 +157,15 @@ jobs:
124157 with :
125158 fetch-depth : 0
126159
127- - name : Setup Node (npm registry)
128- uses : actions/setup-node@v6
129- with :
130- node-version : 20
131- registry-url : https://registry.npmjs.org/
132-
133160 - name : Setup pnpm
134161 uses : pnpm/action-setup@v6
135162
136- - name : Get pnpm store directory
137- shell : bash
138- run : |
139- echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
140-
141- - name : Setup pnpm cache
142- uses : actions/cache@v5
163+ - name : Setup Node (npm registry)
164+ uses : actions/setup-node@v6
143165 with :
144- path : ${{ env.STORE_PATH }}
145- key : ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
146- restore-keys : |
147- ${{ runner.os }}-pnpm-store-
166+ node-version : 24
167+ cache : pnpm
168+ registry-url : https://registry.npmjs.org/
148169
149170 - run : pnpm install --frozen-lockfile
150171 - run : pnpm run build
@@ -177,4 +198,4 @@ jobs:
177198 env :
178199 NODE_AUTH_TOKEN : ${{ secrets.NPM_TOKEN }}
179200 run : |
180- pnpm publish --access public --no-git-checks
201+ pnpm publish --access public --no-git-checks
0 commit comments