create-raredays-app 0.1.4 → 0.1.5
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/package.json +7 -3
- package/templates/client/.node-version +1 -0
- package/templates/client/.semgrepignore +12 -0
- package/templates/client/_github/workflows/ci.yml +63 -22
- package/templates/client/_github/workflows/coverage.yml +41 -0
- package/templates/client/_github/workflows/sast.yml +42 -0
- package/templates/client/_github/workflows/security.yml +34 -0
- package/templates/client/_github/workflows/zizmor.yml +43 -0
- package/templates/client/package.json +4 -1
- package/templates/full-stack/.node-version +1 -0
- package/templates/full-stack/.semgrepignore +12 -0
- package/templates/full-stack/_github/workflows/ci.yml +63 -23
- package/templates/full-stack/_github/workflows/coverage.yml +41 -0
- package/templates/full-stack/_github/workflows/db-check.yml +8 -3
- package/templates/full-stack/_github/workflows/db-migrate.yml +8 -3
- package/templates/full-stack/_github/workflows/sast.yml +42 -0
- package/templates/full-stack/_github/workflows/security.yml +34 -0
- package/templates/full-stack/_github/workflows/zizmor.yml +43 -0
- package/templates/full-stack/package.json +4 -1
- package/templates/static/.node-version +1 -0
- package/templates/static/.semgrepignore +12 -0
- package/templates/static/_github/workflows/ci.yml +58 -19
- package/templates/static/_github/workflows/sast.yml +42 -0
- package/templates/static/_github/workflows/security.yml +34 -0
- package/templates/static/_github/workflows/zizmor.yml +43 -0
- package/templates/static/package.json +3 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-raredays-app",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "Scaffold a new RareDays Next.js app.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"author": "Jeremy Harper <jeremy@raredays.com>",
|
|
@@ -14,13 +14,16 @@
|
|
|
14
14
|
"skillex"
|
|
15
15
|
],
|
|
16
16
|
"type": "module",
|
|
17
|
+
"publishConfig": {
|
|
18
|
+
"access": "public"
|
|
19
|
+
},
|
|
17
20
|
"dependencies": {
|
|
18
21
|
"@clack/prompts": "1.4.0"
|
|
19
22
|
},
|
|
20
23
|
"devDependencies": {
|
|
21
24
|
"typescript": "5.9.3",
|
|
22
|
-
"vitest": "4.1.
|
|
23
|
-
"@raredays/lint": "0.
|
|
25
|
+
"vitest": "4.1.9",
|
|
26
|
+
"@raredays/lint": "0.2.0"
|
|
24
27
|
},
|
|
25
28
|
"engines": {
|
|
26
29
|
"node": ">=20"
|
|
@@ -34,6 +37,7 @@
|
|
|
34
37
|
"style": "raredays style",
|
|
35
38
|
"style:fix": "raredays style:fix",
|
|
36
39
|
"fix": "raredays fix",
|
|
40
|
+
"publint": "publint run --strict",
|
|
37
41
|
"check": "raredays check",
|
|
38
42
|
"test": "vitest run",
|
|
39
43
|
"test:watch": "vitest",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
24.15.0
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Semgrep ignore — paths excluded from SAST scans.
|
|
2
|
+
#
|
|
3
|
+
# Add specific paths or globs here when a rule produces unavoidable
|
|
4
|
+
# false positives (e.g. mock servers using http://localhost, generated
|
|
5
|
+
# code that trips security patterns).
|
|
6
|
+
#
|
|
7
|
+
# Example:
|
|
8
|
+
# src/mocks/*.test.ts
|
|
9
|
+
#
|
|
10
|
+
# Prefer inline `// nosemgrep: <rule-id> -- <reason>` over path-level
|
|
11
|
+
# exclusions when the case is local; reserve this file for whole files
|
|
12
|
+
# that legitimately need to be skipped.
|
|
@@ -6,6 +6,9 @@ on:
|
|
|
6
6
|
pull_request:
|
|
7
7
|
branches: [main]
|
|
8
8
|
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
9
12
|
concurrency:
|
|
10
13
|
group: ci-${{ github.ref }}
|
|
11
14
|
cancel-in-progress: true
|
|
@@ -20,9 +23,11 @@ jobs:
|
|
|
20
23
|
runs-on: ubuntu-latest
|
|
21
24
|
timeout-minutes: 10
|
|
22
25
|
steps:
|
|
23
|
-
- uses: actions/checkout@
|
|
24
|
-
|
|
25
|
-
|
|
26
|
+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
27
|
+
with:
|
|
28
|
+
persist-credentials: false
|
|
29
|
+
- uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
|
|
30
|
+
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
|
26
31
|
with: { node-version: 24, cache: pnpm }
|
|
27
32
|
- run: pnpm install --frozen-lockfile
|
|
28
33
|
- run: pnpm lint
|
|
@@ -32,9 +37,11 @@ jobs:
|
|
|
32
37
|
runs-on: ubuntu-latest
|
|
33
38
|
timeout-minutes: 10
|
|
34
39
|
steps:
|
|
35
|
-
- uses: actions/checkout@
|
|
36
|
-
|
|
37
|
-
|
|
40
|
+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
41
|
+
with:
|
|
42
|
+
persist-credentials: false
|
|
43
|
+
- uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
|
|
44
|
+
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
|
38
45
|
with: { node-version: 24, cache: pnpm }
|
|
39
46
|
- run: pnpm install --frozen-lockfile
|
|
40
47
|
- run: pnpm format:check
|
|
@@ -44,9 +51,11 @@ jobs:
|
|
|
44
51
|
runs-on: ubuntu-latest
|
|
45
52
|
timeout-minutes: 10
|
|
46
53
|
steps:
|
|
47
|
-
- uses: actions/checkout@
|
|
48
|
-
|
|
49
|
-
|
|
54
|
+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
55
|
+
with:
|
|
56
|
+
persist-credentials: false
|
|
57
|
+
- uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
|
|
58
|
+
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
|
50
59
|
with: { node-version: 24, cache: pnpm }
|
|
51
60
|
- run: pnpm install --frozen-lockfile
|
|
52
61
|
- run: pnpm style
|
|
@@ -56,9 +65,11 @@ jobs:
|
|
|
56
65
|
runs-on: ubuntu-latest
|
|
57
66
|
timeout-minutes: 10
|
|
58
67
|
steps:
|
|
59
|
-
- uses: actions/checkout@
|
|
60
|
-
|
|
61
|
-
|
|
68
|
+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
69
|
+
with:
|
|
70
|
+
persist-credentials: false
|
|
71
|
+
- uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
|
|
72
|
+
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
|
62
73
|
with: { node-version: 24, cache: pnpm }
|
|
63
74
|
- run: pnpm install --frozen-lockfile
|
|
64
75
|
- run: pnpm theme:build
|
|
@@ -69,9 +80,11 @@ jobs:
|
|
|
69
80
|
runs-on: ubuntu-latest
|
|
70
81
|
timeout-minutes: 15
|
|
71
82
|
steps:
|
|
72
|
-
- uses: actions/checkout@
|
|
73
|
-
|
|
74
|
-
|
|
83
|
+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
84
|
+
with:
|
|
85
|
+
persist-credentials: false
|
|
86
|
+
- uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
|
|
87
|
+
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
|
75
88
|
with: { node-version: 24, cache: pnpm }
|
|
76
89
|
- run: pnpm install --frozen-lockfile
|
|
77
90
|
- run: pnpm theme:build
|
|
@@ -82,9 +95,11 @@ jobs:
|
|
|
82
95
|
runs-on: ubuntu-latest
|
|
83
96
|
timeout-minutes: 15
|
|
84
97
|
steps:
|
|
85
|
-
- uses: actions/checkout@
|
|
86
|
-
|
|
87
|
-
|
|
98
|
+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
99
|
+
with:
|
|
100
|
+
persist-credentials: false
|
|
101
|
+
- uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
|
|
102
|
+
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
|
88
103
|
with: { node-version: 24, cache: pnpm }
|
|
89
104
|
- run: pnpm install --frozen-lockfile
|
|
90
105
|
- run: pnpm build
|
|
@@ -93,12 +108,38 @@ jobs:
|
|
|
93
108
|
name: Storybook build
|
|
94
109
|
runs-on: ubuntu-latest
|
|
95
110
|
timeout-minutes: 15
|
|
96
|
-
if: hashFiles('.storybook/main.ts', '.storybook/main.js') != ''
|
|
97
111
|
steps:
|
|
98
|
-
- uses: actions/checkout@
|
|
99
|
-
|
|
100
|
-
|
|
112
|
+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
113
|
+
with:
|
|
114
|
+
persist-credentials: false
|
|
115
|
+
- uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
|
|
116
|
+
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
|
101
117
|
with: { node-version: 24, cache: pnpm }
|
|
102
118
|
- run: pnpm install --frozen-lockfile
|
|
103
119
|
- run: pnpm theme:build
|
|
104
120
|
- run: pnpm storybook:build
|
|
121
|
+
|
|
122
|
+
# Single aggregation gate — make THIS the only required status check in
|
|
123
|
+
# branch protection so you don't have to enumerate every job (skipped
|
|
124
|
+
# jobs are tolerated by the gate).
|
|
125
|
+
ci-result:
|
|
126
|
+
name: ci-result
|
|
127
|
+
needs: [lint, format-check, style, typecheck, test, build, storybook]
|
|
128
|
+
if: ${{ !cancelled() }}
|
|
129
|
+
runs-on: ubuntu-latest
|
|
130
|
+
permissions: {}
|
|
131
|
+
steps:
|
|
132
|
+
- name: Aggregate CI results
|
|
133
|
+
env:
|
|
134
|
+
NEEDS: ${{ toJson(needs) }}
|
|
135
|
+
run: |
|
|
136
|
+
echo "$NEEDS" | jq -r 'to_entries[] | "\(.key)\t\(.value.result)"'
|
|
137
|
+
failed=$(echo "$NEEDS" | jq -r '
|
|
138
|
+
to_entries[]
|
|
139
|
+
| select(.value.result == "failure" or .value.result == "cancelled")
|
|
140
|
+
| .key')
|
|
141
|
+
if [ -n "$failed" ]; then
|
|
142
|
+
echo "::error::Failed or cancelled jobs: $failed"
|
|
143
|
+
exit 1
|
|
144
|
+
fi
|
|
145
|
+
echo "All required CI jobs passed (or were skipped)."
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
name: Coverage
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
concurrency:
|
|
13
|
+
group: coverage-${{ github.ref }}
|
|
14
|
+
cancel-in-progress: true
|
|
15
|
+
|
|
16
|
+
# Runs Vitest with --coverage and uploads the HTML report as an artifact.
|
|
17
|
+
# Coverage thresholds are not enforced by default — opt in by configuring
|
|
18
|
+
# `test.coverage.thresholds` in vitest.config.ts when your suite is ready.
|
|
19
|
+
|
|
20
|
+
jobs:
|
|
21
|
+
coverage:
|
|
22
|
+
name: Coverage
|
|
23
|
+
runs-on: ubuntu-latest
|
|
24
|
+
timeout-minutes: 15
|
|
25
|
+
steps:
|
|
26
|
+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
27
|
+
with:
|
|
28
|
+
persist-credentials: false
|
|
29
|
+
- uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
|
|
30
|
+
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
|
31
|
+
with: { node-version: 24, cache: pnpm }
|
|
32
|
+
- run: pnpm install --frozen-lockfile
|
|
33
|
+
- run: pnpm theme:build
|
|
34
|
+
- run: pnpm coverage
|
|
35
|
+
- name: Upload coverage report
|
|
36
|
+
if: always()
|
|
37
|
+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
38
|
+
with:
|
|
39
|
+
name: coverage-report
|
|
40
|
+
path: coverage/
|
|
41
|
+
retention-days: 14
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
name: SAST
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
concurrency:
|
|
13
|
+
group: sast-${{ github.ref }}
|
|
14
|
+
cancel-in-progress: true
|
|
15
|
+
|
|
16
|
+
# Static Application Security Testing via Semgrep. Scans first-party
|
|
17
|
+
# source against TypeScript, JavaScript, React, and OWASP top-ten
|
|
18
|
+
# rulesets. Findings fail the build — silence false positives with
|
|
19
|
+
# `.semgrepignore` (path-level) or inline `// nosemgrep: <rule-id>` with
|
|
20
|
+
# a justification.
|
|
21
|
+
|
|
22
|
+
jobs:
|
|
23
|
+
semgrep:
|
|
24
|
+
name: Semgrep
|
|
25
|
+
runs-on: ubuntu-latest
|
|
26
|
+
timeout-minutes: 10
|
|
27
|
+
if: github.actor != 'dependabot[bot]'
|
|
28
|
+
container:
|
|
29
|
+
image: semgrep/semgrep:1.168.0@sha256:59fbed6127ea7c5dde3ba6a85142733bb20ea9aaa36120c953904f1539aaf66e
|
|
30
|
+
steps:
|
|
31
|
+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
32
|
+
with:
|
|
33
|
+
persist-credentials: false
|
|
34
|
+
- name: Run Semgrep
|
|
35
|
+
run: |
|
|
36
|
+
semgrep scan \
|
|
37
|
+
--config=p/typescript \
|
|
38
|
+
--config=p/javascript \
|
|
39
|
+
--config=p/react \
|
|
40
|
+
--config=p/owasp-top-ten \
|
|
41
|
+
--error \
|
|
42
|
+
--metrics=off
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
name: Security
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
concurrency:
|
|
13
|
+
group: security-${{ github.ref }}
|
|
14
|
+
cancel-in-progress: true
|
|
15
|
+
|
|
16
|
+
# Audits production dependencies for known vulnerabilities. Fails the
|
|
17
|
+
# build on high or critical severity findings; moderate and low findings
|
|
18
|
+
# appear in the log but don't block merges.
|
|
19
|
+
|
|
20
|
+
jobs:
|
|
21
|
+
audit:
|
|
22
|
+
name: pnpm audit
|
|
23
|
+
runs-on: ubuntu-latest
|
|
24
|
+
timeout-minutes: 5
|
|
25
|
+
steps:
|
|
26
|
+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
27
|
+
with:
|
|
28
|
+
persist-credentials: false
|
|
29
|
+
- uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
|
|
30
|
+
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
|
31
|
+
with: { node-version: 24, cache: pnpm }
|
|
32
|
+
- run: pnpm install --frozen-lockfile
|
|
33
|
+
- name: Audit production dependencies
|
|
34
|
+
run: pnpm audit --audit-level=high --prod
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
name: Zizmor
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
paths: [".github/**"]
|
|
7
|
+
pull_request:
|
|
8
|
+
branches: [main]
|
|
9
|
+
paths: [".github/**"]
|
|
10
|
+
|
|
11
|
+
permissions: {}
|
|
12
|
+
|
|
13
|
+
concurrency:
|
|
14
|
+
group: zizmor-${{ github.ref }}
|
|
15
|
+
cancel-in-progress: true
|
|
16
|
+
|
|
17
|
+
# Static security analysis of this app's GitHub Actions workflows
|
|
18
|
+
# (template-injection, unpinned actions, excessive token permissions,
|
|
19
|
+
# credential persistence). Free runner-hosted SAST — no GitHub Advanced
|
|
20
|
+
# Security needed. Findings surface as inline PR annotations. Starts at
|
|
21
|
+
# min-severity: high; lower to medium/low once your workflows are clean.
|
|
22
|
+
|
|
23
|
+
jobs:
|
|
24
|
+
zizmor:
|
|
25
|
+
name: GitHub Actions security (zizmor)
|
|
26
|
+
runs-on: ubuntu-latest
|
|
27
|
+
timeout-minutes: 10
|
|
28
|
+
permissions:
|
|
29
|
+
contents: read
|
|
30
|
+
actions: read
|
|
31
|
+
steps:
|
|
32
|
+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
33
|
+
with:
|
|
34
|
+
persist-credentials: false
|
|
35
|
+
- uses: zizmorcore/zizmor-action@5f14fd08f7cf1cb1609c1e344975f152c7ee938d # v0.5.6
|
|
36
|
+
with:
|
|
37
|
+
min-severity: high
|
|
38
|
+
min-confidence: medium
|
|
39
|
+
# This app's repo may not have GitHub Advanced Security; disable the
|
|
40
|
+
# SARIF/code-scanning upload (mutually exclusive with annotations) and
|
|
41
|
+
# surface findings as inline PR annotations instead.
|
|
42
|
+
advanced-security: false
|
|
43
|
+
annotations: true
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
"theme:build": "node ./scripts/build-theme.mjs",
|
|
13
13
|
"test": "vitest run",
|
|
14
14
|
"test:watch": "vitest",
|
|
15
|
+
"coverage": "vitest run --coverage",
|
|
15
16
|
"storybook": "storybook dev -p 6006",
|
|
16
17
|
"storybook:build": "storybook build -o storybook-static",
|
|
17
18
|
"lint": "raredays lint",
|
|
@@ -48,6 +49,7 @@
|
|
|
48
49
|
"@types/react": "19.2.14",
|
|
49
50
|
"@types/react-dom": "19.2.3",
|
|
50
51
|
"@vitejs/plugin-react": "6.0.1",
|
|
52
|
+
"@vitest/coverage-v8": "4.1.5",
|
|
51
53
|
"happy-dom": "15.11.7",
|
|
52
54
|
"storybook": "10.3.6",
|
|
53
55
|
"tailwindcss": "4.2.4",
|
|
@@ -57,5 +59,6 @@
|
|
|
57
59
|
},
|
|
58
60
|
"engines": {
|
|
59
61
|
"node": ">=24"
|
|
60
|
-
}
|
|
62
|
+
},
|
|
63
|
+
"packageManager": "pnpm@11.9.0"
|
|
61
64
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
24.15.0
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Semgrep ignore — paths excluded from SAST scans.
|
|
2
|
+
#
|
|
3
|
+
# Add specific paths or globs here when a rule produces unavoidable
|
|
4
|
+
# false positives (e.g. mock servers using http://localhost, generated
|
|
5
|
+
# code that trips security patterns).
|
|
6
|
+
#
|
|
7
|
+
# Example:
|
|
8
|
+
# src/mocks/*.test.ts
|
|
9
|
+
#
|
|
10
|
+
# Prefer inline `// nosemgrep: <rule-id> -- <reason>` over path-level
|
|
11
|
+
# exclusions when the case is local; reserve this file for whole files
|
|
12
|
+
# that legitimately need to be skipped.
|
|
@@ -6,6 +6,9 @@ on:
|
|
|
6
6
|
pull_request:
|
|
7
7
|
branches: [main]
|
|
8
8
|
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
9
12
|
concurrency:
|
|
10
13
|
group: ci-${{ github.ref }}
|
|
11
14
|
cancel-in-progress: true
|
|
@@ -24,9 +27,11 @@ jobs:
|
|
|
24
27
|
runs-on: ubuntu-latest
|
|
25
28
|
timeout-minutes: 10
|
|
26
29
|
steps:
|
|
27
|
-
- uses: actions/checkout@
|
|
28
|
-
|
|
29
|
-
|
|
30
|
+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
31
|
+
with:
|
|
32
|
+
persist-credentials: false
|
|
33
|
+
- uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
|
|
34
|
+
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
|
30
35
|
with: { node-version: 24, cache: pnpm }
|
|
31
36
|
- run: pnpm install --frozen-lockfile
|
|
32
37
|
- run: pnpm lint
|
|
@@ -36,9 +41,11 @@ jobs:
|
|
|
36
41
|
runs-on: ubuntu-latest
|
|
37
42
|
timeout-minutes: 10
|
|
38
43
|
steps:
|
|
39
|
-
- uses: actions/checkout@
|
|
40
|
-
|
|
41
|
-
|
|
44
|
+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
45
|
+
with:
|
|
46
|
+
persist-credentials: false
|
|
47
|
+
- uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
|
|
48
|
+
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
|
42
49
|
with: { node-version: 24, cache: pnpm }
|
|
43
50
|
- run: pnpm install --frozen-lockfile
|
|
44
51
|
- run: pnpm format:check
|
|
@@ -48,9 +55,11 @@ jobs:
|
|
|
48
55
|
runs-on: ubuntu-latest
|
|
49
56
|
timeout-minutes: 10
|
|
50
57
|
steps:
|
|
51
|
-
- uses: actions/checkout@
|
|
52
|
-
|
|
53
|
-
|
|
58
|
+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
59
|
+
with:
|
|
60
|
+
persist-credentials: false
|
|
61
|
+
- uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
|
|
62
|
+
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
|
54
63
|
with: { node-version: 24, cache: pnpm }
|
|
55
64
|
- run: pnpm install --frozen-lockfile
|
|
56
65
|
- run: pnpm style
|
|
@@ -60,9 +69,11 @@ jobs:
|
|
|
60
69
|
runs-on: ubuntu-latest
|
|
61
70
|
timeout-minutes: 10
|
|
62
71
|
steps:
|
|
63
|
-
- uses: actions/checkout@
|
|
64
|
-
|
|
65
|
-
|
|
72
|
+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
73
|
+
with:
|
|
74
|
+
persist-credentials: false
|
|
75
|
+
- uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
|
|
76
|
+
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
|
66
77
|
with: { node-version: 24, cache: pnpm }
|
|
67
78
|
- run: pnpm install --frozen-lockfile
|
|
68
79
|
- run: pnpm theme:build
|
|
@@ -73,9 +84,11 @@ jobs:
|
|
|
73
84
|
runs-on: ubuntu-latest
|
|
74
85
|
timeout-minutes: 15
|
|
75
86
|
steps:
|
|
76
|
-
- uses: actions/checkout@
|
|
77
|
-
|
|
78
|
-
|
|
87
|
+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
88
|
+
with:
|
|
89
|
+
persist-credentials: false
|
|
90
|
+
- uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
|
|
91
|
+
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
|
79
92
|
with: { node-version: 24, cache: pnpm }
|
|
80
93
|
- run: pnpm install --frozen-lockfile
|
|
81
94
|
- run: pnpm theme:build
|
|
@@ -86,9 +99,11 @@ jobs:
|
|
|
86
99
|
runs-on: ubuntu-latest
|
|
87
100
|
timeout-minutes: 15
|
|
88
101
|
steps:
|
|
89
|
-
- uses: actions/checkout@
|
|
90
|
-
|
|
91
|
-
|
|
102
|
+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
103
|
+
with:
|
|
104
|
+
persist-credentials: false
|
|
105
|
+
- uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
|
|
106
|
+
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
|
92
107
|
with: { node-version: 24, cache: pnpm }
|
|
93
108
|
- run: pnpm install --frozen-lockfile
|
|
94
109
|
- run: pnpm build
|
|
@@ -97,13 +112,38 @@ jobs:
|
|
|
97
112
|
name: Storybook build
|
|
98
113
|
runs-on: ubuntu-latest
|
|
99
114
|
timeout-minutes: 15
|
|
100
|
-
# Only run if storybook is configured.
|
|
101
|
-
if: hashFiles('.storybook/main.ts', '.storybook/main.js') != ''
|
|
102
115
|
steps:
|
|
103
|
-
- uses: actions/checkout@
|
|
104
|
-
|
|
105
|
-
|
|
116
|
+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
117
|
+
with:
|
|
118
|
+
persist-credentials: false
|
|
119
|
+
- uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
|
|
120
|
+
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
|
106
121
|
with: { node-version: 24, cache: pnpm }
|
|
107
122
|
- run: pnpm install --frozen-lockfile
|
|
108
123
|
- run: pnpm theme:build
|
|
109
124
|
- run: pnpm storybook:build
|
|
125
|
+
|
|
126
|
+
# Single aggregation gate — make THIS the only required status check in
|
|
127
|
+
# branch protection so you don't have to enumerate every job (skipped
|
|
128
|
+
# jobs are tolerated by the gate).
|
|
129
|
+
ci-result:
|
|
130
|
+
name: ci-result
|
|
131
|
+
needs: [lint, format-check, style, typecheck, test, build, storybook]
|
|
132
|
+
if: ${{ !cancelled() }}
|
|
133
|
+
runs-on: ubuntu-latest
|
|
134
|
+
permissions: {}
|
|
135
|
+
steps:
|
|
136
|
+
- name: Aggregate CI results
|
|
137
|
+
env:
|
|
138
|
+
NEEDS: ${{ toJson(needs) }}
|
|
139
|
+
run: |
|
|
140
|
+
echo "$NEEDS" | jq -r 'to_entries[] | "\(.key)\t\(.value.result)"'
|
|
141
|
+
failed=$(echo "$NEEDS" | jq -r '
|
|
142
|
+
to_entries[]
|
|
143
|
+
| select(.value.result == "failure" or .value.result == "cancelled")
|
|
144
|
+
| .key')
|
|
145
|
+
if [ -n "$failed" ]; then
|
|
146
|
+
echo "::error::Failed or cancelled jobs: $failed"
|
|
147
|
+
exit 1
|
|
148
|
+
fi
|
|
149
|
+
echo "All required CI jobs passed (or were skipped)."
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
name: Coverage
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
concurrency:
|
|
13
|
+
group: coverage-${{ github.ref }}
|
|
14
|
+
cancel-in-progress: true
|
|
15
|
+
|
|
16
|
+
# Runs Vitest with --coverage and uploads the HTML report as an artifact.
|
|
17
|
+
# Coverage thresholds are not enforced by default — opt in by configuring
|
|
18
|
+
# `test.coverage.thresholds` in vitest.config.ts when your suite is ready.
|
|
19
|
+
|
|
20
|
+
jobs:
|
|
21
|
+
coverage:
|
|
22
|
+
name: Coverage
|
|
23
|
+
runs-on: ubuntu-latest
|
|
24
|
+
timeout-minutes: 15
|
|
25
|
+
steps:
|
|
26
|
+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
27
|
+
with:
|
|
28
|
+
persist-credentials: false
|
|
29
|
+
- uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
|
|
30
|
+
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
|
31
|
+
with: { node-version: 24, cache: pnpm }
|
|
32
|
+
- run: pnpm install --frozen-lockfile
|
|
33
|
+
- run: pnpm theme:build
|
|
34
|
+
- run: pnpm coverage
|
|
35
|
+
- name: Upload coverage report
|
|
36
|
+
if: always()
|
|
37
|
+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
38
|
+
with:
|
|
39
|
+
name: coverage-report
|
|
40
|
+
path: coverage/
|
|
41
|
+
retention-days: 14
|
|
@@ -19,13 +19,18 @@ on:
|
|
|
19
19
|
- "package.json"
|
|
20
20
|
- ".github/workflows/db-check.yml"
|
|
21
21
|
|
|
22
|
+
permissions:
|
|
23
|
+
contents: read
|
|
24
|
+
|
|
22
25
|
jobs:
|
|
23
26
|
db-check:
|
|
24
27
|
runs-on: ubuntu-latest
|
|
25
28
|
steps:
|
|
26
|
-
- uses: actions/checkout@
|
|
27
|
-
|
|
28
|
-
|
|
29
|
+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
30
|
+
with:
|
|
31
|
+
persist-credentials: false
|
|
32
|
+
- uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
|
|
33
|
+
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
|
29
34
|
with:
|
|
30
35
|
node-version: 24
|
|
31
36
|
cache: pnpm
|
|
@@ -15,6 +15,9 @@ on:
|
|
|
15
15
|
- ".github/workflows/db-migrate.yml"
|
|
16
16
|
workflow_dispatch:
|
|
17
17
|
|
|
18
|
+
permissions:
|
|
19
|
+
contents: read
|
|
20
|
+
|
|
18
21
|
concurrency:
|
|
19
22
|
# Advisory lock at the workflow layer too — defense in depth alongside
|
|
20
23
|
# safeMigrate's per-database advisory lock.
|
|
@@ -26,9 +29,11 @@ jobs:
|
|
|
26
29
|
runs-on: ubuntu-latest
|
|
27
30
|
environment: production
|
|
28
31
|
steps:
|
|
29
|
-
- uses: actions/checkout@
|
|
30
|
-
|
|
31
|
-
|
|
32
|
+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
33
|
+
with:
|
|
34
|
+
persist-credentials: false
|
|
35
|
+
- uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
|
|
36
|
+
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
|
32
37
|
with:
|
|
33
38
|
node-version: 24
|
|
34
39
|
cache: pnpm
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
name: SAST
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
concurrency:
|
|
13
|
+
group: sast-${{ github.ref }}
|
|
14
|
+
cancel-in-progress: true
|
|
15
|
+
|
|
16
|
+
# Static Application Security Testing via Semgrep. Scans first-party
|
|
17
|
+
# source against TypeScript, JavaScript, React, and OWASP top-ten
|
|
18
|
+
# rulesets. Findings fail the build — silence false positives with
|
|
19
|
+
# `.semgrepignore` (path-level) or inline `// nosemgrep: <rule-id>` with
|
|
20
|
+
# a justification.
|
|
21
|
+
|
|
22
|
+
jobs:
|
|
23
|
+
semgrep:
|
|
24
|
+
name: Semgrep
|
|
25
|
+
runs-on: ubuntu-latest
|
|
26
|
+
timeout-minutes: 10
|
|
27
|
+
if: github.actor != 'dependabot[bot]'
|
|
28
|
+
container:
|
|
29
|
+
image: semgrep/semgrep:1.168.0@sha256:59fbed6127ea7c5dde3ba6a85142733bb20ea9aaa36120c953904f1539aaf66e
|
|
30
|
+
steps:
|
|
31
|
+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
32
|
+
with:
|
|
33
|
+
persist-credentials: false
|
|
34
|
+
- name: Run Semgrep
|
|
35
|
+
run: |
|
|
36
|
+
semgrep scan \
|
|
37
|
+
--config=p/typescript \
|
|
38
|
+
--config=p/javascript \
|
|
39
|
+
--config=p/react \
|
|
40
|
+
--config=p/owasp-top-ten \
|
|
41
|
+
--error \
|
|
42
|
+
--metrics=off
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
name: Security
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
concurrency:
|
|
13
|
+
group: security-${{ github.ref }}
|
|
14
|
+
cancel-in-progress: true
|
|
15
|
+
|
|
16
|
+
# Audits production dependencies for known vulnerabilities. Fails the
|
|
17
|
+
# build on high or critical severity findings; moderate and low findings
|
|
18
|
+
# appear in the log but don't block merges.
|
|
19
|
+
|
|
20
|
+
jobs:
|
|
21
|
+
audit:
|
|
22
|
+
name: pnpm audit
|
|
23
|
+
runs-on: ubuntu-latest
|
|
24
|
+
timeout-minutes: 5
|
|
25
|
+
steps:
|
|
26
|
+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
27
|
+
with:
|
|
28
|
+
persist-credentials: false
|
|
29
|
+
- uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
|
|
30
|
+
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
|
31
|
+
with: { node-version: 24, cache: pnpm }
|
|
32
|
+
- run: pnpm install --frozen-lockfile
|
|
33
|
+
- name: Audit production dependencies
|
|
34
|
+
run: pnpm audit --audit-level=high --prod
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
name: Zizmor
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
paths: [".github/**"]
|
|
7
|
+
pull_request:
|
|
8
|
+
branches: [main]
|
|
9
|
+
paths: [".github/**"]
|
|
10
|
+
|
|
11
|
+
permissions: {}
|
|
12
|
+
|
|
13
|
+
concurrency:
|
|
14
|
+
group: zizmor-${{ github.ref }}
|
|
15
|
+
cancel-in-progress: true
|
|
16
|
+
|
|
17
|
+
# Static security analysis of this app's GitHub Actions workflows
|
|
18
|
+
# (template-injection, unpinned actions, excessive token permissions,
|
|
19
|
+
# credential persistence). Free runner-hosted SAST — no GitHub Advanced
|
|
20
|
+
# Security needed. Findings surface as inline PR annotations. Starts at
|
|
21
|
+
# min-severity: high; lower to medium/low once your workflows are clean.
|
|
22
|
+
|
|
23
|
+
jobs:
|
|
24
|
+
zizmor:
|
|
25
|
+
name: GitHub Actions security (zizmor)
|
|
26
|
+
runs-on: ubuntu-latest
|
|
27
|
+
timeout-minutes: 10
|
|
28
|
+
permissions:
|
|
29
|
+
contents: read
|
|
30
|
+
actions: read
|
|
31
|
+
steps:
|
|
32
|
+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
33
|
+
with:
|
|
34
|
+
persist-credentials: false
|
|
35
|
+
- uses: zizmorcore/zizmor-action@5f14fd08f7cf1cb1609c1e344975f152c7ee938d # v0.5.6
|
|
36
|
+
with:
|
|
37
|
+
min-severity: high
|
|
38
|
+
min-confidence: medium
|
|
39
|
+
# This app's repo may not have GitHub Advanced Security; disable the
|
|
40
|
+
# SARIF/code-scanning upload (mutually exclusive with annotations) and
|
|
41
|
+
# surface findings as inline PR annotations instead.
|
|
42
|
+
advanced-security: false
|
|
43
|
+
annotations: true
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
"theme:build": "node ./scripts/build-theme.mjs",
|
|
13
13
|
"test": "vitest run",
|
|
14
14
|
"test:watch": "vitest",
|
|
15
|
+
"coverage": "vitest run --coverage",
|
|
15
16
|
"storybook": "storybook dev -p 6006",
|
|
16
17
|
"storybook:build": "storybook build -o storybook-static",
|
|
17
18
|
"db:generate": "drizzle-kit generate",
|
|
@@ -58,6 +59,7 @@
|
|
|
58
59
|
"@storybook/react-vite": "10.3.6",
|
|
59
60
|
"@tailwindcss/vite": "4.2.4",
|
|
60
61
|
"@vitejs/plugin-react": "6.0.1",
|
|
62
|
+
"@vitest/coverage-v8": "4.1.5",
|
|
61
63
|
"drizzle-kit": "0.31.10",
|
|
62
64
|
"happy-dom": "15.11.7",
|
|
63
65
|
"storybook": "10.3.6",
|
|
@@ -68,5 +70,6 @@
|
|
|
68
70
|
},
|
|
69
71
|
"engines": {
|
|
70
72
|
"node": ">=24"
|
|
71
|
-
}
|
|
73
|
+
},
|
|
74
|
+
"packageManager": "pnpm@11.9.0"
|
|
72
75
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
24.15.0
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Semgrep ignore — paths excluded from SAST scans.
|
|
2
|
+
#
|
|
3
|
+
# Add specific paths or globs here when a rule produces unavoidable
|
|
4
|
+
# false positives (e.g. mock servers using http://localhost, generated
|
|
5
|
+
# code that trips security patterns).
|
|
6
|
+
#
|
|
7
|
+
# Example:
|
|
8
|
+
# src/mocks/*.test.ts
|
|
9
|
+
#
|
|
10
|
+
# Prefer inline `// nosemgrep: <rule-id> -- <reason>` over path-level
|
|
11
|
+
# exclusions when the case is local; reserve this file for whole files
|
|
12
|
+
# that legitimately need to be skipped.
|
|
@@ -6,6 +6,9 @@ on:
|
|
|
6
6
|
pull_request:
|
|
7
7
|
branches: [main]
|
|
8
8
|
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
9
12
|
concurrency:
|
|
10
13
|
group: ci-${{ github.ref }}
|
|
11
14
|
cancel-in-progress: true
|
|
@@ -20,9 +23,11 @@ jobs:
|
|
|
20
23
|
runs-on: ubuntu-latest
|
|
21
24
|
timeout-minutes: 10
|
|
22
25
|
steps:
|
|
23
|
-
- uses: actions/checkout@
|
|
24
|
-
|
|
25
|
-
|
|
26
|
+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
27
|
+
with:
|
|
28
|
+
persist-credentials: false
|
|
29
|
+
- uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
|
|
30
|
+
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
|
26
31
|
with: { node-version: 24, cache: pnpm }
|
|
27
32
|
- run: pnpm install --frozen-lockfile
|
|
28
33
|
- run: pnpm lint
|
|
@@ -32,9 +37,11 @@ jobs:
|
|
|
32
37
|
runs-on: ubuntu-latest
|
|
33
38
|
timeout-minutes: 10
|
|
34
39
|
steps:
|
|
35
|
-
- uses: actions/checkout@
|
|
36
|
-
|
|
37
|
-
|
|
40
|
+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
41
|
+
with:
|
|
42
|
+
persist-credentials: false
|
|
43
|
+
- uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
|
|
44
|
+
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
|
38
45
|
with: { node-version: 24, cache: pnpm }
|
|
39
46
|
- run: pnpm install --frozen-lockfile
|
|
40
47
|
- run: pnpm format:check
|
|
@@ -44,9 +51,11 @@ jobs:
|
|
|
44
51
|
runs-on: ubuntu-latest
|
|
45
52
|
timeout-minutes: 10
|
|
46
53
|
steps:
|
|
47
|
-
- uses: actions/checkout@
|
|
48
|
-
|
|
49
|
-
|
|
54
|
+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
55
|
+
with:
|
|
56
|
+
persist-credentials: false
|
|
57
|
+
- uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
|
|
58
|
+
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
|
50
59
|
with: { node-version: 24, cache: pnpm }
|
|
51
60
|
- run: pnpm install --frozen-lockfile
|
|
52
61
|
- run: pnpm style
|
|
@@ -56,9 +65,11 @@ jobs:
|
|
|
56
65
|
runs-on: ubuntu-latest
|
|
57
66
|
timeout-minutes: 10
|
|
58
67
|
steps:
|
|
59
|
-
- uses: actions/checkout@
|
|
60
|
-
|
|
61
|
-
|
|
68
|
+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
69
|
+
with:
|
|
70
|
+
persist-credentials: false
|
|
71
|
+
- uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
|
|
72
|
+
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
|
62
73
|
with: { node-version: 24, cache: pnpm }
|
|
63
74
|
- run: pnpm install --frozen-lockfile
|
|
64
75
|
- run: pnpm theme:build
|
|
@@ -69,9 +80,11 @@ jobs:
|
|
|
69
80
|
runs-on: ubuntu-latest
|
|
70
81
|
timeout-minutes: 15
|
|
71
82
|
steps:
|
|
72
|
-
- uses: actions/checkout@
|
|
73
|
-
|
|
74
|
-
|
|
83
|
+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
84
|
+
with:
|
|
85
|
+
persist-credentials: false
|
|
86
|
+
- uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
|
|
87
|
+
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
|
75
88
|
with: { node-version: 24, cache: pnpm }
|
|
76
89
|
- run: pnpm install --frozen-lockfile
|
|
77
90
|
- run: pnpm build
|
|
@@ -80,12 +93,38 @@ jobs:
|
|
|
80
93
|
name: Storybook build
|
|
81
94
|
runs-on: ubuntu-latest
|
|
82
95
|
timeout-minutes: 15
|
|
83
|
-
if: hashFiles('.storybook/main.ts', '.storybook/main.js') != ''
|
|
84
96
|
steps:
|
|
85
|
-
- uses: actions/checkout@
|
|
86
|
-
|
|
87
|
-
|
|
97
|
+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
98
|
+
with:
|
|
99
|
+
persist-credentials: false
|
|
100
|
+
- uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
|
|
101
|
+
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
|
88
102
|
with: { node-version: 24, cache: pnpm }
|
|
89
103
|
- run: pnpm install --frozen-lockfile
|
|
90
104
|
- run: pnpm theme:build
|
|
91
105
|
- run: pnpm storybook:build
|
|
106
|
+
|
|
107
|
+
# Single aggregation gate — make THIS the only required status check in
|
|
108
|
+
# branch protection so you don't have to enumerate every job (skipped
|
|
109
|
+
# jobs are tolerated by the gate).
|
|
110
|
+
ci-result:
|
|
111
|
+
name: ci-result
|
|
112
|
+
needs: [lint, format-check, style, typecheck, build, storybook]
|
|
113
|
+
if: ${{ !cancelled() }}
|
|
114
|
+
runs-on: ubuntu-latest
|
|
115
|
+
permissions: {}
|
|
116
|
+
steps:
|
|
117
|
+
- name: Aggregate CI results
|
|
118
|
+
env:
|
|
119
|
+
NEEDS: ${{ toJson(needs) }}
|
|
120
|
+
run: |
|
|
121
|
+
echo "$NEEDS" | jq -r 'to_entries[] | "\(.key)\t\(.value.result)"'
|
|
122
|
+
failed=$(echo "$NEEDS" | jq -r '
|
|
123
|
+
to_entries[]
|
|
124
|
+
| select(.value.result == "failure" or .value.result == "cancelled")
|
|
125
|
+
| .key')
|
|
126
|
+
if [ -n "$failed" ]; then
|
|
127
|
+
echo "::error::Failed or cancelled jobs: $failed"
|
|
128
|
+
exit 1
|
|
129
|
+
fi
|
|
130
|
+
echo "All required CI jobs passed (or were skipped)."
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
name: SAST
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
concurrency:
|
|
13
|
+
group: sast-${{ github.ref }}
|
|
14
|
+
cancel-in-progress: true
|
|
15
|
+
|
|
16
|
+
# Static Application Security Testing via Semgrep. Scans first-party
|
|
17
|
+
# source against TypeScript, JavaScript, React, and OWASP top-ten
|
|
18
|
+
# rulesets. Findings fail the build — silence false positives with
|
|
19
|
+
# `.semgrepignore` (path-level) or inline `// nosemgrep: <rule-id>` with
|
|
20
|
+
# a justification.
|
|
21
|
+
|
|
22
|
+
jobs:
|
|
23
|
+
semgrep:
|
|
24
|
+
name: Semgrep
|
|
25
|
+
runs-on: ubuntu-latest
|
|
26
|
+
timeout-minutes: 10
|
|
27
|
+
if: github.actor != 'dependabot[bot]'
|
|
28
|
+
container:
|
|
29
|
+
image: semgrep/semgrep:1.168.0@sha256:59fbed6127ea7c5dde3ba6a85142733bb20ea9aaa36120c953904f1539aaf66e
|
|
30
|
+
steps:
|
|
31
|
+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
32
|
+
with:
|
|
33
|
+
persist-credentials: false
|
|
34
|
+
- name: Run Semgrep
|
|
35
|
+
run: |
|
|
36
|
+
semgrep scan \
|
|
37
|
+
--config=p/typescript \
|
|
38
|
+
--config=p/javascript \
|
|
39
|
+
--config=p/react \
|
|
40
|
+
--config=p/owasp-top-ten \
|
|
41
|
+
--error \
|
|
42
|
+
--metrics=off
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
name: Security
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
concurrency:
|
|
13
|
+
group: security-${{ github.ref }}
|
|
14
|
+
cancel-in-progress: true
|
|
15
|
+
|
|
16
|
+
# Audits production dependencies for known vulnerabilities. Fails the
|
|
17
|
+
# build on high or critical severity findings; moderate and low findings
|
|
18
|
+
# appear in the log but don't block merges.
|
|
19
|
+
|
|
20
|
+
jobs:
|
|
21
|
+
audit:
|
|
22
|
+
name: pnpm audit
|
|
23
|
+
runs-on: ubuntu-latest
|
|
24
|
+
timeout-minutes: 5
|
|
25
|
+
steps:
|
|
26
|
+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
27
|
+
with:
|
|
28
|
+
persist-credentials: false
|
|
29
|
+
- uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
|
|
30
|
+
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
|
31
|
+
with: { node-version: 24, cache: pnpm }
|
|
32
|
+
- run: pnpm install --frozen-lockfile
|
|
33
|
+
- name: Audit production dependencies
|
|
34
|
+
run: pnpm audit --audit-level=high --prod
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
name: Zizmor
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
paths: [".github/**"]
|
|
7
|
+
pull_request:
|
|
8
|
+
branches: [main]
|
|
9
|
+
paths: [".github/**"]
|
|
10
|
+
|
|
11
|
+
permissions: {}
|
|
12
|
+
|
|
13
|
+
concurrency:
|
|
14
|
+
group: zizmor-${{ github.ref }}
|
|
15
|
+
cancel-in-progress: true
|
|
16
|
+
|
|
17
|
+
# Static security analysis of this app's GitHub Actions workflows
|
|
18
|
+
# (template-injection, unpinned actions, excessive token permissions,
|
|
19
|
+
# credential persistence). Free runner-hosted SAST — no GitHub Advanced
|
|
20
|
+
# Security needed. Findings surface as inline PR annotations. Starts at
|
|
21
|
+
# min-severity: high; lower to medium/low once your workflows are clean.
|
|
22
|
+
|
|
23
|
+
jobs:
|
|
24
|
+
zizmor:
|
|
25
|
+
name: GitHub Actions security (zizmor)
|
|
26
|
+
runs-on: ubuntu-latest
|
|
27
|
+
timeout-minutes: 10
|
|
28
|
+
permissions:
|
|
29
|
+
contents: read
|
|
30
|
+
actions: read
|
|
31
|
+
steps:
|
|
32
|
+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
33
|
+
with:
|
|
34
|
+
persist-credentials: false
|
|
35
|
+
- uses: zizmorcore/zizmor-action@5f14fd08f7cf1cb1609c1e344975f152c7ee938d # v0.5.6
|
|
36
|
+
with:
|
|
37
|
+
min-severity: high
|
|
38
|
+
min-confidence: medium
|
|
39
|
+
# This app's repo may not have GitHub Advanced Security; disable the
|
|
40
|
+
# SARIF/code-scanning upload (mutually exclusive with annotations) and
|
|
41
|
+
# surface findings as inline PR annotations instead.
|
|
42
|
+
advanced-security: false
|
|
43
|
+
annotations: true
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
"@raredays/components": "^0.1.0",
|
|
26
26
|
"@raredays/core": "^0.1.0",
|
|
27
27
|
"@raredays/style": "^0.1.0",
|
|
28
|
+
"lucide-react": "1.14.0",
|
|
28
29
|
"next": "16.2.4",
|
|
29
30
|
"react": "19.2.5",
|
|
30
31
|
"react-dom": "19.2.5"
|
|
@@ -49,5 +50,6 @@
|
|
|
49
50
|
},
|
|
50
51
|
"engines": {
|
|
51
52
|
"node": ">=24"
|
|
52
|
-
}
|
|
53
|
+
},
|
|
54
|
+
"packageManager": "pnpm@11.9.0"
|
|
53
55
|
}
|