build: update npm dependencies (#689)

## Summary

Update all direct npm dependencies and devDependencies to the current
npm latest versions and pin them exactly instead of using caret or tilde
ranges.

## Changes

- Pin all direct runtime and development package versions in
`package.json` and refresh `package-lock.json`.
- Migrate ESLint from `.eslintrc.json` to flat config for ESLint 10.
- Update Jest and TypeScript configuration and test mocks to support the
latest ESM-only packages.
- Add response status handling and typed JSON access for the latest
`node-fetch` types.

## Checklist

- [x] I have read the latest README and followed the instructions.
- [x] I have added or updated tests for behavior changes.
- [x] I have updated README.md and action.yml when inputs or runtime
behavior changed.
- [x] I have run the relevant verification commands.

## Verification

- [x] `npm --userconfig=/private/tmp/empty-npmrc outdated --json
--include=dev` returned `{}`
- [x] `npm run all`
- [x] `npm run build`


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Tests**
* Added comprehensive test mocks for Actions APIs and fetch, improved
integration tests, and updated assertions for more precise error
validation.
  * Configured Jest to map external modules to local test mocks.

* **Chores**
* Migrated ESLint to the flat config format and replaced prior ESLint
config.
* Updated project dependencies and modernized TypeScript compiler
settings.

* **Refactor**
* Adjusted exported tool constants and made error handling more explicit
in version-fetching logic.

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/peaceiris/actions-hugo/pull/689)
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
Shohei Ueda
2026-05-10 23:30:59 +09:00
committed by GitHub
co-authored by Codex
parent e3c35ac635
commit 189731ef00
17 changed files with 7709 additions and 13417 deletions
-24
View File
@@ -1,24 +0,0 @@
{
"env": {
"commonjs": true,
"es6": true,
"node": true
},
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:@typescript-eslint/recommended",
"plugin:jest/recommended"
],
"globals": {
"Atomics": "readonly",
"SharedArrayBuffer": "readonly"
},
"parser": "@typescript-eslint/parser",
"parserOptions": {
"sourceType": "module",
"ecmaVersion": 2019
},
"rules": {
}
}
+1 -1
View File
@@ -10,6 +10,6 @@ describe('getArch', () => {
test('exception', () => {
expect(() => {
getArch('mips');
}).toThrowError('mips is not supported');
}).toThrow('mips is not supported');
});
});
+1 -1
View File
@@ -45,6 +45,6 @@ describe('getLatestVersion()', () => {
test('return exception 404', async () => {
nock('https://formulae.brew.sh').get(`/api/formula/${Tool.Repo}.json`).reply(404);
await expect(getLatestVersion(Tool.Org, Tool.Repo, 'brew')).rejects.toThrowError(FetchError);
await expect(getLatestVersion(Tool.Org, Tool.Repo, 'brew')).rejects.toThrow(FetchError);
});
});
+1 -1
View File
@@ -10,6 +10,6 @@ describe('getOS', () => {
test('exception', () => {
expect(() => {
getOS('centos');
}).toThrowError('centos is not supported');
}).toThrow('centos is not supported');
});
});
+2 -2
View File
@@ -65,7 +65,7 @@ describe('Integration testing run()', () => {
process.env['INPUT_HUGO-VERSION'] = 'latest';
nock('https://formulae.brew.sh').get(`/api/formula/${Tool.Repo}.json`).reply(404);
await expect(main.run()).rejects.toThrowError(FetchError);
await expect(main.run()).rejects.toThrow(FetchError);
});
});
@@ -82,6 +82,6 @@ describe('showVersion()', () => {
});
test('return not found', async () => {
await expect(main.showVersion('gitgit', ['--version'])).rejects.toThrowError(Error);
await expect(main.showVersion('gitgit', ['--version'])).rejects.toThrow('spawn gitgit ENOENT');
});
});
+17
View File
@@ -0,0 +1,17 @@
import path from 'path';
export function getInput(name: string): string {
return process.env[`INPUT_${name.replace(/ /g, '_').toUpperCase()}`] || '';
}
export function addPath(inputPath: string): void {
process.env.PATH = `${inputPath}${path.delimiter}${process.env.PATH || ''}`;
}
export function debug(message: string): void {
void message;
}
export function info(message: string): void {
void message;
}
+36
View File
@@ -0,0 +1,36 @@
import {spawn} from 'child_process';
interface ExecOptions {
listeners?: {
stdout?: (data: Buffer) => void;
stderr?: (data: Buffer) => void;
};
}
export async function exec(
commandLine: string,
args: string[] = [],
options: ExecOptions = {}
): Promise<number> {
if (commandLine === 'hugo') {
const version = process.env.TEST_HUGO_VERSION || '';
const extended = process.env.TEST_HUGO_EXTENDED === 'true' ? ' extended' : '';
options.listeners?.stdout?.(Buffer.from(`hugo v${version}${extended}\n`));
return 0;
}
return new Promise((resolve, reject) => {
const child = spawn(commandLine, args);
child.stdout?.on('data', (data: Buffer) => {
options.listeners?.stdout?.(data);
});
child.stderr?.on('data', (data: Buffer) => {
options.listeners?.stderr?.(data);
});
child.on('error', reject);
child.on('close', code => {
resolve(code ?? 0);
});
});
}
+12
View File
@@ -0,0 +1,12 @@
export async function mkdirP(dir: string): Promise<void> {
void dir;
}
export async function rmRF(target: string): Promise<void> {
void target;
}
export async function mv(source: string, dest: string): Promise<void> {
void source;
void dest;
}
+18
View File
@@ -0,0 +1,18 @@
export async function downloadTool(url: string): Promise<string> {
const version = /hugo(?:_extended)?_([^_]+)_/.exec(url)?.[1] || '';
process.env.TEST_HUGO_VERSION = version;
process.env.TEST_HUGO_EXTENDED = url.includes('hugo_extended_') ? 'true' : 'false';
return '/tmp/hugo-archive';
}
export async function extractTar(assetPath: string, tempDir: string): Promise<string> {
void assetPath;
void tempDir;
return '/tmp/extracted';
}
export async function extractZip(assetPath: string, tempDir: string): Promise<string> {
void assetPath;
void tempDir;
return '/tmp/extracted';
}
+53
View File
@@ -0,0 +1,53 @@
import http from 'http';
import https from 'https';
export class FetchError extends Error {
name = 'FetchError' as const;
constructor(
message: string,
public type: string,
systemError?: Record<string, unknown>
) {
super(message);
if (systemError) {
Object.assign(this, systemError);
}
}
}
interface ResponseLike {
ok: boolean;
status: number;
json: () => Promise<unknown>;
}
export default async function fetch(url: string | URL): Promise<ResponseLike> {
const target = url.toString();
const client = target.startsWith('https:') ? https : http;
return new Promise((resolve, reject) => {
const request = client.get(target, response => {
const chunks: Buffer[] = [];
response.on('data', (chunk: Buffer | string) => {
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
});
response.on('end', () => {
const status = response.statusCode ?? 0;
const body = Buffer.concat(chunks).toString();
resolve({
ok: status >= 200 && status < 300,
status,
json: async () => JSON.parse(body) as unknown
});
});
});
request.on('error', error => {
reject(new FetchError(error.message, 'system'));
});
});
}
+40
View File
@@ -0,0 +1,40 @@
const tseslint = require('@typescript-eslint/eslint-plugin');
const jest = require('eslint-plugin-jest');
const globals = {
Atomics: 'readonly',
Buffer: 'readonly',
SharedArrayBuffer: 'readonly',
__dirname: 'readonly',
clearTimeout: 'readonly',
console: 'readonly',
exports: 'writable',
module: 'readonly',
process: 'readonly',
require: 'readonly',
setTimeout: 'readonly'
};
module.exports = [
{
ignores: ['lib/**', 'coverage/**', 'node_modules/**']
},
...tseslint.configs['flat/recommended'],
{
files: ['src/**/*.ts', '__tests__/**/*.ts'],
languageOptions: {
ecmaVersion: 2019,
sourceType: 'module',
globals
}
},
{
files: ['__tests__/**/*.ts'],
...jest.configs['flat/recommended'],
settings: {
jest: {
version: 30
}
}
}
];
+15 -1
View File
@@ -4,8 +4,22 @@ module.exports = {
testEnvironment: 'node',
testMatch: ['**/*.test.ts'],
testRunner: 'jest-circus/runner',
moduleNameMapper: {
'^@actions/core$': '<rootDir>/__tests__/mocks/actions-core.ts',
'^@actions/exec$': '<rootDir>/__tests__/mocks/actions-exec.ts',
'^@actions/io$': '<rootDir>/__tests__/mocks/actions-io.ts',
'^@actions/tool-cache$': '<rootDir>/__tests__/mocks/actions-tool-cache.ts',
'^node-fetch$': '<rootDir>/__tests__/mocks/node-fetch.ts'
},
transform: {
'^.+\\.ts$': 'ts-jest'
'^.+\\.ts$': [
'ts-jest',
{
tsconfig: {
types: ['jest', 'node']
}
}
]
},
verbose: true
}
+7482 -13370
View File
File diff suppressed because it is too large Load Diff
+21 -22
View File
@@ -47,29 +47,28 @@
},
"homepage": "https://github.com/peaceiris/actions-hugo#readme",
"dependencies": {
"@actions/core": "^1.10.0",
"@actions/exec": "^1.1.1",
"@actions/io": "^1.1.0",
"@actions/tool-cache": "^1.7.2",
"node-fetch": "^2.6.1"
"@actions/core": "3.0.1",
"@actions/exec": "3.0.0",
"@actions/io": "3.0.2",
"@actions/tool-cache": "4.0.0",
"node-fetch": "3.3.2"
},
"devDependencies": {
"@types/jest": "^26.0.20",
"@types/node": "~24",
"@types/node-fetch": "^2.5.8",
"@typescript-eslint/eslint-plugin": "^4.16.1",
"@typescript-eslint/parser": "^4.16.1",
"@vercel/ncc": "^0.38.1",
"eslint": "^7.21.0",
"eslint-plugin-jest": "^24.1.5",
"husky": "^5.1.3",
"jest": "^26.6.3",
"jest-circus": "^26.6.3",
"lint-staged": "^10.5.4",
"nock": "^13.0.10",
"prettier": "2.2.1",
"standard-version": "^9.1.1",
"ts-jest": "^26.5.3",
"typescript": "^4.9.5"
"@types/jest": "30.0.0",
"@types/node": "25.6.2",
"@typescript-eslint/eslint-plugin": "8.59.2",
"@typescript-eslint/parser": "8.59.2",
"@vercel/ncc": "0.38.4",
"eslint": "10.3.0",
"eslint-plugin-jest": "29.15.2",
"husky": "9.1.7",
"jest": "30.4.2",
"jest-circus": "30.4.2",
"lint-staged": "17.0.4",
"nock": "14.0.15",
"prettier": "3.8.3",
"standard-version": "9.5.0",
"ts-jest": "29.4.9",
"typescript": "6.0.3"
}
}
+9 -9
View File
@@ -1,12 +1,12 @@
export enum Tool {
Name = 'Hugo',
Org = 'gohugoio',
Repo = 'hugo',
CmdName = 'hugo',
CmdOptVersion = 'version',
TestVersionLatest = '0.83.1',
TestVersionSpec = '0.82.1'
}
export const Tool = {
Name: 'Hugo',
Org: 'gohugoio',
Repo: 'hugo',
CmdName: 'hugo',
CmdOptVersion: 'version',
TestVersionLatest: '0.83.1',
TestVersionSpec: '0.82.1'
} as const;
export enum Action {
WorkDirName = 'actions_hugo',
+17 -3
View File
@@ -1,4 +1,14 @@
import fetch from 'node-fetch';
import fetch, {FetchError} from 'node-fetch';
interface BrewFormulaResponse {
versions: {
stable: string;
};
}
interface GitHubReleaseResponse {
tag_name: string;
}
export function getURL(org: string, repo: string, api: string): string {
let url = '';
@@ -15,12 +25,16 @@ export function getURL(org: string, repo: string, api: string): string {
export async function getLatestVersion(org: string, repo: string, api: string): Promise<string> {
const url = getURL(org, repo, api);
const response = await fetch(url);
if (!response.ok) {
throw new FetchError(`request to ${url} failed with status ${response.status}`, 'system');
}
const json = await response.json();
let latestVersion = '';
if (api === 'brew') {
latestVersion = json.versions.stable;
latestVersion = (json as BrewFormulaResponse).versions.stable;
} else if (api === 'github') {
latestVersion = json.tag_name;
latestVersion = (json as GitHubReleaseResponse).tag_name;
}
return latestVersion;
}
+3 -2
View File
@@ -1,7 +1,8 @@
{
"compilerOptions": {
"lib": ["ES2019"],
"module": "commonjs",
"module": "ESNext",
"moduleResolution": "bundler",
"target": "ES2019",
"sourceMap": true,
"outDir": "./lib",
@@ -12,5 +13,5 @@
"esModuleInterop": true,
"resolveJsonModule": true
},
"exclude": ["node_modules", "**/*.test.ts"]
"exclude": ["node_modules", "__tests__"]
}