@circlesac/lint 26.2.0 → 26.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/main.js +11 -13
- package/dist/main.js.map +6 -8
- package/package.json +2 -2
package/dist/main.js
CHANGED
|
@@ -1,22 +1,20 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
var
|
|
3
|
-
`))console.error(
|
|
4
|
-
|
|
5
|
-
`).
|
|
6
|
-
`)),v.split(`
|
|
7
|
-
`).length>5)console.error("...")}if(I)console.error(""),console.error(`\uD83D\uDCC4 Full failure log saved to: ${I}`),await G(`### ❌ ${n.title} Failed
|
|
2
|
+
import{runMain as R}from"citty";import{defineCommand as D,showUsage as E}from"citty";var B={bin:{lint:"dist/main.js"},dependencies:{chalk:"^4.1.2",citty:"^0.2.0",eslint:"^9.34.0","read-package-up":"^11.0.0","typescript-eslint":"^8.42.0"},description:"🔧 A zero-config lint tool that uses ESLint, Prettier, and Biome",devDependencies:{"@tsed/barrels":"^6.6.3","@types/bun":"^1.2.21","@types/node":"^24.3.0","@vitest/coverage-v8":"3.2.4",typescript:"^5.9.2",vitest:"^3.2.4"},exports:{"./biome":"./biome.jsonc","./eslint":"./eslint.config.mjs","./prettier":"./prettier.config.mjs"},files:["dist",".prettierignore","biome.jsonc","eslint.config.mjs","prettier.config.mjs"],keywords:["cli","lint","eslint","prettier","biome","zeroconf"],license:"MIT",main:"dist/main.js",name:"@circlesac/lint",private:!1,publishConfig:{access:"public"},repository:{type:"git",url:"https://github.com/circlesac/lint.git"},scripts:{build:"barrels && bun lint && npx -p @typescript/native-preview tsgo --noEmit && bun run build.ts",dev:"bun run src/main.ts",lint:"bun dev --all",prepublishOnly:"bun run build",start:"bun run build && bun run dist/main.js",test:"vitest run --coverage"},type:"module",version:"26.2.2"};import{execSync as $}from"child_process";import{access as f,appendFile as y,writeFile as N}from"fs/promises";import{tmpdir as G}from"os";import{dirname as Q,join as H,resolve as V}from"path";import{readPackageUp as L}from"read-package-up";import{fileURLToPath as w}from"url";async function W(i){let b=await F(),p=[...i.args];if(i.configArg&&i.configFile){let j=V(b,i.configFile);p.push(i.configArg,j)}if(i.ignoreArg&&i.ignoreFile){let j=V(b,i.ignoreFile);p.push(i.ignoreArg,j)}try{let j=`${i.command} ${p.join(" ")}`;return console.info(`\uD83D\uDD27 Running ${i.title}...`),console.info(` Command: ${j}`),$(j,{stdio:"pipe",cwd:process.cwd(),encoding:"utf8"}),console.info(`✅ ${i.title} completed`),!0}catch(j){console.info(`❌ ${i.title} failed`);let v="",n="",I,z="";if(j&&typeof j==="object"){let c=j;v=c.stdout?.toString()||"",n=c.stderr?.toString()||"",z=c.message||"",I=c.status}else z=String(j);let A={tool:i.name,command:i.command,args:p,workingDir:process.cwd(),timestamp:new Date().toISOString()};if(I!==void 0)A.exitCode=I;if(v)A.stdout=v;if(n||z)A.stderr=n||z;let O=await J(A);if(v||n||z){let c=v||n||z,Y=c.split(`
|
|
3
|
+
`).slice(0,5);if(console.error(Y.join(`
|
|
4
|
+
`)),c.split(`
|
|
5
|
+
`).length>5)console.error("...")}if(O)console.error(""),console.error(`\uD83D\uDCC4 Full failure log saved to: ${O}`),await U(`### ❌ ${i.title} Failed
|
|
8
6
|
|
|
9
|
-
**Full log:** \`${
|
|
7
|
+
**Full log:** \`${O}\`
|
|
10
8
|
|
|
11
9
|
<details><summary>View error details</summary>
|
|
12
10
|
|
|
13
11
|
\`\`\`
|
|
14
|
-
${(
|
|
12
|
+
${(n||v||z).slice(0,500)}
|
|
15
13
|
\`\`\`
|
|
16
14
|
|
|
17
|
-
</details>`);else console.error(
|
|
18
|
-
`)}async function
|
|
19
|
-
`}catch{}await
|
|
20
|
-
`,"utf8")}catch{}}var
|
|
15
|
+
</details>`);else console.error(v||n||z);return!1}}async function F(){let i=w(import.meta.url),b=Q(i),p=await L({cwd:b});if(!p)throw Error("Could not find package.json");return Q(p.path)}async function J(i){try{let b=G(),p=S(i),j=new Date().toISOString().replace(/[:.]/g,"-"),v=`circlesac-lint-${i.tool}-${j}.log`,n=H(b,v);return await N(n,p,"utf8"),n}catch{return null}}function S(i){let b=[];if(b.push("=".repeat(80)),b.push(`Tool: ${i.tool}`),i.toolVersion)b.push(`Version: ${i.toolVersion}`);if(b.push(`Command: ${i.command}`),b.push(`Arguments: ${JSON.stringify(i.args)}`),i.exitCode!==void 0)b.push(`Exit Code: ${i.exitCode}`);if(b.push(`Working Directory: ${i.workingDir}`),b.push(`Timestamp: ${i.timestamp}`),b.push("=".repeat(80)),b.push(""),i.stdout){let p=i.stdout.toString();b.push("STDOUT:"),b.push("-".repeat(80)),b.push(p),b.push("")}if(i.stderr){let p=i.stderr.toString();b.push("STDERR:"),b.push("-".repeat(80)),b.push(p),b.push("")}return b.join(`
|
|
16
|
+
`)}async function U(i){try{let b=process.env.GITHUB_STEP_SUMMARY;if(!b)return;let p="";try{await f(b),p=`
|
|
17
|
+
`}catch{}await y(b,p+i+`
|
|
18
|
+
`,"utf8")}catch{}}var q=[{name:"oxlint",title:"Oxlint",command:"npx oxlint",args:["--fix"]},{name:"eslint",title:"ESLint",command:"npx eslint",args:["--fix"],configArg:"--config",configFile:"eslint.config.mjs"},{name:"prettier",title:"Prettier",command:"npx prettier@latest",args:["'**/*.{ts,tsx,js,jsx,json,jsonc,md,cjs,mjs,mts,yml,yaml}'","'!**/sst-env.d.ts'","--write"],configArg:"--config",configFile:"prettier.config.mjs",ignoreArg:"--ignore-path",ignoreFile:".prettierignore"},{name:"biome",title:"Biome",command:"npx @biomejs/biome",args:["check","--write"],configArg:"--config-path",configFile:"biome.jsonc"}],K=D({meta:{name:B.name,version:B.version,description:B.description},args:{all:{type:"boolean",description:"Run all tools"},...Object.fromEntries(q.map((i)=>[i.name,{type:"boolean",description:`Run ${i.title}`}]))},async run({args:i}){console.info("\uD83D\uDD27 Running lint tools...");let b=i;if(b.all)return await X(q);let p=q.filter((j)=>b[j.name]);if(p.length>0)return await X(p);await E(K),process.exit(0)}});async function X(i){let b=[];for(let v of i)b.push({tool:v.name,success:await W(v)});let p=b.filter((v)=>!v.success).map((v)=>v.tool),j=b.filter((v)=>v.success).map((v)=>v.tool);if(j.length>0)console.info(`✓ Completed: ${j.join(", ")}`);if(p.length>0)console.error(`✗ Failed: ${p.join(", ")}`),process.exitCode=1}R(K);
|
|
21
19
|
|
|
22
|
-
//# debugId=
|
|
20
|
+
//# debugId=1073E2C9D9824B2764756E2164756E21
|
package/dist/main.js.map
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../src/
|
|
3
|
+
"sources": ["../src/main.ts", "../src/commands/lint.ts", "../src/utils/tools.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"
|
|
6
|
-
"import
|
|
7
|
-
"import { execSync } from \"child_process\"\nimport { access, appendFile, writeFile } from \"fs/promises\"\nimport { tmpdir } from \"os\"\nimport { dirname, join, resolve } from \"path\"\nimport { readPackageUp } from \"read-package-up\"\nimport { fileURLToPath } from \"url\"\n\nexport interface ToolConfig {\n\tname: string\n\ttitle: string\n\tcommand: string\n\targs: string[]\n\tconfigArg?: string\n\tconfigFile?: string\n\tignoreArg?: string\n\tignoreFile?: string\n}\n\ninterface FailureLogData {\n\ttool: string\n\ttoolVersion?: string\n\tcommand: string\n\targs: string[]\n\texitCode?: number\n\tstdout?: string\n\tstderr?: string\n\tworkingDir: string\n\ttimestamp: string\n}\n\nexport async function runTool(tool: ToolConfig): Promise<boolean> {\n\tconst packageRoot = await getPackageRoot()\n\n\tconst args = [...tool.args]\n\n\t// Add config file if specified\n\tif (tool.configArg && tool.configFile) {\n\t\tconst configPath = resolve(packageRoot, tool.configFile)\n\t\targs.push(tool.configArg, configPath)\n\t}\n\n\t// Add ignore file if specified\n\tif (tool.ignoreArg && tool.ignoreFile) {\n\t\tconst ignorePath = resolve(packageRoot, tool.ignoreFile)\n\t\targs.push(tool.ignoreArg, ignorePath)\n\t}\n\n\ttry {\n\t\tconst command = `${tool.command} ${args.join(\" \")}`\n\t\tconsole.info(`🔧 Running ${tool.title}...`)\n\t\tconsole.info(` Command: ${command}`)\n\n\t\t// Capture output to suppress it on success\n\t\texecSync(command, {\n\t\t\tstdio: \"pipe\",\n\t\t\tcwd: process.cwd(),\n\t\t\tencoding: \"utf8\"\n\t\t})\n\n\t\tconsole.info(`✅ ${tool.title} completed`)\n\t\treturn true\n\t} catch (error) {\n\t\t// On error, capture and save failure log\n\t\tconsole.info(`❌ ${tool.title} failed`)\n\n\t\tlet stdout = \"\"\n\t\tlet stderr = \"\"\n\t\tlet exitCode: number | undefined\n\t\tlet errorMessage = \"\"\n\n\t\tif (error && typeof error === \"object\") {\n\t\t\tconst execError = error as {\n\t\t\t\tstdout?: string | Buffer\n\t\t\t\tstderr?: string | Buffer\n\t\t\t\tmessage?: string\n\t\t\t\tstatus?: number\n\t\t\t\tsignal?: string\n\t\t\t}\n\t\t\tstdout = execError.stdout?.toString() || \"\"\n\t\t\tstderr = execError.stderr?.toString() || \"\"\n\t\t\terrorMessage = execError.message || \"\"\n\t\t\texitCode = execError.status\n\t\t} else {\n\t\t\terrorMessage = String(error)\n\t\t}\n\n\t\t// Save failure log\n\t\tconst logData: {\n\t\t\ttool: string\n\t\t\tcommand: string\n\t\t\targs: string[]\n\t\t\texitCode?: number\n\t\t\tstdout?: string\n\t\t\tstderr?: string\n\t\t\tworkingDir: string\n\t\t\ttimestamp: string\n\t\t} = {\n\t\t\ttool: tool.name,\n\t\t\tcommand: tool.command,\n\t\t\targs,\n\t\t\tworkingDir: process.cwd(),\n\t\t\ttimestamp: new Date().toISOString()\n\t\t}\n\t\tif (exitCode !== undefined) {\n\t\t\tlogData.exitCode = exitCode\n\t\t}\n\t\tif (stdout) {\n\t\t\tlogData.stdout = stdout\n\t\t}\n\t\tif (stderr || errorMessage) {\n\t\t\tlogData.stderr = stderr || errorMessage\n\t\t}\n\t\tconst logPath = await saveFailureLog(logData)\n\n\t\t// Show concise error output\n\t\tif (stdout || stderr || errorMessage) {\n\t\t\tconst output = stdout || stderr || errorMessage\n\t\t\t// Show first few lines only\n\t\t\tconst lines = output.split(\"\\n\").slice(0, 5)\n\t\t\tconsole.error(lines.join(\"\\n\"))\n\t\t\tif (output.split(\"\\n\").length > 5) {\n\t\t\t\tconsole.error(\"...\")\n\t\t\t}\n\t\t}\n\n\t\t// Show log path message\n\t\tif (logPath) {\n\t\t\t// Log is in tmp folder, show full path\n\t\t\tconsole.error(\"\")\n\t\t\tconsole.error(`📄 Full failure log saved to: ${logPath}`)\n\n\t\t\t// Append to GitHub Step Summary if in CI\n\t\t\tawait appendToGitHubStepSummary(\n\t\t\t\t`### ❌ ${tool.title} Failed\\n\\n` +\n\t\t\t\t\t`**Full log:** \\`${logPath}\\`\\n\\n` +\n\t\t\t\t\t`<details><summary>View error details</summary>\\n\\n` +\n\t\t\t\t\t`\\`\\`\\`\\n${(stderr || stdout || errorMessage).slice(0, 500)}\\n\\`\\`\\`\\n\\n` +\n\t\t\t\t\t`</details>`\n\t\t\t)\n\t\t} else {\n\t\t\tconsole.error(stdout || stderr || errorMessage)\n\t\t}\n\n\t\treturn false\n\t}\n}\n\n// Find the package root using read-package-up\nexport async function getPackageRoot(): Promise<string> {\n\tconst __filename = fileURLToPath(import.meta.url)\n\tconst __dirname = dirname(__filename)\n\n\tconst result = await readPackageUp({ cwd: __dirname })\n\tif (!result) {\n\t\tthrow new Error(\"Could not find package.json\")\n\t}\n\t// result.path is the path to package.json, so get its directory\n\treturn dirname(result.path)\n}\n\nexport async function saveFailureLog(data: FailureLogData): Promise<string | null> {\n\ttry {\n\t\tconst tmpDir = tmpdir()\n\n\t\t// Create log entry\n\t\tconst logEntry = createLogEntry(data)\n\n\t\t// Generate filename: circlesac-lint-<program>-<timestamp>.log\n\t\tconst timestamp = new Date().toISOString().replace(/[:.]/g, \"-\")\n\t\tconst filename = `circlesac-lint-${data.tool}-${timestamp}.log`\n\t\tconst logPath = join(tmpDir, filename)\n\n\t\t// Write log file\n\t\tawait writeFile(logPath, logEntry, \"utf8\")\n\n\t\treturn logPath\n\t} catch {\n\t\t// Silently fail if we can't write logs\n\t\treturn null\n\t}\n}\n\nfunction createLogEntry(data: FailureLogData): string {\n\tconst lines: string[] = []\n\tlines.push(\"=\".repeat(80))\n\tlines.push(`Tool: ${data.tool}`)\n\tif (data.toolVersion) lines.push(`Version: ${data.toolVersion}`)\n\tlines.push(`Command: ${data.command}`)\n\tlines.push(`Arguments: ${JSON.stringify(data.args)}`)\n\tif (data.exitCode !== undefined) lines.push(`Exit Code: ${data.exitCode}`)\n\tlines.push(`Working Directory: ${data.workingDir}`)\n\tlines.push(`Timestamp: ${data.timestamp}`)\n\tlines.push(\"=\".repeat(80))\n\tlines.push(\"\")\n\n\tif (data.stdout) {\n\t\tconst stdout = data.stdout.toString()\n\t\tlines.push(\"STDOUT:\")\n\t\tlines.push(\"-\".repeat(80))\n\t\tlines.push(stdout)\n\t\tlines.push(\"\")\n\t}\n\n\tif (data.stderr) {\n\t\tconst stderr = data.stderr.toString()\n\t\tlines.push(\"STDERR:\")\n\t\tlines.push(\"-\".repeat(80))\n\t\tlines.push(stderr)\n\t\tlines.push(\"\")\n\t}\n\n\treturn lines.join(\"\\n\")\n}\n\nexport async function appendToGitHubStepSummary(message: string): Promise<void> {\n\ttry {\n\t\tconst summaryPath = process.env.GITHUB_STEP_SUMMARY\n\t\tif (!summaryPath) return\n\n\t\t// Check if file exists, then append with newline prefix if it does\n\t\tlet summaryContent = \"\"\n\t\ttry {\n\t\t\tawait access(summaryPath)\n\t\t\tsummaryContent = \"\\n\"\n\t\t} catch {\n\t\t\t// File doesn't exist, start fresh\n\t\t}\n\t\tawait appendFile(summaryPath, summaryContent + message + \"\\n\", \"utf8\")\n\t} catch {\n\t\t// Silently fail if we can't write to GitHub summary (env var not set or other errors)\n\t}\n}\n"
|
|
8
|
-
"import { BaseCommand } from \"../utils/base.js\"\nimport { runTool, type ToolConfig } from \"../utils/tools.js\"\n\nconst tools: ToolConfig[] = [\n\t{\n\t\tname: \"oxlint\",\n\t\ttitle: \"Oxlint\",\n\t\tcommand: \"npx oxlint\",\n\t\targs: [\"--fix\"]\n\t},\n\t{\n\t\tname: \"eslint\",\n\t\ttitle: \"ESLint\",\n\t\tcommand: \"npx eslint\",\n\t\targs: [\"--fix\"],\n\t\tconfigArg: \"--config\",\n\t\tconfigFile: \"eslint.config.mjs\"\n\t},\n\t{\n\t\tname: \"prettier\",\n\t\ttitle: \"Prettier\",\n\t\tcommand: \"npx prettier@latest\",\n\t\targs: [\"'**/*.{ts,tsx,js,jsx,json,jsonc,md,cjs,mjs,mts,yml,yaml}'\", \"'!**/sst-env.d.ts'\", \"--write\"],\n\t\tconfigArg: \"--config\",\n\t\tconfigFile: \"prettier.config.mjs\",\n\t\tignoreArg: \"--ignore-path\",\n\t\tignoreFile: \".prettierignore\"\n\t},\n\t{\n\t\tname: \"biome\",\n\t\ttitle: \"Biome\",\n\t\tcommand: \"npx @biomejs/biome\",\n\t\targs: [\"check\", \"--write\"],\n\t\tconfigArg: \"--config-path\",\n\t\tconfigFile: \"biome.jsonc\"\n\t}\n]\n\nexport class LintCommand extends BaseCommand {\n\tconstructor() {\n\t\tsuper(\"lint\")\n\t\tthis.option(\"--all\", \"Run all tools\")\n\t\tfor (const tool of tools) {\n\t\t\tthis.option(`--${tool.name}`, `Run ${tool.title}`)\n\t\t}\n\t}\n\n\tprotected async execute(options: Record<string, boolean>) {\n\t\tconsole.info(\"🔧 Running lint tools...\")\n\n\t\t// Validate options and determine which tools to run\n\t\tconst enabledTools = this.getEnabledTools(tools, options)\n\n\t\t// Run enabled tools sequentially\n\t\tconst results: { tool: string; success: boolean }[] = []\n\t\tfor (const tool of enabledTools) {\n\t\t\tresults.push({\n\t\t\t\ttool: tool.name,\n\t\t\t\tsuccess: await runTool(tool)\n\t\t\t})\n\t\t}\n\n\t\t// Report results\n\t\tconst failedTools = results.filter((r) => !r.success).map((r) => r.tool)\n\t\tconst successfulTools = results.filter((r) => r.success).map((r) => r.tool)\n\n\t\tif (successfulTools.length > 0) {\n\t\t\tconsole.info(`✓ Completed: ${successfulTools.join(\", \")}`)\n\t\t}\n\n\t\tif (failedTools.length > 0) {\n\t\t\tconsole.error(`✗ Failed: ${failedTools.join(\", \")}`)\n\t\t\tprocess.exitCode = 1\n\t\t}\n\t}\n\n\tprivate getEnabledTools(tools: ToolConfig[], options: Record<string, boolean>): ToolConfig[] {\n\t\t// If --all is specified, run all tools (regardless of individual tool options)\n\t\tif (options.all) return tools\n\n\t\t// Find all specified individual tool options\n\t\tconst selectedTools = tools.filter((tool) => options[tool.name])\n\t\tif (selectedTools.length !== 0) return selectedTools\n\n\t\t// No tools selected, show help and exit\n\t\tthis.help()\n\t\tprocess.exit(0)\n\t\treturn [] // This line is unreachable, but satisfies TypeScript\n\t}\n}\n",
|
|
9
|
-
"#!/usr/bin/env node\n\nimport packageJson from \"../package.json\" with { type: \"json\" }\nimport { LintCommand } from \"./commands/lint.js\"\n\nconst program = new LintCommand()\nprogram.name(packageJson.name)\nprogram.description(packageJson.description)\nprogram.version(packageJson.version)\n\nprogram.parse()\n"
|
|
5
|
+
"#!/usr/bin/env node\n\nimport { runMain } from \"citty\"\nimport { lintCommand } from \"./commands/lint.js\"\n\nrunMain(lintCommand)\n",
|
|
6
|
+
"import { defineCommand, showUsage } from \"citty\"\nimport packageJson from \"../../package.json\" with { type: \"json\" }\nimport { runTool, type ToolConfig } from \"../utils/tools.js\"\n\nconst tools: ToolConfig[] = [\n\t{\n\t\tname: \"oxlint\",\n\t\ttitle: \"Oxlint\",\n\t\tcommand: \"npx oxlint\",\n\t\targs: [\"--fix\"]\n\t},\n\t{\n\t\tname: \"eslint\",\n\t\ttitle: \"ESLint\",\n\t\tcommand: \"npx eslint\",\n\t\targs: [\"--fix\"],\n\t\tconfigArg: \"--config\",\n\t\tconfigFile: \"eslint.config.mjs\"\n\t},\n\t{\n\t\tname: \"prettier\",\n\t\ttitle: \"Prettier\",\n\t\tcommand: \"npx prettier@latest\",\n\t\targs: [\"'**/*.{ts,tsx,js,jsx,json,jsonc,md,cjs,mjs,mts,yml,yaml}'\", \"'!**/sst-env.d.ts'\", \"--write\"],\n\t\tconfigArg: \"--config\",\n\t\tconfigFile: \"prettier.config.mjs\",\n\t\tignoreArg: \"--ignore-path\",\n\t\tignoreFile: \".prettierignore\"\n\t},\n\t{\n\t\tname: \"biome\",\n\t\ttitle: \"Biome\",\n\t\tcommand: \"npx @biomejs/biome\",\n\t\targs: [\"check\", \"--write\"],\n\t\tconfigArg: \"--config-path\",\n\t\tconfigFile: \"biome.jsonc\"\n\t}\n]\n\nexport const lintCommand = defineCommand({\n\tmeta: {\n\t\tname: packageJson.name,\n\t\tversion: packageJson.version,\n\t\tdescription: packageJson.description\n\t},\n\targs: {\n\t\tall: {\n\t\t\ttype: \"boolean\",\n\t\t\tdescription: \"Run all tools\"\n\t\t},\n\t\t...Object.fromEntries(tools.map((tool) => [tool.name, { type: \"boolean\" as const, description: `Run ${tool.title}` }]))\n\t},\n\tasync run({ args }) {\n\t\tconsole.info(\"🔧 Running lint tools...\")\n\n\t\tconst options = args as Record<string, boolean>\n\n\t\t// Validate options and determine which tools to run\n\t\tif (options.all) {\n\t\t\treturn await runTools(tools)\n\t\t}\n\n\t\tconst selectedTools = tools.filter((tool) => options[tool.name])\n\t\tif (selectedTools.length > 0) {\n\t\t\treturn await runTools(selectedTools)\n\t\t}\n\n\t\t// No tools selected, show help and exit\n\t\tawait showUsage(lintCommand)\n\t\tprocess.exit(0)\n\t}\n})\n\nasync function runTools(enabledTools: ToolConfig[]) {\n\tconst results: { tool: string; success: boolean }[] = []\n\tfor (const tool of enabledTools) {\n\t\tresults.push({\n\t\t\ttool: tool.name,\n\t\t\tsuccess: await runTool(tool)\n\t\t})\n\t}\n\n\tconst failedTools = results.filter((r) => !r.success).map((r) => r.tool)\n\tconst successfulTools = results.filter((r) => r.success).map((r) => r.tool)\n\n\tif (successfulTools.length > 0) {\n\t\tconsole.info(`✓ Completed: ${successfulTools.join(\", \")}`)\n\t}\n\n\tif (failedTools.length > 0) {\n\t\tconsole.error(`✗ Failed: ${failedTools.join(\", \")}`)\n\t\tprocess.exitCode = 1\n\t}\n}\n",
|
|
7
|
+
"import { execSync } from \"child_process\"\nimport { access, appendFile, writeFile } from \"fs/promises\"\nimport { tmpdir } from \"os\"\nimport { dirname, join, resolve } from \"path\"\nimport { readPackageUp } from \"read-package-up\"\nimport { fileURLToPath } from \"url\"\n\nexport interface ToolConfig {\n\tname: string\n\ttitle: string\n\tcommand: string\n\targs: string[]\n\tconfigArg?: string\n\tconfigFile?: string\n\tignoreArg?: string\n\tignoreFile?: string\n}\n\ninterface FailureLogData {\n\ttool: string\n\ttoolVersion?: string\n\tcommand: string\n\targs: string[]\n\texitCode?: number\n\tstdout?: string\n\tstderr?: string\n\tworkingDir: string\n\ttimestamp: string\n}\n\nexport async function runTool(tool: ToolConfig): Promise<boolean> {\n\tconst packageRoot = await getPackageRoot()\n\n\tconst args = [...tool.args]\n\n\t// Add config file if specified\n\tif (tool.configArg && tool.configFile) {\n\t\tconst configPath = resolve(packageRoot, tool.configFile)\n\t\targs.push(tool.configArg, configPath)\n\t}\n\n\t// Add ignore file if specified\n\tif (tool.ignoreArg && tool.ignoreFile) {\n\t\tconst ignorePath = resolve(packageRoot, tool.ignoreFile)\n\t\targs.push(tool.ignoreArg, ignorePath)\n\t}\n\n\ttry {\n\t\tconst command = `${tool.command} ${args.join(\" \")}`\n\t\tconsole.info(`🔧 Running ${tool.title}...`)\n\t\tconsole.info(` Command: ${command}`)\n\n\t\t// Capture output to suppress it on success\n\t\texecSync(command, {\n\t\t\tstdio: \"pipe\",\n\t\t\tcwd: process.cwd(),\n\t\t\tencoding: \"utf8\"\n\t\t})\n\n\t\tconsole.info(`✅ ${tool.title} completed`)\n\t\treturn true\n\t} catch (error) {\n\t\t// On error, capture and save failure log\n\t\tconsole.info(`❌ ${tool.title} failed`)\n\n\t\tlet stdout = \"\"\n\t\tlet stderr = \"\"\n\t\tlet exitCode: number | undefined\n\t\tlet errorMessage = \"\"\n\n\t\tif (error && typeof error === \"object\") {\n\t\t\tconst execError = error as {\n\t\t\t\tstdout?: string | Buffer\n\t\t\t\tstderr?: string | Buffer\n\t\t\t\tmessage?: string\n\t\t\t\tstatus?: number\n\t\t\t\tsignal?: string\n\t\t\t}\n\t\t\tstdout = execError.stdout?.toString() || \"\"\n\t\t\tstderr = execError.stderr?.toString() || \"\"\n\t\t\terrorMessage = execError.message || \"\"\n\t\t\texitCode = execError.status\n\t\t} else {\n\t\t\terrorMessage = String(error)\n\t\t}\n\n\t\t// Save failure log\n\t\tconst logData: {\n\t\t\ttool: string\n\t\t\tcommand: string\n\t\t\targs: string[]\n\t\t\texitCode?: number\n\t\t\tstdout?: string\n\t\t\tstderr?: string\n\t\t\tworkingDir: string\n\t\t\ttimestamp: string\n\t\t} = {\n\t\t\ttool: tool.name,\n\t\t\tcommand: tool.command,\n\t\t\targs,\n\t\t\tworkingDir: process.cwd(),\n\t\t\ttimestamp: new Date().toISOString()\n\t\t}\n\t\tif (exitCode !== undefined) {\n\t\t\tlogData.exitCode = exitCode\n\t\t}\n\t\tif (stdout) {\n\t\t\tlogData.stdout = stdout\n\t\t}\n\t\tif (stderr || errorMessage) {\n\t\t\tlogData.stderr = stderr || errorMessage\n\t\t}\n\t\tconst logPath = await saveFailureLog(logData)\n\n\t\t// Show concise error output\n\t\tif (stdout || stderr || errorMessage) {\n\t\t\tconst output = stdout || stderr || errorMessage\n\t\t\t// Show first few lines only\n\t\t\tconst lines = output.split(\"\\n\").slice(0, 5)\n\t\t\tconsole.error(lines.join(\"\\n\"))\n\t\t\tif (output.split(\"\\n\").length > 5) {\n\t\t\t\tconsole.error(\"...\")\n\t\t\t}\n\t\t}\n\n\t\t// Show log path message\n\t\tif (logPath) {\n\t\t\t// Log is in tmp folder, show full path\n\t\t\tconsole.error(\"\")\n\t\t\tconsole.error(`📄 Full failure log saved to: ${logPath}`)\n\n\t\t\t// Append to GitHub Step Summary if in CI\n\t\t\tawait appendToGitHubStepSummary(\n\t\t\t\t`### ❌ ${tool.title} Failed\\n\\n` +\n\t\t\t\t\t`**Full log:** \\`${logPath}\\`\\n\\n` +\n\t\t\t\t\t`<details><summary>View error details</summary>\\n\\n` +\n\t\t\t\t\t`\\`\\`\\`\\n${(stderr || stdout || errorMessage).slice(0, 500)}\\n\\`\\`\\`\\n\\n` +\n\t\t\t\t\t`</details>`\n\t\t\t)\n\t\t} else {\n\t\t\tconsole.error(stdout || stderr || errorMessage)\n\t\t}\n\n\t\treturn false\n\t}\n}\n\n// Find the package root using read-package-up\nexport async function getPackageRoot(): Promise<string> {\n\tconst __filename = fileURLToPath(import.meta.url)\n\tconst __dirname = dirname(__filename)\n\n\tconst result = await readPackageUp({ cwd: __dirname })\n\tif (!result) {\n\t\tthrow new Error(\"Could not find package.json\")\n\t}\n\t// result.path is the path to package.json, so get its directory\n\treturn dirname(result.path)\n}\n\nexport async function saveFailureLog(data: FailureLogData): Promise<string | null> {\n\ttry {\n\t\tconst tmpDir = tmpdir()\n\n\t\t// Create log entry\n\t\tconst logEntry = createLogEntry(data)\n\n\t\t// Generate filename: circlesac-lint-<program>-<timestamp>.log\n\t\tconst timestamp = new Date().toISOString().replace(/[:.]/g, \"-\")\n\t\tconst filename = `circlesac-lint-${data.tool}-${timestamp}.log`\n\t\tconst logPath = join(tmpDir, filename)\n\n\t\t// Write log file\n\t\tawait writeFile(logPath, logEntry, \"utf8\")\n\n\t\treturn logPath\n\t} catch {\n\t\t// Silently fail if we can't write logs\n\t\treturn null\n\t}\n}\n\nfunction createLogEntry(data: FailureLogData): string {\n\tconst lines: string[] = []\n\tlines.push(\"=\".repeat(80))\n\tlines.push(`Tool: ${data.tool}`)\n\tif (data.toolVersion) lines.push(`Version: ${data.toolVersion}`)\n\tlines.push(`Command: ${data.command}`)\n\tlines.push(`Arguments: ${JSON.stringify(data.args)}`)\n\tif (data.exitCode !== undefined) lines.push(`Exit Code: ${data.exitCode}`)\n\tlines.push(`Working Directory: ${data.workingDir}`)\n\tlines.push(`Timestamp: ${data.timestamp}`)\n\tlines.push(\"=\".repeat(80))\n\tlines.push(\"\")\n\n\tif (data.stdout) {\n\t\tconst stdout = data.stdout.toString()\n\t\tlines.push(\"STDOUT:\")\n\t\tlines.push(\"-\".repeat(80))\n\t\tlines.push(stdout)\n\t\tlines.push(\"\")\n\t}\n\n\tif (data.stderr) {\n\t\tconst stderr = data.stderr.toString()\n\t\tlines.push(\"STDERR:\")\n\t\tlines.push(\"-\".repeat(80))\n\t\tlines.push(stderr)\n\t\tlines.push(\"\")\n\t}\n\n\treturn lines.join(\"\\n\")\n}\n\nexport async function appendToGitHubStepSummary(message: string): Promise<void> {\n\ttry {\n\t\tconst summaryPath = process.env.GITHUB_STEP_SUMMARY\n\t\tif (!summaryPath) return\n\n\t\t// Check if file exists, then append with newline prefix if it does\n\t\tlet summaryContent = \"\"\n\t\ttry {\n\t\t\tawait access(summaryPath)\n\t\t\tsummaryContent = \"\\n\"\n\t\t} catch {\n\t\t\t// File doesn't exist, start fresh\n\t\t}\n\t\tawait appendFile(summaryPath, summaryContent + message + \"\\n\", \"utf8\")\n\t} catch {\n\t\t// Silently fail if we can't write to GitHub summary (env var not set or other errors)\n\t}\n}\n"
|
|
10
8
|
],
|
|
11
|
-
"mappings": ";
|
|
12
|
-
"debugId": "
|
|
9
|
+
"mappings": "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",
|
|
10
|
+
"debugId": "1073E2C9D9824B2764756E2164756E21",
|
|
13
11
|
"names": []
|
|
14
12
|
}
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
},
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"chalk": "^4.1.2",
|
|
7
|
-
"
|
|
7
|
+
"citty": "^0.2.0",
|
|
8
8
|
"eslint": "^9.34.0",
|
|
9
9
|
"read-package-up": "^11.0.0",
|
|
10
10
|
"typescript-eslint": "^8.42.0"
|
|
@@ -58,5 +58,5 @@
|
|
|
58
58
|
"test": "vitest run --coverage"
|
|
59
59
|
},
|
|
60
60
|
"type": "module",
|
|
61
|
-
"version": "26.2.
|
|
61
|
+
"version": "26.2.2"
|
|
62
62
|
}
|