Skip to content

Commit c1f616e

Browse files
authored
chore: add initial scaffold for labs release check orchestration (#2919)
* chore: add initial scaffold for labs release check orchestration * chore: tweak gha
1 parent 032d4c1 commit c1f616e

2 files changed

Lines changed: 152 additions & 8 deletions

File tree

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
name: 🧪 Dispatch Release Qualification
2+
3+
on:
4+
pull_request:
5+
branches:
6+
- stable
7+
types:
8+
- opened
9+
- ready_for_review
10+
- reopened
11+
- synchronize
12+
13+
permissions:
14+
contents: read
15+
pull-requests: read
16+
17+
concurrency:
18+
group: release-qualification-dispatch-${{ github.event.pull_request.number }}
19+
cancel-in-progress: true
20+
21+
jobs:
22+
dispatch-release-qualification:
23+
if: github.event.pull_request.head.repo.full_name == github.repository
24+
runs-on: ubuntu-latest
25+
steps:
26+
- name: Build dispatch payload
27+
env:
28+
PR_TITLE: ${{ github.event.pull_request.title }}
29+
run: |
30+
set -euo pipefail
31+
32+
MERGE_PREPARATION_STATUS="unknown"
33+
if [[ "${PR_TITLE}" == *"conflicts need resolution"* ]]; then
34+
MERGE_PREPARATION_STATUS="conflicts"
35+
fi
36+
37+
cat <<EOF > release-qualification-payload.json
38+
{
39+
"repositoryOwner": "${{ github.repository_owner }}",
40+
"repositoryName": "${GITHUB_REPOSITORY#*/}",
41+
"repositoryFullName": "${{ github.repository }}",
42+
"pullRequestNumber": ${{ github.event.pull_request.number }},
43+
"pullRequestUrl": "${{ github.event.pull_request.html_url }}",
44+
"baseRef": "${{ github.event.pull_request.base.ref }}",
45+
"headRef": "${{ github.event.pull_request.head.ref }}",
46+
"headSha": "${{ github.event.pull_request.head.sha }}",
47+
"mergePreparationStatus": "${MERGE_PREPARATION_STATUS}",
48+
"triggerEvent": "${{ github.event.action }}"
49+
}
50+
EOF
51+
52+
- name: Dispatch to Labs release orchestrator
53+
id: dispatch
54+
env:
55+
LABS_RELEASE_QUALIFICATION_TOKEN: ${{ secrets.LABS_RELEASE_QUALIFICATION_TOKEN }}
56+
LABS_RELEASE_QUALIFICATION_URL: ${{ vars.LABS_RELEASE_QUALIFICATION_URL }}
57+
run: |
58+
set -euo pipefail
59+
60+
if [[ -z "${LABS_RELEASE_QUALIFICATION_URL}" ]]; then
61+
echo "LABS_RELEASE_QUALIFICATION_URL is required." >&2
62+
exit 1
63+
fi
64+
65+
if [[ -z "${LABS_RELEASE_QUALIFICATION_TOKEN}" ]]; then
66+
echo "LABS_RELEASE_QUALIFICATION_TOKEN is required." >&2
67+
exit 1
68+
fi
69+
70+
RESPONSE_FILE="$(mktemp)"
71+
set +e
72+
HTTP_STATUS="$(curl \
73+
--fail-with-body \
74+
--silent \
75+
--show-error \
76+
--output "${RESPONSE_FILE}" \
77+
--write-out '%{http_code}' \
78+
-X POST \
79+
-H 'content-type: application/json' \
80+
-H "x-labs-internal-token: ${LABS_RELEASE_QUALIFICATION_TOKEN}" \
81+
--data @release-qualification-payload.json \
82+
"${LABS_RELEASE_QUALIFICATION_URL}")"
83+
CURL_EXIT=$?
84+
set -e
85+
86+
if [[ "${CURL_EXIT}" -ne 0 ]]; then
87+
cat "${RESPONSE_FILE}"
88+
exit "${CURL_EXIT}"
89+
fi
90+
91+
if [[ "${HTTP_STATUS}" -lt 200 || "${HTTP_STATUS}" -ge 300 ]]; then
92+
cat "${RESPONSE_FILE}"
93+
exit 1
94+
fi
95+
96+
RUN_ID="$(jq -r '.run.runId // empty' "${RESPONSE_FILE}")"
97+
RUN_STATUS="$(jq -r '.run.status // empty' "${RESPONSE_FILE}")"
98+
CREATED="$(jq -r '.created // false' "${RESPONSE_FILE}")"
99+
100+
if [[ -z "${RUN_ID}" || -z "${RUN_STATUS}" ]]; then
101+
cat "${RESPONSE_FILE}"
102+
echo "Labs response did not include the expected run metadata." >&2
103+
exit 1
104+
fi
105+
106+
echo "run_id=${RUN_ID}" >> "${GITHUB_OUTPUT}"
107+
echo "run_status=${RUN_STATUS}" >> "${GITHUB_OUTPUT}"
108+
echo "created=${CREATED}" >> "${GITHUB_OUTPUT}"
109+
110+
- name: Write workflow summary
111+
run: |
112+
{
113+
echo "### Release Qualification Dispatch"
114+
echo
115+
echo "| Field | Value |"
116+
echo "| --- | --- |"
117+
echo "| PR | #${{ github.event.pull_request.number }} |"
118+
echo "| Base branch | \`${{ github.event.pull_request.base.ref }}\` |"
119+
echo "| Head branch | \`${{ github.event.pull_request.head.ref }}\` |"
120+
echo "| Head SHA | \`${{ github.event.pull_request.head.sha }}\` |"
121+
echo "| Labs run | \`${{ steps.dispatch.outputs.run_id }}\` |"
122+
echo "| Labs status | \`${{ steps.dispatch.outputs.run_status }}\` |"
123+
echo "| New run created | \`${{ steps.dispatch.outputs.created }}\` |"
124+
} >> "${GITHUB_STEP_SUMMARY}"

