my / scripts /release.js
BillAnll's picture
chore: add release scripts and deploy docs
5579564
Raw History Blame Contribute Delete
4.7 kB
#!/usr/bin/env node
const fs = require('fs');
const path = require('path');
const { execSync } = require('child_process');
const ROOT = path.resolve(__dirname, '..');
const DIST = path.join(ROOT, 'release');
const COMMANDS = ['prepare', 'check', 'build', 'zip', 'clean'];
function printHelp() {
console.log('Usage: node scripts/release.js <command>');
console.log('Commands:');
console.log(' prepare Install production deps');
console.log(' check Validate release readiness');
console.log(' build Copy release files to release/');
console.log(' zip Create release zip from release/');
console.log(' clean Remove release/ and dist/');
}
function prepare() {
console.log('Installing production dependencies...');
execSync('npm install --production', { cwd: ROOT, stdio: 'inherit' });
}
function check() {
console.log('Checking release readiness...');
const required = ['index.js', 'package.json'];
const missing = required.filter(file => !fs.existsSync(path.join(ROOT, file)));
if (missing.length) {
console.error('Missing required files:', missing.join(', '));
process.exit(1);
}
const pkg = JSON.parse(fs.readFileSync(path.join(ROOT, 'package.json'), 'utf8'));
if (!pkg.files || pkg.files.length === 0) {
console.error('package.json missing "files" field');
process.exit(1);
}
console.log('Check passed.');
}
function build() {
if (!fs.existsSync(DIST)) {
fs.mkdirSync(DIST, { recursive: true });
}
const pkg = JSON.parse(fs.readFileSync(path.join(ROOT, 'package.json'), 'utf8'));
const files = pkg.files || [];
files.forEach(file => {
const src = path.join(ROOT, file);
const dest = path.join(DIST, file);
if (!fs.existsSync(src)) {
return;
}
const stat = fs.statSync(src);
if (stat.isDirectory()) {
if (fs.existsSync(dest)) {
fs.rmSync(dest, { recursive: true });
}
fs.mkdirSync(dest, { recursive: true });
fs.readdirSync(src).forEach(child => {
const childSrc = path.join(src, child);
const childDest = path.join(dest, child);
if (fs.statSync(childSrc).isDirectory()) {
if (fs.existsSync(childDest)) {
fs.rmSync(childDest, { recursive: true });
}
fs.mkdirSync(childDest, { recursive: true });
fs.cpSync(childSrc, childDest, { recursive: true });
} else {
fs.copyFileSync(childSrc, childDest);
}
});
} else {
fs.copyFileSync(src, dest);
}
});
const nodeModules = path.join(ROOT, 'node_modules');
const destModules = path.join(DIST, 'node_modules');
if (fs.existsSync(nodeModules)) {
if (fs.existsSync(destModules)) {
fs.rmSync(destModules, { recursive: true });
}
fs.cpSync(nodeModules, destModules, { recursive: true });
}
console.log('Release files prepared at:', DIST);
}
function zip() {
if (!fs.existsSync(DIST)) {
console.error('release/ not found. Run build first.');
process.exit(1);
}
const outDir = path.join(ROOT, 'dist');
if (!fs.existsSync(outDir)) {
fs.mkdirSync(outDir, { recursive: true });
}
const safeName = (ROOT.match(/[^\\/]+$/) || ['release'])[0];
const zipName = `${safeName}-release-${Date.now()}.zip`;
const zipPath = path.join(outDir, zipName);
if (process.platform === 'win32') {
const psCmd = `Compress-Archive -Path 'release\*' -DestinationPath '${zipPath.replace(/'/g, "'")}' -Force`;
execSync(`powershell -Command "${psCmd}"`, { cwd: ROOT, stdio: 'inherit' });
} else {
const excludes = [
'node_modules/.cache/*',
'scripts/*',
'*.git*',
'release/*',
'dist/*',
'.gitignore',
'*.tgz'
];
const args = ['-r', `"${zipPath}"`, '.'];
excludes.forEach(exclude => {
args.push('-x', `"${exclude}"`);
});
const command = `zip ${args.join(' ')}`;
console.log('Running:', command);
execSync(command, { cwd: ROOT, stdio: 'inherit' });
}
const stats = fs.statSync(zipPath);
const sizeMB = (stats.size / (1024 * 1024)).toFixed(2);
console.log('Created zip:', zipPath);
console.log('Zip size:', sizeMB, 'MB');
}
function clean() {
[DIST, path.join(ROOT, 'dist')].forEach(dir => {
if (fs.existsSync(dir)) {
fs.rmSync(dir, { recursive: true });
console.log('Removed:', dir);
}
});
}
const command = process.argv[2];
if (!COMMANDS.includes(command)) {
printHelp();
process.exit(1);
}
switch (command) {
case 'prepare':
prepare();
break;
case 'check':
check();
break;
case 'build':
build();
break;
case 'zip':
zip();
break;
case 'clean':
clean();
break;
}