Skip to content
Merged
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
158 changes: 83 additions & 75 deletions main.js

Large diffs are not rendered by default.

676 changes: 105 additions & 571 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"test": "test"
},
"scripts": {
"build": "esbuild src/main.ts --bundle --outfile=main.js --format=esm --platform=node --target=node24",
"build": "node scripts/build.mjs",
"format": "prettier --write src test",
"lint": "npm run lint:format && npm run lint:js && npm run lint:types",
"lint:format": "prettier --check src test",
Expand All @@ -38,7 +38,7 @@
"@actions/github": "^6.0.1",
"@eslint/js": "^9.36.0",
"@types/node": "^24.5.2",
"esbuild": "^0.24.2",
"esbuild": "^0.25.10",
"eslint": "^9.36.0",
"prettier": "^3.6.2",
"typescript": "^5.9.2",
Expand Down
18 changes: 18 additions & 0 deletions scripts/build.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import * as esbuild from 'esbuild';

await esbuild.build({
entryPoints: ['src/main.ts'],
bundle: true,
outfile: 'main.js',
format: 'esm',
target: 'node24',
platform: 'node',
banner: {
js: `
import { fileURLToPath } from 'node:url';
import { createRequire as topLevelCreateRequire } from 'node:module';
import { dirname as topLevelDirname } from 'path';
const require = topLevelCreateRequire(import.meta.url);
`.trim()
}
});
7 changes: 2 additions & 5 deletions src/git.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ export function getFileFromRef(
try {
const content = execFileSync('git', ['show', `${ref}:${filePath}`], {
encoding: 'utf8',
cwd
cwd,
stdio: 'pipe'
});
return content;
} catch (err) {
Expand All @@ -33,7 +34,3 @@ export function getBaseRef(): string {

return 'origin/main';
}

export function getCurrentRef(): string {
return github.context.sha ?? 'HEAD';
}
4 changes: 2 additions & 2 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as process from 'process';
import * as core from '@actions/core';
import * as github from '@actions/github';
import {parseLockfile, detectLockfile} from './lockfile.js';
import {getFileFromRef, getBaseRef, getCurrentRef} from './git.js';
import {getFileFromRef, getBaseRef} from './git.js';
import {fetchPackageMetadata} from './npm.js';

function formatBytes(bytes: number): string {
Expand All @@ -19,7 +19,7 @@ async function run(): Promise<void> {
try {
const workspacePath = process.env.GITHUB_WORKSPACE || process.cwd();
const baseRef = getBaseRef();
const currentRef = getCurrentRef();
const currentRef = github.context.sha;
const lockfilePath = detectLockfile(workspacePath);
const token = core.getInput('github-token', {required: true});
const prNumber = parseInt(core.getInput('pr-number', {required: true}), 10);
Expand Down
74 changes: 74 additions & 0 deletions test/git_test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import {describe, it, expect, beforeEach, vi} from 'vitest';
import * as git from '../src/git.js';
import * as github from '@actions/github';
import process from 'process';
import {fileURLToPath} from 'node:url';
import path from 'node:path';

const currentDir = path.dirname(fileURLToPath(import.meta.url));
const rootDir = path.join(currentDir, '..');

describe('getBaseRef', () => {
it('should return input base ref if provided', () => {
try {
process.env['INPUT_BASE-REF'] = 'feature-branch';
const baseRef = git.getBaseRef();
expect(baseRef).toBe('feature-branch');
} finally {
delete process.env['INPUT_BASE-REF'];
}
});

it('should return pull request base ref if in PR context', () => {
const originalPayload = github.context.payload;
try {
github.context.payload = {
pull_request: {
number: 303,
base: {
ref: 'develop'
}
}
};
const baseRef = git.getBaseRef();
expect(baseRef).toBe('origin/develop');
} finally {
github.context.payload = originalPayload;
}
});

it('should return default base ref if no input or PR context', () => {
const originalPayload = github.context.payload;
try {
github.context.payload = {};
const baseRef = git.getBaseRef();
expect(baseRef).toBe('origin/main');
} finally {
github.context.payload = originalPayload;
}
});
});

describe('getFileFromRef', () => {
beforeEach(() => {
vi.mock(import('@actions/core'), async (importModule) => {
const mod = await importModule();
return {
...mod,
info: vi.fn(),
error: vi.fn()
};
});
});

it('should return file content from a given ref', () => {
const content = git.getFileFromRef('HEAD', 'package.json', rootDir);
expect(content).toBeDefined();
expect(content).toContain('"name":');
});

it('should return null if file does not exist in the given ref', () => {
const content = git.getFileFromRef('HEAD', 'nonexistentfile.txt', rootDir);
expect(content).toBeNull();
});
});
60 changes: 60 additions & 0 deletions test/npm_test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import * as core from '@actions/core';
import {
describe,
it,
vi,
beforeEach,
afterEach,
type MockInstance,
expect
} from 'vitest';
import {fetchPackageMetadata} from '../src/npm.js';

describe('fetchPackageMetadata', () => {
let fetchMock: MockInstance<typeof globalThis.fetch>;

beforeEach(() => {
fetchMock = vi.spyOn(globalThis, 'fetch');
vi.mock(import('@actions/core'), async (importModule) => {
const mod = await importModule();
return {
...mod,
info: vi.fn(),
error: vi.fn()
};
});
});

afterEach(() => {
fetchMock.mockRestore();
vi.clearAllMocks();
});

it('should return null if request fails', async () => {
const response = new Response(null, {status: 404});
fetchMock.mockResolvedValue(response);
const result = await fetchPackageMetadata('nonexistent-package', '1.0.0');
expect(result).toBeNull();
});

it('should return package metadata for valid package and version', async () => {
const mockMetadata = {
name: 'some-package',
version: '1.0.0'
};
const response = new Response(JSON.stringify(mockMetadata), {status: 200});
fetchMock.mockResolvedValue(response);
const result = await fetchPackageMetadata('some-package', '1.0.0');
expect(result).toEqual(mockMetadata);
});

it('should return null if fetch fails', async () => {
const infoSpy = vi.mocked(core.info);
fetchMock.mockRejectedValue(new Error('Network error'));
const result = await fetchPackageMetadata('some-package', '1.0.0');
expect(result).toBeNull();
expect(infoSpy).toHaveBeenCalledWith(
'Failed to fetch metadata for [email protected]: Error: Network error'
);
});
});
9 changes: 9 additions & 0 deletions vitest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { defineConfig } from 'vitest/config'

export default defineConfig({
test: {
include: [
'test/**/*_test.ts'
]
}
})