-
Notifications
You must be signed in to change notification settings - Fork 55
ci: add Platform testnet sync status reporting #3958
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: v3.1-dev
Are you sure you want to change the base?
Changes from all commits
1749712
4069ab1
64a8fca
f6783a6
06c6405
07c7e22
3546112
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,135 @@ | ||
| name: "Platform testnet sync status" | ||
|
|
||
| "on": | ||
| repository_dispatch: | ||
| types: | ||
| - platform-testnet-sync-completed | ||
| workflow_dispatch: | ||
| inputs: | ||
| status: | ||
| description: Final completed run status | ||
| required: true | ||
| type: choice | ||
| options: | ||
| - sync_passed | ||
| - build_failed | ||
| - sync_failed | ||
| target_sha: | ||
| description: Platform commit SHA to report against | ||
| required: false | ||
| type: string | ||
| target_url: | ||
| description: URL for run logs or status details | ||
| required: false | ||
| type: string | ||
| core_version: | ||
| description: Public Core version used by the run | ||
| required: false | ||
| type: string | ||
| platform_sha: | ||
| description: Platform commit SHA built and synced | ||
| required: false | ||
| type: string | ||
| completed_at: | ||
| description: Completion timestamp, preferably ISO 8601 UTC | ||
| required: false | ||
| type: string | ||
|
|
||
| permissions: | ||
| contents: read | ||
| statuses: write | ||
|
Comment on lines
+7
to
+40
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔴 Blocking: workflow_dispatch path can forge 'Platform testnet sync' commit statuses The job is granted statuses: write and accepts workflow_dispatch input directly for status, target_sha, target_url, core_version, platform_sha, and completed_at. The only checks performed are the enum match on status, a 40-hex regex on the SHA, repos.getCommit (which only proves the SHA exists in the repo), and an origin restriction on target_url. None of those prove the external sync worker actually ran against that commit. Any account with workflow-dispatch privilege on this repo (broader than the operator credential the design intends) can therefore publish state=success, context='Platform testnet sync' on an arbitrary commit. If branch protection, dashboards, or release automation treat this context as authoritative — and docs/PLATFORM_TESTNET_SYNC.md presents it that way — this is a status-forgery path that bypasses the worker boundary. Either drop the workflow_dispatch trigger (rely on repository_dispatch from the operator-held credential only), or gate the manual path behind a protected environment with a required reviewer and a restricted actor allowlist. Note: per the file-handling constraint, suggestion field is null; do not interpret that as agreement to leave the trigger in place. source: ['codex'] |
||
|
|
||
| jobs: | ||
| report: | ||
| name: Report latest completed sync status | ||
| runs-on: ubuntu-24.04 | ||
| steps: | ||
| - name: Publish commit status | ||
| uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0 | ||
| env: | ||
| CLIENT_PAYLOAD: ${{ toJson(github.event.client_payload) }} | ||
| with: | ||
| script: | | ||
| const dispatchPayload = JSON.parse(process.env.CLIENT_PAYLOAD || '{}'); | ||
| const payload = context.eventName === 'workflow_dispatch' | ||
| ? context.payload.inputs | ||
| : dispatchPayload; | ||
|
|
||
| if (!payload || typeof payload !== 'object' || Array.isArray(payload)) { | ||
| core.setFailed('Missing repository_dispatch client_payload'); | ||
| return; | ||
| } | ||
|
|
||
| const status = payload.status; | ||
| const labels = { | ||
| sync_passed: 'Sync Passed', | ||
| build_failed: 'Build Failed', | ||
| sync_failed: 'Sync Failed', | ||
| }; | ||
|
|
||
| if (!Object.prototype.hasOwnProperty.call(labels, status)) { | ||
| core.setFailed(`Unsupported status: ${status || '<empty>'}`); | ||
| return; | ||
| } | ||
|
|
||
| const targetSha = String(payload.target_sha || payload.platform_sha || '').trim(); | ||
| if (!/^[0-9a-f]{40}$/i.test(targetSha)) { | ||
| core.setFailed('target_sha or platform_sha must be an explicit 40-character commit SHA'); | ||
| return; | ||
| } | ||
|
|
||
| await github.rest.repos.getCommit({ | ||
| owner: context.repo.owner, | ||
| repo: context.repo.repo, | ||
| ref: targetSha, | ||
| }); | ||
|
|
||
| let targetUrl; | ||
| if (payload.target_url) { | ||
| let parsedUrl; | ||
| try { | ||
| parsedUrl = new URL(payload.target_url); | ||
| } catch (error) { | ||
| core.setFailed(`target_url must be a valid URL: ${error.message}`); | ||
| return; | ||
| } | ||
|
|
||
| if (parsedUrl.origin !== 'https://github.com') { | ||
| core.setFailed('target_url must use the https://github.com origin'); | ||
| return; | ||
| } | ||
| targetUrl = parsedUrl.toString(); | ||
| } | ||
|
|
||
| const state = status === 'sync_passed' ? 'success' : 'failure'; | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| const label = labels[status]; | ||
| const details = [ | ||
| payload.core_version, | ||
| payload.platform_sha ? payload.platform_sha.slice(0, 12) : null, | ||
| payload.completed_at, | ||
| ].filter(Boolean).join(' '); | ||
| const description = `${label}${details ? ` - ${details}` : ''}`.slice(0, 140); | ||
|
|
||
| await github.rest.repos.createCommitStatus({ | ||
| owner: context.repo.owner, | ||
| repo: context.repo.repo, | ||
| sha: targetSha, | ||
| state, | ||
| context: 'Platform testnet sync', | ||
| description, | ||
| target_url: targetUrl || undefined, | ||
| }); | ||
|
|
||
| await core.summary | ||
| .addHeading('Platform testnet sync') | ||
| .addRaw(`Status: ${label}\n\n`) | ||
| .addRaw(`Target SHA: ${targetSha}\n\n`) | ||
| .addRaw(`Core version: ${payload.core_version || 'not provided'}\n\n`) | ||
| .addRaw(`Platform SHA: ${payload.platform_sha || targetSha}\n\n`) | ||
| .addRaw(`Completed at: ${payload.completed_at || 'not provided'}\n\n`) | ||
| .addRaw(`Details: ${targetUrl || 'not provided'}\n`) | ||
| .write(); | ||
|
|
||
| if (status !== 'sync_passed') { | ||
| core.setFailed(label); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| # Platform Testnet Sync | ||
|
|
||
| This document defines the repo-side reporting contract for Platform sync against the latest public Dash Core release on testnet. | ||
|
|
||
| The Platform sync worker and server infrastructure live outside this repository. This repository only owns the public status surface: | ||
|
|
||
| - the `Platform Sync` README badge | ||
| - the `Platform testnet sync status` workflow | ||
| - the `Platform testnet sync` commit status context | ||
|
|
||
| ## Visible Status | ||
|
|
||
| The visible status is intentionally the last completed run only. A currently running build or sync must not replace the useful completed result with a running state. | ||
|
|
||
| The external worker reports one final completed status for the tested Platform commit: | ||
|
|
||
| - Context: `Platform testnet sync` | ||
| - Success state: | ||
| - `Sync Passed` | ||
| - Failure states: | ||
| - `Build Failed` | ||
| - `Sync Failed` | ||
|
|
||
| Detailed build, baseline, and sync logs belong behind the status `target_url`, not in the status description. | ||
|
|
||
| ## Reporting Flow | ||
|
|
||
| When a run completes, the external worker sends a `repository_dispatch` event to `dashpay/platform`: | ||
|
|
||
| ```json | ||
| { | ||
| "event_type": "platform-testnet-sync-completed", | ||
| "client_payload": { | ||
| "status": "sync_passed", | ||
| "target_sha": "0000000000000000000000000000000000000000", | ||
| "target_url": "https://github.com/dashpay/platform/actions/runs/0000000000", | ||
| "core_version": "vX.Y.Z", | ||
| "platform_sha": "0000000000000000000000000000000000000000", | ||
| "completed_at": "2026-06-24T05:30:00Z" | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| Allowed `status` values: | ||
|
|
||
| - `sync_passed` | ||
| - `build_failed` | ||
| - `sync_failed` | ||
|
|
||
| The workflow validates the target commit SHA, writes the `Platform testnet sync` commit status, and fails the workflow run for `build_failed` and `sync_failed` so the README badge reflects the final outcome. | ||
|
|
||
| Manual status testing is available through the `Platform testnet sync status` workflow's `workflow_dispatch` trigger with the same fields. | ||
|
|
||
| ## Expectations For The External Worker | ||
|
|
||
| The worker should: | ||
|
|
||
| - maintain or consume a synced latest-public-Core testnet baseline | ||
| - build Platform for the target `dashpay/platform` commit | ||
| - run Platform sync against that baseline | ||
| - report only the final completed result to this repository | ||
| - keep detailed logs and diagnostics outside this repository, linked through `target_url` | ||
|
|
||
| The dispatch credential should be held only by the external worker/operator account. The status workflow requires an explicit 40-character commit SHA that exists in `dashpay/platform`, and workflow-provided `target_url` values are limited to the `https://github.com` origin. | ||
|
|
||
| The GitHub status description stays short so the repo panel remains readable. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🟡 Suggestion: workflow_dispatch lets any repo writer forge a sync status
The receiver exposes both
repository_dispatch(driven by the operator-only worker credential) andworkflow_dispatchwith free-formstatusandtarget_shainputs. The job carriesstatuses: write, so anyone with repo write access can manually publish 'Sync Passed' against any existing SHA without the worker ever running. If maintainers, branch protection, or merge automation treat theLatest public Core testnet synccontext as evidence the testnet sync actually ran, this becomes a trust-laundering primitive that bypasses the operator-credential boundary the rest of the pipeline carefully maintains. Either drop theworkflow_dispatchtrigger, or gate it behind an environment with required reviewers so the manual path requires an operator approval distinct from generic repo write.source: ['codex']
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Resolved in this update — workflow_dispatch lets any repo writer forge a sync status no longer present.
Auto-resolved by the review system based on the latest commit diff. If you believe this was closed in error, reopen the thread.