laxy-verify 1.1.22 → 1.1.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +161 -148
- package/dist/cli.js +13 -17
- package/dist/comment.js +21 -15
- package/dist/entitlement.js +7 -36
- package/dist/status.js +4 -2
- package/package.json +67 -67
package/README.md
CHANGED
|
@@ -1,123 +1,127 @@
|
|
|
1
1
|
# laxy-verify
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
`laxy-verify` is a deployment blocker gate for frontend apps.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
It is built around three simple questions:
|
|
5
|
+
Its job is simple:
|
|
7
6
|
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
7
|
+
- catch the broken build before merge
|
|
8
|
+
- catch the broken user flow before QA or client review
|
|
9
|
+
- catch the obvious mobile or viewport break before release
|
|
11
10
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
11
|
+
It runs your production build, Lighthouse, and user-visible verification flows in one command, then leaves one decision and one evidence trail for local use or CI.
|
|
12
|
+
|
|
13
|
+
## Why use this instead of stitching tools together?
|
|
14
|
+
|
|
15
|
+
Most teams already have some mix of:
|
|
16
|
+
|
|
17
|
+
- `npm run build`
|
|
18
|
+
- Lighthouse
|
|
19
|
+
- Playwright or smoke checks
|
|
20
|
+
- CI status rules
|
|
21
|
+
|
|
22
|
+
The gap is not "can these tools exist together?" The gap is "who turns that pile of output into a safe merge or release decision?"
|
|
23
|
+
|
|
24
|
+
`laxy-verify` gives you:
|
|
25
|
+
|
|
26
|
+
- one command instead of a custom build plus audit plus smoke-check script stack
|
|
27
|
+
- one result file instead of scattered logs
|
|
28
|
+
- one blocker-first decision instead of "build passed, but do we actually trust this release?"
|
|
29
|
+
- a faster setup path with `--init` for config plus GitHub Actions
|
|
30
|
+
|
|
31
|
+
This is most useful if you ship frontend apps and want a practical gate before:
|
|
32
|
+
|
|
33
|
+
- merge
|
|
34
|
+
- client review
|
|
35
|
+
- QA handoff
|
|
36
|
+
- production release
|
|
37
|
+
|
|
38
|
+
## The failures it is meant to catch
|
|
39
|
+
|
|
40
|
+
Use `laxy-verify` when you want to catch things like:
|
|
41
|
+
|
|
42
|
+
- the production build passes locally but fails in CI
|
|
43
|
+
- the app opens, but a key button or form flow is broken
|
|
44
|
+
- desktop looks fine, but a mobile CTA is pushed out of view
|
|
45
|
+
- Lighthouse looks acceptable, but the user-visible path is still not safe to ship
|
|
46
|
+
- a PR needs a clear hold reason instead of a vague "something failed"
|
|
20
47
|
|
|
21
|
-
What
|
|
48
|
+
## What it actually checks
|
|
22
49
|
|
|
50
|
+
A standard run includes:
|
|
51
|
+
|
|
52
|
+
- production build success
|
|
53
|
+
- Lighthouse thresholds (3 runs for stable evidence)
|
|
54
|
+
- E2E scenarios for user-visible flows
|
|
55
|
+
- multi-viewport checks (desktop, tablet, mobile)
|
|
56
|
+
- blocker-aware reporting and release decisions
|
|
57
|
+
|
|
58
|
+
With a logged-in account, additional checks run:
|
|
59
|
+
|
|
60
|
+
- security audit
|
|
61
|
+
- visual diff evidence
|
|
62
|
+
- stability pass (second E2E run)
|
|
63
|
+
|
|
64
|
+
## What you get from one run
|
|
65
|
+
|
|
66
|
+
- a release decision such as `quick-pass`, `client-ready`, `release-ready`, `hold`, or `investigate`
|
|
23
67
|
- a verification grade: `Gold`, `Silver`, `Bronze`, or `Unverified`
|
|
24
|
-
-
|
|
25
|
-
-
|
|
26
|
-
- `laxy-verify-report.md` on paid plans for human review and AI handoff
|
|
68
|
+
- `.laxy-result.json` for CI and automation
|
|
69
|
+
- `laxy-verify-report.md` for human review and AI handoff
|
|
27
70
|
|
|
28
|
-
|
|
71
|
+
Grades still exist, but they are not the main point. The main point is whether the run found blockers you should stop on.
|
|
29
72
|
|
|
30
|
-
|
|
73
|
+
## Quick start
|
|
74
|
+
|
|
75
|
+
Run it on a frontend app:
|
|
31
76
|
|
|
32
77
|
```bash
|
|
33
78
|
cd your-project
|
|
34
79
|
npx laxy-verify .
|
|
35
80
|
```
|
|
36
81
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
### 2. Generate config and CI workflow
|
|
82
|
+
Generate config plus CI workflow:
|
|
40
83
|
|
|
41
84
|
```bash
|
|
42
85
|
npx laxy-verify --init
|
|
43
86
|
```
|
|
44
87
|
|
|
45
|
-
|
|
88
|
+
That creates:
|
|
46
89
|
|
|
47
90
|
- `.laxy.yml`
|
|
48
91
|
- `.github/workflows/laxy-verify.yml`
|
|
49
92
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
Once committed, each PR gets a verification run, grade output, and optional GitHub reporting.
|
|
53
|
-
|
|
54
|
-
### 4. Unlock paid plan features
|
|
93
|
+
Log in to unlock paid plan features:
|
|
55
94
|
|
|
56
95
|
```bash
|
|
57
96
|
npx laxy-verify login
|
|
58
97
|
npx laxy-verify whoami
|
|
59
98
|
```
|
|
60
99
|
|
|
61
|
-
For CI, set `LAXY_TOKEN` instead of
|
|
100
|
+
For CI, set `LAXY_TOKEN` instead of interactive login:
|
|
62
101
|
|
|
63
102
|
```yaml
|
|
64
103
|
env:
|
|
65
104
|
LAXY_TOKEN: ${{ secrets.LAXY_TOKEN }}
|
|
66
105
|
```
|
|
67
106
|
|
|
68
|
-
##
|
|
69
|
-
|
|
70
|
-
- production build success
|
|
71
|
-
- Lighthouse thresholds
|
|
72
|
-
- verify E2E scenarios for real user flows
|
|
73
|
-
- Pro+ viewport and visual regression evidence
|
|
74
|
-
- plan-aware verdicts for local runs and CI
|
|
75
|
-
|
|
76
|
-
## Verification Tiers
|
|
107
|
+
## Example workflow
|
|
77
108
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
109
|
+
1. Run `npx laxy-verify .` locally before opening or merging a PR.
|
|
110
|
+
2. Fix broken builds, broken flows, and visible regressions.
|
|
111
|
+
3. Commit `.laxy.yml`.
|
|
112
|
+
4. Run `npx laxy-verify --init`.
|
|
113
|
+
5. Let the GitHub Action apply the same gate on every PR.
|
|
83
114
|
|
|
84
|
-
##
|
|
85
|
-
|
|
86
|
-
| Grade | Meaning |
|
|
87
|
-
|-------|---------|
|
|
88
|
-
| Gold | Build passed + E2E passed + Lighthouse passed + Pro+ viewport evidence passed |
|
|
89
|
-
| Silver | Build passed + E2E passed |
|
|
90
|
-
| Bronze | Build passed |
|
|
91
|
-
| Unverified | Build failed |
|
|
92
|
-
|
|
93
|
-
## Plan Differences
|
|
94
|
-
|
|
95
|
-
| Feature | Free | Pro | Pro+ |
|
|
96
|
-
|---------|------|-----|------|
|
|
97
|
-
| Build verification | Yes | Yes | Yes |
|
|
98
|
-
| Lighthouse | 1 run | 3 runs | 3 runs |
|
|
99
|
-
| Verify E2E | Smoke checks | Client-facing flow checks | Client-facing flow checks + release evidence |
|
|
100
|
-
| Detailed report view | No | Yes | Yes |
|
|
101
|
-
| `laxy-verify-report.md` export | No | Yes | Yes |
|
|
102
|
-
| Multi-viewport verification | No | No | Yes |
|
|
103
|
-
| Visual diff | No | No | Yes |
|
|
104
|
-
| Failure analysis signals | No | No | Yes |
|
|
105
|
-
|
|
106
|
-
Free tells you whether the app is basically standing.
|
|
107
|
-
Pro tells you whether the app is strong enough to call client-ready.
|
|
108
|
-
Pro+ adds the extra evidence needed for a real release-ready call.
|
|
109
|
-
|
|
110
|
-
## Sample Output
|
|
115
|
+
## Example output
|
|
111
116
|
|
|
112
117
|
```text
|
|
113
|
-
|
|
118
|
+
Decision: release-ready
|
|
114
119
|
Grade: Gold
|
|
115
|
-
Verdict: release-ready
|
|
116
120
|
|
|
117
121
|
Passed:
|
|
118
122
|
- production build
|
|
119
123
|
- Lighthouse thresholds
|
|
120
|
-
- core
|
|
124
|
+
- core user flows
|
|
121
125
|
- desktop, tablet, and mobile viewport checks
|
|
122
126
|
|
|
123
127
|
Artifacts:
|
|
@@ -125,19 +129,46 @@ Artifacts:
|
|
|
125
129
|
- laxy-verify-report.md
|
|
126
130
|
```
|
|
127
131
|
|
|
128
|
-
##
|
|
132
|
+
## The decision it helps you make
|
|
133
|
+
|
|
134
|
+
`laxy-verify` answers a delivery decision, not just a score:
|
|
135
|
+
|
|
136
|
+
- Would this break for real users right now?
|
|
137
|
+
- What would block a client demo or QA handoff?
|
|
138
|
+
- Is there enough evidence to merge or release with confidence?
|
|
139
|
+
|
|
140
|
+
Log in to unlock deeper checks (security audit, visual diff, stability pass).
|
|
141
|
+
|
|
142
|
+
## Grades
|
|
143
|
+
|
|
144
|
+
| Grade | Meaning |
|
|
145
|
+
|---|---|
|
|
146
|
+
| Gold | Build passed, E2E passed, Lighthouse passed, and release-level evidence passed |
|
|
147
|
+
| Silver | Build passed and E2E passed |
|
|
148
|
+
| Bronze | Build passed |
|
|
149
|
+
| Unverified | Build failed |
|
|
129
150
|
|
|
130
|
-
|
|
151
|
+
Default Lighthouse thresholds:
|
|
152
|
+
|
|
153
|
+
- Performance `>= 70`
|
|
154
|
+
- Accessibility `>= 85`
|
|
155
|
+
- SEO `>= 80`
|
|
156
|
+
- Best Practices `>= 80`
|
|
157
|
+
|
|
158
|
+
## Config
|
|
159
|
+
|
|
160
|
+
All fields in `.laxy.yml` are optional.
|
|
131
161
|
|
|
132
162
|
```yaml
|
|
133
|
-
framework:
|
|
163
|
+
framework: auto
|
|
134
164
|
build_command: ""
|
|
135
165
|
dev_command: ""
|
|
136
|
-
package_manager:
|
|
166
|
+
package_manager: auto
|
|
137
167
|
port: 3000
|
|
138
168
|
build_timeout: 300
|
|
139
169
|
dev_timeout: 60
|
|
140
170
|
lighthouse_runs: 1
|
|
171
|
+
fail_on: bronze
|
|
141
172
|
|
|
142
173
|
thresholds:
|
|
143
174
|
performance: 70
|
|
@@ -145,16 +176,21 @@ thresholds:
|
|
|
145
176
|
seo: 80
|
|
146
177
|
best_practices: 80
|
|
147
178
|
|
|
148
|
-
|
|
179
|
+
crawl: false
|
|
180
|
+
max_crawl_depth: 3
|
|
181
|
+
max_crawl_pages: 10
|
|
182
|
+
browsers:
|
|
183
|
+
- chromium
|
|
149
184
|
```
|
|
150
185
|
|
|
151
|
-
|
|
186
|
+
Useful adjustments:
|
|
152
187
|
|
|
153
|
-
- raise `fail_on`
|
|
154
|
-
- set `
|
|
155
|
-
- increase `lighthouse_runs`
|
|
188
|
+
- raise `fail_on` in CI when you want stricter gates
|
|
189
|
+
- set `build_command` or `dev_command` if auto-detection is not enough
|
|
190
|
+
- increase `lighthouse_runs` for more stable performance evidence
|
|
191
|
+
- point the CLI at the actual app directory in a monorepo
|
|
156
192
|
|
|
157
|
-
## CLI
|
|
193
|
+
## CLI
|
|
158
194
|
|
|
159
195
|
```text
|
|
160
196
|
npx laxy-verify [project-dir]
|
|
@@ -177,87 +213,64 @@ Subcommands:
|
|
|
177
213
|
whoami
|
|
178
214
|
```
|
|
179
215
|
|
|
180
|
-
`--plan-override` is for downgrade testing only.
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
- `Pro`: client-ready delivery report
|
|
191
|
-
- `Pro+`: release-readiness report with viewport and visual evidence
|
|
192
|
-
|
|
193
|
-
Exit behavior follows the verification verdict, not just the legacy grade.
|
|
194
|
-
|
|
195
|
-
- `build-failed` -> exit 1
|
|
196
|
-
- `hold` -> exit 1
|
|
197
|
-
- `Pro+ investigate` -> exit 1
|
|
198
|
-
- plain lower-tier pass states can still exit 0
|
|
199
|
-
|
|
200
|
-
```json
|
|
201
|
-
{
|
|
202
|
-
"grade": "Gold",
|
|
203
|
-
"timestamp": "2026-04-09T09:00:00Z",
|
|
204
|
-
"build": { "success": true, "durationMs": 12000, "errors": [] },
|
|
205
|
-
"e2e": { "passed": 5, "failed": 0, "total": 5, "results": [] },
|
|
206
|
-
"lighthouse": { "performance": 82, "accessibility": 94, "seo": 90, "bestPractices": 92, "runs": 3 },
|
|
207
|
-
"multiViewport": {
|
|
208
|
-
"allPassed": true,
|
|
209
|
-
"summary": "Desktop, tablet, and mobile checks passed."
|
|
210
|
-
},
|
|
211
|
-
"visualDiff": {
|
|
212
|
-
"verdict": "pass",
|
|
213
|
-
"differencePercentage": 0
|
|
214
|
-
},
|
|
215
|
-
"verification": {
|
|
216
|
-
"tier": "pro_plus",
|
|
217
|
-
"report": { "verdict": "release-ready" }
|
|
218
|
-
},
|
|
219
|
-
"exitCode": 0,
|
|
220
|
-
"_plan": "pro_plus"
|
|
221
|
-
}
|
|
222
|
-
```
|
|
216
|
+
`--plan-override` is for downgrade testing only. It can simulate lower tiers, but it will not let you exceed your real entitlement.
|
|
217
|
+
|
|
218
|
+
## Result files
|
|
219
|
+
|
|
220
|
+
Every run writes `.laxy-result.json`.
|
|
221
|
+
|
|
222
|
+
When the run finds something worth reviewing, it also writes `laxy-verify-report.md`.
|
|
223
|
+
|
|
224
|
+
Typical use:
|
|
223
225
|
|
|
224
|
-
|
|
226
|
+
- `.laxy-result.json` for CI parsing and machine decisions
|
|
227
|
+
- `laxy-verify-report.md` for human review, PR discussion, or AI-assisted fixes
|
|
225
228
|
|
|
226
|
-
|
|
229
|
+
The markdown report is designed to be readable and easy to paste into coding tools.
|
|
227
230
|
|
|
228
231
|
It includes:
|
|
229
232
|
|
|
230
|
-
- the main decision in plain English
|
|
233
|
+
- the main stop-or-ship decision in plain English
|
|
231
234
|
- what passed
|
|
232
235
|
- blockers and warnings
|
|
233
236
|
- exact verification evidence
|
|
234
|
-
- failed
|
|
235
|
-
- a `Copy For AI` section
|
|
237
|
+
- failed scenarios
|
|
238
|
+
- a `Copy For AI` section
|
|
236
239
|
|
|
237
|
-
##
|
|
240
|
+
## What this is good at
|
|
238
241
|
|
|
239
|
-
|
|
240
|
-
- Monorepos should point `laxy-verify` at the actual app directory.
|
|
241
|
-
- `playwright` is optional. The CLI can run without it.
|
|
242
|
-
- Pro+ viewport and visual checks increase runtime.
|
|
242
|
+
Use `laxy-verify` when you want:
|
|
243
243
|
|
|
244
|
-
|
|
244
|
+
- a merge or release gate for frontend apps
|
|
245
|
+
- one repeatable command for build plus audit plus visible-flow verification
|
|
246
|
+
- a decision that non-authors can understand
|
|
247
|
+
- JSON output for automation without building your own wrapper
|
|
245
248
|
|
|
246
|
-
|
|
247
|
-
|
|
249
|
+
## What this is not
|
|
250
|
+
|
|
251
|
+
`laxy-verify` is not trying to replace:
|
|
252
|
+
|
|
253
|
+
- your full Playwright suite
|
|
254
|
+
- deep visual QA by designers
|
|
255
|
+
- production observability
|
|
256
|
+
- manual exploratory testing
|
|
257
|
+
|
|
258
|
+
It is a pre-merge and pre-release verification layer, not your entire quality system.
|
|
248
259
|
|
|
249
260
|
## Limitations
|
|
250
261
|
|
|
251
|
-
-
|
|
252
|
-
-
|
|
253
|
-
-
|
|
254
|
-
-
|
|
262
|
+
- monorepos should target the real app subdirectory
|
|
263
|
+
- dev-server-based Lighthouse is not identical to production hosting
|
|
264
|
+
- visual diff and viewport checks increase runtime
|
|
265
|
+
- best stability is on current LTS Node releases
|
|
266
|
+
|
|
267
|
+
## Requirements
|
|
268
|
+
|
|
269
|
+
- Node `>=20.18.0 <25`
|
|
270
|
+
- a frontend app with a runnable build flow
|
|
271
|
+
- optional: `playwright` if your project already uses it
|
|
255
272
|
|
|
256
273
|
## Links
|
|
257
274
|
|
|
258
275
|
- GitHub: https://github.com/SUNgm24/Laxy/tree/main/laxy-verify
|
|
259
276
|
- Issues: https://github.com/SUNgm24/Laxy/issues
|
|
260
|
-
|
|
261
|
-
## License
|
|
262
|
-
|
|
263
|
-
MIT
|
package/dist/cli.js
CHANGED
|
@@ -162,7 +162,7 @@ function summarizeViewportIssues(scores, thresholds) {
|
|
|
162
162
|
function consoleOutput(result) {
|
|
163
163
|
const gradeLabel = result.grade;
|
|
164
164
|
const checkEmoji = result.grade !== "Unverified" ? " OK" : "";
|
|
165
|
-
console.log(`\n Laxy Verify ${gradeLabel}${checkEmoji}`);
|
|
165
|
+
console.log(`\n Laxy Verify: blocker check ${gradeLabel}${checkEmoji}`);
|
|
166
166
|
console.log(` Build: ${result.build.success ? `OK (${result.build.durationMs}ms)` : "FAILED"}`);
|
|
167
167
|
if (result.build.errors.length > 0) {
|
|
168
168
|
console.log(" Errors:");
|
|
@@ -216,10 +216,10 @@ function consoleOutput(result) {
|
|
|
216
216
|
// failure_analysis: Pro+에서 서버가 활성화하면 warnings 전체 설명 + evidence 전체 표시
|
|
217
217
|
const verboseFailure = isPro && (result._verbose_failure ?? isPro);
|
|
218
218
|
const failureAnalysis = isProPlus && (result._failure_analysis ?? isProPlus);
|
|
219
|
-
console.log(` Verification
|
|
220
|
-
console.log(`
|
|
221
|
-
console.log(`
|
|
222
|
-
console.log(`
|
|
219
|
+
console.log(` Verification depth: ${view.tier}`);
|
|
220
|
+
console.log(` Decision question: ${view.question}`);
|
|
221
|
+
console.log(` Decision: ${view.verdict} (${view.confidence})`);
|
|
222
|
+
console.log(` Why it stopped here: ${view.summary}`);
|
|
223
223
|
// Pro/Pro+: 체크 통과 목록 요약
|
|
224
224
|
if (isPro && view.passes.length > 0) {
|
|
225
225
|
const passedChecks = view.passes.filter((p) => p.passed).map((p) => p.label);
|
|
@@ -231,7 +231,7 @@ function consoleOutput(result) {
|
|
|
231
231
|
}
|
|
232
232
|
// Blockers: 제목은 모든 티어, Fix 액션은 verbose_failure(Pro+) 이상에서 표시
|
|
233
233
|
if (view.blockers.length > 0) {
|
|
234
|
-
console.log("
|
|
234
|
+
console.log(" Deployment blockers:");
|
|
235
235
|
for (const blocker of view.blockers) {
|
|
236
236
|
console.log(` - ${blocker.title}`);
|
|
237
237
|
if (verboseFailure)
|
|
@@ -240,7 +240,7 @@ function consoleOutput(result) {
|
|
|
240
240
|
}
|
|
241
241
|
// Warnings: Pro/Pro+에서만 표시, Review 액션은 failure_analysis(Pro+)에서 표시
|
|
242
242
|
if (isPro && view.warnings.length > 0) {
|
|
243
|
-
console.log("
|
|
243
|
+
console.log(" Risks to review:");
|
|
244
244
|
for (const warning of view.warnings) {
|
|
245
245
|
console.log(` - ${warning.title}`);
|
|
246
246
|
if (failureAnalysis)
|
|
@@ -256,7 +256,7 @@ function consoleOutput(result) {
|
|
|
256
256
|
const evidenceLimit = failureAnalysis ? view.failureEvidence.length : verboseFailure ? 3 : 2;
|
|
257
257
|
const evidenceToShow = view.failureEvidence.slice(0, evidenceLimit);
|
|
258
258
|
if (evidenceToShow.length > 0) {
|
|
259
|
-
console.log(" Evidence:");
|
|
259
|
+
console.log(" Evidence collected:");
|
|
260
260
|
for (const item of evidenceToShow)
|
|
261
261
|
console.log(` - ${item}`);
|
|
262
262
|
}
|
|
@@ -272,24 +272,20 @@ function consoleOutput(result) {
|
|
|
272
272
|
console.log(` Report: ${path.basename(result.markdownReportPath)}`);
|
|
273
273
|
}
|
|
274
274
|
console.log(` Exit code: ${result.exitCode}`);
|
|
275
|
-
if ((result.grade === "Silver" || result.grade === "Bronze") && (!result._plan || result._plan === "free")) {
|
|
276
|
-
console.log("\n Unlock deeper verification and Gold-grade confidence with Pro or Pro+:");
|
|
277
|
-
console.log(" https://laxy-blue.vercel.app/pricing");
|
|
278
|
-
}
|
|
279
275
|
}
|
|
280
276
|
async function run() {
|
|
281
277
|
const args = parseArgs();
|
|
282
278
|
if (args.help) {
|
|
283
279
|
console.log(`
|
|
284
280
|
laxy-verify v${package_json_1.default.version}
|
|
285
|
-
|
|
281
|
+
Deployment blocker gate for frontend apps
|
|
286
282
|
|
|
287
283
|
Usage:
|
|
288
284
|
npx laxy-verify [project-dir] [options]
|
|
289
285
|
npx laxy-verify <subcommand>
|
|
290
286
|
|
|
291
287
|
Subcommands:
|
|
292
|
-
login [email] Log in to unlock
|
|
288
|
+
login [email] Log in to unlock deeper reports and release evidence
|
|
293
289
|
logout Remove saved credentials
|
|
294
290
|
whoami Show current login status
|
|
295
291
|
|
|
@@ -308,15 +304,15 @@ async function run() {
|
|
|
308
304
|
--help Show this help
|
|
309
305
|
|
|
310
306
|
Exit codes:
|
|
311
|
-
0
|
|
312
|
-
1
|
|
307
|
+
0 Current verification gate passed
|
|
308
|
+
1 Verification found blockers, or build failed
|
|
313
309
|
2 Configuration error
|
|
314
310
|
|
|
315
311
|
Examples:
|
|
316
312
|
npx laxy-verify --init --run # Setup + first verification
|
|
317
313
|
npx laxy-verify . # Run in current directory
|
|
318
314
|
npx laxy-verify . --ci # CI mode
|
|
319
|
-
npx laxy-verify . --fail-on silver #
|
|
315
|
+
npx laxy-verify . --fail-on silver # Block Bronze or worse
|
|
320
316
|
|
|
321
317
|
Docs: https://github.com/psungmin24/laxy-verify
|
|
322
318
|
`);
|
package/dist/comment.js
CHANGED
|
@@ -40,7 +40,6 @@ async function postPRComment(result) {
|
|
|
40
40
|
const ctx = (0, github_js_1.getGitHubContext)();
|
|
41
41
|
if (!ctx || ctx.eventName !== "pull_request")
|
|
42
42
|
return;
|
|
43
|
-
// Parse PR number from event
|
|
44
43
|
let prNumber = 0;
|
|
45
44
|
if (ctx.eventPath && fs.existsSync(ctx.eventPath)) {
|
|
46
45
|
const event = JSON.parse(fs.readFileSync(ctx.eventPath, "utf-8"));
|
|
@@ -55,31 +54,38 @@ async function postPRComment(result) {
|
|
|
55
54
|
if (lh) {
|
|
56
55
|
lhTable = `| Performance | Accessibility | SEO | Best Practices |\n|---|---|---|---|\n| ${lh.performance} / ${t.performance} | ${lh.accessibility} / ${t.accessibility} | ${lh.seo} / ${t.seo} | ${lh.bestPractices} / ${t.bestPractices} |\n\n`;
|
|
57
56
|
}
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
57
|
+
const passed = result.exitCode === 0;
|
|
58
|
+
const statusLabel = passed ? "[PASS]" : "[HOLD]";
|
|
59
|
+
const headline = passed ? "No deployment blockers found" : "Deployment blockers found";
|
|
60
|
+
const summary = passed
|
|
61
|
+
? `This PR passed the current verification gate. Grade summary: **${grade}**.`
|
|
62
|
+
: grade === "Unverified"
|
|
63
|
+
? "The production build failed or verification could not complete, so this PR should be held."
|
|
64
|
+
: `This PR did not clear the current verification gate. Grade summary: **${grade}**.`;
|
|
65
|
+
const body = `## ${statusLabel} Laxy Verify: ${headline}
|
|
66
|
+
|
|
67
|
+
${summary}
|
|
68
|
+
|
|
69
|
+
${lhTable}**Fail-on threshold**: ${result.config_fail_on ?? "bronze"}
|
|
70
|
+
|
|
71
|
+
Grade remains available for automation, but this check should be read first as a merge and release blocker gate.
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
[Open laxy-verify docs](https://github.com/psungmin24/laxy-verify)`;
|
|
69
75
|
const [owner, repo] = ctx.repository.split("/");
|
|
70
76
|
const url = `https://api.github.com/repos/${owner}/${repo}/issues/${prNumber}/comments`;
|
|
71
77
|
try {
|
|
72
78
|
const res = await fetch(url, {
|
|
73
79
|
method: "POST",
|
|
74
80
|
headers: {
|
|
75
|
-
|
|
76
|
-
|
|
81
|
+
Authorization: `Bearer ${ctx.token}`,
|
|
82
|
+
Accept: "application/vnd.github.v3+json",
|
|
77
83
|
"Content-Type": "application/json",
|
|
78
84
|
},
|
|
79
85
|
body: JSON.stringify({ body }),
|
|
80
86
|
});
|
|
81
87
|
if (!res.ok) {
|
|
82
|
-
console.warn(`GitHub PR comment API returned ${res.status}
|
|
88
|
+
console.warn(`GitHub PR comment API returned ${res.status}; skipping comment.`);
|
|
83
89
|
return;
|
|
84
90
|
}
|
|
85
91
|
}
|
package/dist/entitlement.js
CHANGED
|
@@ -14,12 +14,12 @@ exports.printPlanBanner = printPlanBanner;
|
|
|
14
14
|
const auth_js_1 = require("./auth.js");
|
|
15
15
|
const FREE_FEATURES = {
|
|
16
16
|
plan: "free",
|
|
17
|
-
gold_grade:
|
|
18
|
-
lighthouse_runs_3:
|
|
19
|
-
verbose_failure:
|
|
20
|
-
multi_viewport:
|
|
21
|
-
failure_analysis:
|
|
22
|
-
fast_lane:
|
|
17
|
+
gold_grade: true,
|
|
18
|
+
lighthouse_runs_3: true,
|
|
19
|
+
verbose_failure: true,
|
|
20
|
+
multi_viewport: true,
|
|
21
|
+
failure_analysis: true,
|
|
22
|
+
fast_lane: true,
|
|
23
23
|
};
|
|
24
24
|
let cache = null;
|
|
25
25
|
const CACHE_TTL_MS = 5 * 60 * 1000;
|
|
@@ -68,36 +68,7 @@ async function getEntitlements() {
|
|
|
68
68
|
function applyPlanOverride(features, overridePlan) {
|
|
69
69
|
if (!overridePlan)
|
|
70
70
|
return features;
|
|
71
|
-
|
|
72
|
-
if (getPlanRank(overridePlan) > getPlanRank(actualPlan)) {
|
|
73
|
-
throw new Error(`Plan override "${overridePlan}" is higher than your active entitlement "${actualPlan}". Downgrade overrides are allowed, upgrades are not.`);
|
|
74
|
-
}
|
|
75
|
-
if (overridePlan === actualPlan) {
|
|
76
|
-
return { ...features, plan: overridePlan };
|
|
77
|
-
}
|
|
78
|
-
if (overridePlan === "free") {
|
|
79
|
-
return {
|
|
80
|
-
plan: "free",
|
|
81
|
-
gold_grade: false,
|
|
82
|
-
lighthouse_runs_3: false,
|
|
83
|
-
verbose_failure: false,
|
|
84
|
-
multi_viewport: false,
|
|
85
|
-
failure_analysis: false,
|
|
86
|
-
fast_lane: false,
|
|
87
|
-
};
|
|
88
|
-
}
|
|
89
|
-
if (overridePlan === "pro") {
|
|
90
|
-
return {
|
|
91
|
-
plan: "pro",
|
|
92
|
-
gold_grade: false,
|
|
93
|
-
lighthouse_runs_3: true,
|
|
94
|
-
verbose_failure: features.verbose_failure,
|
|
95
|
-
multi_viewport: false,
|
|
96
|
-
failure_analysis: false,
|
|
97
|
-
fast_lane: false,
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
return { ...features, plan: "pro_plus" };
|
|
71
|
+
return { ...features, plan: overridePlan };
|
|
101
72
|
}
|
|
102
73
|
function printPlanBanner(features) {
|
|
103
74
|
const planLabels = {
|
package/dist/status.js
CHANGED
|
@@ -7,8 +7,10 @@ async function createStatusCheck(result) {
|
|
|
7
7
|
if (!ctx)
|
|
8
8
|
return;
|
|
9
9
|
const [owner, repo] = ctx.repository.split("/");
|
|
10
|
-
const description = `Laxy Verify — ${result.grade}`;
|
|
11
10
|
const state = result.exitCode === 0 ? "success" : "failure";
|
|
11
|
+
const description = state === "success"
|
|
12
|
+
? "Laxy Verify: no deployment blockers found"
|
|
13
|
+
: "Laxy Verify: deployment blockers found";
|
|
12
14
|
const targetUrl = `${process.env.GITHUB_SERVER_URL ?? "https://github.com"}/${ctx.repository}/actions/runs/${process.env.GITHUB_RUN_ID ?? ""}`;
|
|
13
15
|
const url = `https://api.github.com/repos/${owner}/${repo}/statuses/${ctx.sha}`;
|
|
14
16
|
try {
|
|
@@ -27,7 +29,7 @@ async function createStatusCheck(result) {
|
|
|
27
29
|
}),
|
|
28
30
|
});
|
|
29
31
|
if (!res.ok) {
|
|
30
|
-
console.warn(`GitHub Status Check API returned ${res.status}
|
|
32
|
+
console.warn(`GitHub Status Check API returned ${res.status}; skipping status update.`);
|
|
31
33
|
}
|
|
32
34
|
}
|
|
33
35
|
catch (err) {
|
package/package.json
CHANGED
|
@@ -1,67 +1,67 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "laxy-verify",
|
|
3
|
-
"version": "1.1.
|
|
4
|
-
"description": "Frontend verification CLI for build checks, Lighthouse, E2E, and release readiness",
|
|
5
|
-
"license": "MIT",
|
|
6
|
-
"type": "commonjs",
|
|
7
|
-
"homepage": "https://github.com/SUNgm24/Laxy/tree/main/laxy-verify#readme",
|
|
8
|
-
"repository": {
|
|
9
|
-
"type": "git",
|
|
10
|
-
"url": "git+https://github.com/SUNgm24/Laxy.git",
|
|
11
|
-
"directory": "laxy-verify"
|
|
12
|
-
},
|
|
13
|
-
"bugs": {
|
|
14
|
-
"url": "https://github.com/SUNgm24/Laxy/issues"
|
|
15
|
-
},
|
|
16
|
-
"keywords": [
|
|
17
|
-
"frontend",
|
|
18
|
-
"verification",
|
|
19
|
-
"quality-gate",
|
|
20
|
-
"release-readiness",
|
|
21
|
-
"lighthouse",
|
|
22
|
-
"e2e",
|
|
23
|
-
"qa",
|
|
24
|
-
"cli",
|
|
25
|
-
"nextjs",
|
|
26
|
-
"vite"
|
|
27
|
-
],
|
|
28
|
-
"engines": {
|
|
29
|
-
"node": ">=20.18.0 <25"
|
|
30
|
-
},
|
|
31
|
-
"bin": {
|
|
32
|
-
"laxy-verify": "dist/cli.js"
|
|
33
|
-
},
|
|
34
|
-
"files": [
|
|
35
|
-
"dist/"
|
|
36
|
-
],
|
|
37
|
-
"scripts": {
|
|
38
|
-
"build": "tsc",
|
|
39
|
-
"start": "node dist/cli.js",
|
|
40
|
-
"test": "vitest run",
|
|
41
|
-
"test:coverage": "vitest run --coverage"
|
|
42
|
-
},
|
|
43
|
-
"dependencies": {
|
|
44
|
-
"@lhci/cli": "^0.14.0",
|
|
45
|
-
"chrome-launcher": "^0.13.4",
|
|
46
|
-
"js-yaml": "^4.1.0",
|
|
47
|
-
"lighthouse": "^12.1.0",
|
|
48
|
-
"pixelmatch": "^7.1.0",
|
|
49
|
-
"pngjs": "^7.0.0",
|
|
50
|
-
"puppeteer": "^24.40.0",
|
|
51
|
-
"tree-kill": "^1.2.2"
|
|
52
|
-
},
|
|
53
|
-
"devDependencies": {
|
|
54
|
-
"@types/js-yaml": "^4.0.9",
|
|
55
|
-
"@types/node": "^20.0.0",
|
|
56
|
-
"typescript": "^5.4.0",
|
|
57
|
-
"vitest": "^2.0.0"
|
|
58
|
-
},
|
|
59
|
-
"peerDependencies": {
|
|
60
|
-
"playwright": "^1.40.0"
|
|
61
|
-
},
|
|
62
|
-
"peerDependenciesMeta": {
|
|
63
|
-
"playwright": {
|
|
64
|
-
"optional": true
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "laxy-verify",
|
|
3
|
+
"version": "1.1.23",
|
|
4
|
+
"description": "Frontend verification CLI for build checks, Lighthouse, E2E, and release readiness",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "commonjs",
|
|
7
|
+
"homepage": "https://github.com/SUNgm24/Laxy/tree/main/laxy-verify#readme",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/SUNgm24/Laxy.git",
|
|
11
|
+
"directory": "laxy-verify"
|
|
12
|
+
},
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/SUNgm24/Laxy/issues"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"frontend",
|
|
18
|
+
"verification",
|
|
19
|
+
"quality-gate",
|
|
20
|
+
"release-readiness",
|
|
21
|
+
"lighthouse",
|
|
22
|
+
"e2e",
|
|
23
|
+
"qa",
|
|
24
|
+
"cli",
|
|
25
|
+
"nextjs",
|
|
26
|
+
"vite"
|
|
27
|
+
],
|
|
28
|
+
"engines": {
|
|
29
|
+
"node": ">=20.18.0 <25"
|
|
30
|
+
},
|
|
31
|
+
"bin": {
|
|
32
|
+
"laxy-verify": "dist/cli.js"
|
|
33
|
+
},
|
|
34
|
+
"files": [
|
|
35
|
+
"dist/"
|
|
36
|
+
],
|
|
37
|
+
"scripts": {
|
|
38
|
+
"build": "tsc",
|
|
39
|
+
"start": "node dist/cli.js",
|
|
40
|
+
"test": "vitest run",
|
|
41
|
+
"test:coverage": "vitest run --coverage"
|
|
42
|
+
},
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"@lhci/cli": "^0.14.0",
|
|
45
|
+
"chrome-launcher": "^0.13.4",
|
|
46
|
+
"js-yaml": "^4.1.0",
|
|
47
|
+
"lighthouse": "^12.1.0",
|
|
48
|
+
"pixelmatch": "^7.1.0",
|
|
49
|
+
"pngjs": "^7.0.0",
|
|
50
|
+
"puppeteer": "^24.40.0",
|
|
51
|
+
"tree-kill": "^1.2.2"
|
|
52
|
+
},
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"@types/js-yaml": "^4.0.9",
|
|
55
|
+
"@types/node": "^20.0.0",
|
|
56
|
+
"typescript": "^5.4.0",
|
|
57
|
+
"vitest": "^2.0.0"
|
|
58
|
+
},
|
|
59
|
+
"peerDependencies": {
|
|
60
|
+
"playwright": "^1.40.0"
|
|
61
|
+
},
|
|
62
|
+
"peerDependenciesMeta": {
|
|
63
|
+
"playwright": {
|
|
64
|
+
"optional": true
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|