Skip to content

CI/CD Pipeline

CI/CD Pipeline #53

Workflow file for this run

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