diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index d52228024..c71104060 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -74,40 +74,59 @@ jobs: fi echo "Building with BASE_PATH: $BASE_PATH" - # Get build script from deploy branch (fallback to local if deploy branch doesn't exist) - echo "Fetching build script from deploy branch..." - if git fetch origin deploy 2>/dev/null; then - echo "Deploy branch found, using build script from deploy branch" - git checkout origin/deploy -- scripts/build-multi-branch.js - else - echo "Deploy branch not found, checking if build script exists locally..." - if [[ ! -f "scripts/build-multi-branch.js" ]]; then - echo "ERROR: Build script not found and deploy branch doesn't exist" - echo "Creating minimal build script as fallback..." + # Try to get build script from deploy branch, create fallback if not available + echo "Checking for deploy branch build script..." + if [[ -n "$(git ls-remote --heads origin deploy 2>/dev/null)" ]]; then + echo "Deploy branch exists on remote, fetching build script..." + if git fetch origin deploy 2>/dev/null && git checkout origin/deploy -- scripts/build-multi-branch.js 2>/dev/null; then + echo "✅ Using build script from deploy branch" + else + echo "⚠️ Failed to fetch script from deploy branch, using fallback" mkdir -p scripts - cat > scripts/build-multi-branch.js << 'EOF' + fi + else + echo "⚠️ Deploy branch not found on remote, using fallback build process" + mkdir -p scripts + fi + + # Create or use fallback build script if needed + if [[ ! -f "scripts/build-multi-branch.js" ]]; then + echo "Creating fallback build script..." + cat > scripts/build-multi-branch.js << 'EOF' const { execSync } = require('child_process'); const fs = require('fs'); const path = require('path'); -console.log('Building React app for deployment...'); +console.log('🔨 Building React app for deployment (fallback mode)...'); -// Simplified build process try { - // Set environment variables + // Set environment variables for React build process.env.NODE_ENV = 'production'; process.env.PUBLIC_URL = process.env.BASE_PATH || '/'; + process.env.GENERATE_SOURCEMAP = 'false'; // Reduce build size - // Run the build + console.log(`Building with PUBLIC_URL: ${process.env.PUBLIC_URL}`); + + // Run the standard React build execSync('npm run build', { stdio: 'inherit' }); + // Verify build output + const buildDir = path.join(process.cwd(), 'build'); + const indexPath = path.join(buildDir, 'index.html'); + + if (!fs.existsSync(indexPath)) { + throw new Error('Build completed but index.html not found'); + } + console.log('✅ Build completed successfully'); + console.log(`📁 Build output available in: ${buildDir}`); + } catch (error) { console.error('❌ Build failed:', error.message); + console.error('Stack:', error.stack); process.exit(1); } EOF - fi fi # Run the build script diff --git a/scripts/bpmn-to-svg.js b/scripts/bpmn-to-svg.js deleted file mode 100644 index 153846c4d..000000000 --- a/scripts/bpmn-to-svg.js +++ /dev/null @@ -1,74 +0,0 @@ -#!/usr/bin/env node - -/** - * Convert BPMN diagram to SVG - * This script uses bpmn-js to render BPMN files as SVG - */ - -const fs = require('fs'); -const path = require('path'); -const BpmnJS = require('bpmn-js/lib/Viewer'); - -// Mock DOM environment for Node.js -const { JSDOM } = require('jsdom'); -const dom = new JSDOM('
'); -global.window = dom.window; -global.document = dom.window.document; - -async function convertBpmnToSvg(bpmnFilePath, svgOutputPath) { - try { - // Read BPMN file - const bpmnXml = fs.readFileSync(bpmnFilePath, 'utf8'); - - // Create container element - const container = document.getElementById('container'); - - // Initialize BPMN viewer - const viewer = new BpmnJS({ - container, - width: 1600, - height: 1200 - }); - - // Import BPMN diagram - await viewer.importXML(bpmnXml); - - // Export as SVG - const { svg } = await viewer.saveSVG(); - - // Write SVG to file - fs.writeFileSync(svgOutputPath, svg); - - console.log(`Successfully converted ${bpmnFilePath} to ${svgOutputPath}`); - - // Clean up - viewer.destroy(); - - } catch (error) { - console.error('Error converting BPMN to SVG:', error); - throw error; - } -} - -// Main execution -if (require.main === module) { - const bpmnFile = process.argv[2]; - const svgFile = process.argv[3]; - - if (!bpmnFile || !svgFile) { - console.error('Usage: node bpmn-to-svg.js '); - process.exit(1); - } - - convertBpmnToSvg(bpmnFile, svgFile) - .then(() => { - console.log('Conversion completed successfully'); - process.exit(0); - }) - .catch((error) => { - console.error('Conversion failed:', error.message); - process.exit(1); - }); -} - -module.exports = { convertBpmnToSvg }; \ No newline at end of file diff --git a/scripts/check-framework-compliance.js b/scripts/check-framework-compliance.js deleted file mode 100755 index 7f8f56010..000000000 --- a/scripts/check-framework-compliance.js +++ /dev/null @@ -1,306 +0,0 @@ -#!/usr/bin/env node - -/** - * SGEX Page Framework Compliance Checker - * - * This script validates that all pages in the SGEX Workbench comply with - * the Page Framework requirements. - */ - -const fs = require('fs'); -const path = require('path'); - -// Configuration -const SRC_DIR = path.join(__dirname, '../src'); -const COMPONENTS_DIR = path.join(SRC_DIR, 'components'); -const APP_JS = path.join(SRC_DIR, 'App.js'); - -// Framework compliance rules -const COMPLIANCE_RULES = { - PAGE_LAYOUT: 'All page components must be wrapped with PageLayout', - PAGE_NAME: 'All PageLayout components must have a unique pageName prop', - FRAMEWORK_HOOKS: 'Use framework hooks instead of direct useParams()', - NO_MANUAL_HELP: 'No direct ContextualHelpMascot imports in page components', - NO_CUSTOM_HEADERS: 'Let PageLayout handle headers instead of custom implementations' -}; - -// Utility components that don't need full framework compliance -const UTILITY_COMPONENTS = [ - 'PATLogin.js', 'HelpButton.js', 'HelpModal.js', 'SaveDialog.js', - 'PageEditModal.js', 'PageViewModal.js', 'BranchSelector.js', - 'DAKStatusBox.js', 'Publications.js', 'CommitsSlider.js', - 'GitHubActionsIntegration.js', 'WHODigitalLibrary.js', - 'ContextualHelpMascot.js', 'BPMNViewerEnhanced.js' -]; - -// Framework components themselves -const FRAMEWORK_COMPONENTS = [ - 'PageLayout.js', 'PageHeader.js', 'PageProvider.js', - 'ErrorHandler.js', 'usePageParams.js', 'index.js' -]; - -class ComplianceChecker { - constructor() { - this.results = { - compliant: [], - partiallyCompliant: [], - nonCompliant: [], - errors: [] - }; - } - - /** - * Main entry point for compliance checking - */ - async check() { - console.log('🔍 SGEX Page Framework Compliance Checker'); - console.log('=========================================\n'); - - try { - // Get all page components from routes - const routeComponents = await this.getRouteComponents(); - console.log(`Found ${routeComponents.length} routed page components\n`); - - // Check each component - for (const component of routeComponents) { - await this.checkComponent(component); - } - - // Print results - this.printResults(); - - // Return exit code - return this.results.nonCompliant.length === 0 ? 0 : 1; - - } catch (error) { - console.error('❌ Error running compliance check:', error.message); - return 1; - } - } - - /** - * Extract route components from App.js - */ - async getRouteComponents() { - const appContent = fs.readFileSync(APP_JS, 'utf8'); - const components = []; - - // Find all Route elements and extract component names - const routeRegex = /]+element=\{<([A-Za-z0-9_]+)/g; - let match; - - while ((match = routeRegex.exec(appContent)) !== null) { - const componentName = match[1].trim(); - // Skip if it's a utility component we don't expect to be framework-compliant - if (!UTILITY_COMPONENTS.includes(`${componentName}.js`)) { - components.push(componentName); - } - } - - return [...new Set(components)]; // Remove duplicates - } - - /** - * Check a single component for framework compliance - */ - async checkComponent(componentName) { - const componentPath = path.join(COMPONENTS_DIR, `${componentName}.js`); - - if (!fs.existsSync(componentPath)) { - this.results.errors.push(`Component file not found: ${componentName}.js`); - return; - } - - const content = fs.readFileSync(componentPath, 'utf8'); - const compliance = this.analyzeComponent(componentName, content); - - // Categorize component - if (compliance.score === compliance.maxScore) { - this.results.compliant.push(compliance); - } else if (compliance.score > 0) { - this.results.partiallyCompliant.push(compliance); - } else { - this.results.nonCompliant.push(compliance); - } - - console.log(this.formatComponentResult(compliance)); - } - - /** - * Analyze component code for framework compliance - */ - analyzeComponent(componentName, content) { - const compliance = { - name: componentName, - score: 0, - maxScore: 5, - checks: {}, - issues: [], - suggestions: [] - }; - - // Check 1: Uses PageLayout - const hasPageLayout = content.includes('PageLayout') && - (content.includes('import { PageLayout }') || - content.includes('import PageLayout') || - content.includes('from \'./framework\'')); - compliance.checks.pageLayout = hasPageLayout; - if (hasPageLayout) compliance.score++; - else compliance.issues.push('Missing PageLayout wrapper'); - - // Check 2: Has pageName prop - const hasPageName = /]+pageName=["']([^"']+)["']/.test(content); - compliance.checks.pageName = hasPageName; - if (hasPageName) compliance.score++; - else if (hasPageLayout) compliance.issues.push('PageLayout missing pageName prop'); - - // Check 3: Uses framework hooks instead of useParams - const usesFrameworkHooks = content.includes('usePageParams') || - content.includes('useDAKParams') || - content.includes('useUserParams'); - const usesDirectParams = content.includes('useParams') && !content.includes('//') && - !content.includes('framework'); - - if (usesFrameworkHooks && !usesDirectParams) { - compliance.checks.frameworkHooks = true; - compliance.score++; - } else if (!usesDirectParams) { - compliance.checks.frameworkHooks = true; // No params used, OK - compliance.score++; - } else { - compliance.checks.frameworkHooks = false; - compliance.issues.push('Uses direct useParams() instead of framework hooks'); - } - - // Check 4: No manual ContextualHelpMascot import - const hasManualHelpMascot = content.includes('import') && - content.includes('ContextualHelpMascot') && - !content.includes('framework'); - compliance.checks.noManualHelp = !hasManualHelpMascot; - if (!hasManualHelpMascot) compliance.score++; - else compliance.issues.push('Has manual ContextualHelpMascot import (PageLayout provides it)'); - - // Check 5: No custom header implementation (basic check) - const hasCustomHeader = content.includes('header') && - (content.includes('className="header"') || - content.includes('className=\'header\'') || - content.includes(' 0) { - emoji = '⚠️'; - status = 'PARTIAL'; - } - - let result = `${emoji} ${name}: ${score}/${maxScore} (${percentage}%) - ${status}`; - - if (issues.length > 0) { - result += `\n Issues: ${issues.join(', ')}`; - } - - if (suggestions.length > 0 && score < maxScore) { - result += `\n Suggestions: ${suggestions.join(', ')}`; - } - - return result + '\n'; - } - - /** - * Print overall results summary - */ - printResults() { - const total = this.results.compliant.length + - this.results.partiallyCompliant.length + - this.results.nonCompliant.length; - - console.log('\n📊 COMPLIANCE SUMMARY'); - console.log('===================='); - console.log(`✅ Fully Compliant: ${this.results.compliant.length}/${total}`); - console.log(`⚠️ Partially Compliant: ${this.results.partiallyCompliant.length}/${total}`); - console.log(`❌ Non-Compliant: ${this.results.nonCompliant.length}/${total}`); - - if (this.results.errors.length > 0) { - console.log(`🚫 Errors: ${this.results.errors.length}`); - this.results.errors.forEach(error => console.log(` - ${error}`)); - } - - const overallCompliance = Math.round( - (this.results.compliant.length / total) * 100 - ); - console.log(`\n📈 Overall Compliance: ${overallCompliance}%`); - - if (this.results.nonCompliant.length > 0) { - console.log('\n🔧 PRIORITY FIXES NEEDED:'); - this.results.nonCompliant - .sort((a, b) => b.score - a.score) - .forEach(component => { - console.log(` ${component.name}: ${component.issues.join(', ')}`); - }); - } - - if (this.results.partiallyCompliant.length > 0) { - console.log('\n⚠️ IMPROVEMENTS NEEDED:'); - this.results.partiallyCompliant - .sort((a, b) => b.score - a.score) - .forEach(component => { - console.log(` ${component.name}: ${component.issues.join(', ')}`); - }); - } - - // Exit code guidance - if (this.results.nonCompliant.length > 0) { - console.log('\n❌ COMPLIANCE CHECK FAILED'); - console.log('Fix non-compliant pages before merging.'); - } else { - console.log('\n✅ COMPLIANCE CHECK PASSED'); - if (this.results.partiallyCompliant.length > 0) { - console.log('Consider addressing partial compliance issues.'); - } - } - } -} - -// Run the compliance check if called directly -if (require.main === module) { - const checker = new ComplianceChecker(); - checker.check().then(exitCode => { - process.exit(exitCode); - }).catch(error => { - console.error('Fatal error:', error); - process.exit(1); - }); -} - -module.exports = ComplianceChecker; \ No newline at end of file diff --git a/scripts/test-deployment.sh b/scripts/test-deployment.sh deleted file mode 100755 index a7d925bfc..000000000 --- a/scripts/test-deployment.sh +++ /dev/null @@ -1,210 +0,0 @@ -#!/bin/bash - -# Multi-Branch Deployment Test Script -# This script simulates the deployment workflow locally to verify it works correctly - -set -e - -echo "🧪 Multi-Branch GitHub Pages Deployment Test" -echo "==============================================" - -# Test directory setup -TEST_DIR="/tmp/sgex-deployment-test" -REPO_DIR="/home/runner/work/sgex/sgex" - -echo "📁 Setting up test environment..." -if [[ -d "$TEST_DIR" ]]; then - rm -rf "$TEST_DIR" -fi -mkdir -p "$TEST_DIR" - -# Copy repo to test directory -cp -r "$REPO_DIR"/* "$TEST_DIR/" -cp -r "$REPO_DIR"/.github "$TEST_DIR/" # Copy hidden .github directory -cd "$TEST_DIR" - -echo "✅ Test environment ready at: $TEST_DIR" - -# Test 1: Branch name validation -echo "" -echo "🧪 Test 1: Branch name validation" -echo "--------------------------------" - -test_branches=( - "main" - "feature/awesome-feature" - "bugfix/critical-fix" - "develop" - "release/v1.0.0" - "hotfix/security-patch" -) - -for branch in "${test_branches[@]}"; do - echo "Testing branch: $branch" - - # Simulate validation logic - if [[ "$branch" =~ [^a-zA-Z0-9._/-] ]]; then - echo "❌ $branch: Contains unsafe characters" - continue - fi - - safe_branch_name=$(echo "$branch" | tr '/' '-') - echo " Safe name: $safe_branch_name" - - repo_root="$(pwd)" - target_dir="$repo_root/$safe_branch_name" - - if [[ "$target_dir" != "$repo_root"* ]] || [[ ${#target_dir} -le ${#repo_root} ]]; then - echo "❌ $branch: Safety validation failed" - continue - fi - - echo "✅ $branch: Validation passed" -done - -# Test 2: Build processes -echo "" -echo "🧪 Test 2: Build processes" -echo "-------------------------" - -echo "Testing branch-specific build..." -export GITHUB_REF_NAME="test-branch" - -# Fetch build script from deploy branch (if available) -if git show-ref --verify --quiet refs/remotes/origin/deploy; then - echo "Fetching build script from deploy branch..." - git fetch origin deploy - git checkout origin/deploy -- scripts/build-multi-branch.js -fi - -if node scripts/build-multi-branch.js branch > /dev/null 2>&1; then - echo "✅ Branch-specific build: Success" - if [[ -f "build/index.html" ]]; then - echo "✅ Branch build produces index.html" - else - echo "❌ Branch build missing index.html" - fi -else - echo "❌ Branch-specific build: Failed" -fi - -echo "" -echo "Testing root landing page build..." - -# Fetch landing page from deploy branch (if available) -if git show-ref --verify --quiet refs/remotes/origin/deploy; then - git checkout origin/deploy -- public/branch-listing.html -fi - -if node scripts/build-multi-branch.js root > /dev/null 2>&1; then - echo "✅ Root landing page build: Success" - if [[ -f "build/index.html" ]]; then - echo "✅ Root build produces index.html" - - # Check if the built HTML contains landing page elements - if grep -q "branch-listing" build/index.html; then - echo "✅ Root build contains landing page structure" - else - echo "⚠️ Root build may not contain expected landing page structure" - fi - else - echo "❌ Root build missing index.html" - fi -else - echo "❌ Root landing page build: Failed" -fi - -# Test 3: GitHub API simulation -echo "" -echo "🧪 Test 3: GitHub API integration" -echo "--------------------------------" - -echo "Testing GitHub API call for branches..." -API_URL="https://api.github.com/repos/litlfred/sgex/branches" - -if curl -s --head "$API_URL" | head -n 1 | grep "200 OK" > /dev/null; then - echo "✅ GitHub API is accessible" - - # Get branch count - branch_count=$(curl -s "$API_URL" | grep -c '"name"' || echo "0") - echo "✅ Found $branch_count branches in repository" -else - echo "⚠️ GitHub API may not be accessible (rate limiting or network issue)" -fi - -# Test 4: Component validation -echo "" -echo "🧪 Test 4: Component validation" -echo "------------------------------" - -echo "Testing deployment components..." -if [[ -f "public/branch-listing.html" ]] && [[ -f "public/sgex-mascot.png" ]]; then - echo "✅ Landing page assets exist" - - # Check for key elements in landing page - if grep -q "GitHub API" public/branch-listing.html; then - echo "✅ Landing page includes GitHub API integration" - fi - - if grep -q "branch-card" public/branch-listing.html; then - echo "✅ Landing page includes card styling" - fi - - if grep -q "safeBranchName" public/branch-listing.html; then - echo "✅ Landing page handles safe branch names" - fi -else - echo "⚠️ Landing page assets may need to be fetched from deploy branch" -fi - -# Test 5: Workflow file validation -echo "" -echo "🧪 Test 5: Workflow validation" -echo "-----------------------------" - -echo "Testing workflow file..." -if [[ -f ".github/workflows/pages.yml" ]]; then - echo "✅ Workflow file exists" - - # Check for key safety features - if grep -q "Safety validation failed" .github/workflows/pages.yml; then - echo "✅ Workflow includes safety validation" - fi - - if grep -q "git rm -rf" .github/workflows/pages.yml; then - echo "✅ Workflow uses git-based cleanup" - fi - - if grep -q "branches-ignore:" .github/workflows/pages.yml && grep -q "gh-pages" .github/workflows/pages.yml; then - echo "✅ Workflow excludes gh-pages branch" - fi - - if grep -q "safe_branch_name" .github/workflows/pages.yml; then - echo "✅ Workflow handles safe branch names" - fi - - if grep -q "current-branch-backup" .github/workflows/pages.yml; then - echo "✅ Workflow includes branch directory backup protection" - fi -else - echo "❌ Workflow file missing" -fi - -echo "" -echo "🎉 Deployment Test Summary" -echo "=========================" -echo "✅ All tests completed successfully!" -echo "" -echo "The multi-branch GitHub Pages deployment system is ready for production use." -echo "" -echo "🚀 Next steps:" -echo " 1. Push this branch to trigger the actual deployment workflow" -echo " 2. Visit https://litlfred.github.io/sgex/ to see the landing page" -echo " 3. Check branch previews at https://litlfred.github.io/sgex/sgex/BRANCH-NAME/" -echo "" -echo "🐱 Meow! The deployment system is purrfectly ready!" - -# Cleanup -cd / -rm -rf "$TEST_DIR" -echo "🧹 Test environment cleaned up" \ No newline at end of file