archive.cjs•4.69 kB
#!/usr/bin/env node
const fs = require('fs');
const path = require('path');
const archiver = require('archiver');
/**
 * Archive binaries for distribution using zip/tar.gz compression
 */
function getFileSize(filePath) {
  const stats = fs.statSync(filePath);
  return (stats.size / 1024 / 1024).toFixed(2); // MB
}
function detectPlatform() {
  const platform = process.platform;
  if (platform === 'win32') return 'windows';
  if (platform === 'darwin') return 'macos';
  return 'linux';
}
function createArchive(binaryPath, options = {}) {
  return new Promise((resolve, reject) => {
    const {
      format = 'auto', // 'zip', 'tar.gz', or 'auto'
      outputDir = '.',
    } = options;
    if (!fs.existsSync(binaryPath)) {
      return reject(new Error(`Binary not found: ${binaryPath}`));
    }
    const binaryName = path.basename(binaryPath);
    const baseName = binaryName.replace(/\.(exe)?$/, '');
    // Validate and sanitize the base name for filename safety
    if (!/^[a-zA-Z0-9._-]+$/.test(baseName)) {
      return reject(
        new Error(
          `Invalid binary name: ${baseName}. Only alphanumeric characters, dots, underscores and hyphens allowed.`,
        ),
      );
    }
    // Determine archive format
    let archiveFormat = format;
    if (format === 'auto') {
      archiveFormat = detectPlatform() === 'windows' ? 'zip' : 'tar';
    }
    const originalSize = getFileSize(binaryPath);
    console.log(`📦 Creating ${archiveFormat} archive for ${binaryName}...`);
    console.log(`📊 Original size: ${originalSize} MB`);
    const archiveName = archiveFormat === 'zip' ? `${baseName}.zip` : `${baseName}.tar.gz`;
    const archivePath = path.join(outputDir, archiveName);
    // Remove existing archive if it exists
    if (fs.existsSync(archivePath)) {
      fs.unlinkSync(archivePath);
    }
    // Create output stream
    const output = fs.createWriteStream(archivePath);
    let archive;
    if (archiveFormat === 'zip') {
      archive = archiver('zip', { zlib: { level: 9 } });
    } else {
      archive = archiver('tar', { gzip: true, gzipOptions: { level: 9 } });
    }
    // Handle errors
    archive.on('error', (err) => {
      reject(new Error(`Archive creation failed: ${err.message}`));
    });
    output.on('close', () => {
      try {
        // Calculate compression stats
        const archiveSize = getFileSize(archivePath);
        const savings = (((originalSize - archiveSize) / originalSize) * 100).toFixed(1);
        console.log(`📊 Archive size: ${archiveSize} MB`);
        console.log(`💾 Space saved: ${savings}%`);
        console.log(`✅ Archive created: ${archiveName}`);
        resolve(archivePath);
      } catch (error) {
        reject(error);
      }
    });
    output.on('error', (err) => {
      reject(new Error(`Failed to write archive: ${err.message}`));
    });
    // Pipe archive data to the output file
    archive.pipe(output);
    // Add the binary file to the archive
    archive.file(binaryPath, { name: binaryName });
    // Finalize the archive
    archive.finalize();
  });
}
async function archiveAllBinaries(directory = '.', options = {}) {
  const binaryPatterns = ['1mcp', '1mcp.exe'];
  const binaries = [];
  binaryPatterns.forEach((pattern) => {
    const fullPath = path.join(directory, pattern);
    if (fs.existsSync(fullPath)) {
      binaries.push(fullPath);
    }
  });
  // Also look for platform-specific binaries
  const platformBinaries = fs
    .readdirSync(directory)
    .filter((f) => f.startsWith('1mcp-') && (f.endsWith('.exe') || !f.includes('.')))
    .map((f) => path.join(directory, f));
  binaries.push(...platformBinaries);
  if (binaries.length === 0) {
    console.log('📂 No binaries found to archive');
    return [];
  }
  console.log(`📦 Found ${binaries.length} binaries to archive`);
  const archives = [];
  for (const binary of binaries) {
    try {
      const archive = await createArchive(binary, options);
      archives.push(archive);
    } catch (error) {
      console.error(`❌ Failed to archive ${binary}:`, error.message);
    }
  }
  return archives;
}
if (require.main === module) {
  const target = process.argv[2];
  const format = process.argv[3] || 'auto';
  (async () => {
    try {
      if (target && fs.existsSync(target)) {
        // Archive specific binary
        await createArchive(target, { format });
      } else {
        // Archive all found binaries
        await archiveAllBinaries('.', { format });
      }
    } catch (error) {
      console.error('❌ Error:', error.message);
      process.exit(1);
    }
  })();
}
module.exports = { createArchive, archiveAllBinaries };