refactor: to node action (#47)
CD / Release (push) Successful in 33s

Refactoring the action from a composite to Node action. This change improves security through pinning of packages as well as better test-ability and error handling.

Reviewed-on: #47
Co-authored-by: Timo Behrendt <t.behrendt@t00n.de>
Co-committed-by: Timo Behrendt <t.behrendt@t00n.de>
This commit was merged in pull request #47.
This commit is contained in:
2026-06-27 14:00:15 +02:00
committed by t.behrendt
parent 3730a56a77
commit 30070f7f9c
20 changed files with 24600 additions and 174 deletions
+3
View File
@@ -0,0 +1,3 @@
import { run } from "./main";
run();
+18
View File
@@ -0,0 +1,18 @@
import * as core from "@actions/core";
import { readFileSync } from "node:fs";
import { validateJson } from "./validation";
export async function run(): Promise<void> {
try {
const jsonFilePath = core.getInput("json-file");
core.info(`Validating JSON file: ${jsonFilePath}`);
const json = readFileSync(jsonFilePath, "utf8");
await validateJson(json);
core.info("JSON file is valid");
} catch (error) {
core.setFailed(`Error validating JSON file: ${error}`);
}
}
+109
View File
@@ -0,0 +1,109 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
import { validateJson } from "./validation";
global.fetch = vi.fn();
const validSchema = JSON.stringify({
$id: "https://t00n.de/schema.json",
title:
"JSON schema for Renovate 43.244.4 config files (https://renovatebot.com/)",
$schema: "http://json-schema.org/draft-07/schema#",
type: "object",
properties: {
$schema: {
type: "string",
},
name: {
type: "string",
},
},
required: ["name"],
additionalProperties: false,
});
describe("validateJson()", () => {
beforeEach(() => {
vi.clearAllMocks();
});
it("throws an error if the JSON file is not valid JSON", async () => {
const jsonFile = {
despiteThisBeingValidJson: "toString() breaks it, making it invalid",
}.toString();
await expect(validateJson(jsonFile)).rejects.toThrow(
'"[object Object]" is not valid JSON',
);
});
it("throws an error if the JSON file does not contain a $schema property", async () => {
const jsonFile = JSON.stringify({
validJson: true,
});
await expect(validateJson(jsonFile)).rejects.toThrow(
"No schema found in JSON file.",
);
});
it("throws an error if the schema cannot be fetched", async () => {
vi.mocked(fetch).mockRejectedValue(new Error("Failed to fetch schema"));
const jsonFile = JSON.stringify({ $schema: "http://t00n.de/404.json" });
await expect(validateJson(jsonFile)).rejects.toThrow(
"Failed to fetch schema",
);
});
it("throws an error if the schema file is not valid", async () => {
vi.mocked(fetch).mockResolvedValue(
// @ts-expect-error - mock response
{
ok: true,
text: async () =>
({
despiteThisBeingValidJson:
"toString() breaks it, making it invalid",
}).toString(),
},
);
const jsonFile = JSON.stringify({
$schema: "https://json-schema.org/draft-07/schema",
});
await expect(validateJson(jsonFile)).rejects.toThrow(
'"[object Object]" is not valid JSON',
);
});
it("throws an error if the JSON file does not match the schema", async () => {
vi.mocked(fetch).mockResolvedValue(
// @ts-expect-error - mock response
{
ok: true,
text: async () => validSchema,
},
);
const jsonFile = JSON.stringify({
$schema: "https://json-schema.org/draft-07/schema",
notTheExpectedField: 42,
});
await expect(validateJson(jsonFile)).rejects.toThrow(
"JSON file is not valid: No errors",
);
});
it("does not throw an error if the JSON file is valid", async () => {
vi.mocked(fetch).mockResolvedValue(
// @ts-expect-error - mock response
{
ok: true,
text: async () => validSchema,
},
);
const jsonFile = JSON.stringify({
$schema: "https://t00n.de/schema.json",
name: "John Doe",
});
await expect(validateJson(jsonFile)).resolves.toBeUndefined();
});
});
+25
View File
@@ -0,0 +1,25 @@
import Ajv from "ajv";
export const validateJson = async (jsonFileContent: string): Promise<void> => {
const jsonObject = JSON.parse(jsonFileContent);
const schema = jsonObject["$schema"];
if (!schema) {
throw new Error("No schema found in JSON file.");
}
const schemaUrl = new URL(schema);
const schemaContent = await fetch(schemaUrl.toString());
const schemaText = await schemaContent.text();
const schemaObject = JSON.parse(schemaText);
const validator = new Ajv({
strict: false,
});
const validate = validator.compile(schemaObject);
const isValid = validate(jsonObject);
if (!isValid) {
throw new Error(`JSON file is not valid: ${validator.errorsText()}`);
}
};