@scalequality/check 0.1.0

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.
Files changed (3) hide show
  1. package/README.md +47 -0
  2. package/bin/check.mjs +90 -0
  3. package/package.json +30 -0
package/README.md ADDED
@@ -0,0 +1,47 @@
1
+ # @scalequality/check
2
+
3
+ Continuous code-maturity gate for CI. On every build it asks ScaleQuality to
4
+ re-measure your repo and **fails the pipeline when maturity drops below your line**,
5
+ so quality regressions are caught before they merge.
6
+
7
+ ```bash
8
+ npx @scalequality/check --token "$SQ_TOKEN" --project <projectId>
9
+ ```
10
+
11
+ Get your token and project id from ScaleQuality: open a connected repo's diagnosis
12
+ and **Turn on continuous measurement**. Store the token as a CI secret named
13
+ `SQ_TOKEN`.
14
+
15
+ ## Options
16
+
17
+ | Flag | Env | Default | Meaning |
18
+ |------|-----|---------|---------|
19
+ | `--token` | `SQ_TOKEN` | — | Your SQ token (required) |
20
+ | `--project` | `SQ_PROJECT` | — | ScaleQuality project id (required) |
21
+ | `--host` | `SQ_HOST` | `https://app.scalequality.io` | API host |
22
+ | `--timeout` | — | `600` | Max seconds to wait for the measurement |
23
+ | `--json` | — | off | Print the raw result as JSON |
24
+
25
+ Exit codes: `0` passed (or track-only), `1` below the cutoff, `2` error.
26
+
27
+ ## CI examples
28
+
29
+ GitHub Actions (a step in your job):
30
+
31
+ ```yaml
32
+ - run: npx @scalequality/check --token "$SQ_TOKEN" --project <projectId>
33
+ env:
34
+ SQ_TOKEN: ${{ secrets.SQ_TOKEN }}
35
+ ```
36
+
37
+ GitLab CI:
38
+
39
+ ```yaml
40
+ scalequality:
41
+ image: node:20
42
+ script:
43
+ - npx @scalequality/check --token "$SQ_TOKEN" --project <projectId>
44
+ ```
45
+
46
+ Any other CI: run the same `npx @scalequality/check` command, with `SQ_TOKEN`
47
+ exposed as a secret/variable.
package/bin/check.mjs ADDED
@@ -0,0 +1,90 @@
1
+ #!/usr/bin/env node
2
+ // ScaleQuality continuous check. Reports the project to ScaleQuality (re-measures
3
+ // the repo) and exits non-zero when maturity is below the configured cutoff, so a
4
+ // CI step can fail the build. Pure Node 18+ (global fetch), no dependencies.
5
+
6
+ const args = process.argv.slice(2)
7
+ const flag = (name) => {
8
+ const i = args.indexOf(name)
9
+ return i >= 0 ? args[i + 1] : undefined
10
+ }
11
+ const has = (name) => args.includes(name)
12
+
13
+ if (has("--help") || has("-h")) {
14
+ console.log(`scalequality check — continuous code-maturity gate for CI
15
+
16
+ Usage:
17
+ npx @scalequality/check --token <SQ_TOKEN> --project <projectId> [options]
18
+
19
+ Options:
20
+ --token <t> SQ token (or env SQ_TOKEN)
21
+ --project <id> ScaleQuality project id (or env SQ_PROJECT)
22
+ --host <url> API host (default https://app.scalequality.io, or env SQ_HOST)
23
+ --timeout <s> Max seconds to wait for the measurement (default 600)
24
+ --json Print the raw result as JSON
25
+ -h, --help Show this help
26
+
27
+ Exit codes: 0 passed (or track-only), 1 below the cutoff, 2 error.`)
28
+ process.exit(0)
29
+ }
30
+
31
+ const token = flag("--token") || process.env.SQ_TOKEN
32
+ const project = flag("--project") || process.env.SQ_PROJECT
33
+ const host = (flag("--host") || process.env.SQ_HOST || "https://app.scalequality.io").replace(/\/+$/, "")
34
+ const jsonOut = has("--json")
35
+ const timeoutMs = Math.max(30, Number(flag("--timeout") || 600)) * 1000
36
+
37
+ const base = `${host}/api/insights/continuous`
38
+ const log = (m) => { if (!jsonOut) console.log(m) }
39
+ const fail = (m) => { console.error(`ScaleQuality: ${m}`); process.exit(2) }
40
+ const sleep = (ms) => new Promise((r) => setTimeout(r, ms))
41
+
42
+ if (!token || !project) {
43
+ fail("--token and --project are required (or env SQ_TOKEN / SQ_PROJECT). See --help.")
44
+ }
45
+
46
+ async function main() {
47
+ const auth = { Authorization: `Bearer ${token}` }
48
+ const r = await fetch(`${base}/report`, {
49
+ method: "POST",
50
+ headers: { ...auth, "Content-Type": "application/json" },
51
+ body: JSON.stringify({ project }),
52
+ }).catch((e) => fail(`could not reach ${host} (${e.message})`))
53
+ if (!r.ok) {
54
+ const body = await r.text().catch(() => "")
55
+ if (r.status === 401) fail("invalid token.")
56
+ if (r.status === 402) fail("continuous measurement needs an active subscription.")
57
+ if (r.status === 409) fail("continuous is not enabled for this project. Turn it on in ScaleQuality.")
58
+ fail(`report failed (${r.status}) ${body}`)
59
+ }
60
+ const { runId } = await r.json()
61
+ if (!runId) fail("no run id returned.")
62
+ log(`ScaleQuality: measuring ${project} …`)
63
+
64
+ const deadline = Date.now() + timeoutMs
65
+ let last
66
+ while (Date.now() < deadline) {
67
+ await sleep(5000)
68
+ const sr = await fetch(`${base}/report/${runId}`, { headers: auth }).catch((e) => fail(`status check failed (${e.message})`))
69
+ if (!sr.ok) { const b = await sr.text().catch(() => ""); fail(`status failed (${sr.status}) ${b}`) }
70
+ last = await sr.json()
71
+ if (last.status === "COMPLETED" || last.status === "FAILED") break
72
+ if (!jsonOut) process.stdout.write(".")
73
+ }
74
+ if (!jsonOut) process.stdout.write("\n")
75
+
76
+ if (!last || (last.status !== "COMPLETED" && last.status !== "FAILED")) fail("timed out waiting for the measurement.")
77
+ if (last.status === "FAILED") fail(`measurement failed: ${last.failureReason || "unknown"}`)
78
+
79
+ if (jsonOut) {
80
+ console.log(JSON.stringify(last))
81
+ } else {
82
+ const cut = last.cutoff != null ? ` (cutoff ${last.cutoff})` : " (track only)"
83
+ log(`ScaleQuality: maturity ${last.maturity ?? "—"}${cut}`)
84
+ if (last.passed === false) console.error("ScaleQuality: below the line — failing the build.")
85
+ else log("ScaleQuality: passed.")
86
+ }
87
+ process.exit(last.passed === false ? 1 : 0)
88
+ }
89
+
90
+ main().catch((e) => fail(e?.message || String(e)))
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "@scalequality/check",
3
+ "version": "0.1.0",
4
+ "description": "ScaleQuality continuous code-maturity check for CI. Re-measures your repo on every build and fails the pipeline below your maturity line.",
5
+ "type": "module",
6
+ "bin": {
7
+ "check": "bin/check.mjs",
8
+ "scalequality-check": "bin/check.mjs"
9
+ },
10
+ "engines": {
11
+ "node": ">=18"
12
+ },
13
+ "files": [
14
+ "bin",
15
+ "README.md"
16
+ ],
17
+ "keywords": [
18
+ "scalequality",
19
+ "code-quality",
20
+ "code-maturity",
21
+ "ci",
22
+ "continuous-measurement",
23
+ "ai-code"
24
+ ],
25
+ "license": "MIT",
26
+ "homepage": "https://scalequality.io",
27
+ "publishConfig": {
28
+ "access": "public"
29
+ }
30
+ }