diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..f728db3
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,130 @@
+name: CI
+
+on:
+ pull_request:
+ push:
+ branches:
+ - main
+ - master
+ - work
+
+jobs:
+ upgrade-readiness-precheck:
+ runs-on: ubuntu-latest
+ env:
+ MAVEN_SETTINGS_FILE: .mvn/settings-mirror.xml
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Setup JDK 17
+ uses: actions/setup-java@v4
+ with:
+ distribution: temurin
+ java-version: '17'
+ cache: maven
+
+ - name: Upgrade readiness precheck
+ run: ./scripts/upgrade-precheck.sh --phase boot3 --strict --report build/upgrade-precheck-report.txt
+
+ - name: Upload upgrade precheck report
+ if: always()
+ uses: actions/upload-artifact@v4
+ with:
+ name: upgrade-precheck-report
+ path: build/upgrade-precheck-report.txt
+
+ - name: Build upgrade execution checklist
+ if: always()
+ run: ./scripts/upgrade-build-checklist.sh build/upgrade-precheck-report.txt build/upgrade-execution-checklist.md config/upgrade-owners.map --fail-on-unmapped
+
+ - name: Validate upgrade execution checklist format
+ if: always()
+ run: |
+ grep -q "Phase 0 - Recommended Migration Batches" build/upgrade-execution-checklist.md
+ grep -q "owner:" build/upgrade-execution-checklist.md
+ grep -q "unmapped owners:" build/upgrade-execution-checklist.md
+
+ - name: Upload upgrade execution checklist
+ if: always()
+ uses: actions/upload-artifact@v4
+ with:
+ name: upgrade-execution-checklist
+ path: build/upgrade-execution-checklist.md
+
+ - name: Build boot3 jakarta migration plan
+ if: always()
+ run: ./scripts/boot3-migration-plan.sh build/boot3-jakarta-migration-plan.md
+
+ - name: Upload boot3 migration plan
+ if: always()
+ uses: actions/upload-artifact@v4
+ with:
+ name: boot3-jakarta-migration-plan
+ path: build/boot3-jakarta-migration-plan.md
+
+ - name: Build boot3 jakarta task board
+ if: always()
+ run: ./scripts/boot3-task-board.sh build/boot3-jakarta-task-board.md config/upgrade-owners.map
+
+ - name: Upload boot3 task board
+ if: always()
+ uses: actions/upload-artifact@v4
+ with:
+ name: boot3-jakarta-task-board
+ path: build/boot3-jakarta-task-board.md
+
+ - name: Publish upgrade precheck summary
+ if: always()
+ run: |
+ echo "## Upgrade readiness precheck" >> "$GITHUB_STEP_SUMMARY"
+ if [ -f build/upgrade-precheck-report.txt ]; then
+ while IFS='=' read -r key value; do
+ echo "- \`${key}\`: ${value}" >> "$GITHUB_STEP_SUMMARY"
+ done < build/upgrade-precheck-report.txt
+ else
+ echo "- report not found" >> "$GITHUB_STEP_SUMMARY"
+ fi
+ echo "" >> "$GITHUB_STEP_SUMMARY"
+ echo "## Upgrade execution checklist artifact" >> "$GITHUB_STEP_SUMMARY"
+ echo "- artifact: \`upgrade-execution-checklist\`" >> "$GITHUB_STEP_SUMMARY"
+ echo "- artifact: \`boot3-jakarta-migration-plan\`" >> "$GITHUB_STEP_SUMMARY"
+ echo "- artifact: \`boot3-jakarta-task-board\`" >> "$GITHUB_STEP_SUMMARY"
+
+ unit-and-validation-tests:
+ needs: upgrade-readiness-precheck
+ runs-on: ubuntu-latest
+ env:
+ MAVEN_SETTINGS_FILE: .mvn/settings-mirror.xml
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Setup JDK 17
+ uses: actions/setup-java@v4
+ with:
+ distribution: temurin
+ java-version: '17'
+ cache: maven
+
+ - name: Smoke gate
+ run: ./scripts/test-gate.sh smoke
+
+ full-regression:
+ runs-on: ubuntu-latest
+ if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master'
+ env:
+ MAVEN_SETTINGS_FILE: .mvn/settings-mirror.xml
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Setup JDK 17
+ uses: actions/setup-java@v4
+ with:
+ distribution: temurin
+ java-version: '17'
+ cache: maven
+
+ - name: Full test suite
+ run: ./scripts/test-gate.sh full
diff --git a/.mvn/settings-mirror.xml b/.mvn/settings-mirror.xml
new file mode 100644
index 0000000..2d6a54a
--- /dev/null
+++ b/.mvn/settings-mirror.xml
@@ -0,0 +1,12 @@
+