configenvy 0.1.0
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/README.md +9 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +84 -0
- package/package.json +46 -0
- package/src/index.ts +99 -0
package/README.md
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# configenvy
|
|
2
|
+
|
|
3
|
+
Catch missing, stale, undocumented, and risky environment variables before setup breaks.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npx configenvy doctor
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
See the full documentation at [github.com/sonsriver4815/configenvy](https://github.com/sonsriver4815/configenvy).
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { writeFile } from "node:fs/promises";
|
|
3
|
+
import { resolve } from "node:path";
|
|
4
|
+
import { Command } from "commander";
|
|
5
|
+
import { buildMarkdownTable, explainVariable, scanProject, toJson } from "configenvy-core";
|
|
6
|
+
const program = new Command();
|
|
7
|
+
program
|
|
8
|
+
.name("configenvy")
|
|
9
|
+
.description("Find missing, unused, undocumented, and risky environment variables.")
|
|
10
|
+
.version("0.1.0");
|
|
11
|
+
program
|
|
12
|
+
.command("doctor")
|
|
13
|
+
.argument("[path]", "project directory", ".")
|
|
14
|
+
.option("--format <format>", "output format: text or json", "text")
|
|
15
|
+
.option("--strict", "treat documentation warnings as errors")
|
|
16
|
+
.action(async (projectPath, options) => {
|
|
17
|
+
await runDoctor(projectPath, options);
|
|
18
|
+
});
|
|
19
|
+
program
|
|
20
|
+
.command("check")
|
|
21
|
+
.argument("[path]", "project directory", ".")
|
|
22
|
+
.option("--ci", "fail on warnings and errors")
|
|
23
|
+
.option("--format <format>", "output format: text or json", "text")
|
|
24
|
+
.action(async (projectPath, options) => {
|
|
25
|
+
await runDoctor(projectPath, { ...options, strict: Boolean(options.ci), ci: Boolean(options.ci) });
|
|
26
|
+
});
|
|
27
|
+
program
|
|
28
|
+
.command("table")
|
|
29
|
+
.argument("[path]", "project directory", ".")
|
|
30
|
+
.option("--out <file>", "write markdown table to a file")
|
|
31
|
+
.action(async (projectPath, options) => {
|
|
32
|
+
const result = await scanProject({ rootDir: resolve(projectPath) });
|
|
33
|
+
const table = buildMarkdownTable(result);
|
|
34
|
+
if (options.out) {
|
|
35
|
+
await writeFile(resolve(options.out), `${table}\n`, "utf8");
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
console.log(table);
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
program
|
|
42
|
+
.command("explain")
|
|
43
|
+
.argument("<variable>", "environment variable name")
|
|
44
|
+
.argument("[path]", "project directory", ".")
|
|
45
|
+
.action(async (variable, projectPath) => {
|
|
46
|
+
const result = await scanProject({ rootDir: resolve(projectPath) });
|
|
47
|
+
console.log(explainVariable(result, variable));
|
|
48
|
+
});
|
|
49
|
+
program.parseAsync(process.argv).catch((error) => {
|
|
50
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
51
|
+
process.exit(3);
|
|
52
|
+
});
|
|
53
|
+
async function runDoctor(projectPath, options) {
|
|
54
|
+
const result = await scanProject({ rootDir: resolve(projectPath), strict: Boolean(options.strict) });
|
|
55
|
+
if (options.format === "json") {
|
|
56
|
+
console.log(toJson(result));
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
printHumanReport(result.diagnostics);
|
|
60
|
+
}
|
|
61
|
+
const hasError = result.diagnostics.some((diagnostic) => diagnostic.severity === "error");
|
|
62
|
+
const hasWarning = result.diagnostics.some((diagnostic) => diagnostic.severity === "warning");
|
|
63
|
+
if (hasError || (options.ci && hasWarning))
|
|
64
|
+
process.exit(2);
|
|
65
|
+
if (hasWarning)
|
|
66
|
+
process.exit(1);
|
|
67
|
+
}
|
|
68
|
+
function printHumanReport(diagnostics) {
|
|
69
|
+
if (diagnostics.length === 0) {
|
|
70
|
+
console.log("PASS configenvy found no environment variable issues.");
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
for (const diagnostic of diagnostics) {
|
|
74
|
+
const label = diagnostic.severity === "error" ? "FAIL" : "WARN";
|
|
75
|
+
console.log(`${label} ${diagnostic.code} ${diagnostic.variable}`);
|
|
76
|
+
console.log(` ${diagnostic.message}`);
|
|
77
|
+
if (diagnostic.files.length > 0) {
|
|
78
|
+
console.log(` files: ${diagnostic.files.join(", ")}`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
const errors = diagnostics.filter((diagnostic) => diagnostic.severity === "error").length;
|
|
82
|
+
const warnings = diagnostics.length - errors;
|
|
83
|
+
console.log(`Summary: ${errors} error(s), ${warnings} warning(s).`);
|
|
84
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "configenvy",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Find missing, unused, undocumented, and risky environment variables before setup breaks.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/sonsriver4815/configenvy.git",
|
|
9
|
+
"directory": "packages/cli"
|
|
10
|
+
},
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/sonsriver4815/configenvy/issues"
|
|
13
|
+
},
|
|
14
|
+
"homepage": "https://github.com/sonsriver4815/configenvy#readme",
|
|
15
|
+
"bin": {
|
|
16
|
+
"configenvy": "dist/index.js"
|
|
17
|
+
},
|
|
18
|
+
"main": "./dist/index.js",
|
|
19
|
+
"types": "./dist/index.d.ts",
|
|
20
|
+
"exports": {
|
|
21
|
+
".": {
|
|
22
|
+
"types": "./dist/index.d.ts",
|
|
23
|
+
"import": "./dist/index.js"
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"files": [
|
|
27
|
+
"dist",
|
|
28
|
+
"src",
|
|
29
|
+
"README.md"
|
|
30
|
+
],
|
|
31
|
+
"scripts": {
|
|
32
|
+
"build": "tsup src/index.ts --format esm --dts --clean"
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"configenvy-core": "0.1.0",
|
|
36
|
+
"commander": "^12.1.0"
|
|
37
|
+
},
|
|
38
|
+
"keywords": [
|
|
39
|
+
"env",
|
|
40
|
+
"dotenv",
|
|
41
|
+
"cli",
|
|
42
|
+
"developer-tools",
|
|
43
|
+
"documentation"
|
|
44
|
+
],
|
|
45
|
+
"license": "MIT"
|
|
46
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { writeFile } from "node:fs/promises";
|
|
3
|
+
import { resolve } from "node:path";
|
|
4
|
+
import { Command } from "commander";
|
|
5
|
+
import { buildMarkdownTable, explainVariable, scanProject, toJson, type Diagnostic } from "configenvy-core";
|
|
6
|
+
|
|
7
|
+
type DoctorOptions = {
|
|
8
|
+
format?: "text" | "json";
|
|
9
|
+
strict?: boolean;
|
|
10
|
+
ci?: boolean;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const program = new Command();
|
|
14
|
+
|
|
15
|
+
program
|
|
16
|
+
.name("configenvy")
|
|
17
|
+
.description("Find missing, unused, undocumented, and risky environment variables.")
|
|
18
|
+
.version("0.1.0");
|
|
19
|
+
|
|
20
|
+
program
|
|
21
|
+
.command("doctor")
|
|
22
|
+
.argument("[path]", "project directory", ".")
|
|
23
|
+
.option("--format <format>", "output format: text or json", "text")
|
|
24
|
+
.option("--strict", "treat documentation warnings as errors")
|
|
25
|
+
.action(async (projectPath: string, options: DoctorOptions) => {
|
|
26
|
+
await runDoctor(projectPath, options);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
program
|
|
30
|
+
.command("check")
|
|
31
|
+
.argument("[path]", "project directory", ".")
|
|
32
|
+
.option("--ci", "fail on warnings and errors")
|
|
33
|
+
.option("--format <format>", "output format: text or json", "text")
|
|
34
|
+
.action(async (projectPath: string, options: DoctorOptions) => {
|
|
35
|
+
await runDoctor(projectPath, { ...options, strict: Boolean(options.ci), ci: Boolean(options.ci) });
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
program
|
|
39
|
+
.command("table")
|
|
40
|
+
.argument("[path]", "project directory", ".")
|
|
41
|
+
.option("--out <file>", "write markdown table to a file")
|
|
42
|
+
.action(async (projectPath: string, options: { out?: string }) => {
|
|
43
|
+
const result = await scanProject({ rootDir: resolve(projectPath) });
|
|
44
|
+
const table = buildMarkdownTable(result);
|
|
45
|
+
if (options.out) {
|
|
46
|
+
await writeFile(resolve(options.out), `${table}\n`, "utf8");
|
|
47
|
+
} else {
|
|
48
|
+
console.log(table);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
program
|
|
53
|
+
.command("explain")
|
|
54
|
+
.argument("<variable>", "environment variable name")
|
|
55
|
+
.argument("[path]", "project directory", ".")
|
|
56
|
+
.action(async (variable: string, projectPath: string) => {
|
|
57
|
+
const result = await scanProject({ rootDir: resolve(projectPath) });
|
|
58
|
+
console.log(explainVariable(result, variable));
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
program.parseAsync(process.argv).catch((error: unknown) => {
|
|
62
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
63
|
+
process.exit(3);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
async function runDoctor(projectPath: string, options: DoctorOptions): Promise<void> {
|
|
67
|
+
const result = await scanProject({ rootDir: resolve(projectPath), strict: Boolean(options.strict) });
|
|
68
|
+
|
|
69
|
+
if (options.format === "json") {
|
|
70
|
+
console.log(toJson(result));
|
|
71
|
+
} else {
|
|
72
|
+
printHumanReport(result.diagnostics);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const hasError = result.diagnostics.some((diagnostic) => diagnostic.severity === "error");
|
|
76
|
+
const hasWarning = result.diagnostics.some((diagnostic) => diagnostic.severity === "warning");
|
|
77
|
+
if (hasError || (options.ci && hasWarning)) process.exit(2);
|
|
78
|
+
if (hasWarning) process.exit(1);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function printHumanReport(diagnostics: Diagnostic[]): void {
|
|
82
|
+
if (diagnostics.length === 0) {
|
|
83
|
+
console.log("PASS configenvy found no environment variable issues.");
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
for (const diagnostic of diagnostics) {
|
|
88
|
+
const label = diagnostic.severity === "error" ? "FAIL" : "WARN";
|
|
89
|
+
console.log(`${label} ${diagnostic.code} ${diagnostic.variable}`);
|
|
90
|
+
console.log(` ${diagnostic.message}`);
|
|
91
|
+
if (diagnostic.files.length > 0) {
|
|
92
|
+
console.log(` files: ${diagnostic.files.join(", ")}`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const errors = diagnostics.filter((diagnostic) => diagnostic.severity === "error").length;
|
|
97
|
+
const warnings = diagnostics.length - errors;
|
|
98
|
+
console.log(`Summary: ${errors} error(s), ${warnings} warning(s).`);
|
|
99
|
+
}
|