feat: use action logging and error reporting

This commit is contained in:
2025-09-11 15:21:48 +02:00
parent 14f7b74d0f
commit 568e5abbc3
2 changed files with 110 additions and 21 deletions

View File

@@ -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<boolean> {
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<boolean> {
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<void> {
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<void> {
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()
}

View File

@@ -4,8 +4,10 @@ import { checkDatapack } from "./check_datapack.js"
async function run(): Promise<void> {
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) {