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 core from '@spyglassmc/core'
import * as je from '@spyglassmc/java-edition' import * as je from '@spyglassmc/java-edition'
import * as mcdoc from '@spyglassmc/mcdoc' import * as mcdoc from '@spyglassmc/mcdoc'
@@ -6,11 +7,42 @@ import path from "path"
import { glob } from "glob" import { glob } from "glob"
import fs from "fs" 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 { export class CustomService extends core.Service {
rootDir: string rootDir: string
options: ActionOptions
constructor(rootDir: string, version: string) { constructor(rootDir: string, version: string, options: ActionOptions) {
rootDir = path.resolve(rootDir) rootDir = path.resolve(rootDir)
action.info(`rootDir = ${rootDir}`)
action.info(`version = ${version}`)
const fullRootDir = core.fileUtil.ensureEndingSlash("file://" + rootDir) const fullRootDir = core.fileUtil.ensureEndingSlash("file://" + rootDir)
const config = { const config = {
env: { env: {
@@ -34,7 +66,7 @@ export class CustomService extends core.Service {
} }
} }
super({ super({
logger: console, logger: new ActionLogger(),
profilers: new core.ProfilerFactory(console, [ profilers: new core.ProfilerFactory(console, [
'cache#load', 'cache#load',
'cache#save', 'cache#save',
@@ -53,11 +85,13 @@ export class CustomService extends core.Service {
}, },
}) })
this.rootDir = rootDir this.rootDir = rootDir
this.options = options
} }
async shutdown() { async shutdown() {
console.log("Shutting down") action.startGroup("Shutdown")
await this.project.close() await this.project.close()
action.endGroup()
} }
private listFilesByExtension(extension: string) { 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) 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) const docAndNode = this.project.getClientManaged(url)
if (!docAndNode) { if (!docAndNode) {
console.error(`File ${path} is not loaded`) action.error(`File ${path} is not loaded`)
return return false
} }
const { node } = docAndNode const { node } = docAndNode
const errors = core.FileNode.getErrors(node) const errors = core.FileNode.getErrors(node)
if (errors.length !== 0) { 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() { async checkAllFiles(): Promise<boolean> {
console.log("Checking all files")
const jsonFiles = await this.listFilesByExtension("json") const jsonFiles = await this.listFilesByExtension("json")
const funcFiles = await this.listFilesByExtension("mcfunction") const funcFiles = await this.listFilesByExtension("mcfunction")
console.log("Files to check") action.info(
console.log(`- JSON: ${jsonFiles.length}`) "Files to check:" + "\n" +
console.log(`- Function: ${funcFiles.length}`) `- JSON: ${jsonFiles.length}` + "\n" +
`- Function: ${funcFiles.length}`
)
let success = true
for (const jsonFile of jsonFiles) { 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) { 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> { export async function checkDatapack(rootDir: string, version: string, reportAll: boolean, verbose: boolean): Promise<void> {
console.log(`Checking datapack in directory ${rootDir} for Minecraft version ${version}`) action.startGroup("Initialization")
const service = new CustomService(rootDir, version) const service = new CustomService(rootDir, version, {
reportAllErrors: reportAll,
verbose: verbose
})
await service.project.ready() 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() await service.shutdown()
} }

View File

@@ -4,8 +4,10 @@ import { checkDatapack } from "./check_datapack.js"
async function run(): Promise<void> { async function run(): Promise<void> {
try { try {
const rootDir = core.getInput("rootDir") const rootDir = core.getInput("rootDir")
let version = core.getInput("version") const version = core.getInput("version")
await checkDatapack(rootDir, version) const reportAll = core.getBooleanInput("reportAllErrors")
const verbose = core.getBooleanInput("verbose")
await checkDatapack(rootDir, version, reportAll, verbose)
} catch (error) { } catch (error) {
if (error instanceof Error) { if (error instanceof Error) {