cicd.md

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,25 @@ main (next) → stable (latest) → X.x (maintenance)
8181
- If the merge conflicts, commits the conflicted merge to the branch so a human can resolve it there
8282
- Merging that PR triggers the automatic stable release workflow
8383

84-
#### 4. Create Patch Branch (`create-patch.yml`)
84+
#### 4. Release Qualification Dispatch (`release-qualification-dispatch.yml`)
85+
86+
**Trigger**: Pull requests targeting `stable` (`opened`, `reopened`, `synchronize`, `ready_for_review`)
87+
88+
**Actions**:
89+
90+
- Sends the PR head SHA and branch metadata to the Labs release-orchestrator service
91+
- Lets Labs create a generic GitHub check run on that SHA
92+
- Does not wait on or expose any private Labs details in the repository
93+
- Re-triggers automatically when new commits are pushed to the PR branch
94+
95+
Only same-repository PRs dispatch to Labs. Forked PRs are intentionally skipped so private Labs credentials are never exposed to untrusted branches.
96+
97+
**Required configuration**:
98+
99+
- variable: `LABS_RELEASE_QUALIFICATION_URL`
100+
- secret: `LABS_RELEASE_QUALIFICATION_TOKEN`
101+
102+
#### 5. Create Patch Branch (`create-patch.yml`)
85103

86104
**Trigger**: Manual workflow dispatch
87105

@@ -92,7 +110,7 @@ main (next) → stable (latest) → X.x (maintenance)
92110
- Creates `X.x` branch from last stable tag
93111
- Enables patching of old versions
94112

95-
#### 5. Forward Port (`forward-port.yml`)
113+
#### 6. Forward Port (`forward-port.yml`)
96114

97115
**Triggers**:
98116

@@ -107,7 +125,7 @@ main (next) → stable (latest) → X.x (maintenance)
107125

108126
### Support Workflows
109127

110-
#### 6. Test Suite (`test-suite.yml`)
128+
#### 7. Test Suite (`test-suite.yml`)
111129

112130
**Type**: Reusable workflow
113131

@@ -118,7 +136,7 @@ main (next) → stable (latest) → X.x (maintenance)
118136
- Visual regression tests (Playwright)
119137
- E2E tests (external service)
120138

121-
#### 7. Visual Tests (`test-example-apps.yml`)
139+
#### 8. Visual Tests (`test-example-apps.yml`)
122140

123141
**Triggers**:
124142

@@ -211,10 +229,12 @@ These skip semantic-release entirely — useful for re-publishing a failed platf
211229

212230
1. Run "Promote to Stable" workflow
213231
2. Review the generated PR from the candidate branch into `stable`
214-
3. If needed, resolve merge conflicts on the candidate branch
215-
4. Merge the PR into `stable`
216-
5. Automatically publishes `1.1.0` as @latest
217-
6. Syncs back to main with version bump
232+
3. Labs receives the PR head SHA and creates the `Release Qualification` GitHub check run
233+
4. If needed, resolve merge conflicts on the candidate branch and push fixes
234+
5. Re-run or wait for qualification on the new PR head SHA
235+
6. Merge the PR into `stable`
236+
7. Automatically publishes `1.1.0` as @latest
237+
8. Syncs back to main with version bump
218238

219239
### Scenario 3: Hotfix to Current Stable
220240

0 commit comments

Comments
 (0)