Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ jobs:
with:
bun-version: 1.3.10
- run: bun install --frozen-lockfile
- run: bunx depcheck --ignores depcheck,@types/bun,@types/bun-types,bun:test
- run: bunx depcheck --ignores depcheck,@types/bun,@types/bun-types,vitest,@vitest/coverage-v8

test:
runs-on: ubuntu-24.04
Expand Down
8 changes: 4 additions & 4 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,18 @@ CLI tool to run GitLab CI pipelines locally. Written in TypeScript, built with B

## Build & Runtime

- **Runtime**: Bun (not Node.js). All scripts use `bun`/`bun test`/`bun run`.
- **Runtime**: Bun (not Node.js). All scripts use `bun`/`bun run`. Tests use vitest.
- **npm publish**: Still uses `npm publish --provenance` because Bun doesn't support provenance.
- **`bin` field**: Points to `dist/index.js` (Node.js-compatible bundle built by `bun run build:node`), not `src/index.ts`. This keeps `npm install -g` working without Bun.
- **Standalone binaries**: Built with `bun build --compile` for linux-amd64, linux-arm64, macos-x64, macos-arm64, win.
- **Version**: Hardcoded as `0.0.0` in `package.json`. CI replaces it via `sed` before build/publish. At runtime, `src/index.ts` reads it from `package.json` import.

## Testing

- **Never run the full test suite** (`bun test`), it takes too long. Always run targeted tests: `bun test --timeout 60000 tests/test-cases/<name>/`
- **Timeout**: `bunfig.toml` timeout setting does not work. The `--timeout 60000` flag in package.json scripts is required.
- **Never run the full test suite** (`bun run test`), it takes too long. Always run targeted tests: `bunx vitest run tests/test-cases/<name>/`
- **Timeout**: Configured in `vitest.config.ts` (`testTimeout: 60_000`).
- **Docker tests**: Tests under `dind-*` require Docker and are slow.
- **depcheck ignores**: `depcheck,@types/bun,@types/bun-types,bun:test`
- **depcheck ignores**: `depcheck,@types/bun,@types/bun-types,vitest,@vitest/coverage-v8`

## Schema

Expand Down
194 changes: 191 additions & 3 deletions bun.lock

Large diffs are not rendered by default.

