You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							132 lines
						
					
					
						
							3.5 KiB
						
					
					
				
			
		
		
		
			
			
			
				
					
				
				
					
				
			
		
		
	
	
							132 lines
						
					
					
						
							3.5 KiB
						
					
					
				
								#!/usr/bin/env node
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * API Changes Check Script
							 | 
						|
								 * 
							 | 
						|
								 * Checks for unintended API changes and blocks release if API changed
							 | 
						|
								 * without proper commit type (feat/breaking).
							 | 
						|
								 * 
							 | 
						|
								 * @author Matthew Raymer
							 | 
						|
								 * @version 1.0.0
							 | 
						|
								 */
							 | 
						|
								
							 | 
						|
								const fs = require('fs');
							 | 
						|
								const path = require('path');
							 | 
						|
								const { execSync } = require('child_process');
							 | 
						|
								
							 | 
						|
								const API_CHECKSUM_FILE = path.join(__dirname, '..', 'api-checksum.txt');
							 | 
						|
								const DIST_TYPES_DIR = path.join(__dirname, '..', 'dist', 'esm');
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Generate checksum of API definitions
							 | 
						|
								 * @returns {string} Checksum of API files
							 | 
						|
								 */
							 | 
						|
								function generateApiChecksum() {
							 | 
						|
								  try {
							 | 
						|
								    // Get all .d.ts files in dist/esm
							 | 
						|
								    const typeFiles = execSync(`find "${DIST_TYPES_DIR}" -name "*.d.ts" -type f`, { encoding: 'utf8' })
							 | 
						|
								      .trim()
							 | 
						|
								      .split('\n')
							 | 
						|
								      .filter(file => file.length > 0);
							 | 
						|
								
							 | 
						|
								    if (typeFiles.length === 0) {
							 | 
						|
								      console.warn('⚠️  No TypeScript definition files found in dist/esm');
							 | 
						|
								      return '';
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    // Generate checksum of all type files
							 | 
						|
								    const checksum = execSync(`cat ${typeFiles.join(' ')} | shasum -a 256`, { encoding: 'utf8' })
							 | 
						|
								      .trim()
							 | 
						|
								      .split(' ')[0];
							 | 
						|
								
							 | 
						|
								    return checksum;
							 | 
						|
								  } catch (error) {
							 | 
						|
								    console.error('Error generating API checksum:', error.message);
							 | 
						|
								    return '';
							 | 
						|
								  }
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Get last commit message
							 | 
						|
								 * @returns {string} Last commit message
							 | 
						|
								 */
							 | 
						|
								function getLastCommitMessage() {
							 | 
						|
								  try {
							 | 
						|
								    return execSync('git log -1 --pretty=%B', { encoding: 'utf8' }).trim();
							 | 
						|
								  } catch (error) {
							 | 
						|
								    console.error('Error getting last commit message:', error.message);
							 | 
						|
								    return '';
							 | 
						|
								  }
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Check if commit message indicates API change
							 | 
						|
								 * @param {string} commitMessage - Commit message
							 | 
						|
								 * @returns {boolean} True if API change is allowed
							 | 
						|
								 */
							 | 
						|
								function isApiChangeAllowed(commitMessage) {
							 | 
						|
								  const allowedPatterns = [
							 | 
						|
								    /^feat\(/i,
							 | 
						|
								    /^fix\(/i,
							 | 
						|
								    /BREAKING CHANGE/i,
							 | 
						|
								    /^feat!/i,
							 | 
						|
								    /^fix!/i
							 | 
						|
								  ];
							 | 
						|
								
							 | 
						|
								  return allowedPatterns.some(pattern => pattern.test(commitMessage));
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Check API changes and enforce rules
							 | 
						|
								 */
							 | 
						|
								function checkApiChanges() {
							 | 
						|
								  console.log('🔍 Checking API changes...');
							 | 
						|
								
							 | 
						|
								  if (!fs.existsSync(DIST_TYPES_DIR)) {
							 | 
						|
								    console.error('❌ Dist types directory not found. Run "npm run build" first.');
							 | 
						|
								    process.exit(1);
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  const currentChecksum = generateApiChecksum();
							 | 
						|
								  
							 | 
						|
								  if (!currentChecksum) {
							 | 
						|
								    console.error('❌ Could not generate API checksum');
							 | 
						|
								    process.exit(1);
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  let previousChecksum = '';
							 | 
						|
								  if (fs.existsSync(API_CHECKSUM_FILE)) {
							 | 
						|
								    previousChecksum = fs.readFileSync(API_CHECKSUM_FILE, 'utf8').trim();
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  if (previousChecksum === currentChecksum) {
							 | 
						|
								    console.log('✅ No API changes detected');
							 | 
						|
								    return;
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  console.log('⚠️  API changes detected');
							 | 
						|
								  console.log(`Previous: ${previousChecksum.substring(0, 8)}...`);
							 | 
						|
								  console.log(`Current:  ${currentChecksum.substring(0, 8)}...`);
							 | 
						|
								
							 | 
						|
								  const commitMessage = getLastCommitMessage();
							 | 
						|
								  console.log(`Last commit: ${commitMessage}`);
							 | 
						|
								
							 | 
						|
								  if (!isApiChangeAllowed(commitMessage)) {
							 | 
						|
								    console.error('\n❌ API changes detected without proper commit type!');
							 | 
						|
								    console.error('🚫 Release blocked. API changes require:');
							 | 
						|
								    console.error('   - feat(scope): description');
							 | 
						|
								    console.error('   - fix(scope): description');
							 | 
						|
								    console.error('   - BREAKING CHANGE in commit message');
							 | 
						|
								    console.error('   - feat!(scope): or fix!(scope): for breaking changes');
							 | 
						|
								    process.exit(1);
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  console.log('✅ API changes allowed by commit type');
							 | 
						|
								  
							 | 
						|
								  // Update checksum file
							 | 
						|
								  fs.writeFileSync(API_CHECKSUM_FILE, currentChecksum);
							 | 
						|
								  console.log('📝 Updated API checksum file');
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// Run the check
							 | 
						|
								checkApiChanges();
							 | 
						|
								
							 |