@girardmedia/bootspring 3.3.2 → 3.4.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 (171) hide show
  1. package/assets/agents/accessibility-auditor.md +39 -0
  2. package/assets/agents/api-designer.md +40 -0
  3. package/assets/agents/auth-implementer.md +64 -0
  4. package/assets/agents/bug-hunter.md +42 -0
  5. package/assets/agents/bundle-analyzer.md +40 -0
  6. package/assets/agents/cache-optimizer.md +55 -0
  7. package/assets/agents/changelog-writer.md +55 -0
  8. package/assets/agents/ci-cd-builder.md +40 -0
  9. package/assets/agents/code-explainer.md +39 -0
  10. package/assets/agents/code-reviewer.md +39 -0
  11. package/assets/agents/cost-optimizer.md +57 -0
  12. package/assets/agents/cron-scheduler.md +51 -0
  13. package/assets/agents/data-seeder.md +56 -0
  14. package/assets/agents/database-architect.md +40 -0
  15. package/assets/agents/dependency-updater.md +40 -0
  16. package/assets/agents/deploy-checker.md +40 -0
  17. package/assets/agents/docker-optimizer.md +40 -0
  18. package/assets/agents/documentation-writer.md +40 -0
  19. package/assets/agents/email-builder.md +55 -0
  20. package/assets/agents/env-setup.md +40 -0
  21. package/assets/agents/error-handler.md +40 -0
  22. package/assets/agents/eslint-fixer.md +46 -0
  23. package/assets/agents/feature-flagger.md +69 -0
  24. package/assets/agents/git-detective.md +39 -0
  25. package/assets/agents/graphql-builder.md +60 -0
  26. package/assets/agents/incident-responder.md +59 -0
  27. package/assets/agents/log-analyzer.md +39 -0
  28. package/assets/agents/migration-planner.md +41 -0
  29. package/assets/agents/monorepo-navigator.md +39 -0
  30. package/assets/agents/nextjs-expert.md +57 -0
  31. package/assets/agents/notification-builder.md +56 -0
  32. package/assets/agents/onboarding-guide.md +39 -0
  33. package/assets/agents/performance-profiler.md +40 -0
  34. package/assets/agents/prisma-expert.md +57 -0
  35. package/assets/agents/rate-limiter.md +58 -0
  36. package/assets/agents/react-expert.md +58 -0
  37. package/assets/agents/refactorer.md +42 -0
  38. package/assets/agents/regex-builder.md +46 -0
  39. package/assets/agents/release-manager.md +40 -0
  40. package/assets/agents/s3-manager.md +58 -0
  41. package/assets/agents/schema-validator.md +40 -0
  42. package/assets/agents/search-builder.md +62 -0
  43. package/assets/agents/security-auditor.md +39 -0
  44. package/assets/agents/sitemap-generator.md +53 -0
  45. package/assets/agents/stripe-integrator.md +59 -0
  46. package/assets/agents/tailwind-expert.md +55 -0
  47. package/assets/agents/tech-debt-tracker.md +39 -0
  48. package/assets/agents/test-writer.md +42 -0
  49. package/assets/agents/type-fixer.md +45 -0
  50. package/assets/agents/webhook-builder.md +54 -0
  51. package/assets/rules/cpp.md +53 -0
  52. package/assets/rules/css.md +52 -0
  53. package/assets/rules/go.md +50 -0
  54. package/assets/rules/html.md +52 -0
  55. package/assets/rules/java.md +51 -0
  56. package/assets/rules/kotlin.md +50 -0
  57. package/assets/rules/php.md +51 -0
  58. package/assets/rules/python.md +51 -0
  59. package/assets/rules/ruby.md +51 -0
  60. package/assets/rules/rust.md +49 -0
  61. package/assets/rules/shell.md +52 -0
  62. package/assets/rules/sql.md +49 -0
  63. package/assets/rules/swift.md +50 -0
  64. package/assets/rules/typescript.md +52 -0
  65. package/assets/rules/yaml-json.md +51 -0
  66. package/assets/skills/accessibility.md +210 -0
  67. package/assets/skills/agent-patterns.md +387 -0
  68. package/assets/skills/ai-integration.md +263 -0
  69. package/assets/skills/animation-patterns.md +224 -0
  70. package/assets/skills/api-design.md +218 -0
  71. package/assets/skills/api-gateway.md +341 -0
  72. package/assets/skills/api-versioning.md +226 -0
  73. package/assets/skills/astro-patterns.md +233 -0
  74. package/assets/skills/auth-patterns.md +248 -0
  75. package/assets/skills/aws-patterns.md +171 -0
  76. package/assets/skills/background-jobs.md +162 -0
  77. package/assets/skills/browser-extensions.md +309 -0
  78. package/assets/skills/caching-patterns.md +253 -0
  79. package/assets/skills/ci-cd.md +251 -0
  80. package/assets/skills/cli-development.md +296 -0
  81. package/assets/skills/code-review.md +185 -0
  82. package/assets/skills/cron-patterns.md +327 -0
  83. package/assets/skills/data-fetching.md +231 -0
  84. package/assets/skills/database-migrations.md +346 -0
  85. package/assets/skills/database-patterns.md +219 -0
  86. package/assets/skills/debugging.md +281 -0
  87. package/assets/skills/design-system.md +289 -0
  88. package/assets/skills/django-patterns.md +182 -0
  89. package/assets/skills/docker-patterns.md +235 -0
  90. package/assets/skills/e2e-testing.md +287 -0
  91. package/assets/skills/edge-computing.md +268 -0
  92. package/assets/skills/electron-patterns.md +266 -0
  93. package/assets/skills/email-templates.md +206 -0
  94. package/assets/skills/error-handling.md +265 -0
  95. package/assets/skills/event-driven.md +232 -0
  96. package/assets/skills/express-patterns.md +239 -0
  97. package/assets/skills/fastapi-patterns.md +198 -0
  98. package/assets/skills/feature-flags.md +212 -0
  99. package/assets/skills/figma-to-code.md +298 -0
  100. package/assets/skills/file-upload.md +228 -0
  101. package/assets/skills/forms-patterns.md +264 -0
  102. package/assets/skills/gcp-patterns.md +189 -0
  103. package/assets/skills/git-workflow.md +187 -0
  104. package/assets/skills/golang-patterns.md +185 -0
  105. package/assets/skills/graphql-patterns.md +244 -0
  106. package/assets/skills/i18n-patterns.md +172 -0
  107. package/assets/skills/image-processing.md +350 -0
  108. package/assets/skills/java-springboot.md +226 -0
  109. package/assets/skills/kotlin-patterns.md +207 -0
  110. package/assets/skills/kubernetes-patterns.md +326 -0
  111. package/assets/skills/laravel-patterns.md +261 -0
  112. package/assets/skills/llm-fine-tuning.md +335 -0
  113. package/assets/skills/load-testing.md +303 -0
  114. package/assets/skills/logging-observability.md +228 -0
  115. package/assets/skills/markdown-processing.md +318 -0
  116. package/assets/skills/mcp-server-patterns.md +292 -0
  117. package/assets/skills/microservices.md +272 -0
  118. package/assets/skills/migration-patterns.md +239 -0
  119. package/assets/skills/mongodb-patterns.md +189 -0
  120. package/assets/skills/monorepo-patterns.md +287 -0
  121. package/assets/skills/nextjs-app-router.md +237 -0
  122. package/assets/skills/notification-patterns.md +348 -0
  123. package/assets/skills/oauth-patterns.md +246 -0
  124. package/assets/skills/payment-integration.md +222 -0
  125. package/assets/skills/pdf-generation.md +307 -0
  126. package/assets/skills/performance-optimization.md +277 -0
  127. package/assets/skills/php-patterns.md +210 -0
  128. package/assets/skills/prisma-patterns.md +241 -0
  129. package/assets/skills/prompt-engineering.md +193 -0
  130. package/assets/skills/pwa-patterns.md +247 -0
  131. package/assets/skills/python-patterns.md +158 -0
  132. package/assets/skills/python-testing.md +172 -0
  133. package/assets/skills/queue-patterns.md +295 -0
  134. package/assets/skills/rag-patterns.md +159 -0
  135. package/assets/skills/rate-limiting.md +319 -0
  136. package/assets/skills/react-components.md +201 -0
  137. package/assets/skills/react-native-patterns.md +299 -0
  138. package/assets/skills/real-time-patterns.md +181 -0
  139. package/assets/skills/redis-patterns.md +188 -0
  140. package/assets/skills/refactoring.md +218 -0
  141. package/assets/skills/regex-patterns.md +191 -0
  142. package/assets/skills/remix-patterns.md +262 -0
  143. package/assets/skills/responsive-design.md +199 -0
  144. package/assets/skills/ruby-rails-patterns.md +178 -0
  145. package/assets/skills/rust-patterns.md +211 -0
  146. package/assets/skills/search-patterns.md +227 -0
  147. package/assets/skills/security-hardening.md +237 -0
  148. package/assets/skills/seo-patterns.md +179 -0
  149. package/assets/skills/serverless-patterns.md +223 -0
  150. package/assets/skills/sql-optimization.md +154 -0
  151. package/assets/skills/state-management.md +254 -0
  152. package/assets/skills/storybook-patterns.md +330 -0
  153. package/assets/skills/svelte-patterns.md +258 -0
  154. package/assets/skills/swift-patterns.md +227 -0
  155. package/assets/skills/tailwind-patterns.md +272 -0
  156. package/assets/skills/tdd-workflow.md +199 -0
  157. package/assets/skills/terraform-patterns.md +270 -0
  158. package/assets/skills/testing-react.md +240 -0
  159. package/assets/skills/testing-vitest.md +232 -0
  160. package/assets/skills/typescript-strict.md +159 -0
  161. package/assets/skills/video-processing.md +340 -0
  162. package/assets/skills/vue-patterns.md +247 -0
  163. package/assets/skills/web-workers.md +327 -0
  164. package/assets/skills/webhooks-patterns.md +283 -0
  165. package/assets/skills/websocket-patterns.md +306 -0
  166. package/dist/cli/index.js +941 -958
  167. package/dist/core/index.d.ts +341 -11
  168. package/dist/core.js +58 -95
  169. package/dist/mcp/index.d.ts +33 -1
  170. package/dist/mcp-server.js +177 -255
  171. package/package.json +4 -1
