ced2ad479f
* fix(core): add task persistence, terminal handling, and HTTP 300 fixes Consolidated bug fixes from PRs #168, #170, #171: - Task persistence (#168): Scan worktrees for tasks on app restart to prevent loss of in-progress work and wasted API credits. Tasks in .worktrees/*/specs are now loaded and deduplicated with main. - Terminal buttons (#170): Fix "Open Terminal" buttons silently failing on macOS by properly awaiting createTerminal() Promise. Added useTerminalHandler hook with loading states and error display. - HTTP 300 errors (#171): Handle branch/tag name collisions that cause update failures. Added validation script to prevent conflicts before releases and user-friendly error messages with manual download links. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(platform): add path resolution, spaces handling, and XDG support This commit consolidates multiple bug fixes from community PRs: - PR #187: Path resolution fix - Update path detection to find apps/backend instead of legacy auto-claude directory after v2.7.2 restructure - PR #182/#155: Python path spaces fix - Improve parsePythonCommand() to handle quoted paths and paths containing spaces without splitting - PR #161: Ollama detection fix - Add new apps structure paths for ollama_model_detector.py script discovery - PR #160: AppImage support - Add XDG Base Directory compliant paths for Linux sandboxed environments (AppImage, Flatpak, Snap). New files: - config-paths.ts: XDG path utilities - fs-utils.ts: Filesystem utilities with fallback support - PR #159: gh CLI PATH fix - Add getAugmentedEnv() utility to include common binary locations (Homebrew, snap, local) in PATH for child processes. Fixes gh CLI not found when app launched from Finder/Dock. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: address CodeRabbit/Cursor review comments on PR #185 Fixes from code review: - http-client.ts: Use GITHUB_CONFIG instead of hardcoded owner in HTTP 300 error message - validate-release.js: Fix substring matching bug in branch detection that could cause false positives (e.g., v2.7 matching v2.7.2) - bump-version.js: Remove unnecessary try-catch wrapper (exec() already exits on failure) - execution-handlers.ts: Capture original subtask status before mutation for accurate logging - fs-utils.ts: Add error handling to safeWriteFile with proper logging Dismissed as trivial/not applicable: - config-paths.ts: Exhaustive switch check (over-engineering) - env-utils.ts: PATH priority documentation (existing comments sufficient) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: address additional CodeRabbit review comments (round 2) Fixes from second round of code review: - fs-utils.ts: Wrap test file cleanup in try-catch for Windows file locking - fs-utils.ts: Add error handling to safeReadFile for consistency with safeWriteFile - http-client.ts: Use GITHUB_CONFIG in fetchJson (missed in first round) - validate-release.js: Exclude symbolic refs (origin/HEAD -> origin/main) from branch check - python-detector.ts: Return cleanPath instead of pythonPath for empty input edge case Dismissed as trivial/not applicable: - execution-handlers.ts: Redundant checkSubtasksCompletion call (micro-optimization) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
238 lines
6.9 KiB
JavaScript
238 lines
6.9 KiB
JavaScript
#!/usr/bin/env node
|
||
|
||
/**
|
||
* Version Bump Script
|
||
*
|
||
* Automatically bumps the version in package.json and creates a git tag.
|
||
* This ensures version consistency between package.json and git tags.
|
||
*
|
||
* Usage:
|
||
* node scripts/bump-version.js <major|minor|patch|x.y.z>
|
||
*
|
||
* Examples:
|
||
* node scripts/bump-version.js patch # 2.5.5 -> 2.5.6
|
||
* node scripts/bump-version.js minor # 2.5.5 -> 2.6.0
|
||
* node scripts/bump-version.js major # 2.5.5 -> 3.0.0
|
||
* node scripts/bump-version.js 2.6.0 # Set to specific version
|
||
*/
|
||
|
||
const fs = require('fs');
|
||
const path = require('path');
|
||
const { execSync } = require('child_process');
|
||
|
||
// Colors for terminal output
|
||
const colors = {
|
||
reset: '\x1b[0m',
|
||
green: '\x1b[32m',
|
||
yellow: '\x1b[33m',
|
||
red: '\x1b[31m',
|
||
cyan: '\x1b[36m',
|
||
};
|
||
|
||
function log(message, color = colors.reset) {
|
||
console.log(`${color}${message}${colors.reset}`);
|
||
}
|
||
|
||
function error(message) {
|
||
log(`❌ Error: ${message}`, colors.red);
|
||
process.exit(1);
|
||
}
|
||
|
||
function success(message) {
|
||
log(`✅ ${message}`, colors.green);
|
||
}
|
||
|
||
function info(message) {
|
||
log(`ℹ️ ${message}`, colors.cyan);
|
||
}
|
||
|
||
function warning(message) {
|
||
log(`⚠️ ${message}`, colors.yellow);
|
||
}
|
||
|
||
// Parse semver version
|
||
function parseVersion(version) {
|
||
const match = version.match(/^(\d+)\.(\d+)\.(\d+)$/);
|
||
if (!match) {
|
||
error(`Invalid version format: ${version}. Expected format: x.y.z`);
|
||
}
|
||
return {
|
||
major: parseInt(match[1]),
|
||
minor: parseInt(match[2]),
|
||
patch: parseInt(match[3]),
|
||
};
|
||
}
|
||
|
||
// Bump version based on type
|
||
function bumpVersion(currentVersion, bumpType) {
|
||
const version = parseVersion(currentVersion);
|
||
|
||
switch (bumpType) {
|
||
case 'major':
|
||
return `${version.major + 1}.0.0`;
|
||
case 'minor':
|
||
return `${version.major}.${version.minor + 1}.0`;
|
||
case 'patch':
|
||
return `${version.major}.${version.minor}.${version.patch + 1}`;
|
||
default:
|
||
// Assume it's a specific version
|
||
parseVersion(bumpType); // Validate format
|
||
return bumpType;
|
||
}
|
||
}
|
||
|
||
// Execute shell command
|
||
function exec(command, options = {}) {
|
||
try {
|
||
return execSync(command, { encoding: 'utf8', stdio: 'pipe', ...options }).trim();
|
||
} catch (err) {
|
||
error(`Command failed: ${command}\n${err.message}`);
|
||
}
|
||
}
|
||
|
||
// Check if git working directory is clean
|
||
function checkGitStatus() {
|
||
const status = exec('git status --porcelain');
|
||
if (status) {
|
||
error('Git working directory is not clean. Please commit or stash changes first.');
|
||
}
|
||
}
|
||
|
||
// Update package.json version
|
||
function updatePackageJson(newVersion) {
|
||
const frontendPath = path.join(__dirname, '..', 'apps', 'frontend', 'package.json');
|
||
const rootPath = path.join(__dirname, '..', 'package.json');
|
||
|
||
if (!fs.existsSync(frontendPath)) {
|
||
error(`package.json not found at ${frontendPath}`);
|
||
}
|
||
|
||
// Update frontend package.json
|
||
const frontendJson = JSON.parse(fs.readFileSync(frontendPath, 'utf8'));
|
||
const oldVersion = frontendJson.version;
|
||
frontendJson.version = newVersion;
|
||
fs.writeFileSync(frontendPath, JSON.stringify(frontendJson, null, 2) + '\n');
|
||
|
||
// Update root package.json if it exists
|
||
if (fs.existsSync(rootPath)) {
|
||
const rootJson = JSON.parse(fs.readFileSync(rootPath, 'utf8'));
|
||
rootJson.version = newVersion;
|
||
fs.writeFileSync(rootPath, JSON.stringify(rootJson, null, 2) + '\n');
|
||
}
|
||
|
||
return { oldVersion, packagePath: frontendPath };
|
||
}
|
||
|
||
// Update apps/backend/__init__.py version
|
||
function updateBackendInit(newVersion) {
|
||
const initPath = path.join(__dirname, '..', 'apps', 'backend', '__init__.py');
|
||
|
||
if (!fs.existsSync(initPath)) {
|
||
warning(`Backend __init__.py not found at ${initPath}, skipping`);
|
||
return false;
|
||
}
|
||
|
||
let content = fs.readFileSync(initPath, 'utf8');
|
||
content = content.replace(/__version__\s*=\s*"[^"]*"/, `__version__ = "${newVersion}"`);
|
||
fs.writeFileSync(initPath, content);
|
||
return true;
|
||
}
|
||
|
||
// Update README.md version references
|
||
function updateReadme(newVersion, oldVersion) {
|
||
const readmePath = path.join(__dirname, '..', 'README.md');
|
||
|
||
if (!fs.existsSync(readmePath)) {
|
||
warning(`README.md not found at ${readmePath}, skipping`);
|
||
return false;
|
||
}
|
||
|
||
let content = fs.readFileSync(readmePath, 'utf8');
|
||
|
||
// Update version badge: version-X.Y.Z-blue
|
||
content = content.replace(/version-[\d.]+(-\w+)?-blue/g, `version-${newVersion}-blue`);
|
||
|
||
// Update download links: Auto-Claude-X.Y.Z
|
||
content = content.replace(/Auto-Claude-[\d.]+/g, `Auto-Claude-${newVersion}`);
|
||
|
||
fs.writeFileSync(readmePath, content);
|
||
return true;
|
||
}
|
||
|
||
// Main function
|
||
function main() {
|
||
const bumpType = process.argv[2];
|
||
|
||
if (!bumpType) {
|
||
error('Please specify version bump type or version number.\n' +
|
||
'Usage: node scripts/bump-version.js <major|minor|patch|x.y.z>');
|
||
}
|
||
|
||
log('\n🚀 Auto Claude Version Bump\n', colors.cyan);
|
||
|
||
// 1. Check git status
|
||
info('Checking git status...');
|
||
checkGitStatus();
|
||
success('Git working directory is clean');
|
||
|
||
// 2. Read current version
|
||
const packagePath = path.join(__dirname, '..', 'apps', 'frontend', 'package.json');
|
||
const packageJson = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
|
||
const currentVersion = packageJson.version;
|
||
info(`Current version: ${currentVersion}`);
|
||
|
||
// 3. Calculate new version
|
||
const newVersion = bumpVersion(currentVersion, bumpType);
|
||
info(`New version: ${newVersion}`);
|
||
|
||
if (currentVersion === newVersion) {
|
||
error('New version is the same as current version');
|
||
}
|
||
|
||
// 4. Validate release (check for branch/tag conflicts)
|
||
info('Validating release...');
|
||
exec(`node ${path.join(__dirname, 'validate-release.js')} v${newVersion}`);
|
||
success('Release validation passed');
|
||
|
||
// 5. Update all version files
|
||
info('Updating package.json files...');
|
||
updatePackageJson(newVersion);
|
||
success('Updated package.json files');
|
||
|
||
info('Updating apps/backend/__init__.py...');
|
||
if (updateBackendInit(newVersion)) {
|
||
success('Updated apps/backend/__init__.py');
|
||
}
|
||
|
||
info('Updating README.md...');
|
||
if (updateReadme(newVersion, currentVersion)) {
|
||
success('Updated README.md');
|
||
}
|
||
|
||
// 6. Create git commit
|
||
info('Creating git commit...');
|
||
exec('git add apps/frontend/package.json package.json apps/backend/__init__.py README.md');
|
||
exec(`git commit -m "chore: bump version to ${newVersion}"`);
|
||
success(`Created commit: "chore: bump version to ${newVersion}"`);
|
||
|
||
// 7. Create git tag
|
||
info('Creating git tag...');
|
||
exec(`git tag -a v${newVersion} -m "Release v${newVersion}"`);
|
||
success(`Created tag: v${newVersion}`);
|
||
|
||
// 8. Instructions
|
||
log('\n📋 Next steps:', colors.yellow);
|
||
log(` 1. Review the changes: git log -1`, colors.yellow);
|
||
log(` 2. Push the commit: git push origin <branch-name>`, colors.yellow);
|
||
log(` 3. Push the tag: git push origin v${newVersion}`, colors.yellow);
|
||
log(` 4. Create a GitHub release from the tag\n`, colors.yellow);
|
||
|
||
warning('Note: The commit and tag have been created locally but NOT pushed.');
|
||
warning('Please review and push manually when ready.');
|
||
|
||
log('\n✨ Version bump complete!\n', colors.green);
|
||
}
|
||
|
||
// Run
|
||
main();
|