Skip to main content
Glama
diagnose-workflows.js9.15 kB
#!/usr/bin/env node /** * GitHub Actions Workflow Diagnostic Script * * This script helps diagnose common issues with the automated release workflows */ import { execSync } from 'child_process'; import { readFileSync, existsSync } from 'fs'; import { join } from 'path'; class WorkflowDiagnostic { constructor() { this.issues = []; this.warnings = []; this.suggestions = []; } log(message, type = 'info') { const colors = { info: '\x1b[36m', // cyan success: '\x1b[32m', // green warning: '\x1b[33m', // yellow error: '\x1b[31m', // red reset: '\x1b[0m' }; const prefix = type === 'error' ? '❌' : type === 'success' ? '✅' : type === 'warning' ? '⚠️' : 'ℹ️'; console.log(`${colors[type]}${prefix} ${message}${colors.reset}`); } checkWorkflowFiles() { this.log('Checking workflow files...', 'info'); const workflowFiles = [ '.github/workflows/release.yml', '.github/workflows/semantic-release.yml', '.github/workflows/ci.yml' ]; workflowFiles.forEach(file => { if (existsSync(file)) { this.log(`Found: ${file}`, 'success'); this.validateWorkflowSyntax(file); } else { this.log(`Missing: ${file}`, 'error'); this.issues.push(`Workflow file missing: ${file}`); } }); } validateWorkflowSyntax(filePath) { try { const content = readFileSync(filePath, 'utf8'); // Check for common issues if (!content.includes('on:')) { this.issues.push(`${filePath}: Missing 'on:' trigger section`); } if (!content.includes('jobs:')) { this.issues.push(`${filePath}: Missing 'jobs:' section`); } // Check branch configuration if (content.includes('branches: [ main ]') && !content.includes('master')) { this.warnings.push(`${filePath}: Only configured for 'main' branch, consider adding 'master'`); } // Check for secrets usage if (content.includes('NPM_TOKEN') && !content.includes('secrets.NPM_TOKEN')) { this.issues.push(`${filePath}: NPM_TOKEN referenced but not using secrets.NPM_TOKEN`); } this.log(`Syntax check passed: ${filePath}`, 'success'); } catch (error) { this.log(`Syntax error in ${filePath}: ${error.message}`, 'error'); this.issues.push(`Workflow syntax error: ${filePath}`); } } checkGitConfiguration() { this.log('Checking Git configuration...', 'info'); try { const branch = execSync('git rev-parse --abbrev-ref HEAD', { encoding: 'utf8' }).trim(); this.log(`Current branch: ${branch}`, 'info'); if (branch !== 'main' && branch !== 'master') { this.warnings.push(`Current branch '${branch}' may not trigger workflows`); } const remote = execSync('git remote get-url origin', { encoding: 'utf8' }).trim(); this.log(`Remote URL: ${remote}`, 'info'); if (!remote.includes('github.com')) { this.issues.push('Repository is not hosted on GitHub - GitHub Actions won\'t work'); } // Check for recent commits with [release] trigger try { const recentCommits = execSync('git log --oneline -5', { encoding: 'utf8' }); const hasReleaseTrigger = recentCommits.includes('[release]'); if (hasReleaseTrigger) { this.log('Found recent commit with [release] trigger', 'success'); } else { this.warnings.push('No recent commits with [release] trigger found'); } } catch (error) { this.warnings.push('Could not check recent commits'); } } catch (error) { this.log(`Git configuration check failed: ${error.message}`, 'error'); this.issues.push('Git repository not properly configured'); } } checkPackageConfiguration() { this.log('Checking package configuration...', 'info'); try { const packageJson = JSON.parse(readFileSync('package.json', 'utf8')); // Check required fields const requiredFields = ['name', 'version', 'scripts']; requiredFields.forEach(field => { if (!packageJson[field]) { this.issues.push(`package.json missing required field: ${field}`); } }); // Check scripts const requiredScripts = ['build', 'test', 'lint']; requiredScripts.forEach(script => { if (!packageJson.scripts[script]) { this.issues.push(`package.json missing required script: ${script}`); } }); // Check if package is publishable if (packageJson.private === true) { this.warnings.push('Package is marked as private - NPM publishing will fail'); } this.log(`Package name: ${packageJson.name}`, 'info'); this.log(`Current version: ${packageJson.version}`, 'info'); } catch (error) { this.log(`Package configuration check failed: ${error.message}`, 'error'); this.issues.push('package.json is not readable or invalid'); } } checkLocalBuild() { this.log('Testing local build process...', 'info'); try { // Test build execSync('npm run build', { stdio: 'pipe' }); this.log('Build successful', 'success'); } catch (error) { this.log('Build failed', 'error'); this.issues.push('Local build fails - workflow will fail at build step'); } try { // Test linting execSync('npm run lint', { stdio: 'pipe' }); this.log('Linting passed', 'success'); } catch (error) { this.log('Linting failed', 'error'); this.issues.push('Linting fails - workflow will fail at lint step'); } } checkNPMAuthentication() { this.log('Checking NPM authentication...', 'info'); try { const whoami = execSync('npm whoami', { encoding: 'utf8' }).trim(); this.log(`NPM user: ${whoami}`, 'success'); // Test package access try { execSync('npm pack --dry-run', { stdio: 'pipe' }); this.log('Package can be packed', 'success'); } catch (error) { this.warnings.push('Package packing failed - check package.json configuration'); } } catch (error) { this.log('Not authenticated with NPM', 'warning'); this.warnings.push('NPM authentication required for publishing'); this.suggestions.push('Run: npm login'); this.suggestions.push('Create automation token: npm token create --type=automation'); this.suggestions.push('Add NPM_TOKEN to GitHub repository secrets'); } } generateReport() { this.log('\n📊 Diagnostic Report', 'info'); if (this.issues.length === 0) { this.log('✅ No critical issues found!', 'success'); } else { this.log('❌ Critical Issues Found:', 'error'); this.issues.forEach(issue => this.log(` • ${issue}`, 'error')); } if (this.warnings.length > 0) { this.log('\n⚠️ Warnings:', 'warning'); this.warnings.forEach(warning => this.log(` • ${warning}`, 'warning')); } if (this.suggestions.length > 0) { this.log('\n💡 Suggestions:', 'info'); this.suggestions.forEach(suggestion => this.log(` • ${suggestion}`, 'info')); } this.generateActionPlan(); } generateActionPlan() { this.log('\n🎯 Action Plan:', 'info'); if (this.issues.length > 0) { this.log('\n1. Fix Critical Issues:', 'error'); if (this.issues.some(i => i.includes('NPM_TOKEN'))) { this.log(' • Set up NPM_TOKEN in GitHub repository secrets', 'info'); this.log(' https://github.com/stier1ba/licensespring-mcp/settings/secrets/actions', 'info'); } if (this.issues.some(i => i.includes('workflow'))) { this.log(' • Fix workflow file syntax errors', 'info'); this.log(' • Ensure proper branch configuration', 'info'); } if (this.issues.some(i => i.includes('build') || i.includes('lint'))) { this.log(' • Fix local build/lint issues first', 'info'); this.log(' • Run: npm run build && npm run lint && npm test', 'info'); } } this.log('\n2. Test Workflow Trigger:', 'info'); this.log(' • Manual dispatch: https://github.com/stier1ba/licensespring-mcp/actions', 'info'); this.log(' • Or commit with [release]: git commit -m "test: trigger release [release]"', 'info'); this.log('\n3. Monitor Progress:', 'info'); this.log(' • Watch workflow logs in GitHub Actions', 'info'); this.log(' • Check for specific error messages', 'info'); this.log(' • Re-run failed jobs if needed', 'info'); } run() { this.log('🔍 GitHub Actions Workflow Diagnostic\n', 'info'); this.checkWorkflowFiles(); this.checkGitConfiguration(); this.checkPackageConfiguration(); this.checkLocalBuild(); this.checkNPMAuthentication(); this.generateReport(); } } // Run the diagnostic const diagnostic = new WorkflowDiagnostic(); diagnostic.run();

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/stier1ba/licensespring-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server