diff --git a/libs/bot-commander/src/index.ts b/libs/bot-commander/src/index.ts index e9c37f0..6808bff 100644 --- a/libs/bot-commander/src/index.ts +++ b/libs/bot-commander/src/index.ts @@ -201,6 +201,14 @@ export class CommandCenter> { console.log('stop executing handler, reason:', error.message); return; } + if (error instanceof StopErrorWithReply) { + console.log( + 'stop executing handler with reply, reason:', + error.message, + ); + await this.replyText(c, error.message); + return; + } await this.replyText( c, @@ -242,4 +250,13 @@ export class StopError extends Error { } } +/** + * `StopErrorWithReply` is a special error that will be caught by the command center and reply the message to the user. + */ +export class StopErrorWithReply extends Error { + constructor(message: string) { + super(message); + } +} + export * from './utils'; diff --git a/src/constants/opensumi.ts b/src/constants/opensumi.ts index 8129af2..3cf977c 100644 --- a/src/constants/opensumi.ts +++ b/src/constants/opensumi.ts @@ -1,7 +1,3 @@ -export namespace CoreRepo { - export const NEXT_WORKFLOW_FILE = 'release-rc.yml'; -} - export const VERSION_SYNC_KEYWORD = 'versionInfo'; export const kBackportKeyword = 'backport'; @@ -12,7 +8,7 @@ export type RepoInfo = { }; export namespace ActionsRepo { - const info = { + export const info = { owner: 'opensumi', repo: 'actions', }; @@ -38,6 +34,14 @@ export namespace ActionsRepo { ref: 'main', }; + export const CODEBLITZ_RELEASE_NEXT_BY_REF = + 'codeblitz-release-next-by-ref.yml'; + export const CODEBLITZ_RELEASE_NEXT_BY_REF_WORKFLOW = { + ...info, + workflow_id: CODEBLITZ_RELEASE_NEXT_BY_REF, + ref: 'main', + }; + export const BACKPORT_PR_FILE = 'backport-pr.yml'; export const BACKPORT_PR_WORKFLOW = { ...info, diff --git a/src/github/app.ts b/src/github/app.ts index e2b44b2..bd218d1 100644 --- a/src/github/app.ts +++ b/src/github/app.ts @@ -60,7 +60,7 @@ export class App { case 'opensumi/core': file = ActionsRepo.SYNC_FILE; name = 'opensumi'; - await this.opensumiOctoService.syncVersion(version); + await this.opensumiOctoService.syncOpenSumiVersion(version); break; case 'opensumi/codeblitz': file = ActionsRepo.SYNC_CODEBLITZ_FILE; diff --git a/src/github/service/opensumi.ts b/src/github/service/opensumi.ts index 97157dd..ebe629c 100644 --- a/src/github/service/opensumi.ts +++ b/src/github/service/opensumi.ts @@ -4,9 +4,18 @@ import { GitHubService } from '@opensumi/octo-service'; import { firstLine } from '../renderer/line'; export class OpenSumiOctoService extends GitHubService { - async releaseNextVersion(branch: string, workflowRef = 'main') { + async releaseNextVersion( + workflowInfo: { + workflow_id: string; + ref: string; + owner: string; + repo: string; + }, + branch: string, + workflowRef = 'main', + ) { const workflow = await this.octo.actions.createWorkflowDispatch({ - ...ActionsRepo.RELEASE_NEXT_BY_REF_WORKFLOW, + ...workflowInfo, ref: workflowRef, inputs: { ref: branch, @@ -75,7 +84,7 @@ export class OpenSumiOctoService extends GitHubService { }); return workflow; } - async syncVersion(version?: string, workflowRef = 'main') { + async syncOpenSumiVersion(version?: string, workflowRef = 'main') { const inputs = {} as Record; if (version) { inputs.version = version; diff --git a/src/im/commands/opensumi.ts b/src/im/commands/opensumi.ts index 44de306..066b02f 100644 --- a/src/im/commands/opensumi.ts +++ b/src/im/commands/opensumi.ts @@ -1,5 +1,6 @@ -import { CoreRepo } from '@/constants/opensumi'; +import { ActionsRepo, getActionsUrl } from '@/constants/opensumi'; import { convertToDingMarkdown } from '@/github/dingtalk'; +import { StopError, StopErrorWithReply } from '@opensumi/bot-commander'; import { IBotAdapter } from '../types'; @@ -23,17 +24,25 @@ async function repoIntercept(bot: IBotAdapter, ctx: Context, repo: string) { } export function registerOpenSumiCommand(it: IMCommandCenter) { - it.on('deploy', async ({ bot, ctx }) => { + const intercept = async (bot: IBotAdapter, ctx: Context) => { if (await repoIntercept(bot, ctx, KnownRepo.OpenSumi)) { - return; + throw new StopError('command only works in opensumi repo'); } await replyIfAppNotDefined(bot, ctx); if (!hasApp(ctx)) { - return; + throw new StopErrorWithReply( + 'current bot has not configured use GitHub App', + ); } - const { app } = ctx; + return hasApp(ctx); + }; + + it.on('deploy', async ({ bot, ctx }) => { + await intercept(bot, ctx); + + const app = ctx.app!; let text = ''; try { text = @@ -59,21 +68,15 @@ export function registerOpenSumiCommand(it: IMCommandCenter) { }); it.on('deploypre', async ({ bot, ctx }) => { - if (await repoIntercept(bot, ctx, KnownRepo.OpenSumi)) { - return; - } + await intercept(bot, ctx); - await replyIfAppNotDefined(bot, ctx); - if (!hasApp(ctx)) { - return; - } + const app = ctx.app!; let workflowRef: string | undefined = undefined; if (ctx.parsed.raw['workflow-ref']) { workflowRef = ctx.parsed.raw['workflow-ref']; } - const { app } = ctx; let text = ''; try { text = @@ -95,80 +98,40 @@ export function registerOpenSumiCommand(it: IMCommandCenter) { }); it.on('rc', async ({ bot, ctx }) => { - if (await repoIntercept(bot, ctx, KnownRepo.OpenSumi)) { - return; - } - - await replyIfAppNotDefined(bot, ctx); - if (!hasApp(ctx)) { - return; - } + await intercept(bot, ctx); await bot.replyText( '[rc 命令已经 deprecated, 请使用 nx 命令] 开始发布 next 版本', ); - await publishNextVersion({ ctx, bot }); + await publishNextVersion('nx', { ctx, bot }, 'core'); }); it.on('nx', async ({ bot, ctx }) => { - if (await repoIntercept(bot, ctx, KnownRepo.OpenSumi)) { - return; - } - - await replyIfAppNotDefined(bot, ctx); - if (!hasApp(ctx)) { - return; - } + await intercept(bot, ctx); + await publishNextVersion('nx', { ctx, bot }, 'core'); + }); - await publishNextVersion({ ctx, bot }); + it.on('nx-cb', async ({ bot, ctx }) => { + await intercept(bot, ctx); + await publishNextVersion('nx-cb', { ctx, bot }, 'codeblitz'); }); it.on('sync', async ({ bot, ctx }) => { - if (await repoIntercept(bot, ctx, KnownRepo.OpenSumi)) { - return; - } - - await replyIfAppNotDefined(bot, ctx); - if (!hasApp(ctx)) { - return; - } - - const { app } = ctx; - - let version = ctx.parsed.raw.version; - let workflowRef: string | undefined = undefined; - - if (!version) { - if (ctx.parsed['_'].length > 1) { - version = ctx.parsed['_'][1]; - } - if (ctx.parsed.raw['workflow-ref']) { - workflowRef = ctx.parsed.raw['workflow-ref']; - } - } - try { - await app.opensumiOctoService.syncVersion(version, workflowRef); - await bot.reply( - convertToDingMarkdown( - 'starts synchronizing packages', - `[starts synchronizing packages${ - version ? `@${version}` : '' - } to npmmirror](https://github.com/opensumi/actions/actions/workflows/sync.yml)`, - ), - ); - } catch (error) { - await bot.replyText(`执行出错:${(error as Error).message}`); - } + await intercept(bot, ctx); + await syncVersion({ ctx, bot }, 'core'); + }); + it.on('sync-cb', async ({ bot, ctx }) => { + await intercept(bot, ctx); + await syncVersion({ ctx, bot }, 'codeblitz'); }); + it.on( 'report', async ({ bot, ctx }) => { - await replyIfAppNotDefined(bot, ctx); - if (!hasApp(ctx)) { - return; - } - const { app } = ctx; + await intercept(bot, ctx); + + const app = ctx.app!; const params = {} as { time?: string }; @@ -183,7 +146,11 @@ export function registerOpenSumiCommand(it: IMCommandCenter) { ); } -async function publishNextVersion({ ctx, bot }: IMCommandCenterContext) { +async function publishNextVersion( + command: string, + { ctx, bot }: IMCommandCenterContext, + repo: 'core' | 'codeblitz', +) { const app = ctx.app!; let ref = ctx.parsed.raw.ref; @@ -202,7 +169,7 @@ async function publishNextVersion({ ctx, bot }: IMCommandCenterContext) { if (ref) { try { try { - await app.octoService.getRefInfoByRepo(ref, 'opensumi', 'core'); + await app.octoService.getRefInfoByRepo(ref, 'opensumi', repo); } catch (error) { await bot.replyText( `找不到 ref: ${ref}, 错误信息: ${(error as Error).message}`, @@ -210,21 +177,87 @@ async function publishNextVersion({ ctx, bot }: IMCommandCenterContext) { } const text = await app.opensumiOctoService.getLastNCommitsText({ owner: 'opensumi', - repo: 'core', + repo: repo, ref, }); - await app.opensumiOctoService.releaseNextVersion(ref, workflowRef); + const name = repo === 'core' ? 'OpenSumi' : 'CodeBlitz'; + const workflowInfo = + repo === 'core' + ? ActionsRepo.RELEASE_NEXT_BY_REF_WORKFLOW + : ActionsRepo.CODEBLITZ_RELEASE_NEXT_BY_REF_WORKFLOW; + + await app.opensumiOctoService.releaseNextVersion( + workflowInfo, + ref, + workflowRef, + ); await bot.reply( convertToDingMarkdown( - 'Starts releasing the next version', - `Starts releasing the [next version](https://github.com/opensumi/core/actions/workflows/${CoreRepo.NEXT_WORKFLOW_FILE}) on ${ref}\n\n${text}`, + `Releasing a next version of ${name}`, + `Releasing a [next version of ${name}](${getActionsUrl( + workflowInfo, + )}) on ${ref}\n\n${text}`, ), ); } catch (error) { await bot.replyText(`执行出错:${(error as Error).message}`); } } else { - await bot.replyText(`使用方法 nx --ref v2.xx 或 nx v2.xx`); + await bot.replyText(`使用方法 ${command} --ref v2.xx 或 ${command} v2.xx`); + } +} + +async function syncVersion( + { ctx, bot }: IMCommandCenterContext, + repo: 'core' | 'codeblitz', +) { + const app = ctx.app!; + + let version = ctx.parsed.raw.version; + let workflowRef: string | undefined = undefined; + + if (!version) { + if (ctx.parsed['_'].length > 1) { + version = ctx.parsed['_'][1]; + } + if (ctx.parsed.raw['workflow-ref']) { + workflowRef = ctx.parsed.raw['workflow-ref']; + } + } + try { + if (repo === 'core') { + const actionUrl = getActionsUrl({ + ...ActionsRepo.info, + workflow_id: ActionsRepo.SYNC_FILE, + }); + + await app.opensumiOctoService.syncOpenSumiVersion(version, workflowRef); + await bot.reply( + convertToDingMarkdown( + 'Synchronizing OpenSumi packages', + `[Synchronizing OpenSumi packages${ + version ? `@${version}` : '' + } to npmmirror](${actionUrl})`, + ), + ); + } else { + const actionUrl = getActionsUrl({ + ...ActionsRepo.info, + workflow_id: ActionsRepo.SYNC_CODEBLITZ_FILE, + }); + + await app.opensumiOctoService.syncCodeblitzVersion(version, workflowRef); + await bot.reply( + convertToDingMarkdown( + 'Synchronizing CodeBlitz packages', + `[Synchronizing CodeBlitz packages${ + version ? `@${version}` : '' + } to npmmirror](${actionUrl})`, + ), + ); + } + } catch (error) { + await bot.replyText(`sync 出错:${(error as Error).message}`); } } diff --git a/src/im/commands/utils.ts b/src/im/commands/utils.ts index 3d23cc9..1117ca9 100644 --- a/src/im/commands/utils.ts +++ b/src/im/commands/utils.ts @@ -1,4 +1,4 @@ -import { StopError } from '@opensumi/bot-commander'; +import { StopError, StopErrorWithReply } from '@opensumi/bot-commander'; import { IBotAdapter } from '../types'; @@ -12,10 +12,9 @@ export function hasApp( export async function replyIfAppNotDefined(bot: IBotAdapter, ctx: Context) { if (!hasApp(ctx)) { - await bot.replyText( + throw new StopErrorWithReply( 'current bot has not configured use GitHub App. Please contact admin.', ); - throw new StopError('current bot has not configured use GitHub App'); } } @@ -25,10 +24,7 @@ export async function getGitHubUserFromDingtalkId(bot: IBotAdapter) { dingtalkId, ); if (!githubId) { - await bot.replyText( - 'it seem that you have not bind GitHub account, use `bind-github username` command to bind your GitHub account. e.g. `bind-github bytemain`', - ); - throw new StopError( + throw new StopErrorWithReply( 'it seem that you have not bind GitHub account, use `bind-github username` command to bind your GitHub account. e.g. `bind-github bytemain', ); }