refactor(cli): migrate to commander framework and simplify config loading
Publish to Gitea Registry / publish (push) Failing after 4s
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:
@@ -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
|
||||||
|
|||||||
@@ -32,6 +32,7 @@
|
|||||||
},
|
},
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"commander": "^15.0.0",
|
||||||
"js-yaml": "^5.0.0"
|
"js-yaml": "^5.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+35
-34
@@ -1,41 +1,42 @@
|
|||||||
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("显示可用积木列表")
|
||||||
|
.option("--source <path>", "index source path or URL")
|
||||||
|
.action(async (options) => {
|
||||||
|
const source = resolveSource(options.source);
|
||||||
|
const index = await loadIndex(source);
|
||||||
|
|
||||||
run: async (args: string[]) => {
|
if (index.blocks.length === 0) {
|
||||||
const config = resolveConfig(args);
|
console.log("(no blocks found)");
|
||||||
const index = await loadIndex(config);
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (index.blocks.length === 0) {
|
const columns = [
|
||||||
console.log("(no blocks found)");
|
{ key: "name", header: "Name", minWidth: 20, maxWidth: 30 },
|
||||||
return;
|
{ key: "module", header: "Module", minWidth: 8, maxWidth: 12 },
|
||||||
}
|
{ key: "variants", header: "Variants", minWidth: 15, maxWidth: 45 },
|
||||||
|
{ key: "dependsOn", header: "Dependencies", minWidth: 15, maxWidth: 30 },
|
||||||
|
{ key: "description", header: "Description", minWidth: 15, maxWidth: 55 },
|
||||||
|
];
|
||||||
|
|
||||||
const columns = [
|
const rows = index.blocks.map((block) => ({
|
||||||
{ key: "name", header: "Name", minWidth: 20, maxWidth: 30 },
|
name: block.name,
|
||||||
{ key: "module", header: "Module", minWidth: 8, maxWidth: 12 },
|
module: block.module,
|
||||||
{ key: "variants", header: "Variants", minWidth: 15, maxWidth: 45 },
|
variants:
|
||||||
{ key: "dependsOn", header: "Dependencies", minWidth: 15, maxWidth: 30 },
|
block.variants.length > 0
|
||||||
{ key: "description", header: "Description", minWidth: 15, maxWidth: 55 },
|
? block.variants.map((v) => v.label).join(", ")
|
||||||
];
|
: "-",
|
||||||
|
dependsOn:
|
||||||
|
block.dependsOn.length > 0 ? block.dependsOn.join(", ") : "-",
|
||||||
|
description: block.description,
|
||||||
|
}));
|
||||||
|
|
||||||
const rows = index.blocks.map((block) => ({
|
console.log(formatTable(columns, rows));
|
||||||
name: block.name,
|
});
|
||||||
module: block.module,
|
}
|
||||||
variants:
|
|
||||||
block.variants.length > 0
|
|
||||||
? block.variants.map((v) => v.label).join(", ")
|
|
||||||
: "-",
|
|
||||||
dependsOn:
|
|
||||||
block.dependsOn.length > 0 ? block.dependsOn.join(", ") : "-",
|
|
||||||
description: block.description,
|
|
||||||
}));
|
|
||||||
|
|
||||||
console.log(formatTable(columns, rows));
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|||||||
+8
-36
@@ -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
@@ -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
@@ -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`);
|
||||||
|
|||||||
@@ -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>;
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user