diff --git a/package.json b/package.json index 0d64efaa94..d1bf0f8f13 100644 --- a/package.json +++ b/package.json @@ -76,7 +76,7 @@ "lint-fix": "npm run lint-fix:script && npm run lint-fix:demo", "lint-fix:demo": "npm run lint:demo -- --fix", "lint-fix:script": "npm run lint:script -- --fix", - "pre-publish": "npm run test-all -- --skip-build && tsx ./scripts/pre-publish-notice.ts", + "pre-publish": "npm run test-all -- --skip-lint --skip-build --skip-dist --skip-es --skip-lib --skip-test --skip-node && tsx ./scripts/pre-publish.ts", "prepare": "is-ci || husky && dumi setup", "prepublishOnly": "antd-tools run guard", "prettier": "prettier -c --write **/* --cache", @@ -180,6 +180,7 @@ "@emotion/server": "^11.11.0", "@ianvs/prettier-plugin-sort-imports": "^4.2.1", "@madccc/duplicate-package-checker-webpack-plugin": "^1.0.0", + "@octokit/rest": "^20.0.2", "@qixian.cs/github-contributors-list": "^2.0.1", "@size-limit/file": "^11.1.2", "@stackblitz/sdk": "^1.9.0", @@ -203,6 +204,7 @@ "@types/minimist": "^1.2.5", "@types/node": "^20.12.2", "@types/nprogress": "^0.2.3", + "@types/ora": "^3.2.0", "@types/pixelmatch": "^5.2.6", "@types/pngjs": "^6.0.4", "@types/prismjs": "^1.26.3", @@ -280,6 +282,7 @@ "node-notifier": "^10.0.1", "nprogress": "^0.2.0", "open": "^10.1.0", + "ora": "^8.0.1", "pixelmatch": "^5.3.0", "pngjs": "^7.0.0", "prettier": "^3.2.5", diff --git a/scripts/check-commit.ts b/scripts/check-commit.ts index 1e8773ec6b..ed702b40b9 100644 --- a/scripts/check-commit.ts +++ b/scripts/check-commit.ts @@ -38,7 +38,7 @@ async function checkBranch({ current }: StatusResult) { version.includes('-experimental.') ) { console.log(chalk.cyan('😃 Alpha version. Skip branch check.')); - } else if (current !== 'master' && current !== '4.0-prepare') { + } else if (current !== 'master') { console.log(chalk.yellow('🤔 You are not in the master branch!')); exitProcess(); } diff --git a/scripts/pre-publish-notice.ts b/scripts/pre-publish-notice.ts deleted file mode 100644 index 8f113e12d9..0000000000 --- a/scripts/pre-publish-notice.ts +++ /dev/null @@ -1,9 +0,0 @@ -const { Notification: Notifier } = require('node-notifier'); - -new Notifier().notify({ - title: '✅ 准备发布到 npm', - message: '测试用例执行完毕,快回来输入 npm 校验码了!', - sound: 'Crystal', -}); - -process.exit(0); diff --git a/scripts/pre-publish.ts b/scripts/pre-publish.ts new file mode 100644 index 0000000000..b6542131eb --- /dev/null +++ b/scripts/pre-publish.ts @@ -0,0 +1,100 @@ +import { Octokit } from '@octokit/rest'; +import ora from 'ora'; +import chalk from 'chalk'; + +const simpleGit = require('simple-git'); +const { Notification: Notifier } = require('node-notifier'); + +const emojify = (status: string = '') => { + if (!status) { + return ''; + } + const emoji = { + /* status */ + completed: '✅', + queued: '🕒', + in_progress: '⌛', + /* conclusion */ + success: '✅', + failure: '❌', + neutral: '⚪', + cancelled: '❌', + skipped: '⏭️', + timed_out: '⌛', + action_required: '🔴', + }[status]; + return `${emoji || ''} ${(status || '').padEnd(15)}`; +}; + +const runPrePublish = async () => { + const spinner = ora('Loading unicorns').start(); + spinner.info(chalk.black.bgGreenBright('本次发布将跳过本地 CI 检查,远程 CI 通过后方可发布')); + const git = simpleGit(); + if (!process.env.GITHUB_ACCESS_TOKEN) { + spinner.fail( + '请先设置 GITHUB_ACCESS_TOKEN 环境变量到本地,请不要泄露给任何在线页面: https://octokit.github.io/rest.js/v20#authentication', + ); + process.exit(1); + } + const octokit = new Octokit({ auth: process.env.GITHUB_ACCESS_TOKEN }); + const { current: currentBranch } = await git.branch(); + spinner.start(`正在拉取远程分支 ${currentBranch}`); + await git.pull('origin', currentBranch); + spinner.succeed(`成功拉取远程分支 ${currentBranch}`); + spinner.start(`正在推送本地分支 ${currentBranch}`); + await git.push('origin', currentBranch); + spinner.succeed(`成功推送远程分支 ${currentBranch}`); + spinner.succeed(`已经和远程分支保持同步 ${currentBranch}`); + spinner.succeed(`找到本地最新 commit:`); + const { latest } = await git.log(); + spinner.info(` hash: ${latest.hash}`); + spinner.info(` date: ${latest.date}`); + spinner.info(` message: ${latest.message}`); + spinner.info(` body: ${latest.body}`); + spinner.info(` author_name: ${latest.author_name}`); + const owner = 'ant-design'; + const repo = 'ant-design'; + spinner.start(`开始检查远程分支 CI 状态 ${currentBranch}`); + const result = await octokit.checks.listForRef({ + owner, + repo, + ref: latest.hash, + }); + spinner.succeed(`远程分支 CI 状态:`); + result.data.check_runs.forEach((run) => { + spinner.info( + ` ${run.name.padEnd(30)} ${emojify(run.status)} ${emojify(run.conclusion || '')}`, + ); + }); + const conclusions = result.data.check_runs.map((run) => run.conclusion); + if ( + conclusions.includes('failure') || + conclusions.includes('cancelled') || + conclusions.includes('timed_out') + ) { + spinner.fail('远程分支 CI 执行异常,无法继续发布,请尝试修复或重试'); + spinner.info(` 点此查看状态:https://github.com/${owner}/${repo}/commit/${latest.hash}`); + process.exit(1); + } + const statuses = result.data.check_runs.map((run) => run.status); + if ( + result.data.check_runs.length < 1 || + statuses.includes('queued') || + statuses.includes('in_progress') + ) { + spinner.fail('远程分支 CI 还在执行中,请稍候再试'); + spinner.info(` 点此查看状态:https://github.com/${owner}/${repo}/commit/${latest.hash}`); + process.exit(1); + } + + // 远程分支 CI 跑过才能继续 + spinner.succeed(`远程分支 CI 已通过,准备开始发布`); + new Notifier().notify({ + title: '✅ 准备发布到 npm', + message: '测试用例执行完毕,快回来输入 npm 校验码了!', + sound: 'Crystal', + }); + process.exit(0); +}; + +runPrePublish();