Pull Request Validation Workflow
The validation workflow supplies the required checks on protected branches. It verifies that a change builds and that the PR meets process requirements before merge.
The validation system applies different build rules by context. Sync PRs targeting the provider-less fork_upstream tree build core only and skip the image jobs; other Java changes build the Azure profile set, validate a service image, and, once the fork is onboarded to a stack, deploy and test it.
When It Runs
The validation workflow runs on:
- Every pull request to a protected branch (
main,fork_integration,fork_upstream) - Direct pushes to protected branches, where the rulesets permit them
- Manual trigger for a check during setup or troubleshooting
The workflow declares both pull_request and pull_request_target, and routes each PR to exactly one lane. Same-repository sync/ branches take pull_request_target, which reads the workflow from the default branch; in filter mode that is the only lane, because neither fork_upstream nor the sync branch carries workflows. Every other PR takes pull_request. The unused lane reports skipped, so exactly one Validation Summary context reflects a real build.
What Gets Validated
Validation covers four areas:
Code Quality Validation
The system verifies that code compiles, dependencies resolve, and tests pass. Pull-request builds can generate JaCoCo coverage reports. Documentation and configuration-only PRs keep the required summary checks reporting while skipping the heavy Java and container jobs.
Process Compliance Verification
Beyond code quality, the workflow validates semantic PR titles for ordinary PRs to main, detects whitespace/conflict-marker errors, and verifies that PR branches are up to date.
Security and Dependency Analysis
CodeQL runs in its own workflow and supplies the required CodeQL status. Dependabot PRs use dependabot-validation.yml for one Java build with coverage followed by validate-only container construction; they do not run the regular Java and container jobs in this workflow.
Deploy and Test
On a push to main or fork_integration, and on the fork's own pull requests, the lane borrows the service's slot in the attached stack, proves the pushed image with every suite .spi/service.yaml declares, and restores the canonical image (ADR-041). A gate job ahead of it always reports, so a run without the lane says why: a pull request from another repository, a fork not yet onboarded, no descriptor, or no image pushed.
Validation Results
The summary job posts one comment on the pull request, updated in place on every push: a table of every job with its result and the reason for any skip, followed by one line per suite the lane ran, and a link to the run. A failed suite names its failure count there; the Surefire and Failsafe reports are attached to the run as the suite-reports artifact. Pull requests from other repositories and from Dependabot get no comment, because their token cannot write one, and sync PRs on the pull_request_target lane never had one; the checks list and run summary carry the same content.
When every check passes the PR can merge once a reviewer approves it. When a check fails the PR is blocked; the failing check links to the job log with the reason.
How to Fix Common Issues
Build Failures
# Run build locally to debug
mvn clean install -P core,azure
# Check for missing dependencies
mvn dependency:tree
PR Title Issues
Edit the pull request title to use a supported Conventional Commit type, for example feat: add new feature description.
Test Failures
# Run tests locally
mvn test -P core,azure
# Run specific test class
mvn test -P core,azure -Dtest=TestClassName
# Run with coverage report
mvn test -P core,azure jacoco:report
Merge Conflicts
# Update your branch with target branch
git fetch origin
git merge origin/main # or target branch
# Resolve conflicts in IDE
# Then commit resolution
git add .
git commit -m "resolve: merge conflicts with main"
Validation Jobs
The workflow coordinates the following validation jobs:
| Job | Purpose | What It Checks |
|---|---|---|
| Check Initialization | Verifies repository setup | Ensures workflows are properly deployed |
| Check Repository | Detects project type | Identifies Java projects via pom.xml |
| Check Paths | Avoids unnecessary work; enforces ownership | Skips heavy jobs for docs/config-only PRs; fails PRs to main that touch a file owned by origin/fork_upstream (ADR-038) |
| Java Build | Compiles and tests | Uses core,azure by default; core on fork_upstream |
| Docker Build | Validates both images | Builds the canonical service and test-suite Dockerfiles without registry credentials |
| Docker Push | Publishes trusted builds | Pushes multi-arch SHA and branch tags to public GHCR, with the test-suite image beside them |
| Deploy Gate | Decides whether to borrow | Always reports; names the reason when the lane does not run |
| Deploy and Test | Proves the pushed image | Borrow, prove every declared suite, restore |
| Check Code Quality | Process compliance | Semantic PR title, conflict markers, branch status |
| Validation Summary | Required status | Always reports; one table of every job, its result, and any skip reason, posted on the pull request |
Branch-Specific Rules
All protected branches use the same validation rules, with exemptions for specific PR types:
| Branch | Standard Validation | Exemptions |
|---|---|---|
main | Azure Maven, image validation, deploy and test + human approval | Docs/config-only changes skip heavy jobs |
fork_integration | Azure Maven, image validation, deploy and test | Docs/config-only changes skip heavy jobs |
fork_upstream | Core-only Maven validation | No Azure JAR or container image exists on this branch, so nothing to deploy |
| Feature branches | N/A - not protected | Standard PR validation, including deploy and test, when targeting protected branches |
Special Cases
Sync PRs
- Relaxed commit standards - Upstream commits may not follow conventions
- Single lane -
pull_request_targetowns sync PRs and supplies trusted local actions; it buildscoreonly in filter mode and the full profile set in mirror mode. The validate-only image build never runs in this lane (#164); it runs on the cascade PR (and, in mirror mode, on thefork_upstreampush after merge)
Status Check Details
Required Checks on main
CodeQL- Stable summary from the separate CodeQL workflowValidation Summary- Stable summary covering the Java build, both image builds, the push, and the deploy lane; code quality stays advisory
The integration-branch ruleset does not currently require status checks.
Check Exemptions
- Sync PRs: Build
coreonly and skip image validation - Release and automation PRs: Skip semantic PR-title validation
- Dependabot PRs: Build with coverage and validate the image through
dependabot-validation.yml. A bump that edits an upstream-owned file, typically an inherited version in the rootpom.xml, is closed by that workflow with a comment instead of building (ADR-026, ADR-038) - Docs/config-only PRs: Changes limited to
.github/**,devops/**,docs/**, other dotfiles, or Markdown skip Java and container work while summary checks still report..mvn/**and.spi/**are never config-only, so a change there always passes the path filter and the build runs; the branch and deploy-gate rules above still apply - Upstream-owned files on a PR to
main: Check Paths fails when a changed file exists onorigin/fork_upstream(ADR-038), unless the PR carries theportlabel declaring a deliberate port (add the label and re-run Check Paths; a fork initialized before the label existed creates it once withgh label create port, as the template-sync labels issue for that fork also notes), targets a branch other thanmain, or is afork_integration/release/upstream-*head, mirror mode, or a fork with no.github/upstream-filter.yml - Pull requests from other repositories: Build and validate only; the deploy lane skips because such a run carries no deploy identity
Troubleshooting
| Issue | Cause | Solution |
|---|---|---|
| "Initialization check failed" | Repository not properly set up | Ensure workflows are deployed and pom.xml exists |
| "Java build failed" | Compilation or dependency issues | Run mvn clean install locally, check dependency conflicts |
| "Unit tests failing" | Test failures in Maven build | Run mvn test locally, fix failing test cases |
| "Semantic PR validation failed" | PR title doesn't follow the supported format | Use a title such as feat:, fix:, or chore: |
| "Merge conflicts detected" | Git conflict markers found | Resolve conflicts locally and commit resolution |
| "Repository not initialized" | Missing required setup files | Complete repository initialization first |
| "Branch status validation failed" | Branch protection or merge issues | Ensure branch is up to date with target |
| "Deploy and Test skipped" | The gate declined; the notice names why | Onboard the fork with spi onboard, add .spi/service.yaml, or open the PR from a branch in this repository |
| "environment is not deployable after 10 minutes" | The stack stayed in maintenance, or another service's lane held it, for longer than the lane waits | Check spi status for the reason; rerun once it reports deployable |
Configuration
Commit Message Format
type(scope): description
feat: add new feature
fix: resolve bug in component
docs: update API documentation
chore: update dependencies
Container Configuration
The engineering system syncs the canonical build/Dockerfile to every fork. SERVICE_NAME defaults to the repository name, and SERVICE_TARGET_JAR is needed only to disambiguate multiple Azure Spring Boot JARs. Trusted pushes publish to ghcr.io/<owner>/<service> with the workflow GITHUB_TOKEN; untrusted PR contexts never receive package-write permission.
Two more images are built beside the service image from the same commit and skipped when their inputs are absent: <service>-acceptance from the suites the descriptor declares (ADR-040), and <service>-load from deployments/shared-schemas/ when the fork's filter keeps it (ADR-042). Only the schema fork carries that payload.
Related
- Conventional Commits - Commit message standards
- Initialization Security - Security setup details
- Build Workflow - Detailed build process
- ADR-033: GHCR as Service Image Registry
- ADR-037: Canonical Service Dockerfile
- ADR-040: Descriptor-Owned Acceptance Contract
- ADR-041: Borrow, Prove, Restore Lane
- ADR-042: Paired Loader Image from the Service Fork