@lousy-agents/cli 1.1.0 → 1.1.1

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 (30) hide show
  1. package/api/copilot-with-fastify/.devcontainer/devcontainer.json +90 -0
  2. package/api/copilot-with-fastify/.editorconfig +16 -0
  3. package/api/copilot-with-fastify/.github/ISSUE_TEMPLATE/feature-to-spec.yml +55 -0
  4. package/api/copilot-with-fastify/.github/copilot-instructions.md +387 -0
  5. package/api/copilot-with-fastify/.github/instructions/pipeline.instructions.md +149 -0
  6. package/api/copilot-with-fastify/.github/instructions/software-architecture.instructions.md +430 -0
  7. package/api/copilot-with-fastify/.github/instructions/spec.instructions.md +411 -0
  8. package/api/copilot-with-fastify/.github/instructions/test.instructions.md +268 -0
  9. package/api/copilot-with-fastify/.github/specs/README.md +84 -0
  10. package/api/copilot-with-fastify/.github/workflows/assign-copilot.yml +59 -0
  11. package/api/copilot-with-fastify/.github/workflows/ci.yml +88 -0
  12. package/api/copilot-with-fastify/.nvmrc +1 -0
  13. package/api/copilot-with-fastify/.vscode/extensions.json +14 -0
  14. package/api/copilot-with-fastify/.vscode/launch.json +30 -0
  15. package/api/copilot-with-fastify/.vscode/mcp.json +19 -0
  16. package/api/copilot-with-fastify/.yamllint +18 -0
  17. package/api/copilot-with-fastify/biome.json +31 -0
  18. package/api/copilot-with-fastify/package.json +37 -0
  19. package/api/copilot-with-fastify/tsconfig.json +34 -0
  20. package/api/copilot-with-fastify/vitest.config.ts +21 -0
  21. package/api/copilot-with-fastify/vitest.integration.config.ts +18 -0
  22. package/api/copilot-with-fastify/vitest.setup.ts +5 -0
  23. package/dist/commands/init.d.ts.map +1 -1
  24. package/dist/commands/init.js +41 -18
  25. package/dist/commands/init.js.map +1 -1
  26. package/dist/lib/config.d.ts +1 -1
  27. package/dist/lib/config.d.ts.map +1 -1
  28. package/dist/lib/config.js +177 -3
  29. package/dist/lib/config.js.map +1 -1
  30. package/package.json +4 -3
