chore: Add migration setup script

This commit is contained in:
John Doe
2026-03-31 21:19:43 -04:00
parent c22de37584
commit 6b1b896c1d
2 changed files with 253 additions and 21 deletions
+236
View File
@@ -0,0 +1,236 @@
#!/usr/bin/env node
/**
* Post-Migration Repository Setup Script
*
* Sets up CI/CD workflows, CODEOWNERS, README, and CONTRIBUTING
* for all 6 extracted repositories.
*/
const { execSync } = require('child_process');
const fs = require('fs');
const path = require('path');
const REPO_CONFIGS = {
'heretek-openclaw-core': {
readmeTemplate: 'README-core.md',
hasTests: true,
hasTypescript: false,
publishType: 'docker'
},
'heretek-openclaw-cli': {
readmeTemplate: 'README-cli.md',
hasTests: true,
hasTypescript: false,
publishType: 'npm'
},
'heretek-openclaw-dashboard': {
readmeTemplate: 'README-dashboard.md',
hasTests: true,
hasTypescript: true,
publishType: 'none'
},
'heretek-openclaw-plugins': {
readmeTemplate: 'README-plugins.md',
hasTests: true,
hasTypescript: false,
publishType: 'npm'
},
'heretek-openclaw-deploy': {
readmeTemplate: 'README-deploy.md',
hasTests: false,
hasTypescript: false,
publishType: 'none'
},
'heretek-openclaw-docs': {
readmeTemplate: 'README-docs.md',
hasTests: true,
hasTypescript: true,
publishType: 'none'
}
};
const TEMPLATES_DIR = path.join(__dirname, 'templates');
const BASE_DIR = path.join(__dirname, '..', '..');
/**
* Read template file
*/
function readTemplate(filename) {
const filepath = path.join(TEMPLATES_DIR, filename);
return fs.readFileSync(filepath, 'utf-8');
}
/**
* Customize CI workflow for a specific repository
*/
function customizeCIWorkflow(repoName, config) {
let workflow = readTemplate('workflow-ci.yml');
// Customize based on repo
if (!config.hasTests) {
// Remove test job for repos without tests
workflow = workflow.replace(/ test:\n[\s\S]*? needs: \[lint, typecheck, test\]/g, ' build:');
workflow = workflow.replace(/needs: \[lint, typecheck, test\]/g, 'needs: [lint, typecheck]');
}
if (!config.hasTypescript) {
// Remove typecheck job for non-TS repos
workflow = workflow.replace(/ typecheck:\n[\s\S]*? test:/g, ' test:');
workflow = workflow.replace(/needs: \[lint, typecheck\]/g, 'needs: [lint]');
}
return workflow;
}
/**
* Customize CODEOWNERS for a specific repository
*/
function customizeCODEOWNERS(repoName) {
let codeowners = readTemplate('CODEOWNERS');
// Customize based on repo
const repoSpecific = repoName.replace('heretek-openclaw-', '');
codeowners = codeowners.replace(/@heretek\/core-team/g, `@heretek/${repoSpecific}-team`);
codeowners = codeowners.replace(/@heretek\/agent-team/g, `@heretek/${repoSpecific}-team`);
codeowners = codeowners.replace(/@heretek\/research-team/g, `@heretek/${repoSpecific}-team`);
codeowners = codeowners.replace(/@heretek\/safety-team/g, `@heretek/${repoSpecific}-team`);
codeowners = codeowners.replace(/@heretek\/skill-team/g, `@heretek/${repoSpecific}-team`);
codeowners = codeowners.replace(/@heretek\/memory-team/g, `@heretek/${repoSpecific}-team`);
codeowners = codeowners.replace(/@heretek\/plugin-team/g, `@heretek/${repoSpecific}-team`);
codeowners = codeowners.replace(/@heretek\/qa-team/g, `@heretek/${repoSpecific}-team`);
codeowners = codeowners.replace(/@heretek\/docs-team/g, `@heretek/${repoSpecific}-team`);
codeowners = codeowners.replace(/@heretek\/devops-team/g, `@heretek/${repoSpecific}-team`);
return codeowners;
}
/**
* Set up a single repository
*/
function setupRepository(repoName, config) {
const repoPath = path.join(BASE_DIR, repoName);
const workflowsPath = path.join(repoPath, '.github', 'workflows');
console.log(`\n🔧 Setting up ${repoName}...`);
// Ensure .github/workflows directory exists
if (!fs.existsSync(workflowsPath)) {
fs.mkdirSync(workflowsPath, { recursive: true });
console.log(` ✅ Created .github/workflows directory`);
}
// Write CI workflow
const ciWorkflow = customizeCIWorkflow(repoName, config);
fs.writeFileSync(path.join(workflowsPath, 'ci.yml'), ciWorkflow);
console.log(` ✅ Created .github/workflows/ci.yml`);
// Write Release workflow
const releaseWorkflow = readTemplate('workflow-release.yml');
fs.writeFileSync(path.join(workflowsPath, 'release.yml'), releaseWorkflow);
console.log(` ✅ Created .github/workflows/release.yml`);
// Write CODEOWNERS
const codeowners = customizeCODEOWNERS(repoName);
fs.writeFileSync(path.join(repoPath, 'CODEOWNERS'), codeowners);
console.log(` ✅ Created CODEOWNERS`);
// Write README
const readme = readTemplate(config.readmeTemplate);
fs.writeFileSync(path.join(repoPath, 'README.md'), readme);
console.log(` ✅ Created README.md`);
// Write CONTRIBUTING
const contributing = readTemplate('CONTRIBUTING.md');
const customizedContributing = contributing.replace(/heretek-openclaw-<repo>/g, repoName);
fs.writeFileSync(path.join(repoPath, 'CONTRIBUTING.md'), customizedContributing);
console.log(` ✅ Created CONTRIBUTING.md`);
return true;
}
/**
* Commit and push changes for a repository
*/
function commitAndPush(repoName) {
const repoPath = path.join(BASE_DIR, repoName);
console.log(`\n📦 Committing changes in ${repoName}...`);
try {
execSync('git add -A', { cwd: repoPath, stdio: 'pipe' });
const status = execSync('git status --porcelain', { cwd: repoPath, encoding: 'utf-8' });
if (status.trim()) {
execSync('git commit -m "chore: Add CI/CD workflows, CODEOWNERS, and documentation templates"', {
cwd: repoPath,
stdio: 'pipe'
});
console.log(` ✅ Committed changes`);
console.log(` 📤 Pushing to GitHub...`);
execSync('git push', { cwd: repoPath, stdio: 'pipe' });
console.log(` ✅ Pushed to GitHub`);
} else {
console.log(` ⚠️ No changes to commit`);
}
return true;
} catch (error) {
console.error(` ❌ Error: ${error.message}`);
return false;
}
}
/**
* Main function
*/
async function main() {
console.log('🚀 Post-Migration Repository Setup\n');
console.log('This script will set up CI/CD workflows, CODEOWNERS, README, and CONTRIBUTING.md');
console.log('for all 6 extracted repositories.\n');
const results = {};
// Set up each repository
for (const [repoName, config] of Object.entries(REPO_CONFIGS)) {
const setupSuccess = setupRepository(repoName, config);
results[repoName] = { setup: setupSuccess, push: false };
}
console.log('\n\n📤 Pushing changes to GitHub...\n');
// Commit and push each repository
for (const repoName of Object.keys(REPO_CONFIGS)) {
const pushSuccess = commitAndPush(repoName);
results[repoName].push = pushSuccess;
}
// Summary
console.log('\n\n✅ Setup Complete!\n');
console.log('Summary:');
console.log('--------');
let allSuccess = true;
for (const [repoName, result] of Object.entries(results)) {
const setupIcon = result.setup ? '✅' : '❌';
const pushIcon = result.push ? '✅' : '❌';
const status = result.setup && result.push ? '✅' : '⚠️';
if (!result.setup || !result.push) {
allSuccess = false;
}
console.log(`${status} ${repoName}: Setup ${setupIcon} Push ${pushIcon}`);
}
if (allSuccess) {
console.log('\n🎉 All repositories set up successfully!');
} else {
console.log('\n⚠️ Some repositories had issues. Please check the output above.');
}
}
// Run
main().catch(console.error);
+17 -21
View File
@@ -2,12 +2,12 @@
/**
* Heretek OpenClaw Monorepo Split Script
*
*
* Extracts specified paths from the monorepo into dedicated repositories.
* Uses git-filter-repo for efficient history preservation.
*
*
* Usage: node scripts/migration/split-repos.js <repo-name>
*
*
* Available repositories:
* - heretek-openclaw-core
* - heretek-openclaw-cli
@@ -17,9 +17,9 @@
* - heretek-openclaw-docs
*/
const { execSync } = require('child_process');
const fs = require('fs');
const path = require('path');
import { execSync } from 'child_process';
import fs from 'fs';
import path from 'path';
// Repository configuration
const REPOS = {
@@ -306,6 +306,7 @@ function splitRepo(repoName, config) {
console.log(` Paths: ${config.paths.length} entries`);
const repoDir = path.join(process.cwd(), repoName);
const sourceRepoDir = process.cwd(); // Use current directory as source
// Check if git-filter-repo is installed
try {
@@ -317,22 +318,19 @@ function splitRepo(repoName, config) {
process.exit(1);
}
// Clone the monorepo
console.log(' Cloning monorepo...');
// Clone from local repository
console.log(' Cloning from local monorepo...');
if (fs.existsSync(repoDir)) {
console.log(' ⚠️ Directory already exists, removing...');
fs.rmSync(repoDir, { recursive: true, force: true });
}
exec(`git clone --depth 1 git@github.com:heretek/heretek-openclaw.git ${repoName}`, { stdio: 'pipe' });
exec(`git clone file://${sourceRepoDir} ${repoName}`, { stdio: 'pipe' });
// Change to repo directory
const originalDir = process.cwd();
process.chdir(repoDir);
try {
// Initialize git for filter-repo (fresh clone might not have full history)
exec('git fetch --unshallow 2>/dev/null || true', { stdio: 'pipe' });
// Filter to only include specified paths
const pathsArg = config.paths.map(p => `--path ${p}`).join(' ');
console.log(' Filtering repository...');
@@ -344,16 +342,14 @@ function splitRepo(repoName, config) {
config.postProcess(repoDir);
}
// Update remote URL
console.log(' Updating remote...');
exec(`git remote set-url origin git@github.com:heretek/${repoName}.git`);
// Add remote URL for new GitHub repository
console.log(' Adding remote...');
exec(`git remote add origin git@github.com:Heretek-AI/${repoName}.git`);
// Push to new repository
console.log(' Pushing to new repository...');
exec('git push -u origin main --force');
console.log(`${repoName} split complete!`);
console.log(` Repository: https://github.com/heretek/${repoName}`);
console.log(`${repoName} extracted to ${repoDir}`);
console.log(` Next steps:`);
console.log(` 1. cd ${repoDir}`);
console.log(` 2. git push -u origin main --force`);
} catch (error) {
console.error(`❌ Error splitting ${repoName}: ${error.message}`);