Pull Request Validation Workflow
Quality gates | Conventional commits | Conflict detection | Build verification
The pull request validation workflow provides comprehensive quality assurance for all changes entering protected branches. This intelligent system enforces code standards, verifies build integrity, and ensures consistency across the OSDU SPI fork management lifecycle.
Validation Philosophy
Comprehensive Quality Gates
Every pull request undergoes systematic validation to ensure code quality, consistency, and readiness before integration into protected branches.
Intelligent Rule Application
Validation rules adapt based on context - different standards apply to upstream sync PRs, fork management branches, and standard development contributions.
:material-feedback: Clear Developer Feedback
Detailed status reporting provides actionable feedback, helping developers understand and resolve issues quickly.
Validation Architecture
:material-workflow: Multi-Phase Validation Pipeline
graph TD
A[PR/Push Event] --> B[Repository State Analysis]
B --> C{Project Type Detection}
C -->|Java| D[Build & Coverage Validation]
C -->|Other| E[Skip Build Checks]
D --> F[Quality Assurance Checks]
E --> F
F --> G[Commit Message Validation]
G --> H[Merge Conflict Detection]
H --> I[Branch Synchronization Check]
I --> J[Generate Comprehensive Report]
J --> K[Update PR Status]
style A fill:#e1f5fe,stroke:#01579b,stroke-width:2px
style D fill:#e8f5e9,stroke:#1b5e20,stroke-width:2px
style F fill:#fff3e0,stroke:#e65100,stroke-width:2px
style K fill:#fce4ec,stroke:#c2185b,stroke-width:2px
:material-parallel: Parallel Execution Strategy
graph TD
A[check-repo-state] --> B[java-build]
A --> C[validate]
B --> D[report-status]
C --> D
subgraph "Parallel Processing"
B1[Build Verification]
C1[Commit Validation]
C2[Conflict Detection]
C3[Branch Status]
end
B --> B1
C --> C1
C --> C2
C --> C3
Workflow Configuration
:material-trigger: Event Triggers and Scope
on:
pull_request:
branches: [main, fork_integration, fork_upstream] # All protected branches
push:
branches: [main, fork_integration, fork_upstream] # Direct push validation
workflow_dispatch:
inputs:
post_init:
description: 'Post-initialization validation trigger'
type: boolean
default: false
permissions:
contents: read # Repository access
pull-requests: write # PR status updates
statuses: write # Commit status checks
actions: read # Workflow context
Concurrency Management
concurrency:
group: validate-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true # Optimize resource usage
Phase 1: Repository State Analysis
Intelligent Project Detection
Initialization Status Verification
- name: Verify repository initialization
run: |
if [ -f ".github/workflow.env" ]; then
source .github/workflow.env
echo "initialized=true" >> $GITHUB_OUTPUT
echo "Repository fully initialized with upstream: ${UPSTREAM_REPO_URL:-'not configured'}"
else
echo "initialized=false" >> $GITHUB_OUTPUT
echo "::warning::Repository not fully initialized - some validations may be skipped"
fi
Project Type and Build Requirements
- name: Detect project type and build requirements
run: |
# Java project detection (Maven/Gradle)
if [ -f "pom.xml" ]; then
echo "project_type=java-maven" >> $GITHUB_OUTPUT
echo "build_required=true" >> $GITHUB_OUTPUT
echo "📋 Detected Java Maven project"
elif [ -f "build.gradle" ] || [ -f "build.gradle.kts" ]; then
echo "project_type=java-gradle" >> $GITHUB_OUTPUT
echo "build_required=true" >> $GITHUB_OUTPUT
echo "📋 Detected Java Gradle project"
else
echo "project_type=generic" >> $GITHUB_OUTPUT
echo "build_required=false" >> $GITHUB_OUTPUT
echo "📋 Generic project - build validation skipped"
fi
Context-Aware Metadata
outputs:
initialized: ${{ steps.check.outputs.initialized }}
project_type: ${{ steps.detect.outputs.project_type }}
build_required: ${{ steps.detect.outputs.build_required }}
pr_context: ${{ steps.context.outputs.pr_context }}
Phase 2: Build Verification
Java Build Validation
Conditional Build Execution
# Only run for Java projects in initialized repositories
if: |
needs.check-repo-state.outputs.project_type != 'generic' &&
needs.check-repo-state.outputs.initialized == 'true'
Comprehensive Build Process
- name: Setup Java Environment
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
cache: 'maven'
- name: Configure Maven Settings
run: |
# Use community Maven settings if available
if [ -f ".mvn/community-maven.settings.xml" ]; then
mkdir -p ~/.m2
cp .mvn/community-maven.settings.xml ~/.m2/settings.xml
echo "📋 Using community Maven settings"
fi
- name: Execute Build and Tests
run: |
echo "🔨 Building project with comprehensive validation..."
mvn clean verify -B -DskipTests=false -DfailIfNoTests=false
# Capture build success
echo "build_success=true" >> $GITHUB_OUTPUT
Test Coverage Analysis for PRs
- name: Generate Test Coverage Report
if: github.event_name == 'pull_request'
run: |
# Generate JaCoCo coverage report
mvn jacoco:report -q
# Extract coverage percentage
if [ -f "target/site/jacoco/index.html" ]; then
COVERAGE=$(grep -o '<counter type="INSTRUCTION".*' target/site/jacoco/index.html | \
grep -o 'missed="[0-9]*" covered="[0-9]*"' | \
awk -F'"' '{missed+=$2; covered+=$4} END {
if(missed+covered > 0) print int(covered/(missed+covered)*100)
else print 0
}')
echo "test_coverage=$COVERAGE" >> $GITHUB_OUTPUT
echo "📊 Test coverage: $COVERAGE%"
else
echo "test_coverage=0" >> $GITHUB_OUTPUT
echo "⚠️ No coverage report generated"
fi
Build Artifact Validation
- name: Validate Build Artifacts
run: |
# Verify JAR files were created
if [ -d "target" ]; then
JARS=$(find target -name "*.jar" -type f | wc -l)
echo "artifacts_created=$JARS" >> $GITHUB_OUTPUT
echo "📦 Created $JARS JAR artifacts"
fi
# Check for common build issues
if grep -r "BUILD FAILURE" target/ 2>/dev/null; then
echo "::error::Build failures detected in artifacts"
exit 1
fi
Phase 3: Quality Assurance Checks
Intelligent Commit Message Validation
Context-Sensitive Application
# Apply conventional commit validation selectively
if: |
github.event_name == 'pull_request' &&
github.event.pull_request.base.ref == 'main' &&
!contains(github.actor, 'dependabot') &&
!contains(github.event.pull_request.head.ref, 'fork_') &&
!startsWith(github.event.pull_request.title, 'chore(sync)')
Conventional Commits Enforcement
- name: Validate Conventional Commit Format
uses: wagoid/commitlint-github-action@v5
with:
configFile: .commitlintrc.json
firstParent: false
failOnWarnings: false
Supported Commit Types Configuration
{
"extends": ["@commitlint/config-conventional"],
"rules": {
"type-enum": [2, "always", [
"feat", "fix", "docs", "style",
"refactor", "test", "chore", "perf",
"ci", "upstream"
]],
"subject-case": [2, "never", ["pascal-case", "upper-case"]],
"subject-full-stop": [2, "never", "."],
"header-max-length": [2, "always", 100]
}
}
Advanced Conflict Detection
Comprehensive Conflict Scanning
- name: Scan for merge conflicts across file types
run: |
echo "🔍 Scanning for merge conflict markers..."
# Define file types to scan
FILE_PATTERNS=(
"*.java" "*.xml" "*.yml" "*.yaml" "*.json"
"*.md" "*.properties" "*.gradle" "*.kt"
)
# Build find command with all patterns
FIND_CMD="find . -type f \\( "
for i in "${!FILE_PATTERNS[@]}"; do
if [ $i -gt 0 ]; then
FIND_CMD="$FIND_CMD -o "
fi
FIND_CMD="$FIND_CMD -name '${FILE_PATTERNS[$i]}'"
done
FIND_CMD="$FIND_CMD \\)"
# Execute scan
CONFLICTS=$(eval $FIND_CMD | xargs grep -l "^<<<<<<< \\|^======= \\|^>>>>>>> " 2>/dev/null || true)
if [ -n "$CONFLICTS" ]; then
echo "conflicts_found=true" >> $GITHUB_OUTPUT
echo "::error::Merge conflicts detected in:"
echo "$CONFLICTS" | while read file; do
echo "::error:: 📄 $file"
# Show conflict context
grep -n "^<<<<<<< \\|^======= \\|^>>>>>>> " "$file" | head -3
done
else
echo "conflicts_found=false" >> $GITHUB_OUTPUT
echo "✅ No merge conflicts detected"
fi
Branch Synchronization Analysis
Up-to-Date Status Verification
- name: Analyze branch synchronization status
if: github.event_name == 'pull_request'
run: |
BASE_REF="${{ github.event.pull_request.base.ref }}"
# Fetch latest base branch
git fetch origin $BASE_REF --depth=50
# Calculate divergence
BEHIND=$(git rev-list --count HEAD..origin/$BASE_REF)
AHEAD=$(git rev-list --count origin/$BASE_REF..HEAD)
echo "behind_by=$BEHIND" >> $GITHUB_OUTPUT
echo "ahead_by=$AHEAD" >> $GITHUB_OUTPUT
if [ "$BEHIND" -gt 0 ]; then
echo "branch_outdated=true" >> $GITHUB_OUTPUT
echo "🔄 Branch is $BEHIND commits behind $BASE_REF"
echo "::warning::Consider updating your branch with latest changes"
else
echo "branch_outdated=false" >> $GITHUB_OUTPUT
echo "✅ Branch is up to date with $BASE_REF"
fi
echo "📊 Branch status: $AHEAD ahead, $BEHIND behind"
File Change Analysis
Impact Assessment
- name: Analyze file changes and impact
if: github.event_name == 'pull_request'
run: |
# Get changed files
CHANGED_FILES=$(gh pr view ${{ github.event.pull_request.number }} --json files --jq '.files[].path')
TOTAL_FILES=$(echo "$CHANGED_FILES" | wc -l)
# Categorize changes
CONFIG_CHANGES=$(echo "$CHANGED_FILES" | grep -c "\.yml\|\.yaml\|\.json\|\.properties" || echo 0)
CODE_CHANGES=$(echo "$CHANGED_FILES" | grep -c "\.java\|\.kt\|\.scala" || echo 0)
DOC_CHANGES=$(echo "$CHANGED_FILES" | grep -c "\.md\|\.txt\|\.adoc" || echo 0)
echo "total_files=$TOTAL_FILES" >> $GITHUB_OUTPUT
echo "config_changes=$CONFIG_CHANGES" >> $GITHUB_OUTPUT
echo "code_changes=$CODE_CHANGES" >> $GITHUB_OUTPUT
echo "doc_changes=$DOC_CHANGES" >> $GITHUB_OUTPUT
echo "📋 Change summary: $TOTAL_FILES files ($CODE_CHANGES code, $CONFIG_CHANGES config, $DOC_CHANGES docs)"
Phase 4: Comprehensive Status Reporting
Intelligent Status Report Generation
Dynamic Report Creation
- name: Generate comprehensive validation report
uses: actions/github-script@v7
with:
script: |
// Collect all validation results
const status = {
initialized: '${{ needs.check-repo-state.outputs.initialized }}',
projectType: '${{ needs.check-repo-state.outputs.project_type }}',
buildStatus: '${{ needs.java-build.result }}',
buildSuccess: '${{ needs.java-build.outputs.build_success }}',
testCoverage: '${{ needs.java-build.outputs.test_coverage }}',
validationStatus: '${{ needs.validate.result }}',
conflicts: '${{ needs.validate.outputs.conflicts_found }}',
branchOutdated: '${{ needs.validate.outputs.branch_outdated }}',
behindBy: '${{ needs.validate.outputs.behind_by }}',
totalFiles: '${{ needs.validate.outputs.total_files }}',
codeChanges: '${{ needs.validate.outputs.code_changes }}'
};
let report = '## 📋 Validation Report\n\n';
// Repository Status Section
report += '### 🏗️ Repository Status\n';
if (status.initialized === 'true') {
report += '✅ **Status**: Fully initialized and ready\n';
report += `🔧 **Project Type**: ${status.projectType}\n`;
} else {
report += '⚠️ **Status**: Not fully initialized - limited validation\n';
}
// Build Status Section
report += '\n### 🔨 Build Validation\n';
if (status.buildStatus === 'success') {
report += '✅ **Build**: Successful\n';
if (status.testCoverage && status.testCoverage !== '0') {
const coverage = parseInt(status.testCoverage);
const coverageIcon = coverage >= 80 ? '🟢' : coverage >= 60 ? '🟡' : '🔴';
report += `${coverageIcon} **Test Coverage**: ${coverage}%\n`;
}
} else if (status.buildStatus === 'skipped') {
report += '⏭️ **Build**: Not applicable for this project type\n';
} else {
report += '❌ **Build**: Failed - check build logs for details\n';
}
// Quality Checks Section
report += '\n### 🛡️ Quality Checks\n';
// Conflicts
if (status.conflicts === 'true') {
report += '❌ **Merge Conflicts**: Detected - manual resolution required\n';
} else {
report += '✅ **Merge Conflicts**: None detected\n';
}
// Branch Status
if (status.branchOutdated === 'true') {
report += `⚠️ **Branch Status**: ${status.behindBy} commits behind - update recommended\n`;
} else {
report += '✅ **Branch Status**: Up to date\n';
}
// Change Impact
if (status.totalFiles) {
report += `📊 **Change Impact**: ${status.totalFiles} files modified`;
if (status.codeChanges && status.codeChanges !== '0') {
report += ` (${status.codeChanges} code files)`;
}
report += '\n';
}
// Overall Assessment
report += '\n### 🎯 Overall Assessment\n';
const hasIssues = status.conflicts === 'true' ||
status.buildStatus === 'failure' ||
status.branchOutdated === 'true';
if (!hasIssues) {
report += '🟢 **Ready for Review**: All validation checks passed\n';
report += '✨ This PR meets all quality standards and is ready for team review.\n';
} else {
report += '🔴 **Action Required**: Please address the issues above\n';
report += '🔧 Review the validation failures and update your PR accordingly.\n';
}
// Add helpful links
report += '\n---\n';
report += '*💡 Need help? Check the [validation documentation](../docs/workflows/validation.md) or ask in the team chat.*\n';
return report;
Dynamic PR Status Updates
Smart Comment Management
- name: Update PR with validation status
if: github.event_name == 'pull_request'
uses: actions/github-script@v7
with:
script: |
const report = ${{ steps.generate-report.outputs.result }};
// Find existing validation comment
const comments = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number
});
const validationComment = comments.data.find(comment =>
comment.user.type === 'Bot' &&
comment.body.includes('📋 Validation Report')
);
if (validationComment) {
// Update existing comment
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: validationComment.id,
body: report
});
console.log('Updated existing validation comment');
} else {
// Create new comment
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: report
});
console.log('Created new validation comment');
}
Special Context Handling
Dependabot Integration
Automated Dependency Update Support
- name: Handle Dependabot PRs with relaxed validation
if: contains(github.actor, 'dependabot')
run: |
echo "🤖 Dependabot PR detected - applying relaxed validation rules"
echo "dependabot=true" >> $GITHUB_OUTPUT
# Skip certain validations for dependency updates
echo "skip_commit_validation=true" >> $GITHUB_OUTPUT
echo "auto_merge_eligible=true" >> $GITHUB_OUTPUT
Dependabot Exemptions: - Commit message validation bypassed - Auto-merge eligible for patch/minor updates
- Simplified status reporting focused on build success
Fork Branch Handling
Upstream Sync PR Validation
- name: Apply upstream sync validation rules
if: |
contains(github.event.pull_request.head.ref, 'fork_') ||
startsWith(github.event.pull_request.title, 'chore(sync)')
run: |
echo "🔄 Fork management branch detected"
echo "fork_branch=true" >> $GITHUB_OUTPUT
# Apply specialized validation for upstream sync
echo "skip_conventional_commits=true" >> $GITHUB_OUTPUT
echo "focus_conflict_detection=true" >> $GITHUB_OUTPUT
:material-initialization: Post-Initialization Validation
Repository Setup Verification
- name: Post-initialization comprehensive validation
if: inputs.post_init == true
run: |
echo "🎯 Running post-initialization validation suite"
# Comprehensive repository state verification
# - All branches created correctly
# - Workflow files deployed properly
# - Security settings configured
# - Build system functional
Performance and Optimization
Efficient Resource Management
Intelligent Caching Strategy
- name: Optimize validation performance with caching
uses: actions/cache@v4
with:
path: |
~/.m2/repository
~/.gradle/caches
target/
build/
key: validation-${{ runner.os }}-${{ hashFiles('**/*.xml', '**/*.gradle*', '**/pom.xml') }}
restore-keys: |
validation-${{ runner.os }}-
validation-
Parallel Execution Benefits
- Reduced Validation Time: Build and quality checks run simultaneously
- Optimized Resource Usage: Efficient job scheduling and caching
- Early Failure Detection: Quick feedback on critical issues
Conditional Processing
Smart Execution Logic
# Skip expensive operations when not needed
- name: Conditional expensive validation
if: |
needs.check-repo-state.outputs.build_required == 'true' &&
github.event_name == 'pull_request' &&
needs.validate.outputs.code_changes > 0
run: |
# Only run expensive validations for code changes in PRs
Error Handling and Recovery
Graceful Degradation
Non-Critical Failure Management
- name: Optional validation with graceful failure
continue-on-error: true
run: |
# Validation that shouldn't block PR if it fails
echo "🔍 Running optional advanced checks..."
# If this fails, validation continues with warning
Clear Error Communication
- name: Provide actionable error guidance
if: failure()
run: |
echo "::error title=Validation Failed::Review the validation report for specific issues"
echo "::error::Common solutions:"
echo "::error:: - Fix merge conflicts manually"
echo "::error:: - Update branch with latest changes"
echo "::error:: - Ensure conventional commit format"
echo "::error:: - Resolve build failures"
# Link to documentation
echo "::notice::See validation docs: https://docs/workflows/validation.md"
Integration and Extensibility
Workflow Integration Points
Build Workflow Coordination
- Shared Configuration: Consistent Java environment and dependency management
- Artifact Compatibility: Build outputs usable across workflow systems
- Performance Optimization: Shared caching and parallel execution strategies
Release Workflow Support
- Conventional Commits: Ensures proper semantic versioning through validation
- Quality Gates: Clean validation required before release creation
- Change Documentation: Validation results feed into release notes
Sync Workflow Assistance
- Conflict Detection: Early identification of merge conflicts in upstream sync
- Branch Status: Verification of proper branch synchronization
- Special Rules: Customized validation for fork management operations
Customization Framework
Configurable Validation Rules
# Environment-based configuration
env:
JAVA_VERSION: '17' # Java environment version
VALIDATION_TIMEOUT: '15' # Maximum validation time (minutes)
COVERAGE_THRESHOLD: '70' # Minimum test coverage percentage
CONVENTIONAL_COMMITS: 'strict' # Commit validation level
CONFLICT_DETECTION: 'comprehensive' # Conflict scanning depth
Extensible Validation Framework
# Custom validation hooks
- name: Custom Organization Validation
if: env.CUSTOM_VALIDATION == 'enabled'
uses: ./.github/actions/custom-validate
with:
validation-config: .github/validation.yml
severity-level: ${{ env.VALIDATION_SEVERITY }}
The pull request validation workflow provides comprehensive quality assurance with intelligent rule application, ensuring code integrity while maintaining developer productivity through clear feedback and efficient execution.