Files
Taremin 24d6d774e1 fix(release): git status のステータスフラグ除去を正規表現に修正し comfy/ の除外を確実に適用
- line.substring(3) から正規表現 line.replace(/^[\sA-Z?!]+\s+/, '') に変更しスペースやフラグ文字数に依存せずファイルパスを抽出可能に修正
2026-08-12 06:09:29 +09:00

177 lines
6.3 KiB
JavaScript

#!/usr/bin/env node
const { execSync } = require('child_process');
const fs = require('fs');
const path = require('path');
const rootDir = path.resolve(__dirname, '..');
function run(cmd, options = {}) {
console.log(`> ${cmd}`);
return execSync(cmd, { cwd: rootDir, stdio: 'inherit', ...options });
}
function getOutput(cmd) {
return execSync(cmd, { cwd: rootDir, encoding: 'utf-8' }).trim();
}
function getCurrentBranch() {
return getOutput('git rev-parse --abbrev-ref HEAD');
}
function isGitClean() {
// -uno で Untracked ファイル (tests/ 以下のスクショやログ等) を無視し、追跡中ファイルの変更のみ確認
const status = getOutput('git status --porcelain -uno');
if (!status) return true;
const lines = status.split('\n').filter(Boolean);
const criticalChanges = lines.filter(line => {
const filepath = line.replace(/^[\sA-Z?!]+\s+/, '').replace(/^"/, '').replace(/"$/, '').trim();
if (filepath.startsWith('comfy/') || filepath.startsWith('dist/') || filepath.startsWith('tests/') || filepath.endsWith('.log') || filepath.endsWith('.png')) {
return false;
}
return true;
});
if (criticalChanges.length > 0) {
console.log('Detected critical uncommitted files:', criticalChanges);
}
return criticalChanges.length === 0;
}
function parseVersion(versionStr) {
const match = versionStr.match(/^(\d+)\.(\d+)\.(\d+)$/);
if (!match) return null;
return {
major: parseInt(match[1], 10),
minor: parseInt(match[2], 10),
patch: parseInt(match[3], 10)
};
}
function getNextVersion(currentVersion, bumpType) {
const parsed = parseVersion(currentVersion);
if (!parsed) throw new Error(`Invalid current version: ${currentVersion}`);
if (bumpType === 'major') {
return `${parsed.major + 1}.0.0`;
} else if (bumpType === 'minor') {
return `${parsed.major}.${parsed.minor + 1}.0`;
} else if (bumpType === 'patch') {
return `${parsed.major}.${parsed.minor}.${parsed.patch + 1}`;
} else if (parseVersion(bumpType)) {
return bumpType;
} else {
throw new Error(`Invalid bump type or version: "${bumpType}". Use 'patch', 'minor', 'major', or a valid version (x.y.z).`);
}
}
function updatePyprojectVersion(newVersion) {
const pyprojectPath = path.join(rootDir, 'pyproject.toml');
if (!fs.existsSync(pyprojectPath)) return;
let content = fs.readFileSync(pyprojectPath, 'utf-8');
const versionRegex = /(^version\s*=\s*")[^"]+(")/m;
if (versionRegex.test(content)) {
content = content.replace(versionRegex, `$1${newVersion}$2`);
fs.writeFileSync(pyprojectPath, content, 'utf-8');
console.log(`Updated pyproject.toml version to ${newVersion}`);
} else {
console.warn(`Warning: 'version' key not found in pyproject.toml`);
}
}
async function main() {
const args = process.argv.slice(2);
const bumpType = args[0] || 'patch';
if (args.includes('--help') || args.includes('-h')) {
console.log(`
Usage: npm run release -- [patch | minor | major | x.y.z]
Examples:
npm run release patch # 0.3.0 -> 0.3.1
npm run release minor # 0.3.0 -> 0.4.0
npm run release 0.3.5 # Set version to 0.3.5
`);
process.exit(0);
}
const startBranch = getCurrentBranch();
console.log(`Current branch: ${startBranch}`);
if (!isGitClean()) {
console.error(`Error: Uncommitted changes detected in working directory. Please commit or stash them first.`);
process.exit(1);
}
// ビルド成果物 (comfy/) の既知の差分を事前にリセット
try {
run(`git checkout HEAD -- comfy/`);
} catch (e) {}
const packageJsonPath = path.join(rootDir, 'package.json');
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
const currentVersion = packageJson.version;
const newVersion = getNextVersion(currentVersion, bumpType);
const tagName = `v${newVersion}`;
const releaseBranch = `release/${tagName}`;
console.log(`\n🚀 Starting release process: v${currentVersion} -> ${tagName}\n`);
try {
// 1. リリースブランチの作成
run(`git checkout -b ${releaseBranch}`);
// 2. バージョンの同期 (package.json & package-lock.json)
run(`npm version ${newVersion} --no-git-tag-version`);
// 3. pyproject.toml のバージョン同期
updatePyprojectVersion(newVersion);
// 4. プロジェクトのビルド
console.log(`\n📦 Building project...`);
run(`npm run build`);
// 5. 変更内容をコミット
console.log(`\n📝 Committing release changes...`);
run(`git add package.json package-lock.json pyproject.toml comfy/`);
run(`git commit -m "bump: version ${newVersion}"`);
// 6. master へマージ&タグ付け
console.log(`\n🔀 Merging to master and tagging...`);
run(`git checkout master`);
run(`git merge --no-ff ${releaseBranch} -m "Merge branch '${releaseBranch}'"`);
run(`git tag -a ${tagName} -m "Release ${tagName}"`);
// 7. develop へマージして復帰
console.log(`\n↩️ Returning to ${startBranch} branch...`);
run(`git checkout ${startBranch}`);
run(`git merge --no-ff master -m "Merge branch 'master' into ${startBranch}"`);
// 8. 一時リリースブランチの削除
run(`git branch -d ${releaseBranch}`);
console.log(`\n✅ Release ${tagName} completed successfully!`);
console.log(`Currently on branch: ${getCurrentBranch()}`);
console.log(`\nNext step to push changes and tags:`);
console.log(` git push origin master ${startBranch} --tags\n`);
} catch (error) {
console.error(`\n❌ Release failed with error:`, error.message);
console.log(`\nCleaning up and returning to ${startBranch}...`);
try {
run(`git checkout ${startBranch}`);
const branches = getOutput('git branch');
if (branches.includes(releaseBranch)) {
run(`git branch -D ${releaseBranch}`);
}
} catch (cleanupError) {
console.error(`Cleanup failed:`, cleanupError.message);
}
process.exit(1);
}
}
main();