Files
CipherTalk/scripts/generate-release-context.js
T

156 lines
4.4 KiB
JavaScript

const fs = require('fs')
const path = require('path')
const { execSync } = require('child_process')
const rootDir = path.resolve(__dirname, '..')
const releaseDir = path.join(rootDir, 'release')
const owner = process.env.GITHUB_REPOSITORY_OWNER || 'ILoveBingLu'
const repo = (process.env.GITHUB_REPOSITORY || `${owner}/CipherTalk`).split('/')[1] || 'CipherTalk'
const currentTag = process.env.RELEASE_TAG || process.env.GITHUB_REF_NAME || ''
const ghToken = process.env.GH_TOKEN || ''
const pkg = require(path.join(rootDir, 'package.json'))
function runGit(command) {
return execSync(command, {
cwd: rootDir,
encoding: 'utf8',
stdio: ['ignore', 'pipe', 'pipe']
}).trim()
}
function safeJsonParse(value, fallback) {
try {
return JSON.parse(value)
} catch {
return fallback
}
}
function parseList(value) {
if (!value) return []
return String(value)
.split(',')
.map((item) => item.trim())
.filter(Boolean)
}
function getPreviousTag() {
const tags = runGit('git tag --sort=-version:refname')
.split(/\r?\n/)
.map((line) => line.trim())
.filter(Boolean)
if (!currentTag) return tags[0] || null
const currentIndex = tags.indexOf(currentTag)
if (currentIndex === -1) return tags[0] || null
return tags[currentIndex + 1] || null
}
function getCommitRange(previousTag, tag) {
if (!tag) return 'HEAD'
if (!previousTag || previousTag === tag) return tag
return `${previousTag}..${tag}`
}
function extractPrNumbers(commits) {
const prNumbers = new Set()
for (const commit of commits) {
const matches = commit.subject.match(/#(\d+)/g)
if (!matches) continue
for (const match of matches) {
prNumbers.add(Number(match.slice(1)))
}
}
return Array.from(prNumbers)
}
async function fetchPullRequest(prNumber) {
if (!ghToken) return null
const response = await fetch(`https://api.github.com/repos/${owner}/${repo}/pulls/${prNumber}`, {
headers: {
Accept: 'application/vnd.github+json',
Authorization: `Bearer ${ghToken}`,
'User-Agent': 'CipherTalk-Release-Context'
}
})
if (!response.ok) return null
const data = await response.json()
return {
number: data.number,
title: data.title,
url: data.html_url,
authorLogin: data.user?.login || null,
authorName: data.user?.login || null,
mergedBy: data.merged_by?.login || null
}
}
async function main() {
if (!fs.existsSync(releaseDir)) {
fs.mkdirSync(releaseDir, { recursive: true })
}
const previousTag = getPreviousTag()
const commitRange = getCommitRange(previousTag, currentTag || 'HEAD')
console.log(`[ReleaseContext] tag=${currentTag || `v${pkg.version}`}`)
console.log(`[ReleaseContext] previousTag=${previousTag || 'none'}`)
console.log(`[ReleaseContext] commitRange=${commitRange}`)
console.log(`[ReleaseContext] ghTokenConfigured=${Boolean(ghToken)}`)
const commitLines = runGit(`git log ${commitRange} --pretty=format:"%H|%h|%an|%ae|%s"`)
.split(/\r?\n/)
.map((line) => line.trim())
.filter(Boolean)
const commits = commitLines.map((line) => {
const [sha, shortSha, authorName, authorEmail, ...subjectParts] = line.split('|')
return {
sha,
shortSha,
url: `https://github.com/${owner}/${repo}/commit/${sha}`,
authorName,
authorEmail,
subject: subjectParts.join('|')
}
})
const prNumbers = extractPrNumbers(commits)
const prs = []
for (const prNumber of prNumbers) {
const pr = await fetchPullRequest(prNumber)
if (pr) prs.push(pr)
}
console.log(`[ReleaseContext] commits=${commits.length}`)
console.log(`[ReleaseContext] detectedPrNumbers=${prNumbers.length}`)
console.log(`[ReleaseContext] fetchedPullRequests=${prs.length}`)
const context = {
version: pkg.version,
tag: currentTag || `v${pkg.version}`,
previousTag,
generatedAt: new Date().toISOString(),
repository: {
owner,
repo
},
forceUpdate: {
minimumSupportedVersion: process.env.FORCE_UPDATE_MIN_VERSION || null,
blockedVersions: parseList(process.env.FORCE_UPDATE_BLOCKED_VERSIONS)
},
commits,
pullRequests: prs
}
const outputPath = path.join(releaseDir, 'release-context.json')
fs.writeFileSync(outputPath, `${JSON.stringify(context, null, 2)}\n`, 'utf8')
console.log(`✅ release-context.json 已生成: ${outputPath}`)
}
main().catch((error) => {
console.error('❌ 生成 release-context.json 失败:', error)
process.exit(1)
})