@@ -0,0 +1,253 @@
1
+ ---
2
+ name: caching-patterns
3
+ description: Apply caching at every layer — HTTP headers, Redis, stale-while-revalidate, CDN, and in-memory memoization.
4
+ ---
5
+
6
+ # Caching Patterns
7
+
8
+ ## When to Use
9
+
10
+ Apply caching when you need to reduce latency, lower database load, or decrease
11
+ API costs. Cache at the layer closest to the consumer: browser cache for static
12
+ assets, CDN for public pages, Redis for shared application state, in-memory for
13
+ hot function results. Every cache decision requires answering: how stale is
14
+ acceptable, and how do you invalidate?
15
+
16
+ ## How It Works
17
+
18
+ ### 1. HTTP Cache Headers
19
+
20
+ Control browser and CDN caching with response headers.
21
+
22
+ ```typescript
23
+ // Static assets (CSS, JS, images) — cache forever, bust with filename hash
24
+ app.use('/assets', express.static('public', {
25
+ maxAge: '1y',
26
+ immutable: true,
27
+ }));
28
+ // Cache-Control: public, max-age=31536000, immutable
29
+
30
+ // API responses — short cache, revalidate
31
+ app.get('/api/products', (req, res) => {
32
+ res.set('Cache-Control', 'public, max-age=60, s-maxage=300');
33
+ res.set('ETag', computeETag(products));
34
+ res.json(products);
35
+ });
36
+
37
+ // Private user data — never cache in shared caches
38
+ app.get('/api/me', authenticate, (req, res) => {
39
+ res.set('Cache-Control', 'private, no-store');
40
+ res.json(req.user);
41
+ });
42
+ ```
43
+
44
+ Key headers:
45
+ | Header | Purpose |
46
+ |--------|---------|
47
+ | `max-age=N` | Browser caches for N seconds |
48
+ | `s-maxage=N` | CDN/proxy caches for N seconds |
49
+ | `no-store` | Never cache (auth responses, sensitive data) |
50
+ | `stale-while-revalidate=N` | Serve stale while fetching fresh in background |
51
+ | `ETag` + `If-None-Match` | Conditional requests — 304 if unchanged |
52
+
53
+ ### 2. Redis Caching
54
+
55
+ Shared cache across multiple server instances.
56
+
57
+ ```typescript
58
+ import Redis from 'ioredis';
59
+
60
+ const redis = new Redis(process.env.REDIS_URL);
61
+
62
+ async function cachedQuery<T>(
63
+ key: string,
64
+ ttlSeconds: number,
65
+ fetcher: () => Promise<T>,
66
+ ): Promise<T> {
67
+ const cached = await redis.get(key);
68
+ if (cached) return JSON.parse(cached) as T;
69
+
70
+ const fresh = await fetcher();
71
+ await redis.set(key, JSON.stringify(fresh), 'EX', ttlSeconds);
72
+ return fresh;
73
+ }
74
+
75
+ // Usage
76
+ const products = await cachedQuery(
77
+ 'products:featured',
78
+ 300, // 5 minutes
79
+ () => db.product.findMany({ where: { featured: true } }),
80
+ );
81
+ ```
82
+
83
+ ### 3. Stale-While-Revalidate
84
+
85
+ Serve cached data immediately, refresh in the background.
86
+
87
+ ```typescript
88
+ async function swr<T>(
89
+ key: string,
90
+ ttlSeconds: number,
91
+ staleSeconds: number,
92
+ fetcher: () => Promise<T>,
93
+ ): Promise<T> {
94
+ const raw = await redis.get(key);
95
+
96
+ if (raw) {
97
+ const { data, timestamp } = JSON.parse(raw);
98
+ const age = (Date.now() - timestamp) / 1000;
99
+
100
+ if (age < ttlSeconds) {
101
+ return data as T; // fresh
102
+ }
103
+
104
+ if (age < ttlSeconds + staleSeconds) {
105
+ // Stale — return immediately but refresh in background
106
+ fetcher().then(async (fresh) => {
107
+ await redis.set(key, JSON.stringify({ data: fresh, timestamp: Date.now() }));
108
+ });
109
+ return data as T;
110
+ }
111
+ }
112
+
113
+ // Expired or miss — fetch synchronously
114
+ const fresh = await fetcher();
115
+ await redis.set(key, JSON.stringify({ data: fresh, timestamp: Date.now() }));
116
+ return fresh;
117
+ }
118
+ ```
119
+
120
+ ### 4. Cache Invalidation
121
+
122
+ The hardest problem. Three strategies:
123
+
124
+ **Time-based (TTL):** simplest, eventual consistency.
125
+
126
+ ```typescript
127
+ await redis.set('user:123', JSON.stringify(user), 'EX', 60);
128
+ ```
129
+
130
+ **Event-based:** invalidate on mutation.
131
+
132
+ ```typescript
133
+ async function updateUser(id: string, data: UpdateInput) {
134
+ const user = await db.user.update({ where: { id }, data });
135
+ await redis.del(`user:${id}`); // exact key
136
+ await redis.del('users:list'); // invalidate list
137
+ return user;
138
+ }
139
+ ```
140
+
141
+ **Tag-based:** group related keys for bulk invalidation.
142
+
143
+ ```typescript
144
+ async function setWithTags(key: string, value: string, tags: string[], ttl: number) {
145
+ await redis.set(key, value, 'EX', ttl);
146
+ for (const tag of tags) {
147
+ await redis.sadd(`tag:${tag}`, key);
148
+ }
149
+ }
150
+
151
+ async function invalidateTag(tag: string) {
152
+ const keys = await redis.smembers(`tag:${tag}`);
153
+ if (keys.length > 0) {
154
+ await redis.del(...keys);
155
+ }
156
+ await redis.del(`tag:${tag}`);
157
+ }
158
+
159
+ // Usage
160
+ await setWithTags('product:42', data, ['products', 'featured'], 300);
161
+ await invalidateTag('featured'); // clears all featured product caches
162
+ ```
163
+
164
+ ### 5. CDN Caching
165
+
166
+ Use `s-maxage` and `Surrogate-Key` headers for CDN-level control.
167
+
168
+ ```typescript
169
+ app.get('/blog/:slug', async (req, res) => {
170
+ const post = await getPost(req.params.slug);
171
+ res.set('Cache-Control', 'public, s-maxage=3600, stale-while-revalidate=86400');
172
+ res.set('Surrogate-Key', `post-${post.id} blog`);
173
+ res.json(post);
174
+ });
175
+
176
+ // Purge on update (Fastly/Cloudflare API)
177
+ async function purgePost(postId: string) {
178
+ await cdnClient.purgeTag(`post-${postId}`);
179
+ }
180
+ ```
181
+
182
+ ### 6. In-Memory Memoization
183
+
184
+ For expensive pure computations within a single process.
185
+
186
+ ```typescript
187
+ function memoize<Args extends unknown[], Result>(
188
+ fn: (...args: Args) => Result,
189
+ keyFn: (...args: Args) => string = (...args) => JSON.stringify(args),
190
+ maxSize: number = 1000,
191
+ ): (...args: Args) => Result {
192
+ const cache = new Map<string, Result>();
193
+
194
+ return (...args: Args): Result => {
195
+ const key = keyFn(...args);
196
+ if (cache.has(key)) return cache.get(key)!;
197
+
198
+ const result = fn(...args);
199
+
200
+ if (cache.size >= maxSize) {
201
+ const firstKey = cache.keys().next().value!;
202
+ cache.delete(firstKey); // evict oldest
203
+ }
204
+
205
+ cache.set(key, result);
206
+ return result;
207
+ };
208
+ }
209
+
210
+ const computeScore = memoize(
211
+ (userId: string, period: string) => expensiveCalculation(userId, period),
212
+ );
213
+ ```
214
+
215
+ ### 7. Cache Stampede Prevention
216
+
217
+ When cache expires and 100 requests hit the database simultaneously.
218
+
219
+ ```typescript
220
+ const locks = new Map<string, Promise<unknown>>();
221
+
222
+ async function singleFlight<T>(key: string, fetcher: () => Promise<T>): Promise<T> {
223
+ const existing = locks.get(key);
224
+ if (existing) return existing as Promise<T>;
225
+
226
+ const promise = fetcher().finally(() => locks.delete(key));
227
+ locks.set(key, promise);
228
+ return promise;
229
+ }
230
+
231
+ // All concurrent callers share one fetch
232
+ const data = await singleFlight('expensive-query', () => db.query(...));
233
+ ```
234
+
235
+ ## Examples
236
+
237
+ | Layer | TTL | Invalidation | Use case |
238
+ |-------|-----|-------------|----------|
239
+ | Browser | 1 year | Filename hash | Static assets |
240
+ | CDN | 1 hour | Surrogate key purge | Blog posts, marketing pages |
241
+ | Redis | 5 min | Event-based `DEL` | API list endpoints |
242
+ | In-memory | Process lifetime | LRU eviction | Parsed configs, computed scores |
243
+
244
+ ## Checklist
245
+
246
+ - [ ] Static assets use long `max-age` with content hashes in filenames
247
+ - [ ] Private data responses include `Cache-Control: private, no-store`
248
+ - [ ] Redis cache uses consistent key naming (`entity:id:subresource`)
249
+ - [ ] Every cache write has an explicit TTL — no indefinite caching
250
+ - [ ] Mutations invalidate related cache keys
251
+ - [ ] SWR pattern is used for data where slight staleness is acceptable
252
+ - [ ] Cache stampede is prevented with single-flight or mutex
253
+ - [ ] In-memory caches have a max size to prevent unbounded growth
@@ -0,0 +1,251 @@
1
+ ---
2
+ name: ci-cd
3
+ description: Build reliable CI/CD pipelines with GitHub Actions — caching, matrix builds, deploy gates, and rollbacks.
4
+ ---
5
+
6
+ # CI/CD Patterns
7
+
8
+ ## When to Use
9
+
10
+ Set up CI/CD from the first commit. Every merged PR should be automatically
11
+ tested, and every release should flow through a pipeline that catches problems
12
+ before users do. These patterns focus on GitHub Actions but the principles
13
+ apply to any CI system.
14
+
15
+ ## How It Works
16
+
17
+ ### 1. Basic Workflow Structure
18
+
19
+ ```yaml
20
+ # .github/workflows/ci.yml
21
+ name: CI
22
+
23
+ on:
24
+ push:
25
+ branches: [main]
26
+ pull_request:
27
+ branches: [main]
28
+
29
+ concurrency:
30
+ group: ci-${{ github.ref }}
31
+ cancel-in-progress: true # cancel stale PR runs
32
+
33
+ jobs:
34
+ lint:
35
+ runs-on: ubuntu-latest
36
+ steps:
37
+ - uses: actions/checkout@v4
38
+ - uses: actions/setup-node@v4
39
+ with:
40
+ node-version: 20
41
+ cache: npm
42
+ - run: npm ci
43
+ - run: npm run lint
44
+
45
+ typecheck:
46
+ runs-on: ubuntu-latest
47
+ steps:
48
+ - uses: actions/checkout@v4
49
+ - uses: actions/setup-node@v4
50
+ with: { node-version: 20, cache: npm }
51
+ - run: npm ci
52
+ - run: npx tsc --noEmit
53
+
54
+ test:
55
+ runs-on: ubuntu-latest
56
+ steps:
57
+ - uses: actions/checkout@v4
58
+ - uses: actions/setup-node@v4
59
+ with: { node-version: 20, cache: npm }
60
+ - run: npm ci
61
+ - run: npm test -- --coverage
62
+ - uses: actions/upload-artifact@v4
63
+ with:
64
+ name: coverage
65
+ path: coverage/lcov.info
66
+ ```
67
+
68
+ ### 2. Matrix Builds
69
+
70
+ Test across multiple Node versions and operating systems.
71
+
72
+ ```yaml
73
+ test:
74
+ strategy:
75
+ fail-fast: false # don't cancel others when one fails
76
+ matrix:
77
+ node-version: [18, 20, 22]
78
+ os: [ubuntu-latest, macos-latest]
79
+ runs-on: ${{ matrix.os }}
80
+ steps:
81
+ - uses: actions/checkout@v4
82
+ - uses: actions/setup-node@v4
83
+ with:
84
+ node-version: ${{ matrix.node-version }}
85
+ cache: npm
86
+ - run: npm ci
87
+ - run: npm test
88
+ ```
89
+
90
+ ### 3. Dependency Caching
91
+
92
+ Cache `node_modules` and build artifacts to cut install time from minutes to
93
+ seconds.
94
+
95
+ ```yaml
96
+ - uses: actions/setup-node@v4
97
+ with:
98
+ node-version: 20
99
+ cache: npm # built-in npm cache
100
+
101
+ # For monorepos with turbo
102
+ - uses: actions/cache@v4
103
+ with:
104
+ path: node_modules/.cache/turbo
105
+ key: turbo-${{ runner.os }}-${{ hashFiles('**/turbo.json') }}
106
+ restore-keys: turbo-${{ runner.os }}-
107
+
108
+ # For Docker layer caching
109
+ - uses: docker/build-push-action@v5
110
+ with:
111
+ cache-from: type=gha
112
+ cache-to: type=gha,mode=max
113
+ ```
114
+
115
+ ### 4. Deploy Gates
116
+
117
+ Require all checks to pass before deploy. Use environments for approval flows.
118
+
119
+ ```yaml
120
+ deploy-staging:
121
+ needs: [lint, typecheck, test]
122
+ runs-on: ubuntu-latest
123
+ environment: staging # auto-deploys
124
+ steps:
125
+ - uses: actions/checkout@v4
126
+ - run: ./scripts/deploy.sh staging
127
+
128
+ deploy-production:
129
+ needs: [deploy-staging]
130
+ runs-on: ubuntu-latest
131
+ environment: production # requires manual approval
132
+ if: github.ref == 'refs/heads/main'
133
+ steps:
134
+ - uses: actions/checkout@v4
135
+ - run: ./scripts/deploy.sh production
136
+ ```
137
+
138
+ Configure environment protection rules in GitHub:
139
+ - Required reviewers (1-2 people)
140
+ - Wait timer (e.g., 15 minutes after staging deploy)
141
+ - Branch restrictions (only `main`)
142
+
143
+ ### 5. Environment Secrets
144
+
145
+ ```yaml
146
+ env:
147
+ NODE_ENV: production
148
+
149
+ steps:
150
+ - run: npm run deploy
151
+ env:
152
+ DATABASE_URL: ${{ secrets.DATABASE_URL }}
153
+ API_KEY: ${{ secrets.API_KEY }}
154
+ ```
155
+
156
+ Rules:
157
+ - Never echo secrets in logs. GitHub auto-redacts known secrets but custom
158
+ values can leak.
159
+ - Use environment-scoped secrets (`staging` vs `production`)
160
+ - Rotate secrets quarterly. Use OIDC for cloud providers when possible.
161
+
162
+ ### 6. Rollback Strategy
163
+
164
+ ```yaml
165
+ deploy-production:
166
+ steps:
167
+ - name: Deploy
168
+ id: deploy
169
+ run: ./scripts/deploy.sh production
170
+ continue-on-error: true
171
+
172
+ - name: Smoke test
173
+ id: smoke
174
+ run: ./scripts/smoke-test.sh ${{ vars.PRODUCTION_URL }}
175
+
176
+ - name: Rollback on failure
177
+ if: steps.deploy.outcome == 'failure' || steps.smoke.outcome == 'failure'
178
+ run: ./scripts/rollback.sh production
179
+ ```
180
+
181
+ Alternative: blue-green deploys where the old version stays running until the
182
+ new one passes health checks.
183
+
184
+ ### 7. Reusable Workflows
185
+
186
+ Extract common jobs into shared workflows.
187
+
188
+ ```yaml
189
+ # .github/workflows/reusable-test.yml
190
+ on:
191
+ workflow_call:
192
+ inputs:
193
+ node-version:
194
+ type: string
195
+ default: '20'
196
+
197
+ jobs:
198
+ test:
199
+ runs-on: ubuntu-latest
200
+ steps:
201
+ - uses: actions/checkout@v4
202
+ - uses: actions/setup-node@v4
203
+ with:
204
+ node-version: ${{ inputs.node-version }}
205
+ cache: npm
206
+ - run: npm ci
207
+ - run: npm test
208
+ ```
209
+
210
+ ```yaml
211
+ # .github/workflows/ci.yml
212
+ jobs:
213
+ test:
214
+ uses: ./.github/workflows/reusable-test.yml
215
+ with:
216
+ node-version: '20'
217
+ ```
218
+
219
+ ### 8. PR Annotations
220
+
221
+ Post test results and coverage directly on PRs.
222
+
223
+ ```yaml
224
+ - name: Report coverage
225
+ uses: davelosert/vitest-coverage-report-action@v2
226
+ if: github.event_name == 'pull_request'
227
+ with:
228
+ vite-config-path: vitest.config.ts
229
+ ```
230
+
231
+ ## Examples
232
+
233
+ | Scenario | Pattern |
234
+ |----------|---------|
235
+ | Flaky tests blocking PRs | `fail-fast: false` + retry step |
236
+ | Slow installs | `actions/setup-node` with `cache: npm` |
237
+ | Multi-platform support | Matrix with `os` and `node-version` |
238
+ | Production safety | Environment protection + smoke tests + rollback |
239
+ | DRY pipeline config | Reusable workflows with `workflow_call` |
240
+
241
+ ## Checklist
242
+
243
+ - [ ] CI runs on every push and PR — lint, typecheck, and test in parallel
244
+ - [ ] `concurrency` cancels stale PR runs to save compute
245
+ - [ ] Dependency caching is configured (npm cache at minimum)
246
+ - [ ] Production deploys require staging success + manual approval
247
+ - [ ] Secrets are scoped to environments, not repository-wide
248
+ - [ ] Deploy step includes smoke test with automatic rollback on failure
249
+ - [ ] Matrix builds cover minimum and latest supported Node versions
250
+ - [ ] Reusable workflows extract duplicated job definitions
251
+ - [ ] No secrets are printed in logs (audit with `ACTIONS_STEP_DEBUG`)