chore: import upstream snapshot with attribution
Publish CLI Package / publish-npm (push) Waiting to run
Publish Python SDK / publish-pypi (push) Waiting to run
Publish TypeScript SDK / publish-npm (push) Waiting to run
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 13:20:55 +08:00
commit d25d482dc2
13754 changed files with 4996608 additions and 0 deletions
+417
View File
@@ -0,0 +1,417 @@
#!/usr/bin/env bun
import { execSync } from 'node:child_process'
import { Octokit } from '@octokit/rest'
import { sleep } from '@sim/utils/helpers'
const GITHUB_TOKEN = process.env.GH_PAT
const REPO_OWNER = 'simstudioai'
const REPO_NAME = 'sim'
if (!GITHUB_TOKEN) {
console.error('❌ GH_PAT environment variable is required')
process.exit(1)
}
const targetVersion = process.argv[2]
if (!targetVersion) {
console.error('❌ Version argument is required')
console.error('Usage: bun run scripts/create-single-release.ts v0.3.XX')
process.exit(1)
}
const octokit = new Octokit({
auth: GITHUB_TOKEN,
})
interface VersionCommit {
hash: string
version: string
title: string
date: string
author: string
}
interface CommitDetail {
hash: string
message: string
author: string
githubUsername: string
prNumber?: string
}
function execCommand(command: string): string {
try {
return execSync(command, { encoding: 'utf8' }).trim()
} catch (error) {
console.error(`❌ Command failed: ${command}`)
throw error
}
}
function findVersionCommit(version: string): VersionCommit | null {
console.log(`🔍 Finding commit for version ${version}...`)
const gitLog = execCommand('git log --oneline --format="%H|%s|%ai|%an" main')
const lines = gitLog.split('\n').filter((line) => line.trim())
for (const line of lines) {
const [hash, message, date, author] = line.split('|')
const versionMatch = message.match(/^\s*(v\d+\.\d+\.?\d*):\s*(.+)$/)
if (versionMatch && versionMatch[1] === version) {
return {
hash,
version,
title: versionMatch[2],
date: new Date(date).toISOString(),
author,
}
}
}
return null
}
function findPreviousVersionCommit(currentVersion: string): VersionCommit | null {
console.log(`🔍 Finding previous version before ${currentVersion}...`)
const gitLog = execCommand('git log --oneline --format="%H|%s|%ai|%an" main')
const lines = gitLog.split('\n').filter((line) => line.trim())
let foundCurrent = false
for (const line of lines) {
const [hash, message, date, author] = line.split('|')
const versionMatch = message.match(/^\s*(v\d+\.\d+\.?\d*):\s*(.+)$/)
if (versionMatch) {
if (versionMatch[1] === currentVersion) {
foundCurrent = true
continue
}
if (foundCurrent) {
return {
hash,
version: versionMatch[1],
title: versionMatch[2],
date: new Date(date).toISOString(),
author,
}
}
}
}
return null
}
async function fetchGitHubCommitDetails(
commitHashes: string[]
): Promise<Map<string, CommitDetail>> {
console.log(`🔍 Fetching GitHub commit details for ${commitHashes.length} commits...`)
const commitMap = new Map<string, CommitDetail>()
for (let i = 0; i < commitHashes.length; i++) {
const hash = commitHashes[i]
try {
const { data: commit } = await octokit.rest.repos.getCommit({
owner: REPO_OWNER,
repo: REPO_NAME,
ref: hash,
})
const prMatch = commit.commit.message.match(/\(#(\d+)\)/)
const prNumber = prMatch ? prMatch[1] : undefined
const githubUsername = commit.author?.login || commit.committer?.login || 'unknown'
let cleanMessage = commit.commit.message.split('\n')[0]
if (prNumber) {
cleanMessage = cleanMessage.replace(/\s*\(#\d+\)\s*$/, '')
}
commitMap.set(hash, {
hash,
message: cleanMessage,
author: commit.commit.author?.name || 'Unknown',
githubUsername,
prNumber,
})
await sleep(100)
} catch (error: any) {
console.warn(`⚠️ Could not fetch commit ${hash.substring(0, 7)}: ${error?.message || error}`)
try {
const gitData = execCommand(`git log --format="%s|%an" -1 ${hash}`).split('|')
let message = gitData[0] || 'Unknown commit'
const prMatch = message.match(/\(#(\d+)\)/)
const prNumber = prMatch ? prMatch[1] : undefined
if (prNumber) {
message = message.replace(/\s*\(#\d+\)\s*$/, '')
}
commitMap.set(hash, {
hash,
message,
author: gitData[1] || 'Unknown',
githubUsername: 'unknown',
prNumber,
})
} catch (fallbackError) {
console.error(`❌ Failed to get fallback data for ${hash.substring(0, 7)}`)
}
}
}
return commitMap
}
async function getCommitsBetweenVersions(
currentCommit: VersionCommit,
previousCommit?: VersionCommit
): Promise<CommitDetail[]> {
try {
let range: string
if (previousCommit) {
range = `${previousCommit.hash}..${currentCommit.hash}`
console.log(
`🔍 Getting commits between ${previousCommit.version} and ${currentCommit.version}`
)
} else {
range = `${currentCommit.hash}~10..${currentCommit.hash}`
console.log(`🔍 Getting commits before first version ${currentCommit.version}`)
}
const gitLog = execCommand(`git log --oneline --format="%H|%s" ${range}`)
if (!gitLog.trim()) {
console.log(`⚠️ No commits found in range ${range}`)
return []
}
const commitEntries = gitLog.split('\n').filter((line) => line.trim())
const nonVersionCommits = commitEntries.filter((line) => {
const [, message] = line.split('|')
const isVersionCommit = message.match(/^v\d+\.\d+/)
if (isVersionCommit) {
console.log(`⏭️ Skipping version commit: ${message.substring(0, 50)}...`)
return false
}
return true
})
console.log(`📋 After filtering version commits: ${nonVersionCommits.length} commits`)
if (nonVersionCommits.length === 0) {
return []
}
const commitHashes = nonVersionCommits.map((line) => line.split('|')[0])
const commitMap = await fetchGitHubCommitDetails(commitHashes)
return commitHashes.map((hash) => commitMap.get(hash)!).filter(Boolean)
} catch (error) {
console.error(`❌ Error getting commits between versions:`, error)
return []
}
}
function categorizeCommit(message: string): 'features' | 'fixes' | 'improvements' | 'other' {
const msgLower = message.toLowerCase()
if (/^feat(\(|:|!)/.test(msgLower)) {
return 'features'
}
if (/^fix(\(|:|!)/.test(msgLower)) {
return 'fixes'
}
if (/^(improvement|improve|perf|refactor)(\(|:|!)/.test(msgLower)) {
return 'improvements'
}
if (/^(chore|docs|style|test|ci|build)(\(|:|!)/.test(msgLower)) {
return 'other'
}
if (msgLower.includes('feat') || msgLower.includes('implement') || msgLower.includes('new ')) {
return 'features'
}
if (msgLower.includes('fix') || msgLower.includes('bug') || msgLower.includes('error')) {
return 'fixes'
}
if (
msgLower.includes('improve') ||
msgLower.includes('enhance') ||
msgLower.includes('upgrade') ||
msgLower.includes('optimization') ||
msgLower.includes('add') ||
msgLower.includes('update')
) {
return 'improvements'
}
return 'other'
}
async function generateReleaseBody(
versionCommit: VersionCommit,
previousCommit?: VersionCommit
): Promise<string> {
console.log(`📝 Generating release body for ${versionCommit.version}...`)
const commits = await getCommitsBetweenVersions(versionCommit, previousCommit)
if (commits.length === 0) {
console.log(`⚠️ No commits found, using simple format`)
return `${versionCommit.title}
[View changes on GitHub](https://github.com/${REPO_OWNER}/${REPO_NAME}/compare/${previousCommit?.version || 'v1.0.0'}...${versionCommit.version})`
}
console.log(`📋 Processing ${commits.length} commits for categorization`)
const features = commits.filter((c) => categorizeCommit(c.message) === 'features')
const fixes = commits.filter((c) => categorizeCommit(c.message) === 'fixes')
const improvements = commits.filter((c) => categorizeCommit(c.message) === 'improvements')
const others = commits.filter((c) => categorizeCommit(c.message) === 'other')
console.log(
`📊 Categories: ${features.length} features, ${improvements.length} improvements, ${fixes.length} fixes, ${others.length} other`
)
let body = ''
if (features.length > 0) {
body += '## Features\n\n'
for (const commit of features) {
const prLink = commit.prNumber ? ` (#${commit.prNumber})` : ''
body += `- ${commit.message}${prLink}\n`
}
body += '\n'
}
if (improvements.length > 0) {
body += '## Improvements\n\n'
for (const commit of improvements) {
const prLink = commit.prNumber ? ` (#${commit.prNumber})` : ''
body += `- ${commit.message}${prLink}\n`
}
body += '\n'
}
if (fixes.length > 0) {
body += '## Bug Fixes\n\n'
for (const commit of fixes) {
const prLink = commit.prNumber ? ` (#${commit.prNumber})` : ''
body += `- ${commit.message}${prLink}\n`
}
body += '\n'
}
if (others.length > 0) {
body += '## Other Changes\n\n'
for (const commit of others) {
const prLink = commit.prNumber ? ` (#${commit.prNumber})` : ''
body += `- ${commit.message}${prLink}\n`
}
body += '\n'
}
const uniqueContributors = new Set<string>()
commits.forEach((commit) => {
if (commit.githubUsername && commit.githubUsername !== 'unknown') {
uniqueContributors.add(commit.githubUsername)
}
})
if (uniqueContributors.size > 0) {
body += '## Contributors\n\n'
for (const contributor of Array.from(uniqueContributors).sort()) {
body += `- @${contributor}\n`
}
body += '\n'
}
body += `[View changes on GitHub](https://github.com/${REPO_OWNER}/${REPO_NAME}/compare/${previousCommit?.version || 'v1.0.0'}...${versionCommit.version})`
return body.trim()
}
async function main() {
try {
console.log(`🚀 Creating single release for ${targetVersion}...`)
const versionCommit = findVersionCommit(targetVersion)
if (!versionCommit) {
console.error(`❌ No commit found for version ${targetVersion}`)
process.exit(1)
}
console.log(
`✅ Found version commit: ${versionCommit.hash.substring(0, 7)} - ${versionCommit.title}`
)
const previousCommit = findPreviousVersionCommit(targetVersion)
if (previousCommit) {
console.log(`✅ Found previous version: ${previousCommit.version}`)
} else {
console.log(`️ No previous version found (this might be the first release)`)
}
try {
const existingRelease = await octokit.rest.repos.getReleaseByTag({
owner: REPO_OWNER,
repo: REPO_NAME,
tag: targetVersion,
})
if (existingRelease.data) {
console.log(`️ Release ${targetVersion} already exists, skipping creation`)
console.log(
`🔗 View release: https://github.com/${REPO_OWNER}/${REPO_NAME}/releases/tag/${targetVersion}`
)
return
}
} catch (error: any) {
if (error.status !== 404) {
throw error
}
}
const releaseBody = await generateReleaseBody(versionCommit, previousCommit || undefined)
console.log(`🚀 Creating GitHub release for ${targetVersion}...`)
await octokit.rest.repos.createRelease({
owner: REPO_OWNER,
repo: REPO_NAME,
tag_name: targetVersion,
name: targetVersion,
body: releaseBody,
draft: false,
prerelease: false,
target_commitish: versionCommit.hash,
})
console.log(`✅ Successfully created release: ${targetVersion}`)
console.log(
`🔗 View release: https://github.com/${REPO_OWNER}/${REPO_NAME}/releases/tag/${targetVersion}`
)
} catch (error) {
console.error('❌ Script failed:', error)
process.exit(1)
}
}
main()