-
Notifications
You must be signed in to change notification settings - Fork 248
334 lines (315 loc) · 13.4 KB
/
ci.yaml
File metadata and controls
334 lines (315 loc) · 13.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
name: CI
run-name: "CI (${{ github.event_name == 'push' && '`main`' || (contains(github.event.pull_request.head.ref, 'release-please') && 'Release-Please PR' || format('PR `#{0}`', github.event.pull_request.number)) || '' }})"
on:
push:
branches:
- main
pull_request:
types: [opened, synchronize, reopened]
branches:
- main
concurrency:
group: ${{ github.event_name == 'pull_request' && format('ci-pr-{0}', github.event.pull_request.number) || format('ci-main-{0}', github.run_id) }}
cancel-in-progress: ${{ github.event_name == 'pull_request' && !contains(github.event.pull_request.head.ref, 'release-please') }}
jobs:
detect-context:
name: Detect Context
runs-on: ubuntu-slim
outputs:
is_release_please_pr: ${{ steps.context.outputs.is_release_please_pr }}
is_regular_pr: ${{ steps.context.outputs.is_regular_pr }}
is_push_to_main: ${{ steps.context.outputs.is_push_to_main }}
is_fork_pr: ${{ steps.context.outputs.is_fork_pr }}
is_release_please_merge: ${{ steps.context.outputs.is_release_please_merge }}
release_version: ${{ steps.context.outputs.release_version }}
pr_base_sha: ${{ steps.context.outputs.pr_base_sha }}
steps:
- id: context
env:
EVENT_NAME: ${{ github.event_name }}
HEAD_REF: ${{ github.head_ref }}
IS_FORK_PR: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork }}
IS_PUSH_TO_MAIN: ${{ github.event_name == 'push' && github.ref_name == github.event.repository.default_branch }}
IS_REGULAR_PR: ${{ github.event_name == 'pull_request' && !github.event.pull_request.head.repo.fork && !contains(github.event.pull_request.head.ref, 'release-please') }}
IS_RELEASE_PLEASE_PR: ${{ contains(github.event.pull_request.head.ref, 'release-please') }}
HEAD_COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
run: |
echo "Determining context..."
echo "is_fork_pr=$IS_FORK_PR" >> "$GITHUB_OUTPUT"
echo "is_push_to_main=$IS_PUSH_TO_MAIN" >> "$GITHUB_OUTPUT"
echo "is_regular_pr=$IS_REGULAR_PR" >> "$GITHUB_OUTPUT"
echo "is_release_please_pr=$IS_RELEASE_PLEASE_PR" >> "$GITHUB_OUTPUT"
# Detect release-please merge: push to main where the merge commit
# message matches release-please's title pattern (proven in release-post-process.yml).
IS_RELEASE_PLEASE_MERGE=false
RELEASE_VERSION=""
if [[ "$IS_PUSH_TO_MAIN" == "true" && "$HEAD_COMMIT_MESSAGE" =~ ^chore(\(.*\))?:\ release\ ([0-9]+\.[0-9]+\.[0-9]+) ]]; then
IS_RELEASE_PLEASE_MERGE=true
RELEASE_VERSION="${BASH_REMATCH[2]}"
fi
echo "is_release_please_merge=$IS_RELEASE_PLEASE_MERGE" >> "$GITHUB_OUTPUT"
echo "release_version=$RELEASE_VERSION" >> "$GITHUB_OUTPUT"
pr-size-check:
name: PR Size Check
needs: detect-context
if: ${{ github.event_name == 'pull_request'}}
runs-on: ubuntu-latest
permissions:
statuses: write
pull-requests: read
steps:
- name: Validate PR Size
uses: SolaceDev/solace-public-workflows/.github/actions/pr-size-check@main
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
max-lines: 1500
validate-conventional-commit:
name: "Validate Conventional Commit"
needs: detect-context
runs-on: ubuntu-latest
if: ${{ github.event_name == 'pull_request'}}
steps:
- name: Checkout code
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
with:
fetch-depth: 0
- name: Validate PR Title
uses: amannn/action-semantic-pull-request@e32d7e603df1aa1ba07e981f2a23455dee596825 # v5
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
types: |
feat
fix
docs
style
refactor
perf
test
build
ci
chore
revert
requireScope: false
disallowScopes: |
release
subjectPattern: ^.+$
subjectPatternError: |
The subject "{subject}" found in the pull request title "{title}"
didn't match the configured pattern. Please ensure that the subject
is not empty.
fossa_scan:
name: FOSSA Scan
needs: detect-context
if: ${{ fromJSON(needs.detect-context.outputs.is_regular_pr) || github.event_name == 'push' }}
uses: SolaceDev/solace-public-workflows/.github/workflows/sca-scan-and-guard.yaml@main
permissions:
contents: read
packages: read
pull-requests: write
id-token: write
actions: read
checks: write
statuses: write
with:
use_vault: false
config_file: ".github/workflow-config.json"
setup_actions: '["setup-uv"]'
custom_setup_script: "uv export --format requirements-txt --no-dev --output-file requirements.txt"
additional_scan_params: |
fossa.branch=${{ github.event_name == 'pull_request' && 'PR' || 'main' }}
fossa.revision=${{ needs.detect-context.outputs.release_version || github.event.pull_request.head.ref || github.sha }}
secrets:
FOSSA_API_KEY: ${{ secrets.FOSSA_API_KEY }}
test-and-sonarqube:
name: Test and SonarQube
needs: detect-context
uses: ./.github/workflows/test-and-sonarqube.yml
permissions:
contents: read
checks: write
pull-requests: write
with:
git_ref: ${{ github.event.pull_request.head.ref || github.ref_name }}
min-python-version: "3.10"
max-python-version: "3.13"
node-version: "25.5.0"
ui-path: "client/webui/frontend"
run_sonarqube_analysis: ${{ !fromJSON(needs.detect-context.outputs.is_fork_pr)}}
run_quality_gate: ${{ fromJSON(needs.detect-context.outputs.is_regular_pr) }}
secrets:
SONAR_TOKEN: ${{ secrets.SONARQUBE_TOKEN }}
SONAR_HOST_URL: ${{ secrets.SONARQUBE_HOST_URL }}
build-and-push:
name: Build and Push Image
needs: detect-context
uses: ./.github/workflows/build-push-image.yaml
permissions:
contents: write
id-token: write
packages: write
checks: write
pull-requests: write
actions: read
repository-projects: read
with:
ref: ${{ github.event.pull_request.head.ref || github.ref_name }}
push_to_ecr: ${{ fromJSON(needs.detect-context.outputs.is_push_to_main) || fromJSON(needs.detect-context.outputs.is_release_please_pr) }}
run_container_scan: ${{ fromJSON(needs.detect-context.outputs.is_regular_pr) }}
is_release_merge: ${{ fromJSON(needs.detect-context.outputs.is_release_please_merge) }}
secrets: inherit
# ── Release-please PR: release readiness ──────────────────────
release-readiness:
name: Release Readiness
needs: [detect-context, build-and-push, test-and-sonarqube]
if: ${{ fromJSON(needs.detect-context.outputs.is_release_please_pr) }}
uses: ./.github/workflows/release-readiness-check.yaml
with:
commit_sha: ${{ github.event.pull_request.head.sha }}
version: ${{ needs.build-and-push.outputs.version }}
image_tag: ${{ needs.build-and-push.outputs.image_tag }}
check_only: false
check_sonarqube_hotspots: true
secrets: inherit
# ── Release-please PR: RC gate (after release readiness) ──────
rc-gate:
name: RC Gate
needs: [detect-context, build-and-push, release-readiness]
if: >-
needs.detect-context.outputs.is_release_please_pr == 'true' &&
needs.release-readiness.result == 'success'
runs-on: ubuntu-latest
outputs:
rc_status: ${{ steps.capture.outputs.status }}
steps:
- name: Dispatch RC workflow and wait
id: run-rc
uses: SolaceDev/solace-public-workflows/workflow-dispatch-and-wait@main
with:
workflow: rc-workflow.yaml
repo: SolaceDev/rc-sam-community
ref: main
token: ${{ secrets.RC_TOKEN }}
wait-for-completion: "true"
wait-for-completion-timeout: "2h"
wait-for-completion-interval: "1m"
inputs: >-
{"sha":"${{ github.event.pull_request.head.sha }}","version":"${{ needs.build-and-push.outputs.version }}","image_tag":"${{ needs.build-and-push.outputs.image_tag }}","environment":"rc"}
- name: Capture RC status
id: capture
if: always()
env:
RC_CONCLUSION: ${{ steps.run-rc.outputs.workflow-conclusion }}
run: |
STATUS=success
if [[ "$RC_CONCLUSION" != "success" ]]; then
STATUS=failure
fi
echo "status=$STATUS" >> "$GITHUB_OUTPUT"
- name: Enforce RC result
if: steps.run-rc.outputs.workflow-conclusion != 'success'
env:
RC_CONCLUSION: ${{ steps.run-rc.outputs.workflow-conclusion }}
run: |
echo "RC workflow conclusion: ${RC_CONCLUSION}" >&2
exit 1
# ── Release-please PR: propagate statuses ─────────────────────
propagate-release-statuses:
name: Propagate Release Statuses
needs: [detect-context, release-readiness, rc-gate]
if: always() && needs.detect-context.outputs.is_release_please_pr == 'true'
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
checks: write
statuses: write
steps:
- name: Propagate statuses to PR SHAs
uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0
env:
PR_HEAD_SHA: ${{ needs.detect-context.outputs.pr_head_sha }}
PR_BASE_SHA: ${{ needs.detect-context.outputs.pr_base_sha }}
FOSSA_STATUS: ${{ needs.release-readiness.outputs.fossa_status }}
PRISMA_STATUS: ${{ needs.release-readiness.outputs.prisma_status }}
SONAR_STATUS: ${{ needs.release-readiness.outputs.sonarqube_hotspots_status }}
GUARDIAN_STATUS: ${{ needs.release-readiness.outputs.guardian_status }}
RC_STATUS: ${{ needs.rc-gate.outputs.rc_status }}
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const owner = context.repo.owner;
const repo = context.repo.repo;
const targetUrl = `${context.serverUrl}/${owner}/${repo}/actions/runs/${context.runId}`;
const checks = [
['FOSSA / Release Readiness', process.env.FOSSA_STATUS],
['Prisma / Release Readiness', process.env.PRISMA_STATUS],
['SonarQube Hotspots / Release Readiness', process.env.SONAR_STATUS],
['Guardian / Release Readiness', process.env.GUARDIAN_STATUS],
['RC / Integration Tests (Community)', process.env.RC_STATUS],
];
for (const sha of [process.env.PR_HEAD_SHA, process.env.PR_BASE_SHA].filter(Boolean)) {
for (const [contextName, value] of checks) {
const state = (value === 'success' || value === 'skipped') ? 'success' : 'failure';
await github.rest.repos.createCommitStatus({
owner, repo, sha,
state,
context: contextName,
description: `Release readiness: ${value || 'unknown'}`,
target_url: targetUrl,
});
}
}
# ── Unified gate for branch protection ────────────────────────
ci-status:
name: CI Status
if: always()
runs-on: ubuntu-latest
needs:
- detect-context
- fossa_scan
- test-and-sonarqube
- build-and-push
- release-readiness
- rc-gate
- propagate-release-statuses
permissions:
contents: read
id-token: write
checks: write
statuses: write
steps:
- name: Evaluate required checks
env:
EVENT_NAME: ${{ github.event_name }}
IS_RP_PR: ${{ needs.detect-context.outputs.is_release_please_pr }}
CONTEXT_RESULT: ${{ needs.detect-context.result }}
FOSSA_RESULT: ${{ needs.fossa_scan.result }}
TEST_RESULT: ${{ needs.test-and-sonarqube.result }}
BUILD_RESULT: ${{ needs.build-and-push.result }}
READINESS_RESULT: ${{ needs.release-readiness.result }}
RC_GATE_RESULT: ${{ needs.rc-gate.result }}
PROPAGATION_RESULT: ${{ needs.propagate-release-statuses.result }}
run: |
set -euo pipefail
require_result() {
local name="$1"; local value="$2"; shift 2; local allowed=("$@")
for expected in "${allowed[@]}"; do
if [[ "$value" == "$expected" ]]; then
echo "✅ ${name}: ${value}"
return 0
fi
done
echo "::error::${name} is '${value}' (expected: ${allowed[*]})"
exit 1
}
require_result "Detect Context" "${CONTEXT_RESULT}" success
require_result "Test and SonarQube" "${TEST_RESULT}" success
require_result "Build and Push" "${BUILD_RESULT}" success
if [[ "$IS_RP_PR" == "true" ]]; then
require_result "Release Readiness" "${READINESS_RESULT}" success
require_result "RC Gate" "${RC_GATE_RESULT}" success
require_result "Propagate Statuses" "${PROPAGATION_RESULT}" success
else
require_result "FOSSA PR Scan" "${FOSSA_RESULT}" success skipped
fi