Skip to content

fix: #1093 修复开启流式虚拟光标时,代码块被破坏的问题 #4

fix: #1093 修复开启流式虚拟光标时,代码块被破坏的问题

fix: #1093 修复开启流式虚拟光标时,代码块被破坏的问题 #4

name: PR Merge cherry-markdown Dev NPM Preview
on:
pull_request_target:
types: [closed]
paths:
- "packages/cherry-markdown/**"
permissions:
contents: read
pull-requests: write
issues: write
jobs:
dev-deploy:
# 不需要在fork仓库的pr中运行, 仅当pr合并时运行
if: github.repository == 'Tencent/cherry-markdown' && github.event.pull_request.merged == true
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Get Merge Commit SHA
run: echo "COMMIT_SHORT_SHA=${GITHUB_SHA:0:7}" >> $GITHUB_ENV
- name: Install dependencies
run: sudo apt-get install -y moreutils
- name: Update package.json
working-directory: ./packages/cherry-markdown
run: |
BASE_VERSION=$(jq -r .version package.json)
VERSION="${BASE_VERSION}-dev.$(date +'%Y%m%d%H%M').${{ env.COMMIT_SHORT_SHA }}"
jq --arg name "@cherry-markdown/cherry-markdown-dev" \
--arg version "$VERSION" \
'.name = $name | .version = $version | del(.scripts.publish?)' package.json | sponge package.json
echo "PACKAGE_VERSION=$VERSION" >> $GITHUB_ENV
echo "PACKAGE_NAME=@cherry-markdown/cherry-markdown-dev" >> $GITHUB_ENV
- name: Update README
working-directory: ./packages/cherry-markdown
run: |
cat <<-EOF > README.md
**⚠️ 开发预览警告 / Development Preview Warning**
此版本为临时测试版(${PACKAGE_NAME}@${PACKAGE_VERSION}),禁止在生产环境使用!
最后更新时间:`$(date +'%Y-%m-%d %H:%M:%S')`
This is a development preview version (${PACKAGE_NAME}@${PACKAGE_VERSION}), do NOT use in production!
Last updated: `$(date +'%Y-%m-%d %H:%M:%S')`
EOF
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: 18
registry-url: https://registry.npmjs.org/
cache: yarn
- name: Install and build
working-directory: ./packages/cherry-markdown
run: yarn install && yarn build
- name: npm publish
working-directory: ./packages/cherry-markdown
run: npm publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Post comments
uses: actions/github-script@v7
with:
script: |
// 工具函数:增强型 Issue 提取
const extractValidReferences = (text) => {
if (!text) return [];
// 匹配当前仓库格式的 #数字 或 owner/repo#数字(精确匹配当前仓库)
const repoRegex = new RegExp(
`(?:^|\\s)(?:#(\\d+)|${context.repo.owner}/${context.repo.repo}#(\\d+))`,
'gm'
);
const matches = text.matchAll(repoRegex) || [];
return Array.from(matches, match => {
// 优先捕获跨仓库引用(第二捕获组)
return match[2] ? parseInt(match[2], 10) : parseInt(match[1], 10);
}).filter(num => !isNaN(num));
};
// 验证 Issue/PR 归属
const isValidTarget = async (number) => {
try {
await github.rest.issues.get({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: number,
headers: { "X-GitHub-Api-Version": "2022-11-28" }
});
return true;
} catch (error) {
// 404 表示目标不存在或不属于本仓库
if (error.status === 404) return false;
// 其他错误视为无效
console.error('Validation error:', error);
return false;
}
};
// 异步批次处理器
const batchProcessor = async (items, processor, batchSize = 10) => {
const results = [];
for (let i = 0; i < items.length; i += batchSize) {
const batch = items.slice(i, i + batchSize);
const batchResults = await Promise.all(batch.map(processor));
results.push(...batchResults);
// 避免触发 GitHub API 速率限制
await new Promise(resolve => setTimeout(resolve, 500));
}
return results;
};
try {
// 阶段 1:数据收集
const targets = new Set();
// 收集当前 PR 自身
if (context.payload.pull_request?.number) {
targets.add(context.payload.pull_request.number);
}
// 收集 PR 相关内容
if (context.payload.pull_request) {
const { data: pr } = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.payload.pull_request.number,
headers: { "X-GitHub-Api-Version": "2022-11-28" }
});
// 处理标题和正文
[pr.title, pr.body].forEach(text => {
extractValidReferences(text).forEach(num => targets.add(num));
});
// 处理评论(常规评论 + 代码评审评论)
const [comments, reviews] = await Promise.all([
github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.pull_request.number,
per_page: 100
}),
github.rest.pulls.listReviewComments({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.payload.pull_request.number,
per_page: 100
})
]);
[...comments.data, ...reviews.data].forEach(comment => {
extractValidReferences(comment.body).forEach(num => targets.add(num));
});
}
// 处理提交信息
context.payload.commits?.forEach(commit => {
extractValidReferences(commit.message).forEach(num => targets.add(num));
});
// 阶段 2:数据验证
const candidateNumbers = Array.from(targets);
console.log('Candidate targets:', candidateNumbers);
// 批次验证有效性
const validationResults = await batchProcessor(
candidateNumbers,
async num => ({ num, valid: await isValidTarget(num) })
);
const validNumbers = validationResults
.filter(r => r.valid)
.map(r => r.num);
console.log('Validated targets:', validNumbers);
if (validNumbers.length === 0) {
console.log('No valid targets found');
return;
}
// 阶段 3:评论发送
const commentTemplate = `📦 **cherry-markdown dev preview published**
⚠️ **注意**: 此版本为开发预览版,禁止在生产环境使用!
⚠️ **Note**: This version is a developer preview and should not be used in production environments!
**Install [NPM](https://www.npmjs.com/package/${process.env.PACKAGE_NAME}/v/${process.env.PACKAGE_VERSION}) with** :
\`\`\`bash
npm install ${process.env.PACKAGE_NAME}@${process.env.PACKAGE_VERSION}
\`\`\`
`;
// 并发发送控制(每秒 2 个请求)
await batchProcessor(
validNumbers,
async number => {
try {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: number,
body: commentTemplate,
headers: { "X-GitHub-Api-Version": "2022-11-28" }
});
console.log(`Commented on #${number}`);
} catch (error) {
console.error(`Failed to comment on #${number}:`, error.message);
}
},
);
} catch (error) {
console.error('Workflow failed:', error);
// 仅在当前 PR 上报告错误
if (context.payload.pull_request?.number) {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.pull_request.number,
body: `🚨 发布流程失败: ${error.message}`
});
}
}