Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 19 additions & 10 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Project Overview

This is a GitHub Action (`tediousjs/setup-sqlserver`) that installs SQL Server on Windows-based GitHub Action runners. It's built with TypeScript, compiled with `@vercel/ncc`, and runs on Node.js 24.
This is a GitHub Action (`tediousjs/setup-sqlserver`) that installs SQL Server on Windows-based GitHub Action runners. It's built as a native ESM TypeScript module, bundled with [Rollup](https://rollupjs.org/), and runs on Node.js 24.

## Local Development

Expand All @@ -15,25 +15,27 @@ nvm use
## Build, Test & Lint

```sh
npm run build # ncc build + auto-generates README usage section from action.yml
npm run test # mocha + ts-node (runs all tests)
npm run test:coverage # tests with nyc coverage
npm run lint # eslint across src/, misc/, test/
npm run lint:fix # eslint with auto-fix
npm run build # rollup bundles src/ to lib/main/index.js + auto-generates README usage section from action.yml
npm run test # runs node:test with experimental module mocks
npm run test:coverage # tests with Node's native --experimental-test-coverage
npm run lint # eslint across src/, misc/, test/
npm run lint:fix # eslint with auto-fix
```

Run a single test file:

```sh
npx mocha -r ts-node/register './test/utils.ts'
node --test --experimental-test-module-mocks --disable-warning=ExperimentalWarning './test/utils.ts'
```

Run a single test by name:

```sh
npx mocha -r ts-node/register './test/**/**.ts' --grep 'correctly returns for windows-2019'
node --test --experimental-test-module-mocks --disable-warning=ExperimentalWarning --test-name-pattern='correctly returns for windows-2019' './test/**/*.ts'
```

TypeScript is executed directly by Node 24 via native type-stripping — no transpiler (no `ts-node`, no `tsx`) is needed to run `.ts` files. This is why test runs are very fast and imports use `.ts` extensions.

The build step (`npm run build`) also runs `npm run docs`, which regenerates the usage section in `README.md` from `action.yml` via `misc/generate-docs.ts`. If you change `action.yml` inputs, rebuild to keep the README in sync.

## Architecture
Expand All @@ -52,11 +54,18 @@ The build step (`npm run build`) also runs `npm run docs`, which regenerates the

**Version registry:** `src/versions.ts` defines a `Map<string, VersionConfig>` with download URLs (exe/box or SSEI), optional update URLs, and OS compatibility constraints for each supported SQL Server version (2008–2025). SQL Server 2025+ uses the SSEI bootstrapper model (`sseiUrl`) instead of direct exe/box downloads.

**Build output:** `@vercel/ncc` bundles everything into `lib/main/index.js`, which is what `action.yml` references. The `lib/` directory is committed to the repository. **Every commit must include up-to-date build output** — CI checks this by rebuilding and running `git diff-files --quiet`. Always run `npm run build` and commit the resulting changes to `lib/` and `README.md` before pushing.
**Build output:** Rollup (config in `rollup.config.mjs`) bundles everything into `lib/main/index.js` as a minified ESM module, which is what `action.yml` references. The `lib/` directory is committed to the repository. **Every commit must include up-to-date build output** — CI checks this by rebuilding and running `git diff-files --quiet`. Always run `npm run build` and commit the resulting changes to `lib/` and `README.md` before pushing.

**ESM module format:** The package is published as ESM (`"type": "module"` in `package.json`). Consequences for contributors:

- Relative imports must use explicit file extensions. In this codebase we use `.ts` extensions (e.g., `import { foo } from './bar.ts'`), which Node 24 resolves directly via native type-stripping, and Rollup resolves via `@rollup/plugin-typescript`. TypeScript is configured with `allowImportingTsExtensions: true` and `rewriteRelativeImportExtensions: true` so the emitted types remain valid.
- Prefer idiomatic ESM imports throughout: `import * as core from '@actions/core'` for namespace imports, named imports like `import { HttpClient } from '@actions/http-client'`, or default-function exports (e.g., `install-native-client.ts`). Do **not** re-export modules as mutable objects just to enable test stubbing — tests use `node:test`'s `mock.module()` (see below) which mocks modules at the module-resolution layer instead of mutating bindings.

## Conventions

- **Testing:** Mocha + Chai + Sinon. Tests heavily stub `@actions/*` packages and module-level dependencies using `sinon.stub()`. Each test file mirrors its source file (e.g., `test/install.ts` tests `src/install.ts`).
- **Testing:** Uses the built-in [`node:test`](https://nodejs.org/api/test.html) runner with `--experimental-test-module-mocks` and `node:assert/strict`. No Mocha, Chai, Sinon, or c8. Each test file mirrors its source file (e.g., `test/install.ts` tests `src/install.ts`).
- **Mocking pattern:** Create `mock.fn()` stubs at the top of the test file, call `mock.module(specifier, { namedExports, defaultExport })` for each dependency you need to replace, then `await import('../src/sut.ts')` to load the system-under-test against those mocks. Reset call history and implementations in `beforeEach` using `fn.mock.resetCalls()` and `fn.mock.mockImplementation(...)`. Because a given `mock.module()` call cannot be replaced mid-run, the tests share one mock per module across all cases and vary behaviour via `mockImplementation`/`mockImplementationOnce(impl, onCall)` — note that `mockImplementationOnce` without `onCall` defaults to call index `0`, so pass an explicit index when queueing sequential once-implementations.
- **Coverage caveat:** `--experimental-test-coverage` under-reports coverage for files that are `mock.module()`-replaced in sibling tests (the aggregator treats the mock shadow as an uncovered run of the real file). Every source file achieves 100% coverage when its own test file is run in isolation; treat the aggregated numbers as a lower bound while this feature is experimental.
- **Commit messages:** Follow [Conventional Commits](https://www.conventionalcommits.org/) enforced by commitlint. Semantic-release uses these to generate automated releases and changelogs, so correct commit types are critical.
- `fix` — Bug fixes or behavioural corrections that don't change public interfaces. Triggers a **patch** release.
- `feat` — New backwards-compatible functionality (e.g., adding a new input, method, or option without removing anything). Triggers a **minor** release.
Expand Down
6 changes: 0 additions & 6 deletions .nycrc.json

This file was deleted.

8 changes: 5 additions & 3 deletions lib/main/index.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions lib/main/index.js.map

Large diffs are not rendered by default.

12 changes: 8 additions & 4 deletions misc/generate-docs.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import * as fs from 'fs/promises';
import * as os from 'os';
import * as path from 'path';
import * as fs from 'node:fs/promises';
import * as os from 'node:os';
import * as path from 'node:path';
import { fileURLToPath } from 'node:url';
import * as yaml from 'js-yaml';
import { version } from '../package.json';
import pkg from '../package.json' with { type: 'json' };

const { version } = pkg;
const __dirname = path.dirname(fileURLToPath(import.meta.url));

/**
* From @actions/checkout
Expand Down
Loading
Loading