@@ -0,0 +1,90 @@
1
+ {
2
+ "name": "<%= it.projectName %>",
3
+ "image": "mcr.microsoft.com/devcontainers/javascript-node:24-bookworm",
4
+ "features": {
5
+ "ghcr.io/devcontainers/features/github-cli:1": {
6
+ "version": "2.83.2"
7
+ },
8
+ "ghcr.io/rocker-org/devcontainer-features/apt-packages:1": {
9
+ "packages": "yamllint, shellcheck",
10
+ "upgradePackages": true
11
+ },
12
+ "ghcr.io/devcontainers/features/docker-outside-of-docker:1": {
13
+ "version": "27.5.1"
14
+ },
15
+ "ghcr.io/devcontainers-extra/features/actionlint:1": {
16
+ "version": "1.7.9"
17
+ }
18
+ },
19
+ "forwardPorts": [3000, 5432],
20
+ "portsAttributes": {
21
+ "3000": {
22
+ "label": "API Server",
23
+ "onAutoForward": "notify"
24
+ },
25
+ "5432": {
26
+ "label": "PostgreSQL",
27
+ "onAutoForward": "silent"
28
+ }
29
+ },
30
+ "postCreateCommand": "npm cache add @upstash/context7-mcp @modelcontextprotocol/server-sequential-thinking",
31
+ "waitFor": "postCreateCommand",
32
+ "remoteUser": "node",
33
+ "customizations": {
34
+ "vscode": {
35
+ "extensions": [
36
+ "biomejs.biome",
37
+ "editorconfig.editorconfig",
38
+ "vitest.explorer",
39
+ "ms-vscode-remote.remote-containers",
40
+ "GitHub.codespaces",
41
+ "GitHub.Copilot",
42
+ "GitHub.Copilot-Chat",
43
+ "GitHub.github-vscode-theme",
44
+ "redhat.vscode-yaml",
45
+ "ms-azuretools.vscode-docker"
46
+ ],
47
+ "settings": {
48
+ "editor.formatOnSave": true,
49
+ "editor.defaultFormatter": "biomejs.biome",
50
+ "[json]": {
51
+ "editor.defaultFormatter": "biomejs.biome"
52
+ },
53
+ "[javascript]": {
54
+ "editor.defaultFormatter": "biomejs.biome"
55
+ },
56
+ "[typescript]": {
57
+ "editor.defaultFormatter": "biomejs.biome"
58
+ },
59
+ "[yaml]": {
60
+ "editor.defaultFormatter": "redhat.vscode-yaml",
61
+ "editor.tabSize": 2
62
+ },
63
+ "vitest.enable": true,
64
+ "vitest.commandLine": "vitest",
65
+ "github.copilot.chat.agent.model": "claude-sonnet-4.5",
66
+ "github.copilot.chat.ask.model": "claude-sonnet-4.5",
67
+ "yaml.schemas": {
68
+ "https://json.schemastore.org/github-workflow.json": ".github/workflows/*.yml"
69
+ },
70
+ "github.copilot.chat.mcp.enabled": true
71
+ },
72
+ "mcp": {
73
+ "servers": {
74
+ "context7": {
75
+ "type": "stdio",
76
+ "command": "npx",
77
+ "args": ["-y", "@upstash/context7-mcp"]
78
+ },
79
+ "sequential-thinking": {
80
+ "command": "npx",
81
+ "args": [
82
+ "-y",
83
+ "@modelcontextprotocol/server-sequential-thinking"
84
+ ]
85
+ }
86
+ }
87
+ }
88
+ }
89
+ }
90
+ }
@@ -0,0 +1,16 @@
1
+ root = true
2
+
3
+ [*]
4
+ charset = utf-8
5
+ end_of_line = lf
6
+ indent_size = 4
7
+ indent_style = space
8
+ insert_final_newline = true
9
+ max_line_length = off
10
+ trim_trailing_whitespace = true
11
+
12
+ [{*.json}]
13
+ indent_size = 4
14
+
15
+ [{*.yaml,*.yml}]
16
+ indent_size = 2
@@ -0,0 +1,55 @@
1
+ ---
2
+ name: Copilot Feature To Spec
3
+ description: Create a spec for the Copilot coding agent.
4
+ title: "[Spec]: "
5
+ labels: ["copilot-ready", "enhancement"]
6
+ body:
7
+ - type: markdown
8
+ attributes:
9
+ value: |
10
+ ## Copilot Specification
11
+ Please fill out the details below.
12
+ This structure is parsed by the Copilot Agent.
13
+
14
+ - type: textarea
15
+ id: context
16
+ attributes:
17
+ label: Context & Goal
18
+ description: What is the goal? What part of the codebase?
19
+ placeholder: |
20
+ We need to add a rate-limiting middleware to the API
21
+ gateway...
22
+ validations:
23
+ required: true
24
+
25
+ - type: textarea
26
+ id: acceptance-criteria
27
+ attributes:
28
+ label: Acceptance Criteria
29
+ description: How will we verify this is done?
30
+ placeholder: |
31
+ I run `npm run dev`.
32
+ I call `POST /api/users` with valid data.
33
+ I see a 201 response with the created user.
34
+ validations:
35
+ required: true
36
+
37
+ # ---------------------------------------------------------
38
+ # This section triggers the GitHub Script integration
39
+ # ---------------------------------------------------------
40
+ - type: textarea
41
+ id: extra-instructions
42
+ attributes:
43
+ label: Extra Instructions
44
+ description: Specific prompt engineering for the agent. This will be posted as a comment to guide the bot.
45
+ value: |
46
+ 1. Review .github/instructions/spec.instructions.md for more details and use this as your guide for the feature specification.
47
+ 2. Ensure you review all other repository instructions before starting work to understand engineering requirements.
48
+ 3. Create your final spec output in the .github/specs directory and ensure requirements are captured in the EARS format.
49
+ 4. Create a data flow diagram and sequence diagram to illustrate the feature's operation.
50
+ 5. Break down the feature into manageable tasks with clear descriptions.
51
+ 6. As you complete each task in the spec, mark the checkboxes as complete in the spec file using [x] notation.
52
+ 7. If you need to make assumptions, ask questions on the issue.
53
+ 8. Use the diagrams to inform your implementation plan.
54
+ validations:
55
+ required: false
@@ -0,0 +1,387 @@
1
+ ---
2
+ applyTo: "**"
3
+ ---
4
+
5
+ # Fastify REST API Application
6
+
7
+ A Fastify TypeScript REST API following Test-Driven Development, Clean Architecture, and strict validation workflows with PostgreSQL database access via Kysely.
8
+
9
+ ## Commands
10
+
11
+ Run `nvm use` before any npm command. During development, use file-scoped commands for faster feedback, and run the full validation suite (`npx biome check && npm test && npm run build`) before commits.
12
+
13
+ ```bash
14
+ # ALWAYS run first
15
+ nvm use
16
+
17
+ # Core commands
18
+ npm install # Install deps (updates package-lock.json)
19
+ npm test # Run unit tests (vitest)
20
+ npm run test:integration # Run integration tests with Testcontainers
21
+ npm run build # Production build
22
+ npm run dev # Start development server with hot reload
23
+ npx biome check # Lint check
24
+ npx biome check --write # Auto-fix lint/format
25
+
26
+ # File-scoped (faster feedback)
27
+ npx biome check path/to/file.ts
28
+ npm test path/to/file.test.ts
29
+
30
+ # Validation suite (run before commits)
31
+ npx biome check && npm test && npm run build
32
+
33
+ # Other
34
+ npm audit # Security check
35
+ npm run lint:workflows # Validate GitHub Actions (actionlint)
36
+ npm run lint:yaml # Validate YAML (yamllint)
37
+ npm run db:migrate # Run database migrations
38
+ ```
39
+
40
+ ## Workflow: TDD Required
41
+
42
+ Follow this exact sequence for ALL code changes. Work in small increments — make one change at a time and validate before proceeding.
43
+
44
+ 1. **Research**: Search codebase for existing patterns, routes, utilities. Use Context7 MCP tools for library/API documentation.
45
+ 2. **Write failing test**: Create test describing desired behavior
46
+ 3. **Verify failure**: Run `npm test` — confirm clear failure message
47
+ 4. **Implement minimal code**: Write just enough to pass
48
+ 5. **Verify pass**: Run `npm test` — confirm pass
49
+ 6. **Refactor**: Clean up, remove duplication, keep tests green
50
+ 7. **Validate**: `npx biome check && npm test && npm run build`
51
+
52
+ Task is NOT complete until all validation passes.
53
+
54
+ ## Tech Stack
55
+
56
+ - **Framework**: Fastify — high performance, extensible Node.js web framework
57
+ - **Language**: TypeScript (strict mode)
58
+ - **Database**: PostgreSQL with Kysely (type-safe SQL query builder) + Postgres.js (driver)
59
+ - **Validation**: Zod for runtime validation of request/response data
60
+ - **Testing**: Vitest (never Jest), Testcontainers for database integration tests, Chance.js for test fixtures
61
+ - **Linting**: Biome (never ESLint/Prettier separately)
62
+ - **Logging**: Pino with JSON format and child loggers
63
+ - **HTTP**: fetch API only (for external service calls)
64
+ - **Architecture**: Clean Architecture principles
65
+
66
+ ## Project Structure
67
+
68
+ ```
69
+ .github/ GitHub Actions workflows
70
+ src/ Application source code
71
+ entities/ Layer 1: Business domain entities
72
+ use-cases/ Layer 2: Application business rules
73
+ gateways/ Layer 3: Database and external service adapters
74
+ routes/ Layer 3: Fastify route handlers
75
+ plugins/ Fastify plugins (auth, validation, etc.)
76
+ db/ Database configuration and migrations
77
+ lib/ Utilities and helpers
78
+ index.ts Application entry point
79
+ tests/ Test files (mirror src/ structure)
80
+ .nvmrc Node.js version (latest LTS)
81
+ ```
82
+
83
+ ## Code Style
84
+
85
+ ```typescript
86
+ import { z } from 'zod';
87
+ import type { FastifyRequest, FastifyReply } from 'fastify';
88
+
89
+ // Define schema for runtime validation
90
+ const UserSchema = z.object({
91
+ id: z.string().uuid(),
92
+ name: z.string().min(1),
93
+ email: z.string().email(),
94
+ });
95
+
96
+ type User = z.infer<typeof UserSchema>;
97
+
98
+ // ✅ Good - small, typed, single purpose, descriptive names, runtime validation
99
+ async function getUserById(
100
+ request: FastifyRequest<{ Params: { id: string } }>,
101
+ reply: FastifyReply
102
+ ): Promise<void> {
103
+ const { id } = request.params;
104
+
105
+ if (!id) {
106
+ return reply.badRequest('User ID required');
107
+ }
108
+
109
+ const user = await userRepository.findById(id);
110
+
111
+ if (!user) {
112
+ return reply.notFound(`User ${id} not found`);
113
+ }
114
+
115
+ return reply.send(UserSchema.parse(user));
116
+ }
117
+
118
+ // ❌ Bad - untyped, no validation, multiple responsibilities, no error handling
119
+ async function doStuff(req, reply) {
120
+ console.log('getting user');
121
+ const user = await db.query('SELECT * FROM users WHERE id = $1', [req.params.id]);
122
+ return user;
123
+ }
124
+ ```
125
+
126
+ **Rules:**
127
+ - Always use TypeScript type hints
128
+ - Use descriptive names for variables, functions, and modules
129
+ - Functions must be small and have single responsibility
130
+ - Avoid god functions and classes — break into smaller, focused units
131
+ - Avoid repetitive code — extract reusable functions
132
+ - Extract functions when there are multiple code paths
133
+ - Favor immutability and pure functions
134
+ - Avoid temporal coupling
135
+ - Keep cyclomatic complexity low
136
+ - Remove all unused imports and variables
137
+ - Validate external data at runtime with Zod — never use type assertions (`as Type`) on API responses
138
+ - Use Fastify's built-in reply methods for error responses
139
+ - Run lint and tests after EVERY change
140
+
141
+ ## Database Access with Kysely
142
+
143
+ Use Kysely for type-safe database queries. Never use raw SQL strings without Kysely's query builder.
144
+
145
+ ```typescript
146
+ import { Kysely, PostgresDialect } from 'kysely';
147
+ import postgres from 'postgres';
148
+
149
+ // Define database schema types
150
+ interface Database {
151
+ users: UserTable;
152
+ posts: PostTable;
153
+ }
154
+
155
+ interface UserTable {
156
+ id: string;
157
+ name: string;
158
+ email: string;
159
+ created_at: Date;
160
+ }
161
+
162
+ // Create Kysely instance
163
+ const sql = postgres({
164
+ host: process.env.DB_HOST,
165
+ port: Number(process.env.DB_PORT),
166
+ database: process.env.DB_NAME,
167
+ username: process.env.DB_USER,
168
+ password: process.env.DB_PASSWORD,
169
+ });
170
+
171
+ const db = new Kysely<Database>({
172
+ dialect: new PostgresDialect({ pool: sql }),
173
+ });
174
+
175
+ // ✅ Good - type-safe queries
176
+ async function findUserById(id: string): Promise<User | undefined> {
177
+ return db
178
+ .selectFrom('users')
179
+ .select(['id', 'name', 'email'])
180
+ .where('id', '=', id)
181
+ .executeTakeFirst();
182
+ }
183
+
184
+ // ✅ Good - type-safe inserts
185
+ async function createUser(user: Omit<User, 'id'>): Promise<User> {
186
+ return db
187
+ .insertInto('users')
188
+ .values({ ...user, id: crypto.randomUUID() })
189
+ .returningAll()
190
+ .executeTakeFirstOrThrow();
191
+ }
192
+
193
+ // ❌ Bad - raw SQL without type safety (SECURITY RISK: SQL injection vulnerability)
194
+ async function findUser(id: string) {
195
+ // NEVER use string interpolation in SQL - this is vulnerable to SQL injection attacks!
196
+ return db.raw(`SELECT * FROM users WHERE id = '${id}'`);
197
+ }
198
+ ```
199
+
200
+ ## Testing Standards
201
+
202
+ Tests are executable documentation. Use Arrange-Act-Assert pattern. Use Testcontainers for database integration tests. Generate test fixtures with Chance.js.
203
+
204
+ ### Unit Tests
205
+
206
+ ```typescript
207
+ import Chance from 'chance';
208
+ import { describe, it, expect, vi } from 'vitest';
209
+ import { createUserUseCase } from './create-user';
210
+
211
+ const chance = new Chance();
212
+
213
+ // ✅ Good - describes behavior, uses generated fixtures, mocks repository
214
+ describe('Create User Use Case', () => {
215
+ describe('given valid user data', () => {
216
+ it('creates the user and returns it', async () => {
217
+ // Arrange
218
+ const userData = {
219
+ name: chance.name(),
220
+ email: chance.email(),
221
+ };
222
+ const expectedUser = { id: chance.guid(), ...userData };
223
+ const mockRepository = {
224
+ create: vi.fn().mockResolvedValue(expectedUser),
225
+ findByEmail: vi.fn().mockResolvedValue(null),
226
+ };
227
+ const useCase = createUserUseCase(mockRepository);
228
+
229
+ // Act
230
+ const result = await useCase.execute(userData);
231
+
232
+ // Assert
233
+ expect(result).toEqual(expectedUser);
234
+ expect(mockRepository.create).toHaveBeenCalledWith(userData);
235
+ });
236
+ });
237
+
238
+ describe('given an email that already exists', () => {
239
+ it('throws a conflict error', async () => {
240
+ // Arrange
241
+ const existingEmail = chance.email();
242
+ const userData = { name: chance.name(), email: existingEmail };
243
+ const mockRepository = {
244
+ create: vi.fn(),
245
+ findByEmail: vi.fn().mockResolvedValue({ id: chance.guid(), ...userData }),
246
+ };
247
+ const useCase = createUserUseCase(mockRepository);
248
+
249
+ // Act & Assert
250
+ await expect(useCase.execute(userData)).rejects.toThrow('Email already exists');
251
+ });
252
+ });
253
+ });
254
+ ```
255
+
256
+ ### Integration Tests with Testcontainers
257
+
258
+ ```typescript
259
+ import { PostgreSqlContainer, StartedPostgreSqlContainer } from '@testcontainers/postgresql';
260
+ import { Kysely, PostgresDialect } from 'kysely';
261
+ import postgres from 'postgres';
262
+ import { afterAll, beforeAll, describe, it, expect } from 'vitest';
263
+ import { UserRepository } from './user-repository';
264
+
265
+ describe('User Repository Integration', () => {
266
+ let container: StartedPostgreSqlContainer;
267
+ let db: Kysely<Database>;
268
+ let repository: UserRepository;
269
+
270
+ beforeAll(async () => {
271
+ // Start PostgreSQL container
272
+ container = await new PostgreSqlContainer()
273
+ .withDatabase('test_db')
274
+ .start();
275
+
276
+ // Create database connection
277
+ const sql = postgres({
278
+ host: container.getHost(),
279
+ port: container.getPort(),
280
+ database: container.getDatabase(),
281
+ username: container.getUsername(),
282
+ password: container.getPassword(),
283
+ });
284
+
285
+ db = new Kysely<Database>({
286
+ dialect: new PostgresDialect({ pool: sql }),
287
+ });
288
+
289
+ // Run migrations
290
+ await runMigrations(db);
291
+
292
+ repository = new UserRepository(db);
293
+ }, 60000);
294
+
295
+ afterAll(async () => {
296
+ await db.destroy();
297
+ await container.stop();
298
+ });
299
+
300
+ describe('given a new user', () => {
301
+ it('persists the user to the database', async () => {
302
+ // Arrange
303
+ const userData = {
304
+ name: 'John Doe',
305
+ email: 'john@example.com',
306
+ };
307
+
308
+ // Act
309
+ const created = await repository.create(userData);
310
+
311
+ // Assert
312
+ expect(created.id).toBeDefined();
313
+ expect(created.name).toBe(userData.name);
314
+ expect(created.email).toBe(userData.email);
315
+
316
+ // Verify persistence
317
+ const found = await repository.findById(created.id);
318
+ expect(found).toEqual(created);
319
+ });
320
+ });
321
+ });
322
+ ```
323
+
324
+ **Rules:**
325
+ - Tests are executable documentation — describe behavior, not implementation
326
+ - Name `describe` blocks for features/scenarios, not function names
327
+ - Name `it` blocks as specifications that read as complete sentences
328
+ - Use nested `describe` blocks for "given/when" context
329
+ - Use Chance.js to generate test fixtures — avoid hardcoded test data
330
+ - Extract test data to constants — never duplicate values across arrange/act/assert
331
+ - Use Vitest (never Jest)
332
+ - Use Testcontainers for database integration tests
333
+ - Follow Arrange-Act-Assert pattern
334
+ - Tests must be deterministic — same result every run
335
+ - Avoid conditional logic in tests unless absolutely necessary
336
+ - Ensure all code paths have corresponding tests
337
+ - Test happy paths, unhappy paths, and edge cases
338
+ - Never modify tests to pass without understanding root cause
339
+
340
+ ## Dependencies
341
+
342
+ - Use latest LTS Node.js — check with `nvm ls-remote --lts`, update `.nvmrc`
343
+ - Pin ALL dependencies to exact versions (no ^ or ~)
344
+ - Use explicit version numbers when adding new dependencies
345
+ - Search npm for latest stable version before adding
346
+ - Run `npm audit` after any dependency change
347
+ - Ensure `package-lock.json` is updated correctly
348
+ - Use Dependabot to keep dependencies current
349
+
350
+ ## GitHub Actions
351
+
352
+ - Validation must be automated via GitHub Actions and runnable locally the same way
353
+ - Integration tests require Docker for Testcontainers
354
+ - Validate all workflows using actionlint
355
+ - Validate all YAML files using yamllint
356
+ - Pin all 3rd party Actions to specific version or commit SHA
357
+ - Keep all 3rd party Actions updated to latest version
358
+
359
+ ## Boundaries
360
+
361
+ **✅ Always do:**
362
+ - Run `nvm use` before any npm command
363
+ - Write tests before implementation (TDD)
364
+ - Run lint and tests after every change
365
+ - Run full validation before commits
366
+ - Use existing patterns from codebase
367
+ - Work in small increments
368
+ - Use Kysely for all database queries
369
+ - Validate all request/response data with Zod
370
+ - Use Context7 MCP tools for code generation and documentation
371
+
372
+ **⚠️ Ask first:**
373
+ - Adding new dependencies
374
+ - Changing project structure
375
+ - Modifying GitHub Actions workflows
376
+ - Database schema changes
377
+ - Adding new database tables
378
+
379
+ **🚫 Never do:**
380
+ - Skip the TDD workflow
381
+ - Store secrets in code (use environment variables)
382
+ - Use Jest (use Vitest)
383
+ - Use raw SQL strings (use Kysely query builder)
384
+ - Modify tests to pass without fixing root cause
385
+ - Add dependencies without explicit version numbers
386
+ - Use type assertions (`as Type`) on external/API data
387
+ - Use Prisma or other ORMs (use Kysely)
@@ -0,0 +1,149 @@
1
+ ---
2
+ applyTo: ".github/workflows/*.{yml,yaml}"
3
+ ---
4
+
5
+ # Pipeline Instructions for REST API
6
+
7
+ ## MANDATORY: After Modifying Workflows
8
+
9
+ Run these validation commands in order:
10
+
11
+ ```bash
12
+ npm run lint:workflows # Validate GitHub Actions workflows with actionlint
13
+ npm run lint:yaml # Validate YAML syntax with yamllint
14
+ ```
15
+
16
+ ## Workflow Structure Requirements
17
+
18
+ 1. Every workflow MUST include test and lint jobs.
19
+ 2. Reference Node.js version from `.nvmrc` using `actions/setup-node` with `node-version-file` input.
20
+ 3. Use official setup actions: `actions/checkout`, `actions/setup-node`, `actions/cache`.
21
+ 4. Integration tests require Docker for Testcontainers.
22
+
23
+ ## Action Pinning Format
24
+
25
+ Pin ALL third-party actions to exact commit SHA with version comment:
26
+
27
+ ```yaml
28
+ # CORRECT format:
29
+ uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
30
+
31
+ # INCORRECT formats (do NOT use):
32
+ uses: actions/checkout@v4 # ❌ version tag only
33
+ uses: actions/checkout@v4.1.1 # ❌ version tag only
34
+ uses: actions/checkout@main # ❌ branch reference
35
+ ```
36
+
37
+ Before adding any action:
38
+ 1. Check GitHub for the LATEST stable version
39
+ 2. Find the full commit SHA for that version
40
+ 3. Add both SHA and version comment
41
+
42
+ ## Runner Requirements
43
+
44
+ | Workflow | Runner |
45
+ |----------|--------|
46
+ | Default (all workflows) | `ubuntu-latest` |
47
+ | `copilot-setup-steps.yml` | May use different runners as needed |
48
+
49
+ ## Integration Tests with Testcontainers
50
+
51
+ Testcontainers manages Docker containers automatically. No explicit Docker service configuration is needed in the workflow, but Docker must be available on the runner.
52
+
53
+ ### Example CI Workflow with Integration Tests
54
+
55
+ ```yaml
56
+ name: CI
57
+
58
+ on:
59
+ push:
60
+ branches: [main]
61
+ pull_request:
62
+ branches: [main]
63
+
64
+ jobs:
65
+ lint:
66
+ runs-on: ubuntu-latest
67
+ steps:
68
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
69
+ - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
70
+ with:
71
+ node-version-file: '.nvmrc'
72
+ cache: 'npm'
73
+ - run: npm ci
74
+ - run: npx biome check
75
+
76
+ unit-tests:
77
+ runs-on: ubuntu-latest
78
+ steps:
79
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
80
+ - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
81
+ with:
82
+ node-version-file: '.nvmrc'
83
+ cache: 'npm'
84
+ - run: npm ci
85
+ - run: npm test
86
+
87
+ integration-tests:
88
+ runs-on: ubuntu-latest
89
+ steps:
90
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
91
+ - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
92
+ with:
93
+ node-version-file: '.nvmrc'
94
+ cache: 'npm'
95
+ - run: npm ci
96
+ - run: npm run test:integration
97
+ env:
98
+ # Testcontainers configuration
99
+ TESTCONTAINERS_RYUK_DISABLED: false
100
+
101
+ build:
102
+ runs-on: ubuntu-latest
103
+ needs: [lint, unit-tests, integration-tests]
104
+ steps:
105
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
106
+ - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
107
+ with:
108
+ node-version-file: '.nvmrc'
109
+ cache: 'npm'
110
+ - run: npm ci
111
+ - run: npm run build
112
+ ```
113
+
114
+ ## Testcontainers in GitHub Actions
115
+
116
+ Testcontainers works out of the box on `ubuntu-latest` runners since they have Docker pre-installed.
117
+
118
+ ### Environment Variables
119
+
120
+ | Variable | Description | Default |
121
+ |----------|-------------|---------|
122
+ | `TESTCONTAINERS_RYUK_DISABLED` | Disable Ryuk container cleanup | `false` |
123
+ | `TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE` | Override Docker socket path | auto-detected |
124
+
125
+ ### Troubleshooting
126
+
127
+ If container startup is slow:
128
+ - Use `TESTCONTAINERS_RYUK_DISABLED=true` to skip Ryuk (manual cleanup needed)
129
+ - Consider caching Docker images in a separate step
130
+
131
+ If containers fail to start:
132
+ - Ensure Docker is available on the runner
133
+ - Check container logs for startup errors
134
+ - Increase test timeouts for container startup
135
+
136
+ ## Database Migrations in CI
137
+
138
+ Run migrations as part of the integration test setup:
139
+
140
+ ```typescript
141
+ // In test setup
142
+ beforeAll(async () => {
143
+ container = await new PostgreSqlContainer().start();
144
+ db = createConnection(container);
145
+ await runMigrations(db); // Apply schema before tests
146
+ });
147
+ ```
148
+
149
+ Do not run migrations as a separate CI step for integration tests - let Testcontainers handle the isolated database setup.