mirror of
https://github.com/Lydanne/spaceflow.git
synced 2026-03-11 19:52:45 +08:00
chore: 初始化仓库
This commit is contained in:
50
commands/ci-shell/src/ci-shell.command.ts
Normal file
50
commands/ci-shell/src/ci-shell.command.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import { Command, CommandRunner, Option } from "nest-commander";
|
||||
import { t } from "@spaceflow/core";
|
||||
import { CiShellService } from "./ci-shell.service";
|
||||
|
||||
export interface CiShellOptions {
|
||||
dryRun: boolean;
|
||||
}
|
||||
|
||||
@Command({
|
||||
name: "ci-shell",
|
||||
description: t("ci-shell:description"),
|
||||
arguments: "<command>",
|
||||
argsDescription: {
|
||||
command: t("ci-shell:argsDescription.command"),
|
||||
},
|
||||
})
|
||||
export class CiShellCommand extends CommandRunner {
|
||||
constructor(protected readonly ciShellService: CiShellService) {
|
||||
super();
|
||||
}
|
||||
|
||||
async run(passedParams: string[], options: CiShellOptions): Promise<void> {
|
||||
const command = passedParams.join(" ");
|
||||
|
||||
if (!command) {
|
||||
console.error(t("ci-shell:noCommand"));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log(`DRY-RUN mode: ${options.dryRun ? "enabled" : "disabled"}`);
|
||||
|
||||
try {
|
||||
const context = this.ciShellService.getContextFromEnv(options);
|
||||
await this.ciShellService.execute(context, command);
|
||||
} catch (error) {
|
||||
console.error(
|
||||
t("common.executionFailed", { error: error instanceof Error ? error.message : error }),
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
@Option({
|
||||
flags: "-d, --dry-run",
|
||||
description: t("common.options.dryRun"),
|
||||
})
|
||||
parseDryRun(val: boolean): boolean {
|
||||
return val;
|
||||
}
|
||||
}
|
||||
11
commands/ci-shell/src/ci-shell.module.ts
Normal file
11
commands/ci-shell/src/ci-shell.module.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { Module } from "@nestjs/common";
|
||||
import { ConfigModule } from "@nestjs/config";
|
||||
import { GitProviderModule, ciConfig } from "@spaceflow/core";
|
||||
import { CiShellCommand } from "./ci-shell.command";
|
||||
import { CiShellService } from "./ci-shell.service";
|
||||
|
||||
@Module({
|
||||
imports: [ConfigModule.forFeature(ciConfig), GitProviderModule.forFeature()],
|
||||
providers: [CiShellCommand, CiShellService],
|
||||
})
|
||||
export class CiShellModule {}
|
||||
149
commands/ci-shell/src/ci-shell.service.ts
Normal file
149
commands/ci-shell/src/ci-shell.service.ts
Normal file
@@ -0,0 +1,149 @@
|
||||
import { Injectable } from "@nestjs/common";
|
||||
import { ConfigService } from "@nestjs/config";
|
||||
import { execSync } from "child_process";
|
||||
import { GitProviderService, BranchProtection, CiConfig } from "@spaceflow/core";
|
||||
import { CiShellOptions } from "./ci-shell.command";
|
||||
|
||||
export interface CiShellContext extends CiShellOptions {
|
||||
owner: string;
|
||||
repo: string;
|
||||
branch: string;
|
||||
}
|
||||
|
||||
export interface CiShellResult {
|
||||
success: boolean;
|
||||
message: string;
|
||||
protection?: BranchProtection | null;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class CiShellService {
|
||||
constructor(
|
||||
protected readonly gitProvider: GitProviderService,
|
||||
protected readonly configService: ConfigService,
|
||||
) {}
|
||||
|
||||
getContextFromEnv(options: CiShellOptions): CiShellContext {
|
||||
this.gitProvider.validateConfig();
|
||||
|
||||
const ciConf = this.configService.get<CiConfig>("ci");
|
||||
const repository = ciConf?.repository;
|
||||
const branch = ciConf?.refName;
|
||||
|
||||
if (!repository) {
|
||||
throw new Error("缺少配置 ci.repository (环境变量 GITHUB_REPOSITORY)");
|
||||
}
|
||||
|
||||
if (!branch) {
|
||||
throw new Error("缺少配置 ci.refName (环境变量 GITHUB_REF_NAME)");
|
||||
}
|
||||
|
||||
const [owner, repo] = repository.split("/");
|
||||
if (!owner || !repo) {
|
||||
throw new Error(`ci.repository 格式不正确,期望 "owner/repo",实际: "${repository}"`);
|
||||
}
|
||||
|
||||
return {
|
||||
owner,
|
||||
repo,
|
||||
branch,
|
||||
...options,
|
||||
};
|
||||
}
|
||||
|
||||
async execute(context: CiShellContext, command: string): Promise<void> {
|
||||
try {
|
||||
// 1. 锁定分支
|
||||
await this.handleBegin(context);
|
||||
|
||||
try {
|
||||
// 2. 执行命令
|
||||
console.log(`🏃 正在执行命令...`);
|
||||
console.log(`> ${command}`);
|
||||
|
||||
if (context.dryRun) {
|
||||
console.log(`🔍 [DRY-RUN] 跳过命令执行`);
|
||||
} else {
|
||||
execSync(command, { stdio: "inherit" });
|
||||
}
|
||||
|
||||
console.log("✅ 命令执行成功");
|
||||
} catch (error) {
|
||||
console.error("❌ 命令执行失败:", error);
|
||||
// 出错时也要尝试解锁
|
||||
await this.handleEnd(context);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// 3. 解锁分支
|
||||
await this.handleEnd(context);
|
||||
} catch (error) {
|
||||
console.error("执行失败:", error instanceof Error ? error.message : error);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
protected async handleBegin(context: CiShellContext): Promise<CiShellResult> {
|
||||
const { owner, repo, branch, dryRun } = context;
|
||||
|
||||
if (dryRun) {
|
||||
console.log(`🔍 [DRY-RUN] 将锁定分支: ${owner}/${repo}#${branch}`);
|
||||
return {
|
||||
success: true,
|
||||
message: "DRY-RUN: 分支锁定已跳过",
|
||||
protection: null,
|
||||
};
|
||||
}
|
||||
|
||||
console.log(`🔒 正在锁定分支: ${owner}/${repo}#${branch}`);
|
||||
|
||||
const protection = await this.gitProvider.lockBranch(owner, repo, branch);
|
||||
|
||||
console.log(`✅ 分支已锁定`);
|
||||
console.log(` 规则名称: ${protection.rule_name || protection.branch_name}`);
|
||||
console.log(` 允许推送: ${protection.enable_push ? "是" : "否"}`);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "分支锁定完成",
|
||||
protection,
|
||||
};
|
||||
}
|
||||
|
||||
protected async handleEnd(context: CiShellContext): Promise<CiShellResult> {
|
||||
const { owner, repo, branch, dryRun } = context;
|
||||
|
||||
if (dryRun) {
|
||||
console.log(`🔍 [DRY-RUN] 将解锁分支: ${owner}/${repo}#${branch}`);
|
||||
return {
|
||||
success: true,
|
||||
message: "DRY-RUN: 分支解锁已跳过",
|
||||
protection: null,
|
||||
};
|
||||
}
|
||||
|
||||
console.log(`🔓 正在解锁分支: ${owner}/${repo}#${branch}`);
|
||||
|
||||
const protection = await this.gitProvider.unlockBranch(owner, repo, branch);
|
||||
|
||||
if (protection) {
|
||||
console.log(`✅ 分支已解锁`);
|
||||
console.log(` 规则名称: ${protection.rule_name || protection.branch_name}`);
|
||||
console.log(` 允许推送: ${protection.enable_push ? "是" : "否"}`);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "分支解锁完成",
|
||||
protection,
|
||||
};
|
||||
} else {
|
||||
console.log(`✅ 分支本身没有保护规则,无需解锁`);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "分支本身没有保护规则,无需解锁",
|
||||
protection: null,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
24
commands/ci-shell/src/index.ts
Normal file
24
commands/ci-shell/src/index.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import "./locales";
|
||||
import { SpaceflowExtension, SpaceflowExtensionMetadata, t } from "@spaceflow/core";
|
||||
import { CiShellModule } from "./ci-shell.module";
|
||||
export class CiShellExtension implements SpaceflowExtension {
|
||||
getMetadata(): SpaceflowExtensionMetadata {
|
||||
return {
|
||||
name: "ci-shell",
|
||||
commands: ["ci-shell"],
|
||||
configKey: "ci-shell",
|
||||
version: "1.0.0",
|
||||
description: t("ci-shell:extensionDescription"),
|
||||
};
|
||||
}
|
||||
|
||||
getModule() {
|
||||
return CiShellModule;
|
||||
}
|
||||
}
|
||||
|
||||
export default CiShellExtension;
|
||||
|
||||
export * from "./ci-shell.command";
|
||||
export * from "./ci-shell.service";
|
||||
export * from "./ci-shell.module";
|
||||
6
commands/ci-shell/src/locales/en/ci-shell.json
Normal file
6
commands/ci-shell/src/locales/en/ci-shell.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"description": "Execute shell commands between branch lock/unlock",
|
||||
"argsDescription.command": "Shell command to execute",
|
||||
"noCommand": "❌ Please provide a shell command to execute",
|
||||
"extensionDescription": "CI shell command for executing shell commands between branch lock/unlock"
|
||||
}
|
||||
11
commands/ci-shell/src/locales/index.ts
Normal file
11
commands/ci-shell/src/locales/index.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { addLocaleResources } from "@spaceflow/core";
|
||||
import zhCN from "./zh-cn/ci-shell.json";
|
||||
import en from "./en/ci-shell.json";
|
||||
|
||||
/** ci-shell 命令 i18n 资源 */
|
||||
export const ciShellLocales: Record<string, Record<string, string>> = {
|
||||
"zh-CN": zhCN,
|
||||
en,
|
||||
};
|
||||
|
||||
addLocaleResources("ci-shell", ciShellLocales);
|
||||
6
commands/ci-shell/src/locales/zh-cn/ci-shell.json
Normal file
6
commands/ci-shell/src/locales/zh-cn/ci-shell.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"description": "在分支锁定/解锁之间执行 Shell 命令",
|
||||
"argsDescription.command": "要执行的 Shell 命令",
|
||||
"noCommand": "❌ 请提供要执行的 Shell 命令",
|
||||
"extensionDescription": "CI Shell 命令,用于在分支锁定/解锁之间执行 Shell 命令"
|
||||
}
|
||||
Reference in New Issue
Block a user