CI/CD Pipeline #53
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, develop ] | |
| pull_request: | |
| branches: [ main ] | |
| schedule: | |
| # Run security scan daily at 6 AM UTC | |
| - cron: '0 6 * * *' | |
| jobs: | |
| spell-check: | |
| name: Spell 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' | |
| - name: Install cspell | |
| run: npm install -g cspell | |
| - name: Create cspell config | |
| run: | | |
| cat > .cspell.json << 'EOF' | |
| { | |
| "version": "0.2", | |
| "language": "en", | |
| "words": [ | |
| "copilot", | |
| "github", | |
| "cli", | |
| "grype", | |
| "sast", | |
| "sca", | |
| "yaml", | |
| "yml", | |
| "json", | |
| "md", | |
| "readme", | |
| "auth", | |
| "config", | |
| "GPT", | |
| "API", | |
| "JWT", | |
| "OAuth", | |
| "nvm", | |
| "npm", | |
| "pipenv", | |
| "pytest", | |
| "repo", | |
| "repos", | |
| "workflow", | |
| "workflows", | |
| "deps", | |
| "dev", | |
| "prod", | |
| "dockerfile", | |
| "kubernetes", | |
| "kubectl", | |
| "namespace", | |
| "endpoint", | |
| "endpoints", | |
| "fastapi", | |
| "async", | |
| "await", | |
| "datetime", | |
| "timezone", | |
| "username", | |
| "hostname", | |
| "subdomain", | |
| "subcommand", | |
| "subcommands", | |
| "filepath", | |
| "filepaths", | |
| "filename", | |
| "filenames", | |
| "changelog", | |
| "refactor", | |
| "refactoring", | |
| "linter", | |
| "linting" | |
| ], | |
| "ignorePaths": [ | |
| "node_modules/**", | |
| ".git/**", | |
| "*.log", | |
| "package-lock.json", | |
| "yarn.lock" | |
| ], | |
| "ignoreRegExpList": [ | |
| "\\b[A-Fa-f0-9]{7,40}\\b", | |
| "https?://[^\\s]+", | |
| "\\$\\{[^}]+\\}" | |
| ] | |
| } | |
| EOF | |
| - name: Run spell check | |
| run: | | |
| echo "Running spell check on documentation files..." | |
| cspell "**/*.md" "**/*.txt" --no-progress --show-context | |
| continue-on-error: false | |
| security-scan: | |
| name: Security Scan with Grype | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| security-events: write | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Run Grype vulnerability scanner | |
| uses: anchore/scan-action@v3 | |
| id: grype-scan | |
| with: | |
| path: "." | |
| fail-build-on-finding: true | |
| severity-cutoff: high | |
| output-format: sarif | |
| - name: Upload Grype scan results to GitHub Security tab | |
| uses: github/codeql-action/upload-sarif@v3 | |
| if: always() | |
| with: | |
| sarif_file: ${{ steps.grype-scan.outputs.sarif }} | |
| - name: Generate vulnerability report | |
| if: always() | |
| run: | | |
| echo "## Security Scan Results" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| if [ -f "vulnerabilities.json" ]; then | |
| echo "Vulnerabilities found - see Security tab for details" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "✅ No high or critical vulnerabilities found" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| lint-and-format: | |
| name: Lint and Format 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' | |
| - name: Install markdownlint | |
| run: npm install -g markdownlint-cli | |
| - name: Create markdownlint config | |
| run: | | |
| cat > .markdownlint.json << 'EOF' | |
| { | |
| "MD013": { "line_length": 120 }, | |
| "MD033": false, | |
| "MD041": false | |
| } | |
| EOF | |
| - name: Run markdown lint | |
| run: markdownlint "**/*.md" --ignore node_modules | |
| - name: Check for trailing whitespace | |
| run: | | |
| if grep -r -n --include="*.md" --include="*.yml" --include="*.yaml" --include="*.json" '[[:space:]]$' .; then | |
| echo "❌ Found trailing whitespace in files above" | |
| exit 1 | |
| else | |
| echo "✅ No trailing whitespace found" | |
| fi | |
| - name: Validate YAML files | |
| run: | | |
| find . -name "*.yml" -o -name "*.yaml" | while read file; do | |
| echo "Validating $file" | |
| python -c "import yaml; yaml.safe_load(open('$file'))" | |
| done | |
| docs-check: | |
| name: Documentation Check | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Check for required documentation files | |
| run: | | |
| required_files=("README.md" "LICENSE" "docs/usage.md") | |
| missing_files=() | |
| for file in "${required_files[@]}"; do | |
| if [ ! -f "$file" ]; then | |
| missing_files+=("$file") | |
| fi | |
| done | |
| if [ ${#missing_files[@]} -gt 0 ]; then | |
| echo "❌ Missing required documentation files:" | |
| printf '%s\n' "${missing_files[@]}" | |
| exit 1 | |
| else | |
| echo "✅ All required documentation files present" | |
| fi | |
| - name: Check for broken links in markdown | |
| run: | | |
| # Simple check for internal links | |
| find . -name "*.md" -exec grep -H '\[.*\](\..*\.md)' {} \; | while IFS: read -r file link; do | |
| # Extract the link path | |
| link_path=$(echo "$link" | sed -n 's/.*(\(\..*\.md\)).*/\1/p') | |
| if [ -n "$link_path" ]; then | |
| # Convert relative path to absolute | |
| dir=$(dirname "$file") | |
| full_path="$dir/$link_path" | |
| if [ ! -f "$full_path" ]; then | |
| echo "❌ Broken link in $file: $link_path" | |
| exit 1 | |
| fi | |
| fi | |
| done | |
| echo "✅ No broken internal links found" | |
| pr-validation: | |
| name: PR Validation | |
| runs-on: ubuntu-latest | |
| if: github.event_name == 'pull_request' | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Check PR title format | |
| run: | | |
| # Check for conventional commit format in PR title | |
| PR_TITLE="${{ github.event.pull_request.title }}" | |
| if [[ ! "$PR_TITLE" =~ ^(feat|fix|docs|style|refactor|test|chore|ci|build|perf)(\(.+\))?: .+ ]]; then | |
| echo "❌ PR title must follow conventional commit format:" | |
| echo " Format: type(scope): description" | |
| echo " Example: feat(auth): add user authentication" | |
| echo " Current title: $PR_TITLE" | |
| exit 1 | |
| else | |
| echo "✅ PR title follows conventional commit format" | |
| fi | |
| - name: Check for CHANGELOG entry | |
| run: | | |
| if [ ! -f "CHANGELOG.md" ]; then | |
| echo "⚠️ No CHANGELOG.md found - consider adding one for better release tracking" | |
| exit 0 | |
| fi | |
| # Check if CHANGELOG was modified in this PR | |
| if ! git diff --name-only origin/main...HEAD | grep -q "CHANGELOG.md"; then | |
| echo "⚠️ Consider updating CHANGELOG.md for this change" | |
| echo " This is not required but helps with release management" | |
| else | |
| echo "✅ CHANGELOG.md updated" | |
| fi | |
| build-validation: | |
| name: Build Validation | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Validate shell scripts | |
| run: | | |
| # Check if any shell scripts exist and validate them | |
| find . -name "*.sh" -type f | while read -r script; do | |
| echo "Validating shell script: $script" | |
| bash -n "$script" || exit 1 | |
| done | |
| echo "✅ All shell scripts are valid" | |
| - name: Check file permissions | |
| run: | | |
| # Ensure shell scripts are executable | |
| find . -name "*.sh" -type f | while read -r script; do | |
| if [ ! -x "$script" ]; then | |
| echo "⚠️ Making $script executable" | |
| chmod +x "$script" | |
| fi | |
| done | |
| summary: | |
| name: Build Summary | |
| runs-on: ubuntu-latest | |
| needs: [spell-check, security-scan, lint-and-format, docs-check, build-validation] | |
| if: always() | |
| steps: | |
| - name: Generate build summary | |
| run: | | |
| echo "## 🚀 Build Summary" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| if [ "${{ needs.spell-check.result }}" == "success" ]; then | |
| echo "✅ Spell Check: Passed" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "❌ Spell Check: Failed" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| if [ "${{ needs.security-scan.result }}" == "success" ]; then | |
| echo "✅ Security Scan: Passed" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "❌ Security Scan: Failed" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| if [ "${{ needs.lint-and-format.result }}" == "success" ]; then | |
| echo "✅ Lint & Format: Passed" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "❌ Lint & Format: Failed" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| if [ "${{ needs.docs-check.result }}" == "success" ]; then | |
| echo "✅ Documentation: Passed" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "❌ Documentation: Failed" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| if [ "${{ needs.build-validation.result }}" == "success" ]; then | |
| echo "✅ Build Validation: Passed" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "❌ Build Validation: Failed" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "**Workflow completed at:** $(date -u)" >> $GITHUB_STEP_SUMMARY |