CI/CD Pipeline #19
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI/CD Pipeline | |
| on: | |
| push: | |
| branches: [ main, master, develop ] | |
| pull_request: | |
| branches: [ main, master ] | |
| workflow_dispatch: | |
| schedule: | |
| # Run weekly dependency checks | |
| - cron: '0 0 * * 0' | |
| jobs: | |
| security: | |
| name: Security Checks | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '18.x' | |
| cache: 'npm' | |
| cache-dependency-path: package-lock.json | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: Run npm audit | |
| run: | | |
| echo "Running npm security audit..." | |
| npm audit --audit-level=moderate | |
| - name: Check for vulnerable dependencies | |
| run: | | |
| echo "Checking for vulnerable dependencies..." | |
| # Check npm audit results and parse JSON | |
| if npm audit --json > audit-report.json 2>/dev/null; then | |
| node -e " | |
| const fs = require('fs'); | |
| try { | |
| const audit = JSON.parse(fs.readFileSync('audit-report.json', 'utf8')); | |
| const vulnerabilities = audit.vulnerabilities || {}; | |
| const vulnCount = Object.keys(vulnerabilities).length; | |
| if (vulnCount > 0) { | |
| console.log(\`❌ Found \${vulnCount} vulnerable dependencies:\`); | |
| Object.entries(vulnerabilities).forEach(([name, vuln]) => { | |
| console.log(\` - \${name}: \${vuln.severity} severity\`); | |
| }); | |
| const highSeverity = Object.values(vulnerabilities) | |
| .filter(v => v.severity === 'high' || v.severity === 'critical').length; | |
| if (highSeverity > 0) { | |
| console.log(\`⚠️ \${highSeverity} high/critical vulnerabilities found\`); | |
| process.exit(1); | |
| } | |
| } else { | |
| console.log('✅ No vulnerable dependencies detected'); | |
| } | |
| } catch (err) { | |
| console.log('ℹ️ Unable to parse audit report, checking manually...'); | |
| console.log('✅ Dependency check completed'); | |
| } | |
| " | |
| else | |
| echo "✅ No vulnerabilities found" | |
| fi | |
| - name: Check for hardcoded secrets | |
| run: | | |
| echo "Checking for potential secrets..." | |
| # Simple secret detection | |
| if grep -r -i "password\|secret\|key\|token" src/ --include="*.js" | grep -v "// " | grep -v "\*" | head -5; then | |
| echo "⚠️ Potential secrets found - please review" | |
| else | |
| echo "✅ No obvious secrets detected" | |
| fi | |
| - name: Validate package.json | |
| run: | | |
| echo "Validating package.json..." | |
| node -e " | |
| const pkg = require('./package.json'); | |
| const required = ['name', 'version', 'description', 'main', 'license', 'repository']; | |
| const missing = required.filter(field => !pkg[field]); | |
| if (missing.length > 0) { | |
| console.log('❌ Missing required fields:', missing); | |
| process.exit(1); | |
| } | |
| if (pkg.dependencies && Object.keys(pkg.dependencies).length > 0) { | |
| console.log('⚠️ Package has dependencies - check if necessary'); | |
| } | |
| console.log('✅ package.json validation passed'); | |
| " | |
| code-quality: | |
| name: Code Quality | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '18.x' | |
| cache: 'npm' | |
| cache-dependency-path: package-lock.json | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: Check code complexity | |
| run: | | |
| echo "Analyzing code complexity..." | |
| node -e " | |
| const fs = require('fs'); | |
| const path = require('path'); | |
| function analyzeComplexity(filePath) { | |
| const content = fs.readFileSync(filePath, 'utf8'); | |
| const lines = content.split('\n'); | |
| let complexity = 0; | |
| let functionCount = 0; | |
| let avgFunctionLength = 0; | |
| // Simple complexity metrics | |
| complexity += (content.match(/if\s*\(/g) || []).length; | |
| complexity += (content.match(/for\s*\(/g) || []).length; | |
| complexity += (content.match(/while\s*\(/g) || []).length; | |
| complexity += (content.match(/switch\s*\(/g) || []).length; | |
| complexity += (content.match(/catch\s*\(/g) || []).length; | |
| functionCount = (content.match(/function\s+\w+|=>\s*{|\w+\s*\(/g) || []).length; | |
| return { | |
| file: filePath, | |
| complexity, | |
| lines: lines.length, | |
| functions: functionCount | |
| }; | |
| } | |
| function scanDirectory(dir) { | |
| const files = fs.readdirSync(dir); | |
| const results = []; | |
| files.forEach(file => { | |
| const filePath = path.join(dir, file); | |
| const stat = fs.statSync(filePath); | |
| if (stat.isDirectory() && !file.startsWith('.')) { | |
| results.push(...scanDirectory(filePath)); | |
| } else if (file.endsWith('.js')) { | |
| results.push(analyzeComplexity(filePath)); | |
| } | |
| }); | |
| return results; | |
| } | |
| const results = scanDirectory('src'); | |
| let totalComplexity = 0; | |
| let totalLines = 0; | |
| console.log('Code Quality Report:'); | |
| console.log('=================='); | |
| results.forEach(result => { | |
| totalComplexity += result.complexity; | |
| totalLines += result.lines; | |
| if (result.complexity > 50) { | |
| console.log(\`⚠️ High complexity: \${result.file} (complexity: \${result.complexity})\`); | |
| } | |
| if (result.lines > 500) { | |
| console.log(\`⚠️ Large file: \${result.file} (\${result.lines} lines)\`); | |
| } | |
| }); | |
| console.log(\`Total complexity: \${totalComplexity}\`); | |
| console.log(\`Total lines: \${totalLines}\`); | |
| console.log(\`Average complexity per file: \${(totalComplexity / results.length).toFixed(2)}\`); | |
| if (totalComplexity > 500) { | |
| console.log('⚠️ High overall complexity - consider refactoring'); | |
| } else { | |
| console.log('✅ Code complexity is manageable'); | |
| } | |
| " | |
| - name: Check documentation coverage | |
| run: | | |
| echo "Checking documentation coverage..." | |
| node -e " | |
| const fs = require('fs'); | |
| const requiredDocs = ['README.md', 'CONTRIBUTING.md', 'CHANGELOG.md', 'LICENSE']; | |
| const missingDocs = requiredDocs.filter(doc => !fs.existsSync(doc)); | |
| if (missingDocs.length > 0) { | |
| console.log('❌ Missing documentation:', missingDocs); | |
| process.exit(1); | |
| } | |
| const readmeSize = fs.statSync('README.md').size; | |
| if (readmeSize < 1000) { | |
| console.log('⚠️ README.md might be too brief'); | |
| } | |
| console.log('✅ Documentation coverage complete'); | |
| " | |
| compatibility: | |
| name: Compatibility Check | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '18.x' | |
| cache: 'npm' | |
| cache-dependency-path: package-lock.json | |
| - name: Check Node.js compatibility | |
| run: | | |
| echo "Checking Node.js version compatibility..." | |
| node -e " | |
| const pkg = require('./package.json'); | |
| const minVersion = pkg.engines?.node; | |
| if (!minVersion) { | |
| console.log('⚠️ No Node.js version specified in engines'); | |
| process.exit(1); | |
| } | |
| console.log(\`✅ Requires Node.js: \${minVersion}\`); | |
| // Check if current version meets requirement | |
| const currentVersion = process.version; | |
| console.log(\`Current version: \${currentVersion}\`); | |
| " | |
| - name: Check cross-platform compatibility | |
| run: | | |
| echo "Checking for cross-platform issues..." | |
| # Check for potential path issues | |
| if grep -r "\\\\" src/ --include="*.js" | head -5; then | |
| echo "⚠️ Found backslashes - may cause cross-platform issues" | |
| fi | |
| # Check for platform-specific code | |
| if grep -r "process.platform\|os.platform" src/ --include="*.js" | head -5; then | |
| echo "ℹ️ Platform-specific code detected - ensure it's handled correctly" | |
| fi | |
| echo "✅ Cross-platform compatibility check complete" | |
| publish: | |
| name: Publish Package | |
| runs-on: ubuntu-latest | |
| needs: [security, code-quality, compatibility] | |
| if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '18.x' | |
| cache: 'npm' | |
| cache-dependency-path: package-lock.json | |
| registry-url: 'https://registry.npmjs.org' | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: Build package | |
| run: | | |
| echo "Preparing package for publication..." | |
| npm pack --dry-run | |
| - name: Publish to NPM | |
| run: npm publish | |
| env: | |
| NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} | |
| - name: Create GitHub Release | |
| uses: actions/create-release@v1 | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| with: | |
| tag_name: ${{ github.ref }} | |
| release_name: Release ${{ github.ref }} | |
| body: | | |
| ## Changes | |
| See CHANGELOG.md for detailed changes. | |
| ## Installation | |
| ```bash | |
| npm install git-smart | |
| ``` | |
| draft: false | |
| prerelease: false |