Skip to content

Merge pull request #1 from StrangeDaysTech/feat/rebranding-and-cli-sc… #2

Merge pull request #1 from StrangeDaysTech/feat/rebranding-and-cli-sc…

Merge pull request #1 from StrangeDaysTech/feat/rebranding-and-cli-sc… #2

# =============================================================================
# DevTrail - Documentation Validation Workflow
# =============================================================================
#
# This workflow validates documentation on each Pull Request and push to main.
# https://strangedays.tech
#
# Executes:
# 1. File naming convention validation
# 2. Metadata (front-matter) validation
# 3. Sensitive information detection
# 4. Markdown linting
# 5. Internal link verification
#
# =============================================================================
name: Documentation Validation
on:
push:
branches: [main, develop]
paths:
- '.devtrail/**'
- '.github/workflows/docs-validation.yml'
pull_request:
branches: [main, develop]
paths:
- '.devtrail/**'
jobs:
validate-docs:
name: Validate Documentation
runs-on: ubuntu-latest
steps:
# =========================================================================
# Checkout
# =========================================================================
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0 # Required to compare with base branch
# =========================================================================
# Setup Node.js (for markdownlint)
# =========================================================================
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install markdownlint-cli
run: npm install -g markdownlint-cli
# =========================================================================
# Get changed files
# =========================================================================
- name: Get changed files
id: changed-files
uses: tj-actions/changed-files@v44
with:
files: |
.devtrail/**/*.md
# =========================================================================
# Validate file naming convention
# =========================================================================
- name: Validate file naming convention
if: steps.changed-files.outputs.any_changed == 'true'
run: |
echo "📋 Validating file naming convention..."
ERRORS=0
VALID_PATTERN="^(ADR|REQ|TES|OPS|INC|TDE|AILOG|AIDEC|ETH|DOC)-[0-9]{4}-[0-9]{2}-[0-9]{2}-[0-9]{3}-[a-z0-9-]+\.md$"
EXCLUDED="PRINCIPLES.md|DOCUMENTATION-POLICY.md|AGENT-RULES.md|TEMPLATE-.*\.md|README.md|QUICK-REFERENCE.md|INDEX.md|GIT-BRANCHING-STRATEGY.md"
for file in ${{ steps.changed-files.outputs.all_changed_files }}; do
filename=$(basename "$file")
# Skip excluded files
if echo "$filename" | grep -qE "$EXCLUDED"; then
echo " ⊘ Excluded: $filename"
continue
fi
# Validate naming convention
if ! echo "$filename" | grep -qE "$VALID_PATTERN"; then
echo " ✗ Invalid naming: $filename"
echo " Expected: [TYPE]-[YYYY-MM-DD]-[NNN]-[description].md"
ERRORS=$((ERRORS + 1))
else
echo " ✓ $filename"
fi
done
if [ $ERRORS -gt 0 ]; then
echo "::error::Found $ERRORS naming convention errors"
exit 1
fi
echo "✅ Naming convention valid"
# =========================================================================
# Validate front-matter
# =========================================================================
- name: Validate front-matter metadata
if: steps.changed-files.outputs.any_changed == 'true'
run: |
echo "📋 Validating metadata..."
ERRORS=0
EXCLUDED="PRINCIPLES.md|DOCUMENTATION-POLICY.md|AGENT-RULES.md|TEMPLATE-.*\.md|README.md|QUICK-REFERENCE.md|INDEX.md|GIT-BRANCHING-STRATEGY.md"
REQUIRED_FIELDS="id title status created"
for file in ${{ steps.changed-files.outputs.all_changed_files }}; do
filename=$(basename "$file")
# Skip excluded files
if echo "$filename" | grep -qE "$EXCLUDED"; then
continue
fi
# Verify front-matter exists
if ! head -1 "$file" | grep -q "^---"; then
echo " ✗ Missing front-matter: $filename"
ERRORS=$((ERRORS + 1))
continue
fi
# Verify required fields
for field in $REQUIRED_FIELDS; do
if ! grep -q "^$field:" "$file"; then
echo " ✗ Missing field '$field' in: $filename"
ERRORS=$((ERRORS + 1))
fi
done
done
if [ $ERRORS -gt 0 ]; then
echo "::error::Found $ERRORS metadata errors"
exit 1
fi
echo "✅ Metadata valid"
# =========================================================================
# Detect sensitive information
# =========================================================================
- name: Check for sensitive information
if: steps.changed-files.outputs.any_changed == 'true'
run: |
echo "🔒 Checking for sensitive information..."
WARNINGS=0
PATTERNS="password|api_key|apikey|secret|token|private_key|credentials"
for file in ${{ steps.changed-files.outputs.all_changed_files }}; do
MATCHES=$(grep -inE "$PATTERNS" "$file" 2>/dev/null | head -5 || true)
if [ -n "$MATCHES" ]; then
echo "::warning file=$file::Possible sensitive information detected"
echo "$MATCHES"
WARNINGS=$((WARNINGS + 1))
fi
done
if [ $WARNINGS -gt 0 ]; then
echo "⚠️ Detected $WARNINGS files with possible sensitive information"
else
echo "✅ No sensitive information detected"
fi
# =========================================================================
# Markdown Lint
# =========================================================================
- name: Run markdownlint
if: steps.changed-files.outputs.any_changed == 'true'
run: |
echo "📝 Running markdownlint..."
# Create temporary configuration
cat > .markdownlint.json << 'EOF'
{
"default": true,
"MD013": false,
"MD033": false,
"MD041": false,
"MD024": { "siblings_only": true }
}
EOF
markdownlint ${{ steps.changed-files.outputs.all_changed_files }} || {
echo "::warning::markdownlint found formatting issues"
}
echo "✅ Linting completed"
# =========================================================================
# Verify internal links
# =========================================================================
- name: Check internal links
if: steps.changed-files.outputs.any_changed == 'true'
run: |
echo "🔗 Verifying internal links..."
ERRORS=0
for file in ${{ steps.changed-files.outputs.all_changed_files }}; do
# Extract internal markdown links: [text](path)
LINKS=$(grep -oE '\[.+\]\([^http][^)]+\)' "$file" 2>/dev/null || true)
for link in $LINKS; do
# Extract only the path
path=$(echo "$link" | sed 's/.*](//' | sed 's/)//' | sed 's/#.*//')
if [ -n "$path" ]; then
# Resolve relative path
dir=$(dirname "$file")
fullpath="$dir/$path"
if [ ! -f "$fullpath" ] && [ ! -d "$fullpath" ]; then
echo " ✗ Broken link in $file: $path"
ERRORS=$((ERRORS + 1))
fi
fi
done
done
if [ $ERRORS -gt 0 ]; then
echo "::warning::Found $ERRORS broken links"
else
echo "✅ All internal links are valid"
fi
# =========================================================================
# Summary
# =========================================================================
- name: Summary
if: always()
run: |
echo "═══════════════════════════════════════════════════════════════"
echo "📊 Documentation validation completed"
echo "═══════════════════════════════════════════════════════════════"
echo ""
echo "Files validated: ${{ steps.changed-files.outputs.all_changed_files_count }}"
# ===========================================================================
# Job: Generate documentation index (only on main)
# ===========================================================================
generate-index:
name: Generate Documentation Index
runs-on: ubuntu-latest
needs: validate-docs
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Generate documentation index
run: |
echo "📚 Generating documentation index..."
cat > .devtrail/INDEX.md << 'EOF'
# Documentation Index
*Automatically generated on $(date -u +"%Y-%m-%d %H:%M UTC")*
## Governance
EOF
# List documents by folder
for folder in .devtrail/*/; do
folder_name=$(basename "$folder")
echo "" >> .devtrail/INDEX.md
echo "## ${folder_name}" >> .devtrail/INDEX.md
echo "" >> .devtrail/INDEX.md
find "$folder" -name "*.md" -type f | sort | while read file; do
filename=$(basename "$file")
# Extract title from front-matter or use filename
title=$(grep "^title:" "$file" 2>/dev/null | sed 's/title: *//' | head -1 || echo "$filename")
echo "- [$title]($file)" >> .devtrail/INDEX.md
done
done
echo "✅ Index generated: .devtrail/INDEX.md"
- name: Commit index if changed
run: |
git config --local user.email "github-actions[bot]@users.noreply.github.com"
git config --local user.name "github-actions[bot]"
if git diff --quiet .devtrail/INDEX.md; then
echo "No changes to index"
else
git add .devtrail/INDEX.md
git commit -m "docs: update documentation index [skip ci]"
git push
fi