@bensandee/tooling 0.37.0 → 0.38.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/LICENSE +21 -0
- package/dist/bin.mjs +442 -221
- package/dist/{check-Ceom_OgJ.mjs → check-hutCTIXe.mjs} +45 -18
- package/dist/docker-check/index.d.mts +17 -12
- package/dist/docker-check/index.mjs +2 -2
- package/dist/index.d.mts +2 -0
- package/package.json +10 -4
- package/tooling.schema.json +13 -2
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as clack from "@clack/prompts";
|
|
2
|
-
import {
|
|
2
|
+
import { execFileSync } from "node:child_process";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
//#region src/utils/log.ts
|
|
5
5
|
const out = (msg) => console.log(msg);
|
|
@@ -18,6 +18,13 @@ function note(body, title) {
|
|
|
18
18
|
}
|
|
19
19
|
//#endregion
|
|
20
20
|
//#region src/utils/debug.ts
|
|
21
|
+
const REDACT_PATTERNS = [/x-access-token:[^\s@]+/g, /Authorization:\s*(?:token|Bearer)\s+\S+/gi];
|
|
22
|
+
/** Redact secrets (tokens, credentials) from text before logging. */
|
|
23
|
+
function redact(text) {
|
|
24
|
+
let result = text;
|
|
25
|
+
for (const pattern of REDACT_PATTERNS) result = result.replace(pattern, "[REDACTED]");
|
|
26
|
+
return result;
|
|
27
|
+
}
|
|
21
28
|
/** Check if verbose/debug mode is enabled via environment variables. */
|
|
22
29
|
function isEnvVerbose() {
|
|
23
30
|
return process.env["TOOLING_DEBUG"] === "true";
|
|
@@ -29,9 +36,9 @@ function debug(config, message) {
|
|
|
29
36
|
/** Log the result of an exec call when verbose mode is enabled. */
|
|
30
37
|
function debugExec(config, label, result) {
|
|
31
38
|
if (!config.verbose) return;
|
|
32
|
-
const lines = [`[debug] ${label} (exit code ${String(result.exitCode)})`];
|
|
33
|
-
if (result.stdout.trim()) lines.push(` stdout: ${result.stdout.trim()}`);
|
|
34
|
-
if (result.stderr.trim()) lines.push(` stderr: ${result.stderr.trim()}`);
|
|
39
|
+
const lines = [`[debug] ${redact(label)} (exit code ${String(result.exitCode)})`];
|
|
40
|
+
if (result.stdout.trim()) lines.push(` stdout: ${redact(result.stdout.trim())}`);
|
|
41
|
+
if (result.stderr.trim()) lines.push(` stderr: ${redact(result.stderr.trim())}`);
|
|
35
42
|
log.info(lines.join("\n"));
|
|
36
43
|
}
|
|
37
44
|
//#endregion
|
|
@@ -45,10 +52,10 @@ function isExecSyncError(err) {
|
|
|
45
52
|
/** Create an executor that runs real commands, fetches, and manages process signals. */
|
|
46
53
|
function createRealExecutor() {
|
|
47
54
|
return {
|
|
48
|
-
exec(
|
|
55
|
+
exec(file, args, options) {
|
|
49
56
|
try {
|
|
50
57
|
return {
|
|
51
|
-
stdout:
|
|
58
|
+
stdout: execFileSync(file, args, {
|
|
52
59
|
cwd: options?.cwd,
|
|
53
60
|
env: options?.env ? {
|
|
54
61
|
...process.env,
|
|
@@ -77,8 +84,8 @@ function createRealExecutor() {
|
|
|
77
84
|
};
|
|
78
85
|
}
|
|
79
86
|
},
|
|
80
|
-
execInherit(
|
|
81
|
-
|
|
87
|
+
execInherit(file, args, options) {
|
|
88
|
+
execFileSync(file, args, {
|
|
82
89
|
cwd: options?.cwd,
|
|
83
90
|
env: options?.env ? {
|
|
84
91
|
...process.env,
|
|
@@ -107,30 +114,45 @@ const ContainerInfoSchema = z.object({
|
|
|
107
114
|
Service: z.string(),
|
|
108
115
|
Health: z.string()
|
|
109
116
|
});
|
|
110
|
-
/** Build the `docker compose` base
|
|
111
|
-
function
|
|
112
|
-
|
|
117
|
+
/** Build the `docker compose` base args from config. */
|
|
118
|
+
function composeArgs(config) {
|
|
119
|
+
const args = ["compose"];
|
|
120
|
+
for (const f of config.composeFiles) args.push("-f", f);
|
|
121
|
+
if (config.envFile) args.push("--env-file", config.envFile);
|
|
122
|
+
return args;
|
|
113
123
|
}
|
|
114
124
|
/** Run the build command if configured. */
|
|
115
125
|
function buildImages(executor, config) {
|
|
116
126
|
if (!config.buildCommand) return;
|
|
117
|
-
|
|
127
|
+
const [file, ...args] = config.buildCommand;
|
|
128
|
+
if (!file) return;
|
|
129
|
+
executor.execInherit(file, args, { cwd: config.buildCwd ?? config.compose.cwd });
|
|
118
130
|
}
|
|
119
131
|
/** Start the compose stack in detached mode. */
|
|
120
132
|
function composeUp(executor, config) {
|
|
121
|
-
executor.execInherit(
|
|
133
|
+
executor.execInherit("docker", [
|
|
134
|
+
...composeArgs(config),
|
|
135
|
+
"up",
|
|
136
|
+
"-d"
|
|
137
|
+
], { cwd: config.cwd });
|
|
122
138
|
}
|
|
123
139
|
/** Tear down the compose stack, removing volumes and orphans. Swallows errors. */
|
|
124
140
|
function composeDown(executor, config) {
|
|
125
141
|
try {
|
|
126
|
-
executor.execInherit(
|
|
142
|
+
executor.execInherit("docker", [
|
|
143
|
+
...composeArgs(config),
|
|
144
|
+
"down",
|
|
145
|
+
"-v",
|
|
146
|
+
"--remove-orphans"
|
|
147
|
+
], { cwd: config.cwd });
|
|
127
148
|
} catch (_error) {}
|
|
128
149
|
}
|
|
129
150
|
/** Show logs for a specific service (or all services if not specified). Swallows errors. */
|
|
130
151
|
function composeLogs(executor, config, service) {
|
|
131
152
|
try {
|
|
132
|
-
const
|
|
133
|
-
|
|
153
|
+
const args = [...composeArgs(config), "logs"];
|
|
154
|
+
if (service) args.push(service);
|
|
155
|
+
executor.execInherit("docker", args, { cwd: config.cwd });
|
|
134
156
|
} catch (_error) {}
|
|
135
157
|
}
|
|
136
158
|
/**
|
|
@@ -138,7 +160,12 @@ function composeLogs(executor, config, service) {
|
|
|
138
160
|
* Handles both JSON array and newline-delimited JSON (varies by docker compose version).
|
|
139
161
|
*/
|
|
140
162
|
function composePs(executor, config) {
|
|
141
|
-
const output = executor.exec(
|
|
163
|
+
const output = executor.exec("docker", [
|
|
164
|
+
...composeArgs(config),
|
|
165
|
+
"ps",
|
|
166
|
+
"--format",
|
|
167
|
+
"json"
|
|
168
|
+
], { cwd: config.cwd }).stdout.trim();
|
|
142
169
|
if (!output) return [];
|
|
143
170
|
const ArraySchema = z.array(ContainerInfoSchema);
|
|
144
171
|
try {
|
|
@@ -275,4 +302,4 @@ async function runDockerCheck(executor, config) {
|
|
|
275
302
|
}
|
|
276
303
|
}
|
|
277
304
|
//#endregion
|
|
278
|
-
export { composeDown as a, composeUp as c, debug as d, debugExec as f, note as h,
|
|
305
|
+
export { composeDown as a, composeUp as c, debug as d, debugExec as f, note as g, log as h, composeArgs as i, createRealExecutor as l, redact as m, checkHttpHealth as n, composeLogs as o, isEnvVerbose as p, getContainerHealth as r, composePs as s, runDockerCheck as t, isExecSyncError as u };
|
|
@@ -11,17 +11,22 @@ interface ExecResult {
|
|
|
11
11
|
interface ExecOptions {
|
|
12
12
|
cwd?: string;
|
|
13
13
|
env?: Record<string, string>;
|
|
14
|
+
/** Data to pass to the command via stdin. */
|
|
15
|
+
input?: string;
|
|
16
|
+
}
|
|
17
|
+
/** Shared command execution and HTTP fetch interface. */
|
|
18
|
+
interface BaseExecutor {
|
|
19
|
+
/** Run a command with arguments and return the result (stdout/stderr captured). */
|
|
20
|
+
exec(file: string, args: string[], options?: ExecOptions): ExecResult;
|
|
21
|
+
/** Run a command with arguments, streaming stdout/stderr to the console. */
|
|
22
|
+
execInherit(file: string, args: string[], options?: ExecOptions): void;
|
|
23
|
+
/** Perform an HTTP request (mirrors the global fetch signature). */
|
|
24
|
+
fetch(url: string, init?: RequestInit): Promise<Response>;
|
|
14
25
|
}
|
|
15
26
|
//#endregion
|
|
16
27
|
//#region src/docker-check/types.d.ts
|
|
17
28
|
/** Abstraction over side effects for testability. */
|
|
18
|
-
interface DockerCheckExecutor {
|
|
19
|
-
/** Run a shell command and return the result (stdout/stderr captured). */
|
|
20
|
-
exec(command: string, options?: ExecOptions): ExecResult;
|
|
21
|
-
/** Run a shell command, streaming stdout/stderr to the console. */
|
|
22
|
-
execInherit(command: string, options?: ExecOptions): void;
|
|
23
|
-
/** Perform an HTTP request. */
|
|
24
|
-
fetch(url: string, init?: RequestInit): Promise<Response>;
|
|
29
|
+
interface DockerCheckExecutor extends BaseExecutor {
|
|
25
30
|
/** Get current time in ms (mockable clock). */
|
|
26
31
|
now(): number;
|
|
27
32
|
/** Sleep for the given duration (mockable delay). */
|
|
@@ -57,8 +62,8 @@ interface ComposeConfig {
|
|
|
57
62
|
interface CheckConfig {
|
|
58
63
|
/** Docker compose settings. */
|
|
59
64
|
compose: ComposeConfig;
|
|
60
|
-
/** Optional build command to run before starting compose (e.g. "pnpm image:build"). */
|
|
61
|
-
buildCommand?: string;
|
|
65
|
+
/** Optional build command to run before starting compose (e.g. ["pnpm", "image:build"]). */
|
|
66
|
+
buildCommand?: string[];
|
|
62
67
|
/** Working directory for the build command (defaults to compose.cwd). */
|
|
63
68
|
buildCwd?: string;
|
|
64
69
|
/** HTTP health checks to poll. All must pass for success. */
|
|
@@ -96,8 +101,8 @@ declare const ContainerInfoSchema: z.ZodObject<{
|
|
|
96
101
|
Health: z.ZodString;
|
|
97
102
|
}, z.core.$strip>;
|
|
98
103
|
type ContainerInfo = z.infer<typeof ContainerInfoSchema>;
|
|
99
|
-
/** Build the `docker compose` base
|
|
100
|
-
declare function
|
|
104
|
+
/** Build the `docker compose` base args from config. */
|
|
105
|
+
declare function composeArgs(config: ComposeConfig): string[];
|
|
101
106
|
/** Start the compose stack in detached mode. */
|
|
102
107
|
declare function composeUp(executor: DockerCheckExecutor, config: ComposeConfig): void;
|
|
103
108
|
/** Tear down the compose stack, removing volumes and orphans. Swallows errors. */
|
|
@@ -116,4 +121,4 @@ declare function getContainerHealth(containers: ContainerInfo[], serviceName: st
|
|
|
116
121
|
/** Run a single HTTP health check, returning true if the validator passes. */
|
|
117
122
|
declare function checkHttpHealth(executor: DockerCheckExecutor, check: HttpHealthCheck): Promise<boolean>;
|
|
118
123
|
//#endregion
|
|
119
|
-
export { type CheckConfig, type CheckResult, type ComposeConfig, type ContainerInfo, type DockerCheckExecutor, type HttpHealthCheck, checkHttpHealth,
|
|
124
|
+
export { type CheckConfig, type CheckResult, type ComposeConfig, type ContainerInfo, type DockerCheckExecutor, type HttpHealthCheck, checkHttpHealth, composeArgs, composeDown, composeLogs, composePs, composeUp, createRealExecutor, getContainerHealth, runDockerCheck };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as composeDown, c as composeUp, i as
|
|
2
|
-
export { checkHttpHealth,
|
|
1
|
+
import { a as composeDown, c as composeUp, i as composeArgs, l as createRealExecutor, n as checkHttpHealth, o as composeLogs, r as getContainerHealth, s as composePs, t as runDockerCheck } from "../check-hutCTIXe.mjs";
|
|
2
|
+
export { checkHttpHealth, composeArgs, composeDown, composeLogs, composePs, composeUp, createRealExecutor, getContainerHealth, runDockerCheck };
|
package/dist/index.d.mts
CHANGED
|
@@ -86,6 +86,8 @@ interface DetectedProjectState {
|
|
|
86
86
|
hasPackageJson: boolean;
|
|
87
87
|
hasTsconfig: boolean;
|
|
88
88
|
hasOxlintConfig: boolean;
|
|
89
|
+
/** Existing oxlint.config.ts already imports @bensandee/config/oxlint/recommended */
|
|
90
|
+
oxlintExtendsRecommended: boolean;
|
|
89
91
|
/** Legacy .oxlintrc.json found (should be migrated to oxlint.config.ts) */
|
|
90
92
|
hasLegacyOxlintJson: boolean;
|
|
91
93
|
hasGitignore: boolean;
|
package/package.json
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bensandee/tooling",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.38.0",
|
|
4
4
|
"description": "CLI tool to bootstrap and maintain standardized TypeScript project tooling",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://codeberg.org/bensandee/tooling.git",
|
|
9
|
+
"directory": "packages/tooling-cli"
|
|
10
|
+
},
|
|
5
11
|
"bin": {
|
|
6
12
|
"bst": "./dist/bin.mjs"
|
|
7
13
|
},
|
|
@@ -44,8 +50,8 @@
|
|
|
44
50
|
"@release-it/bumper": "7.0.5",
|
|
45
51
|
"@types/node": "24.12.0",
|
|
46
52
|
"@types/picomatch": "4.0.2",
|
|
47
|
-
"commit-and-tag-version": "12.7.
|
|
48
|
-
"knip": "
|
|
53
|
+
"commit-and-tag-version": "12.7.1",
|
|
54
|
+
"knip": "6.0.3",
|
|
49
55
|
"lefthook": "2.1.4",
|
|
50
56
|
"oxfmt": "0.41.0",
|
|
51
57
|
"oxlint": "1.56.0",
|
|
@@ -54,7 +60,7 @@
|
|
|
54
60
|
"tsdown": "0.21.4",
|
|
55
61
|
"typescript": "5.9.3",
|
|
56
62
|
"vitest": "4.1.0",
|
|
57
|
-
"@bensandee/config": "0.9.
|
|
63
|
+
"@bensandee/config": "0.9.2"
|
|
58
64
|
},
|
|
59
65
|
"scripts": {
|
|
60
66
|
"build": "tsdown",
|
package/tooling.schema.json
CHANGED
|
@@ -134,8 +134,11 @@
|
|
|
134
134
|
}
|
|
135
135
|
},
|
|
136
136
|
"buildCommand": {
|
|
137
|
-
"description": "Command to build images before checking",
|
|
138
|
-
"type": "
|
|
137
|
+
"description": "Command to build images before checking (e.g. [\"pnpm\", \"image:build\"])",
|
|
138
|
+
"type": "array",
|
|
139
|
+
"items": {
|
|
140
|
+
"type": "string"
|
|
141
|
+
}
|
|
139
142
|
},
|
|
140
143
|
"buildCwd": {
|
|
141
144
|
"description": "Working directory for build command",
|
|
@@ -157,6 +160,14 @@
|
|
|
157
160
|
"additionalProperties": false
|
|
158
161
|
}
|
|
159
162
|
]
|
|
163
|
+
},
|
|
164
|
+
"registryHost": {
|
|
165
|
+
"description": "Docker registry hostname (e.g. ghcr.io, code.example.com)",
|
|
166
|
+
"type": "string"
|
|
167
|
+
},
|
|
168
|
+
"registryNamespace": {
|
|
169
|
+
"description": "Docker registry namespace (e.g. ghcr.io/myorg, code.example.com/myorg)",
|
|
170
|
+
"type": "string"
|
|
160
171
|
}
|
|
161
172
|
},
|
|
162
173
|
"additionalProperties": false
|