From 568e5abbc3972f253f7b4efda631a1d748ad7001 Mon Sep 17 00:00:00 2001 From: LordBaryhobal Date: Thu, 11 Sep 2025 15:21:48 +0200 Subject: [PATCH] feat: use action logging and error reporting --- src/check_datapack.ts | 125 +++++++++++++++++++++++++++++++++++------- src/index.ts | 6 +- 2 files changed, 110 insertions(+), 21 deletions(-) diff --git a/src/check_datapack.ts b/src/check_datapack.ts index 26363f0..9e703f3 100644 --- a/src/check_datapack.ts +++ b/src/check_datapack.ts @@ -1,3 +1,4 @@ +import * as action from '@actions/core' import * as core from '@spyglassmc/core' import * as je from '@spyglassmc/java-edition' import * as mcdoc from '@spyglassmc/mcdoc' @@ -6,11 +7,42 @@ import path from "path" import { glob } from "glob" import fs from "fs" +export interface ActionOptions { + /** + * If set to true, all errors are reported. If set to false, only the first error is reported + */ + reportAllErrors: boolean + + /** + * If set to true, each error is described, indicating the exact issue. If set to false, only the number of errors in each file is reported + */ + verbose: boolean +} + +class ActionLogger implements core.Logger { + error(data: any, ...args: any[]): void { + action.error(data) + } + info(data: any, ...args: any[]): void { + action.info(data) + } + log(data: any, ...args: any[]): void { + action.debug(data) + } + warn(data: any, ...args: any[]): void { + action.warning(data) + } + +} + export class CustomService extends core.Service { rootDir: string + options: ActionOptions - constructor(rootDir: string, version: string) { + constructor(rootDir: string, version: string, options: ActionOptions) { rootDir = path.resolve(rootDir) + action.info(`rootDir = ${rootDir}`) + action.info(`version = ${version}`) const fullRootDir = core.fileUtil.ensureEndingSlash("file://" + rootDir) const config = { env: { @@ -34,7 +66,7 @@ export class CustomService extends core.Service { } } super({ - logger: console, + logger: new ActionLogger(), profilers: new core.ProfilerFactory(console, [ 'cache#load', 'cache#save', @@ -53,11 +85,13 @@ export class CustomService extends core.Service { }, }) this.rootDir = rootDir + this.options = options } async shutdown() { - console.log("Shutting down") + action.startGroup("Shutdown") await this.project.close() + action.endGroup() } private listFilesByExtension(extension: string) { @@ -74,44 +108,97 @@ export class CustomService extends core.Service { }) } - async checkFile(path: string, lang: string) { + private getLineAndColumn(pos: number, content: string) { + const before = content.slice(0, pos) + const line = before.split("\n").length + const col = pos - before.lastIndexOf("\n") + return { line, col } + } + + private getErrorProperties(error: core.LanguageError, path: string, content: string): action.AnnotationProperties { + const {line: startLine, col: startColumn} = this.getLineAndColumn(error.range.start, content) + const {line: endLine, col: endColumn} = this.getLineAndColumn(error.range.end, content) + return { + file: path, + startLine, startColumn, + endLine, endColumn + } + } + + async checkFile(path: string, lang: string): Promise { const url = this.makeFileUrl(path) - this.project.onDidOpen(url, lang, 0, this.getFileContent(path)) + const content = this.getFileContent(path) + this.project.onDidOpen(url, lang, 0, content) const docAndNode = this.project.getClientManaged(url) if (!docAndNode) { - console.error(`File ${path} is not loaded`) - return + action.error(`File ${path} is not loaded`) + return false } const { node } = docAndNode const errors = core.FileNode.getErrors(node) if (errors.length !== 0) { - console.error(`${errors.length} error${errors.length > 1 ? "s" : ""} in ${path}`) + const msg = `${errors.length} error${errors.length > 1 ? "s" : ""} in ${path}` + + if (this.options.verbose) { + action.startGroup(msg) + for (const err of errors) { + action.error( + err.message, + this.getErrorProperties(err, path, content) + ) + } + action.endGroup() + } else { + action.error(msg) + } + return false } + return true } - async checkAllFiles() { - console.log("Checking all files") + async checkAllFiles(): Promise { const jsonFiles = await this.listFilesByExtension("json") const funcFiles = await this.listFilesByExtension("mcfunction") - console.log("Files to check") - console.log(`- JSON: ${jsonFiles.length}`) - console.log(`- Function: ${funcFiles.length}`) + action.info( + "Files to check:" + "\n" + + `- JSON: ${jsonFiles.length}` + "\n" + + `- Function: ${funcFiles.length}` + ) + let success = true for (const jsonFile of jsonFiles) { - this.checkFile(jsonFile, "json") + if (!this.checkFile(jsonFile, "json")) { + success = false + if (!this.options.reportAllErrors) { + return false + } + } } for (const funcFile of funcFiles) { - this.checkFile(funcFile, "mcfunction") + if (!this.checkFile(funcFile, "mcfunction")) { + success = false + if (!this.options.reportAllErrors) { + return false + } + } } + return success } } -export async function checkDatapack(rootDir: string, version: string): Promise { - console.log(`Checking datapack in directory ${rootDir} for Minecraft version ${version}`) - const service = new CustomService(rootDir, version) +export async function checkDatapack(rootDir: string, version: string, reportAll: boolean, verbose: boolean): Promise { + action.startGroup("Initialization") + const service = new CustomService(rootDir, version, { + reportAllErrors: reportAll, + verbose: verbose + }) await service.project.ready() + action.endGroup() - await service.checkAllFiles() + const success = await service.checkAllFiles() + if (!success) { + action.setFailed("Some files contain errors") + } await service.shutdown() } \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 1f382dc..1e9972c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,8 +4,10 @@ import { checkDatapack } from "./check_datapack.js" async function run(): Promise { try { const rootDir = core.getInput("rootDir") - let version = core.getInput("version") - await checkDatapack(rootDir, version) + const version = core.getInput("version") + const reportAll = core.getBooleanInput("reportAllErrors") + const verbose = core.getBooleanInput("verbose") + await checkDatapack(rootDir, version, reportAll, verbose) } catch (error) { if (error instanceof Error) {