mirror of
https://github.com/lobehub/lobehub.git
synced 2026-03-27 13:29:15 +07:00
* ✨ feat: add hotfix workflow and script for automated hotfix management Signed-off-by: Innei <tukon479@gmail.com> * 🔧 fix: refactor PR creation command to use execFileSync for improved reliability Signed-off-by: Innei <tukon479@gmail.com> * 🔧 chore: update @lobehub/ui version and refactor dynamic import handling - Bump @lobehub/ui dependency from ^4.35.0 to ^4.36.2 in package.json. - Refactor settingsContentToStatic.mts to simplify dynamic import processing by removing business feature checks. - Add initialize.ts to enable immer's map set functionality. - Correct import path in layout.tsx from 'initiallize' to 'initialize'. Signed-off-by: Innei <tukon479@gmail.com> * 🔧 chore: update @types/react version in package.json - Bump @types/react dependency from ^19.2.9 to 19.2.14. - Add @types/react version to overrides section for consistency. Signed-off-by: Innei <tukon479@gmail.com> * 🔧 chore: enhance auto-tag-release workflow for strict semver validation - Updated regex to match strict semantic versioning format, allowing for optional prerelease and build metadata. - Added validation step to ensure the version is a valid semver before proceeding with the release process. Signed-off-by: Innei <tukon479@gmail.com> * 🗑️ chore: remove defaultSecurityBlacklist test file - Deleted the test file for DEFAULT_SECURITY_BLACKLIST as it is no longer needed. - This cleanup helps maintain a more streamlined test suite. Signed-off-by: Innei <tukon479@gmail.com> * 🔧 chore: update localization files for multiple languages - Improved translations in Arabic, Bulgarian, German, English, and Spanish for chat and tool-related strings. - Enhanced descriptions for various parameters and added new keys for file handling and security warnings. - Adjusted phrasing for clarity and consistency across languages. Signed-off-by: Innei <tukon479@gmail.com> * 🔧 chore: update PR comment script to include Actions Artifacts link - Modified the PR comment generation script to accept an additional artifactsUrl parameter. - Updated the comment format to include both Release download and Actions Artifacts links for better accessibility. Signed-off-by: Innei <tukon479@gmail.com> --------- Signed-off-by: Innei <tukon479@gmail.com>
290 lines
8.1 KiB
TypeScript
290 lines
8.1 KiB
TypeScript
import { execSync } from 'node:child_process';
|
|
import * as fs from 'node:fs';
|
|
import path from 'node:path';
|
|
|
|
import { confirm, select } from '@inquirer/prompts';
|
|
import { consola } from 'consola';
|
|
import * as semver from 'semver';
|
|
|
|
const ROOT_DIR = process.cwd();
|
|
const PACKAGE_JSON_PATH = path.join(ROOT_DIR, 'package.json');
|
|
|
|
// Version type
|
|
type VersionType = 'patch' | 'minor' | 'major';
|
|
|
|
// Check if in a Git repository
|
|
function checkGitRepo(): void {
|
|
try {
|
|
execSync('git rev-parse --git-dir', { stdio: 'ignore' });
|
|
} catch {
|
|
consola.error('❌ Current directory is not a Git repository');
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
// Get current version from package.json
|
|
function getCurrentVersion(): string {
|
|
try {
|
|
const pkg = JSON.parse(fs.readFileSync(PACKAGE_JSON_PATH, 'utf8'));
|
|
return pkg.version;
|
|
} catch {
|
|
consola.error('❌ Unable to read version from package.json');
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
// Calculate new version based on type
|
|
function bumpVersion(currentVersion: string, type: VersionType): string {
|
|
const newVersion = semver.inc(currentVersion, type);
|
|
if (!newVersion) {
|
|
consola.error(`❌ Unable to calculate new version (current: ${currentVersion}, type: ${type})`);
|
|
process.exit(1);
|
|
}
|
|
return newVersion;
|
|
}
|
|
|
|
// Get version type from command line arguments
|
|
function getVersionTypeFromArgs(): VersionType | null {
|
|
const args = process.argv.slice(2);
|
|
|
|
if (args.includes('--patch')) return 'patch';
|
|
if (args.includes('--minor')) return 'minor';
|
|
if (args.includes('--major')) return 'major';
|
|
|
|
return null;
|
|
}
|
|
|
|
// Interactive version type selection
|
|
async function selectVersionTypeInteractive(): Promise<VersionType> {
|
|
const currentVersion = getCurrentVersion();
|
|
|
|
const choices: { name: string; value: VersionType }[] = [
|
|
{
|
|
value: 'patch',
|
|
name: `🔧 patch - Bug fixes (e.g., ${currentVersion} -> ${bumpVersion(currentVersion, 'patch')})`,
|
|
},
|
|
{
|
|
value: 'minor',
|
|
name: `✨ minor - New features (e.g., ${currentVersion} -> ${bumpVersion(currentVersion, 'minor')})`,
|
|
},
|
|
{
|
|
value: 'major',
|
|
name: `🚀 major - Breaking changes (e.g., ${currentVersion} -> ${bumpVersion(currentVersion, 'major')})`,
|
|
},
|
|
];
|
|
|
|
const answer = await select<VersionType>({
|
|
choices,
|
|
message: 'Select version bump type:',
|
|
});
|
|
|
|
return answer;
|
|
}
|
|
|
|
// Secondary confirmation
|
|
async function confirmRelease(version: string, type: VersionType): Promise<boolean> {
|
|
const currentVersion = getCurrentVersion();
|
|
|
|
consola.box(
|
|
`
|
|
📦 Release Confirmation
|
|
━━━━━━━━━━━━━━━━━━━━━━━
|
|
Current: ${currentVersion}
|
|
New: ${version}
|
|
Type: ${type}
|
|
Branch: release/v${version}
|
|
Target: main
|
|
━━━━━━━━━━━━━━━━━━━━━━━
|
|
`.trim(),
|
|
);
|
|
|
|
const confirmed = await confirm({
|
|
default: true,
|
|
message: 'Confirm to create release branch and submit PR?',
|
|
});
|
|
|
|
return confirmed;
|
|
}
|
|
|
|
// Checkout and pull latest dev branch
|
|
function checkoutAndPullDev(): void {
|
|
try {
|
|
// Check for dev branch
|
|
const branches = execSync('git branch -a', { encoding: 'utf8' });
|
|
const hasLocalDev = branches.includes(' dev\n') || branches.startsWith('* dev\n');
|
|
const hasRemoteDev = branches.includes('remotes/origin/dev');
|
|
|
|
if (!hasLocalDev && !hasRemoteDev) {
|
|
consola.error('❌ Dev branch not found (local or remote)');
|
|
process.exit(1);
|
|
}
|
|
|
|
consola.info('📥 Fetching latest dev branch...');
|
|
|
|
if (hasRemoteDev) {
|
|
// Checkout from remote dev branch
|
|
try {
|
|
execSync('git checkout dev', { stdio: 'ignore' });
|
|
execSync('git pull origin dev', { stdio: 'inherit' });
|
|
} catch {
|
|
// Create from remote if local doesn't exist
|
|
execSync('git checkout -b dev origin/dev', { stdio: 'inherit' });
|
|
}
|
|
} else {
|
|
// Local dev branch only
|
|
execSync('git checkout dev', { stdio: 'inherit' });
|
|
execSync('git pull', { stdio: 'inherit' });
|
|
}
|
|
|
|
consola.success('✅ Switched to latest dev branch');
|
|
} catch (error) {
|
|
consola.error('❌ Failed to switch or pull dev branch');
|
|
consola.error(error instanceof Error ? error.message : String(error));
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
// Create release branch
|
|
function createReleaseBranch(version: string): void {
|
|
const branchName = `release/v${version}`;
|
|
|
|
try {
|
|
consola.info(`🌿 Creating branch: ${branchName}...`);
|
|
execSync(`git checkout -b ${branchName}`, { stdio: 'inherit' });
|
|
consola.success(`✅ Created and switched to branch: ${branchName}`);
|
|
} catch (error) {
|
|
consola.error(`❌ Failed to create branch or commit: ${branchName}`);
|
|
consola.error(error instanceof Error ? error.message : String(error));
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
// Push branch to remote
|
|
function pushBranch(version: string): void {
|
|
const branchName = `release/v${version}`;
|
|
|
|
try {
|
|
consola.info(`📤 Pushing branch to remote...`);
|
|
execSync(`git push -u origin ${branchName}`, { stdio: 'inherit' });
|
|
consola.success(`✅ Pushed branch to remote: ${branchName}`);
|
|
} catch (error) {
|
|
consola.error('❌ Failed to push branch');
|
|
consola.error(error instanceof Error ? error.message : String(error));
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
// Create Pull Request
|
|
function createPullRequest(version: string): void {
|
|
const title = `🚀 release: v${version}`;
|
|
const body = `## 📦 Release v${version}
|
|
|
|
This branch contains changes for the upcoming v${version} release.
|
|
|
|
### Change Type
|
|
- Checked out from dev branch and merged to main branch
|
|
|
|
### Release Process
|
|
1. ✅ Release branch created
|
|
2. ✅ Pushed to remote
|
|
3. 🔄 Waiting for PR review and merge
|
|
4. ⏳ Release workflow triggered after merge
|
|
|
|
---
|
|
Created by release script`;
|
|
|
|
try {
|
|
consola.info('🔀 Creating Pull Request...');
|
|
|
|
// Create PR using gh CLI
|
|
const cmd = `gh pr create \
|
|
--title "${title}" \
|
|
--body "${body}" \
|
|
--base main \
|
|
--head release/v${version} \
|
|
--label "release"`;
|
|
|
|
execSync(cmd, { stdio: 'inherit' });
|
|
consola.success('✅ PR created successfully!');
|
|
} catch (error) {
|
|
consola.error('❌ Failed to create PR');
|
|
consola.error(error instanceof Error ? error.message : String(error));
|
|
consola.info('\n💡 Tip: Make sure GitHub CLI (gh) is installed and logged in');
|
|
consola.info(' Install: https://cli.github.com/');
|
|
consola.info(' Login: gh auth login');
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
// Display completion info
|
|
function showCompletion(version: string): void {
|
|
const branchName = `release/v${version}`;
|
|
|
|
consola.box(
|
|
`
|
|
🎉 Release process started!
|
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
✅ Branch created: ${branchName}
|
|
✅ Pushed to remote
|
|
✅ PR created targeting main branch
|
|
|
|
📋 PR Title: 🚀 release: v${version}
|
|
|
|
Next steps:
|
|
1. Open the PR link to view details
|
|
2. Complete code review
|
|
3. Merge PR to main branch
|
|
4. Wait for release workflow to complete
|
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
`.trim(),
|
|
);
|
|
}
|
|
|
|
// Main function
|
|
async function main(): Promise<void> {
|
|
consola.info('🚀 LobeChat Release Script\n');
|
|
|
|
// 1. Check Git repository
|
|
checkGitRepo();
|
|
|
|
// 2. Checkout and pull latest dev branch (ensure we have the latest version)
|
|
checkoutAndPullDev();
|
|
|
|
// 3. Get version type
|
|
let versionType = getVersionTypeFromArgs();
|
|
|
|
if (!versionType) {
|
|
// No args, enter interactive mode
|
|
versionType = await selectVersionTypeInteractive();
|
|
}
|
|
|
|
// 4. Calculate new version
|
|
const currentVersion = getCurrentVersion();
|
|
const newVersion = bumpVersion(currentVersion, versionType);
|
|
|
|
// 5. Secondary confirmation
|
|
const confirmed = await confirmRelease(newVersion, versionType);
|
|
|
|
if (!confirmed) {
|
|
consola.info('❌ Release process cancelled');
|
|
process.exit(0);
|
|
}
|
|
|
|
// 6. Create release branch
|
|
createReleaseBranch(newVersion);
|
|
|
|
// 7. Push to remote
|
|
pushBranch(newVersion);
|
|
|
|
// 8. Create PR
|
|
createPullRequest(newVersion);
|
|
|
|
// 9. Show completion info
|
|
showCompletion(newVersion);
|
|
}
|
|
|
|
main().catch((error) => {
|
|
consola.error('❌ Error occurred:', error);
|
|
process.exit(1);
|
|
});
|