10 changes: 6 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@
"build-all": "bun run build:linux-amd64 && bun run build:linux-arm64 && bun run build:win && bun run build:macos-x64 && bun run build:macos-arm64",
"check-all": "bun run lint && bun run coverage",
"lint": "eslint .",
"test": "FORCE_COLOR=1 bun test --timeout 60000",
"test-except-dind": "bun test --timeout 60000 --filter '(?!.*dind)'",
"coverage": "FORCE_COLOR=1 bun test --timeout 60000 --coverage --coverage-reporter=text --coverage-reporter=lcov --verbose",
"test": "vitest run",
"test-except-dind": "vitest run --exclude '**/dind-*/**'",
"coverage": "vitest run --coverage",
"start": "bun src/index.ts --cwd examples/docker-compose-nodejs",
"typecheck": "tsc --noEmit",
"fetch-schema": "curl https://gitlab.com/gitlab-org/gitlab/-/raw/master/app/assets/javascripts/editor/schema/ci.json -sf > src/schema.json"
Expand Down Expand Up @@ -62,11 +62,13 @@
"@types/semver": "7.x.x",
"@types/split2": "4.x.x",
"@types/yargs": "17.x.x",
"@vitest/coverage-v8": "^4.0.18",
"axios-mock-adapter": "2.x",
"depcheck": "1.x.x",
"eslint": "10.x",
"typescript": "5.x.x",
"typescript-eslint": "8.x.x"
"typescript-eslint": "8.x.x",
"vitest": "^4.0.18"
},
"repository": {
"type": "git",
Expand Down
4 changes: 2 additions & 2 deletions tests/cases.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {spyOn, beforeAll, afterAll, test, expect} from "bun:test";
import {vi} from "vitest";
import {WriteStreamsMock} from "../src/write-streams.js";
import chalk from "chalk-template";
import {handler} from "../src/handler.js";
Expand All @@ -16,7 +16,7 @@ afterAll(() => {
});

test("--completion", async () => {
const spy = spyOn(console, "log").mockImplementation(() => {});
const spy = vi.spyOn(console, "log").mockImplementation(() => {});
const writeStreams = new WriteStreamsMock();
await handler({
completion: true,
Expand Down
10 changes: 5 additions & 5 deletions tests/mocks/utils.mock.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {spyOn, expect} from "bun:test";
import {vi, expect} from "vitest";
import {Utils} from "../../src/utils.js";

const originalBash = Utils.bash.bind(Utils);
Expand All @@ -21,7 +21,7 @@ let spawnRejectMocks: {cmdArgs: any[]; rejection: any}[] = [];

export function initBashSpy (spyMocks: {cmd: any; returnValue: any}[]) {
bashMocks = spyMocks;
const spy = spyOn(Utils, "bash");
const spy = vi.spyOn(Utils, "bash");
spy.mockImplementation(async (cmd: string, cwd?: string) => {
for (const spyMock of bashMocks) {
if (matches(cmd, spyMock.cmd)) return spyMock.returnValue;
Expand All @@ -33,7 +33,7 @@ export function initBashSpy (spyMocks: {cmd: any; returnValue: any}[]) {

export function initSyncSpawnSpy (spyMocks: {cmdArgs: string[]; returnValue: any}[]) {
syncSpawnMocks = spyMocks;
const spy = spyOn(Utils, "syncSpawn");
const spy = vi.spyOn(Utils, "syncSpawn");
spy.mockImplementation((cmdArgs: string[], cwd?: string) => {
for (const spyMock of syncSpawnMocks) {
if (matches(cmdArgs, spyMock.cmdArgs)) return spyMock.returnValue;
Expand All @@ -56,14 +56,14 @@ async function spawnMockImpl (cmdArgs: string[], cwd?: string) {
export function initSpawnSpy (spyMocks: {cmdArgs: any[]; returnValue: any}[]) {
spawnResolveMocks = spyMocks;
spawnRejectMocks = [];
const spy = spyOn(Utils, "spawn");
const spy = vi.spyOn(Utils, "spawn");
spy.mockImplementation(spawnMockImpl);
return spy;
}

export function initSpawnSpyReject (spyMocks: {cmdArgs: string[]; rejection: any}[]) {
spawnRejectMocks = spyMocks;
const spy = spyOn(Utils, "spawn");
const spy = vi.spyOn(Utils, "spawn");
spy.mockImplementation(spawnMockImpl);
return spy;
}
6 changes: 3 additions & 3 deletions tests/process-write-streams.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import {spyOn, afterEach, afterAll, test, expect} from "bun:test";
import {vi} from "vitest";
import {WriteStreamsProcess} from "../src/write-streams.js";

const spyStdout = spyOn(process.stdout, "write").mockImplementation(() => true);
const spyStderr = spyOn(process.stderr, "write").mockImplementation(() => true);
const spyStdout = vi.spyOn(process.stdout, "write").mockImplementation(() => true);
const spyStderr = vi.spyOn(process.stderr, "write").mockImplementation(() => true);

afterEach(() => {
spyStdout.mockClear();
Expand Down
8 changes: 4 additions & 4 deletions tests/rules-regex.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {spyOn, mock, beforeEach, describe, test, expect} from "bun:test";
import {vi} from "vitest";
import "../src/global.js";
import {Argv} from "../src/argv";
import {Utils} from "../src/utils";
Expand All @@ -13,7 +13,7 @@ beforeEach(async () => {
writeStreams = new WriteStreamsMock();
gitData = await GitData.init("tests", writeStreams);
argv = await Argv.build({}, writeStreams);
mock.restore();
vi.restoreAllMocks();
});

/* eslint-disable @stylistic/quotes */
Expand Down Expand Up @@ -128,8 +128,8 @@ describe("gitlab rules regex", () => {
.forEach((t) => {
test(`- if: '${t.rule}'\n\t => ${t.evalResult}`, async () => {
const rules = [ {if: t.rule} ];
const evalSpy = spyOn(global, "eval");
const evaluateRuleIfSpy = spyOn(Utils, "evaluateRuleIf");
const evalSpy = vi.spyOn(global, "eval");
const evaluateRuleIfSpy = vi.spyOn(Utils, "evaluateRuleIf");

Utils.getRulesResult({argv, cwd: "", rules, variables: {}}, gitData);
expect(evaluateRuleIfSpy).toHaveReturnedWith(t.evalResult);
Expand Down
4 changes: 2 additions & 2 deletions tests/test-cases/predefined-variables/integration.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {spyOn, type Mock, beforeAll, beforeEach, afterEach, afterAll, describe, test, expect} from "bun:test";
import {vi, type Mock} from "vitest";
import {WriteStreamsMock} from "../../../src/write-streams.js";
import {handler} from "../../../src/handler.js";
import chalk from "chalk-template";
Expand Down Expand Up @@ -91,7 +91,7 @@ beforeAll(() => {
returnValue: {stdout: "git@gitlab.com:GCL/predefined-variables.git"},
};
initSpawnSpy([...WhenStatics.all, spyGitRemote]);
jobIdSpy = spyOn(
jobIdSpy = vi.spyOn(
Job.prototype as any,
"generateJobId",
);
Expand Down
6 changes: 3 additions & 3 deletions tests/utils.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {spyOn, describe, test, expect, beforeAll, afterAll} from "bun:test";
import {vi} from "vitest";
import {GitData} from "../src/git-data.js";
import {Utils} from "../src/utils.js";

Expand Down Expand Up @@ -128,7 +128,7 @@ describe("evaluateRuleChanges", () => {
];
tests.forEach((t) => {
test.concurrent(`${t.description} \t\t [input: ${t.input} pattern: ${t.pattern} hasChanges: ${t.hasChanges}]`, () => {
const spy = spyOn(GitData, "changedFiles");
const spy = vi.spyOn(GitData, "changedFiles");
spy.mockReturnValue(t.input);
expect(Utils.evaluateRuleChanges("origin/master", t.pattern, ".")).toBe(t.hasChanges);
spy.mockRestore();
Expand All @@ -139,7 +139,7 @@ describe("evaluateRuleChanges", () => {
describe("isSubPath where process.cwd() have been mocked to return /home/user/gitlab-ci-local", () => {
let cwdSpy: ReturnType<typeof spyOn>;
beforeAll(() => {
cwdSpy = spyOn(process, "cwd");
cwdSpy = vi.spyOn(process, "cwd");
cwdSpy.mockReturnValue("/home/user/gitlab-ci-local");
});
afterAll(() => {
Expand Down
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true,
"types": ["bun-types"],
"types": ["bun-types", "vitest/globals"],
}
}
18 changes: 18 additions & 0 deletions vitest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import {defineConfig} from "vitest/config";

export default defineConfig({
test: {
globals: true,
testTimeout: 60_000,
include: ["tests/**/*.test.ts"],
exclude: ["**/node_modules/**", "**/.gitlab-ci-local*/**"],
env: {
FORCE_COLOR: "1",
},
coverage: {
provider: "v8",
reporter: ["text", "lcov"],
reportsDirectory: "./coverage",
},
},
});