refactor(cli): migrate to commander framework and simplify config loading
Publish to Gitea Registry / publish (push) Failing after 4s

Replace hand-rolled CLI argument parsing with Commander for command
registration and option handling. Streamline config resolution by
removing the BrickConfig type and accepting a plain source string
with BRICK_SOURCE env var support.

- Replace manual CLI dispatch with Commander-based command tree
- Simplify resolveConfig/resolveSource to a single resolveSource function
- Remove BrickConfig interface in favor of direct source string
- Move isRemote check into index-loader as a local utility
- Update loadIndex signature to accept source string directly
This commit is contained in:
yangzhaohan
2026-06-22 16:46:44 +08:00
parent a258407fae
commit 35c351e716
7 changed files with 59 additions and 122 deletions
-7
View File
@@ -12,15 +12,8 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 18
- run: npm ci - run: npm ci
- run: npm run build - run: npm run build
- name: Publish to Gitea npm registry - name: Publish to Gitea npm registry
run: | run: |
echo "//gitea.synoth.com/api/packages/synoth/npm/:_authToken=${{ secrets.GITEA_TOKEN }}" > ~/.npmrc echo "//gitea.synoth.com/api/packages/synoth/npm/:_authToken=${{ secrets.GITEA_TOKEN }}" > ~/.npmrc
+1
View File
@@ -32,6 +32,7 @@
}, },
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"commander": "^15.0.0",
"js-yaml": "^5.0.0" "js-yaml": "^5.0.0"
} }
} }
+12 -11
View File
@@ -1,15 +1,16 @@
import { resolveConfig } from "../lib/config.js"; import { Command } from "commander";
import { resolveSource } from "../lib/config.js";
import { loadIndex } from "../lib/index-loader.js"; import { loadIndex } from "../lib/index-loader.js";
import { formatTable } from "../lib/table.js"; import { formatTable } from "../lib/table.js";
import type { Command } from "../types/index.js";
export const listCommand: Command = { export function registerList(program: Command): void {
name: "list", program
description: "显示可用积木列表", .command("list")
.description("显示可用积木列表")
run: async (args: string[]) => { .option("--source <path>", "index source path or URL")
const config = resolveConfig(args); .action(async (options) => {
const index = await loadIndex(config); const source = resolveSource(options.source);
const index = await loadIndex(source);
if (index.blocks.length === 0) { if (index.blocks.length === 0) {
console.log("(no blocks found)"); console.log("(no blocks found)");
@@ -37,5 +38,5 @@ export const listCommand: Command = {
})); }));
console.log(formatTable(columns, rows)); console.log(formatTable(columns, rows));
}, });
}; }
+8 -36
View File
@@ -1,42 +1,14 @@
#!/usr/bin/env node #!/usr/bin/env node
import { listCommand } from "./commands/list.js"; import { Command } from "commander";
import type { Command } from "./types/index.js"; import { registerList } from "./commands/list.js";
const USAGE = ` const program = new Command();
Brick CLI — 源码级 Spring Boot 脚手架
Usage: program
brick create <project-name> 创建新项目 .name("brick")
brick add <module>[:variant] 向已有项目增量添加积木 .description("Brick CLI — 源码级 Spring Boot 脚手架");
brick list 显示可用积木列表
Examples: registerList(program);
brick create my-app
brick add cache:redisson
brick list
`;
const commands = new Map<string, Command>(); program.parse();
commands.set(listCommand.name, listCommand);
async function main(): Promise<void> {
const args = process.argv.slice(2);
const commandName = args[0];
if (!commandName) {
console.log(USAGE);
process.exit(0);
}
const command = commands.get(commandName);
if (!command) {
console.error(`Unknown command: ${commandName}`);
console.log(USAGE);
process.exit(1);
}
await command.run(args.slice(1));
}
main();
+4 -26
View File
@@ -1,30 +1,8 @@
import type { BrickConfig } from "../types/index.js";
const DEFAULT_SOURCE = const DEFAULT_SOURCE =
"https://gitea.synoth.com/synoth/brick-index/raw/branch/master/blocks.yaml"; "https://gitea.synoth.com/synoth/brick-index/raw/branch/master/blocks.yaml";
function parseSourceFromArgs(args: string[]): string | undefined { export function resolveSource(cliSource?: string): string {
const idx = args.indexOf("--source"); if (cliSource) return cliSource;
if (idx !== -1 && idx + 1 < args.length) { if (process.env.BRICK_SOURCE) return process.env.BRICK_SOURCE;
return args[idx + 1]; return DEFAULT_SOURCE;
}
return undefined;
}
function isRemoteSource(source: string): boolean {
return source.startsWith("http://") || source.startsWith("https://");
}
export function resolveConfig(args: string[]): BrickConfig {
const cliSource = parseSourceFromArgs(args);
if (cliSource) {
return { source: cliSource, isRemote: isRemoteSource(cliSource) };
}
const envSource = process.env.BRICK_SOURCE;
if (envSource) {
return { source: envSource, isRemote: isRemoteSource(envSource) };
}
return { source: DEFAULT_SOURCE, isRemote: true };
} }
+11 -8
View File
@@ -1,24 +1,27 @@
import { readFile } from "node:fs/promises"; import { readFile } from "node:fs/promises";
import { load } from "js-yaml"; import { load } from "js-yaml";
import type { BlockIndex, BrickConfig } from "../types/index.js"; import type { BlockIndex } from "../types/index.js";
export async function loadIndex(config: BrickConfig): Promise<BlockIndex> { const isRemote = (source: string): boolean =>
source.startsWith("http://") || source.startsWith("https://");
export async function loadIndex(source: string): Promise<BlockIndex> {
let raw: string; let raw: string;
if (config.isRemote) { if (isRemote(source)) {
let response: Response; let response: Response;
try { try {
response = await fetch(config.source); response = await fetch(source);
} catch { } catch {
process.stderr.write( process.stderr.write(
`Error: Failed to fetch index from "${config.source}"\n` `Error: Failed to fetch index from "${source}"\n`
); );
process.exit(1); process.exit(1);
} }
if (!response.ok) { if (!response.ok) {
process.stderr.write( process.stderr.write(
`Error: Failed to fetch index from "${config.source}" (HTTP ${response.status})\n` `Error: Failed to fetch index from "${source}" (HTTP ${response.status})\n`
); );
process.exit(1); process.exit(1);
} }
@@ -26,12 +29,12 @@ export async function loadIndex(config: BrickConfig): Promise<BlockIndex> {
raw = await response.text(); raw = await response.text();
} else { } else {
try { try {
raw = await readFile(config.source, "utf-8"); raw = await readFile(source, "utf-8");
} catch (err: unknown) { } catch (err: unknown) {
const code = (err as NodeJS.ErrnoException)?.code; const code = (err as NodeJS.ErrnoException)?.code;
if (code === "ENOENT") { if (code === "ENOENT") {
process.stderr.write( process.stderr.write(
`Error: Index file not found at "${config.source}"\n` `Error: Index file not found at "${source}"\n`
); );
} else { } else {
process.stderr.write(`Error: ${(err as Error).message}\n`); process.stderr.write(`Error: ${(err as Error).message}\n`);
-11
View File
@@ -17,14 +17,3 @@ export interface Block {
export interface BlockIndex { export interface BlockIndex {
blocks: Block[]; blocks: Block[];
} }
export interface BrickConfig {
source: string;
isRemote: boolean;
}
export interface Command {
name: string;
description: string;
run: (args: string[]) => Promise<void>;
}