refactor: to node action
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
[*]
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
max_line_length = 80
|
||||
@@ -9,7 +9,64 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
- name: Validate JSON
|
||||
- name: Validate Renovate Config
|
||||
uses: ./
|
||||
with:
|
||||
json-file: renovate.json
|
||||
- name: Validate oxfmt config
|
||||
uses: ./
|
||||
with:
|
||||
json-file: .oxfmtrc.json
|
||||
|
||||
test:
|
||||
name: Test
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
- name: Cache dependencies
|
||||
id: cache-deps
|
||||
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
|
||||
with:
|
||||
path: |
|
||||
node_modules
|
||||
key: ${{ runner.os }}-npm-${{ hashFiles('package-lock.json') }}
|
||||
- name: Install dependencies
|
||||
if: steps.cache-deps.outputs.cache-hit != 'true'
|
||||
run: npm ci
|
||||
- name: Typecheck
|
||||
run: npm run typecheck
|
||||
- name: Format code
|
||||
run: npm run format:check
|
||||
- name: Run test
|
||||
run: npm run test
|
||||
|
||||
check-dist:
|
||||
name: Check Dist
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
- name: Cache dependencies
|
||||
id: cache-deps
|
||||
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
|
||||
with:
|
||||
path: |
|
||||
node_modules
|
||||
key: ${{ runner.os }}-npm-${{ hashFiles('package-lock.json') }}
|
||||
- name: Install dependencies
|
||||
if: steps.cache-deps.outputs.cache-hit != 'true'
|
||||
run: npm ci
|
||||
- run: mv dist dist_orig
|
||||
- run: npm run build
|
||||
- run: |
|
||||
original_hash=$(sha256sum dist_orig/index.js | cut -d' ' -f1)
|
||||
new_hash=$(sha256sum dist/index.js | cut -d' ' -f1)
|
||||
if [ "$original_hash" != "$new_hash" ]; then
|
||||
echo "Build is not up to date. Original hash: $original_hash, new hash: $new_hash"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -19,4 +19,4 @@ jobs:
|
||||
prerelease: true
|
||||
- uses: https://gitea.t000-n.de/t.behrendt/actions/release-git-tag@29f50ea246d5d9a62ac3c611fa5c8a6e9846df5a # 0.2.8
|
||||
with:
|
||||
tag: ${{ steps.tag.outputs.new-tag }}
|
||||
tag: ${{ steps.tag.outputs.new-tag }}
|
||||
|
||||
+1
-137
@@ -1,138 +1,2 @@
|
||||
# ---> Node
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
node_modules
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Snowpack dependency directory (https://snowpack.dev/)
|
||||
web_modules/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional stylelint cache
|
||||
.stylelintcache
|
||||
|
||||
# Microbundle cache
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variable files
|
||||
.env
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env.local
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
.parcel-cache
|
||||
|
||||
# Next.js build output
|
||||
.next
|
||||
out
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
.nuxt
|
||||
dist
|
||||
|
||||
# Gatsby files
|
||||
.cache/
|
||||
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
# public
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# vuepress v2.x temp and cache directory
|
||||
.temp
|
||||
.cache
|
||||
|
||||
# vitepress build output
|
||||
**/.vitepress/dist
|
||||
|
||||
# vitepress cache directory
|
||||
**/.vitepress/cache
|
||||
|
||||
# Docusaurus cache and generated files
|
||||
.docusaurus
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
|
||||
# TernJS port file
|
||||
.tern-port
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
.vscode-test
|
||||
|
||||
# yarn v2
|
||||
.yarn/cache
|
||||
.yarn/unplugged
|
||||
.yarn/build-state.yml
|
||||
.yarn/install-state.gz
|
||||
.pnp.*
|
||||
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"$schema": "./node_modules/oxfmt/configuration_schema.json",
|
||||
"ignorePatterns": ["dist/**", "node_modules/**", "coverage/**"]
|
||||
}
|
||||
@@ -2,17 +2,17 @@ MIT License
|
||||
|
||||
Copyright (c) 2025 t.behrendt
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
|
||||
associated documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
|
||||
associated documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the
|
||||
following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial
|
||||
portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
|
||||
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
|
||||
EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
|
||||
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
|
||||
EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
+2
-23
@@ -8,26 +8,5 @@ inputs:
|
||||
required: true
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version: "latest"
|
||||
- name: Extract schema
|
||||
shell: bash
|
||||
run: |
|
||||
schema=$(jq -r '.["$schema"]' < ${{ inputs.json-file }})
|
||||
if [ -n "$schema" ]; then
|
||||
curl -s $schema > schema.json
|
||||
else
|
||||
echo "No schema found"
|
||||
exit 0
|
||||
fi
|
||||
- name: Update the schema to the latest version
|
||||
shell: bash
|
||||
run: |
|
||||
npx ajv-cli migrate -s schema.json -o schema.json
|
||||
- name: Validate JSON
|
||||
shell: bash
|
||||
run: |
|
||||
npx ajv-cli validate -s schema.json -d ${{ inputs.json-file }} --strict=false
|
||||
using: "node24"
|
||||
main: "dist/index.js"
|
||||
|
||||
Vendored
+22249
File diff suppressed because one or more lines are too long
Generated
+2037
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"name": "validate-json-by-json-schema-action",
|
||||
"version": "1.0.0",
|
||||
"type": "commonjs",
|
||||
"main": "src/index.ts",
|
||||
"scripts": {
|
||||
"test": "vitest --config vitext.config.ts",
|
||||
"format": "oxfmt",
|
||||
"format:check": "oxfmt --check",
|
||||
"build": "rolldown -c",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@actions/core": "3.0.1",
|
||||
"ajv": "8.20.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "24.13.2",
|
||||
"@vitest/coverage-v8": "4.1.9",
|
||||
"oxfmt": "0.56.0",
|
||||
"rolldown": "1.1.3",
|
||||
"typescript": "6.0.3",
|
||||
"vitest": "4.1.9"
|
||||
}
|
||||
}
|
||||
+8
-3
@@ -1,7 +1,12 @@
|
||||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"extends": [
|
||||
"local>t.behrendt/renovate-configs:common",
|
||||
"local>t.behrendt/renovate-configs:action"
|
||||
]
|
||||
"local>t.behrendt/renovate-configs:action",
|
||||
"local>t.behrendt/renovate-configs:bun-npm"
|
||||
],
|
||||
"postUpgradeTasks": {
|
||||
"commands": ["npm ci", "npm run build"],
|
||||
"fileFilters": ["dist/**"],
|
||||
"executionMode": "update"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
import { defineConfig } from "rolldown";
|
||||
|
||||
export default defineConfig({
|
||||
input: "src/index.ts",
|
||||
platform: "node",
|
||||
tsconfig: "./tsconfig.json",
|
||||
output: {
|
||||
file: "dist/index.js",
|
||||
format: "cjs",
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,3 @@
|
||||
import { run } from "./main";
|
||||
|
||||
run();
|
||||
+18
@@ -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}`);
|
||||
}
|
||||
}
|
||||
@@ -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 () =>
|
||||
JSON.stringify({
|
||||
despiteThisBeingValidJson:
|
||||
"toString() breaks it, making it invalid",
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
||||
const jsonFile = JSON.stringify({
|
||||
$schema: "https://json-schema.org/draft-07/schema",
|
||||
});
|
||||
await expect(validateJson(jsonFile)).rejects.toThrow(
|
||||
'strict mode: unknown keyword: "despiteThisBeingValidJson"',
|
||||
);
|
||||
});
|
||||
|
||||
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();
|
||||
});
|
||||
});
|
||||
@@ -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()}`);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"module": "commonjs",
|
||||
"lib": ["ES2022"],
|
||||
"types": ["node"],
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"removeComments": false,
|
||||
"noImplicitAny": true,
|
||||
"noImplicitReturns": true,
|
||||
"noImplicitThis": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"exactOptionalPropertyTypes": true,
|
||||
"noImplicitOverride": true
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules", "dist", "**/*.test.ts", "**/*.spec.ts"]
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { defineConfig } from "vitest/config";
|
||||
|
||||
export default defineConfig({
|
||||
test: {
|
||||
coverage: {
|
||||
enabled: true,
|
||||
provider: "v8",
|
||||
reporter: ["lcov"],
|
||||
},
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user