|
1 | | -name: Branch Protection Check |
| 1 | +name: Branch Protection |
2 | 2 |
|
3 | 3 | on: |
4 | 4 | pull_request: |
5 | | - branches: |
6 | | - - main |
7 | | - - master |
8 | | - - development |
9 | | - - dev |
| 5 | + branches: [main, beta] |
10 | 6 |
|
11 | 7 | jobs: |
12 | | - quality-gate: |
13 | | - name: Quality Gate Check |
14 | | - runs-on: ubuntu-latest |
15 | | - |
16 | | - steps: |
17 | | - - name: Checkout code |
18 | | - uses: actions/checkout@v4 |
19 | | - with: |
20 | | - fetch-depth: 0 |
21 | | - |
22 | | - - name: Setup PHP |
23 | | - uses: shivammathur/setup-php@v2 |
24 | | - with: |
25 | | - php-version: '8.1' |
26 | | - extensions: mbstring, xml, ctype, iconv, intl, pdo, pdo_mysql, dom, filter, gd, json, posix, simplexml, xmlreader, xmlwriter, zip |
27 | | - coverage: xdebug |
28 | | - tools: composer:v2 |
29 | | - |
30 | | - - name: Install dependencies |
31 | | - run: composer install --prefer-dist --no-progress --no-interaction |
32 | | - |
33 | | - - name: Initialize Quality Gate Results |
34 | | - run: | |
35 | | - echo "QUALITY_GATE_PASSED=true" >> $GITHUB_ENV |
36 | | - echo "## 🚦 Quality Gate Status" >> $GITHUB_STEP_SUMMARY |
37 | | - |
38 | | - - name: Check 1 - PHP Syntax |
39 | | - id: syntax |
40 | | - run: | |
41 | | - echo "### ✅ Check 1: PHP Syntax" >> $GITHUB_STEP_SUMMARY |
42 | | - if composer lint; then |
43 | | - echo "✅ No syntax errors found" >> $GITHUB_STEP_SUMMARY |
44 | | - echo "status=pass" >> $GITHUB_OUTPUT |
45 | | - else |
46 | | - echo "❌ FAILED: Syntax errors detected" >> $GITHUB_STEP_SUMMARY |
47 | | - echo "status=fail" >> $GITHUB_OUTPUT |
48 | | - echo "QUALITY_GATE_PASSED=false" >> $GITHUB_ENV |
49 | | - fi |
50 | | - |
51 | | - - name: Check 2 - Coding Standards (PHPCS) |
52 | | - id: phpcs |
53 | | - run: | |
54 | | - echo "### Check 2: Coding Standards (PHPCS)" >> $GITHUB_STEP_SUMMARY |
55 | | - composer cs:check |
56 | | - composer phpcs:output |
57 | | - if [ -f phpcs-output.json ]; then |
58 | | - ERRORS=$(jq '.totals.errors' phpcs-output.json) |
59 | | - echo "errors=$ERRORS" >> $GITHUB_OUTPUT |
60 | | - if [ "$ERRORS" -eq "0" ]; then |
61 | | - echo "✅ PASSED: No PHPCS errors" >> $GITHUB_STEP_SUMMARY |
62 | | - echo "status=pass" >> $GITHUB_OUTPUT |
63 | | - else |
64 | | - echo "❌ FAILED: Found $ERRORS PHPCS errors (must be 0)" >> $GITHUB_STEP_SUMMARY |
65 | | - echo "status=fail" >> $GITHUB_OUTPUT |
66 | | - echo "QUALITY_GATE_PASSED=false" >> $GITHUB_ENV |
67 | | - fi |
68 | | - fi |
69 | | - continue-on-error: true |
70 | | - |
71 | | - - name: Check 3 - Code Quality (PHPMD) |
72 | | - id: phpmd |
73 | | - run: | |
74 | | - echo "### Check 3: Code Quality (PHPMD)" >> $GITHUB_STEP_SUMMARY |
75 | | - composer phpmd > phpmd-output.txt || true |
76 | | - VIOLATIONS=$(cat phpmd-output.txt | wc -l) |
77 | | - echo "violations=$VIOLATIONS" >> $GITHUB_OUTPUT |
78 | | - |
79 | | - # Allow up to 50 minor violations, fail if more |
80 | | - if [ "$VIOLATIONS" -le "50" ]; then |
81 | | - echo "✅ PASSED: $VIOLATIONS PHPMD violations (acceptable)" >> $GITHUB_STEP_SUMMARY |
82 | | - echo "status=pass" >> $GITHUB_OUTPUT |
83 | | - else |
84 | | - echo "❌ FAILED: $VIOLATIONS PHPMD violations (max 50)" >> $GITHUB_STEP_SUMMARY |
85 | | - echo "status=fail" >> $GITHUB_OUTPUT |
86 | | - echo "QUALITY_GATE_PASSED=false" >> $GITHUB_ENV |
87 | | - fi |
88 | | - continue-on-error: true |
89 | | - |
90 | | - - name: Check 4 - Overall Quality Score |
91 | | - id: quality |
92 | | - run: | |
93 | | - echo "### Check 4: Overall Quality Score" >> $GITHUB_STEP_SUMMARY |
94 | | - composer phpqa:ci || true |
95 | | - |
96 | | - if [ -f phpqa/phpqa.json ]; then |
97 | | - # Calculate composite quality score |
98 | | - PHPCS_ERRORS="${{ steps.phpcs.outputs.errors }}" |
99 | | - PHPMD_VIOLATIONS="${{ steps.phpmd.outputs.violations }}" |
100 | | - |
101 | | - # Score calculation: Start at 100, deduct points for issues |
102 | | - SCORE=$(echo "scale=2; 100 - ($PHPCS_ERRORS * 2) - ($PHPMD_VIOLATIONS * 0.1)" | bc) |
103 | | - |
104 | | - # Ensure score doesn't go negative |
105 | | - if (( $(echo "$SCORE < 0" | bc -l) )); then |
106 | | - SCORE=0 |
107 | | - fi |
108 | | - |
109 | | - echo "score=$SCORE" >> $GITHUB_OUTPUT |
110 | | - echo "Overall Quality Score: **$SCORE%**" >> $GITHUB_STEP_SUMMARY |
111 | | - |
112 | | - if (( $(echo "$SCORE >= 90" | bc -l) )); then |
113 | | - echo "✅ PASSED: Quality score meets 90% threshold" >> $GITHUB_STEP_SUMMARY |
114 | | - echo "status=pass" >> $GITHUB_OUTPUT |
115 | | - else |
116 | | - echo "❌ FAILED: Quality score ($SCORE%) below 90% threshold" >> $GITHUB_STEP_SUMMARY |
117 | | - echo "status=fail" >> $GITHUB_OUTPUT |
118 | | - echo "QUALITY_GATE_PASSED=false" >> $GITHUB_ENV |
119 | | - fi |
120 | | - else |
121 | | - echo "⚠️ WARNING: Could not calculate quality score" >> $GITHUB_STEP_SUMMARY |
122 | | - echo "status=unknown" >> $GITHUB_OUTPUT |
123 | | - fi |
124 | | - continue-on-error: true |
125 | | - |
126 | | - - name: Check 5 - Unit Tests |
127 | | - id: tests |
128 | | - run: | |
129 | | - echo "### Check 5: Unit Tests" >> $GITHUB_STEP_SUMMARY |
130 | | - if composer test:unit; then |
131 | | - echo "✅ PASSED: All unit tests pass" >> $GITHUB_STEP_SUMMARY |
132 | | - echo "status=pass" >> $GITHUB_OUTPUT |
133 | | - else |
134 | | - echo "⚠️ WARNING: Tests require Nextcloud environment" >> $GITHUB_STEP_SUMMARY |
135 | | - echo "status=skipped" >> $GITHUB_OUTPUT |
136 | | - fi |
137 | | - continue-on-error: true |
138 | | - |
139 | | - - name: Final Quality Gate Decision |
140 | | - run: | |
141 | | - echo "## 🏁 Final Result" >> $GITHUB_STEP_SUMMARY |
142 | | - |
143 | | - if [ "$QUALITY_GATE_PASSED" = "true" ]; then |
144 | | - echo "### ✅ QUALITY GATE PASSED" >> $GITHUB_STEP_SUMMARY |
145 | | - echo "This PR meets all quality requirements and can be merged." >> $GITHUB_STEP_SUMMARY |
146 | | - exit 0 |
147 | | - else |
148 | | - echo "### ❌ QUALITY GATE FAILED" >> $GITHUB_STEP_SUMMARY |
149 | | - echo "This PR does not meet quality requirements." >> $GITHUB_STEP_SUMMARY |
150 | | - echo "" >> $GITHUB_STEP_SUMMARY |
151 | | - echo "**Required actions:**" >> $GITHUB_STEP_SUMMARY |
152 | | - |
153 | | - if [ "${{ steps.syntax.outputs.status }}" = "fail" ]; then |
154 | | - echo "- Fix PHP syntax errors" >> $GITHUB_STEP_SUMMARY |
155 | | - fi |
156 | | - |
157 | | - if [ "${{ steps.phpcs.outputs.status }}" = "fail" ]; then |
158 | | - echo "- Fix PHPCS errors (run 'composer cs:fix')" >> $GITHUB_STEP_SUMMARY |
159 | | - fi |
160 | | - |
161 | | - if [ "${{ steps.phpmd.outputs.status }}" = "fail" ]; then |
162 | | - echo "- Refactor code to reduce PHPMD violations" >> $GITHUB_STEP_SUMMARY |
163 | | - fi |
164 | | - |
165 | | - if [ "${{ steps.quality.outputs.status }}" = "fail" ]; then |
166 | | - echo "- Improve overall code quality to reach 90% score" >> $GITHUB_STEP_SUMMARY |
167 | | - fi |
168 | | - |
169 | | - echo "" >> $GITHUB_STEP_SUMMARY |
170 | | - echo "**Hint:** Run 'composer phpqa' locally to see detailed quality reports." >> $GITHUB_STEP_SUMMARY |
171 | | - exit 1 |
172 | | - fi |
173 | | - |
174 | | - - name: Upload Quality Reports |
175 | | - if: always() |
176 | | - uses: actions/upload-artifact@v3 |
177 | | - with: |
178 | | - name: quality-gate-reports |
179 | | - path: | |
180 | | - phpqa/ |
181 | | - phpcs-output.json |
182 | | - phpmd-output.txt |
183 | | - retention-days: 30 |
184 | | - |
185 | | - - name: Post Quality Gate Summary to PR |
186 | | - if: github.event_name == 'pull_request' |
187 | | - uses: actions/github-script@v7 |
188 | | - with: |
189 | | - script: | |
190 | | - const passed = '${{ env.QUALITY_GATE_PASSED }}' === 'true'; |
191 | | - const syntaxStatus = '${{ steps.syntax.outputs.status }}'; |
192 | | - const phpcsStatus = '${{ steps.phpcs.outputs.status }}'; |
193 | | - const phpmdStatus = '${{ steps.phpmd.outputs.status }}'; |
194 | | - const qualityStatus = '${{ steps.quality.outputs.status }}'; |
195 | | - const testsStatus = '${{ steps.tests.outputs.status }}'; |
196 | | - |
197 | | - const qualityScore = '${{ steps.quality.outputs.score }}'; |
198 | | - const phpcsErrors = '${{ steps.phpcs.outputs.errors }}'; |
199 | | - const phpmdViolations = '${{ steps.phpmd.outputs.violations }}'; |
200 | | - |
201 | | - const statusEmoji = (status) => { |
202 | | - if (status === 'pass') return '✅'; |
203 | | - if (status === 'fail') return '❌'; |
204 | | - if (status === 'skipped') return '⚠️'; |
205 | | - return '❓'; |
206 | | - }; |
207 | | - |
208 | | - const body = `## 🚦 Quality Gate Status: ${passed ? '✅ PASSED' : '❌ FAILED'} |
209 | | - |
210 | | - | Check | Status | Details | |
211 | | - |-------|--------|---------| |
212 | | - | PHP Syntax | ${statusEmoji(syntaxStatus)} ${syntaxStatus.toUpperCase()} | All PHP files must be valid | |
213 | | - | Coding Standards (PHPCS) | ${statusEmoji(phpcsStatus)} ${phpcsStatus.toUpperCase()} | Errors: ${phpcsErrors} (must be 0) | |
214 | | - | Code Quality (PHPMD) | ${statusEmoji(phpmdStatus)} ${phpmdStatus.toUpperCase()} | Violations: ${phpmdViolations} (max 50) | |
215 | | - | Overall Quality Score | ${statusEmoji(qualityStatus)} ${qualityStatus.toUpperCase()} | Score: ${qualityScore}% (min 90%) | |
216 | | - | Unit Tests | ${statusEmoji(testsStatus)} ${testsStatus.toUpperCase()} | Test suite status | |
217 | | - |
218 | | - ### Requirements for Merge |
219 | | - |
220 | | - ${passed ? |
221 | | - '✅ **All quality checks passed!** This PR meets the requirements for merging.' : |
222 | | - '❌ **Quality gate failed.** Please address the issues above before merging.'} |
223 | | - |
224 | | - ${!passed ? ` |
225 | | - ### How to Fix |
226 | | - |
227 | | - 1. Run \`composer cs:fix\` to auto-fix coding standards |
228 | | - 2. Run \`composer phpqa\` to see detailed quality reports |
229 | | - 3. Review the generated report at \`phpqa/phpqa-offline.html\` |
230 | | - 4. Fix any critical issues and re-push your changes |
231 | | - |
232 | | - 📚 See [Quality Assurance Documentation](../docs/quality-assurance.md) for more details. |
233 | | - ` : ''} |
234 | | - |
235 | | - 📊 Detailed reports are available in the workflow artifacts. |
236 | | - `; |
237 | | - |
238 | | - github.rest.issues.createComment({ |
239 | | - issue_number: context.issue.number, |
240 | | - owner: context.repo.owner, |
241 | | - repo: context.repo.repo, |
242 | | - body: body |
243 | | - }); |
244 | | - continue-on-error: true |
245 | | - |
246 | | - |
247 | | - |
| 8 | + check: |
| 9 | + uses: ConductionNL/.github/.github/workflows/branch-protection.yml@main |
0 commit comments