88 required : true
99 type : string
1010 release_ref :
11- description : Optional public OpenClaw release ref, tag, branch, or SHA
12- required : false
13- default : " "
11+ description : Exact public OpenClaw release ref, tag, or SHA
12+ required : true
1413 type : string
1514 package_spec :
16- description : Optional package spec covered by this evidence, for example openclaw@2026.4.27-beta.1
17- required : false
18- default : " "
15+ description : Exact package spec covered by this evidence, for example openclaw@2026.4.27-beta.1
16+ required : true
1917 type : string
2018 runs :
2119 description : " One per line: <label> <owner/repo> <run-id> <blocking|advisory>"
@@ -34,22 +32,28 @@ concurrency:
3432env :
3533 FORCE_JAVASCRIPT_ACTIONS_TO_NODE24 : " true"
3634 NODE_VERSION : " 24.x"
35+ PACKAGE_SPEC : ${{ inputs.package_spec }}
36+ RELEASE_ID : ${{ inputs.release_id }}
37+ RELEASE_REF : ${{ inputs.release_ref }}
3738
3839jobs :
3940 write_evidence :
41+ if : github.ref == 'refs/heads/main'
42+ environment : release-evidence
4043 runs-on : ubuntu-latest
4144 permissions :
4245 actions : read
4346 contents : write
4447 steps :
4548 - name : Checkout release repo
46- uses : actions/checkout@v6
49+ uses : actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
4750 with :
4851 fetch-depth : 0
49- persist-credentials : false
52+ ref : main
53+ token : ${{ github.token }}
5054
5155 - name : Setup Node.js
52- uses : actions/setup-node@v6
56+ uses : actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6.5.0
5357 with :
5458 node-version : ${{ env.NODE_VERSION }}
5559
@@ -69,48 +73,79 @@ jobs:
6973 run : |
7074 set -euo pipefail
7175 node scripts/openclaw-release-evidence.mjs \
72- --release-id "${{ inputs.release_id }} " \
73- --release-ref "${{ inputs.release_ref }} " \
74- --package-spec "${{ inputs.package_spec }} " \
76+ --release-id "$RELEASE_ID " \
77+ --release-ref "$RELEASE_REF " \
78+ --package-spec "$PACKAGE_SPEC " \
7579 --runs-file "$RUNNER_TEMP/openclaw-release-evidence/runs.txt" \
7680 --notes-file "$RUNNER_TEMP/openclaw-release-evidence/notes.md"
7781
7882 - name : Append run summary
7983 run : |
8084 set -euo pipefail
81- evidence_md="evidence/${{ inputs.release_id } }/release-evidence.md"
85+ evidence_md="evidence/${RELEASE_ID }/release-evidence.md"
8286 {
8387 cat "$evidence_md"
8488 echo
8589 echo "Evidence path: \`$evidence_md\`"
8690 } >> "$GITHUB_STEP_SUMMARY"
8791
8892 - name : Commit evidence to main
89- env :
90- PUSH_TOKEN : ${{ secrets.OPENCLAW_RELEASES_PRIVATE_PUSH_TOKEN }}
9193 run : |
9294 set -euo pipefail
93- evidence_path="evidence/${{ inputs.release_id }}"
94- if [[ -z "${PUSH_TOKEN// }" ]]; then
95- echo "::error::OPENCLAW_RELEASES_PRIVATE_PUSH_TOKEN is required to push release evidence directly to main."
96- exit 1
97- fi
95+ evidence_path="evidence/${RELEASE_ID}"
96+ node scripts/openclaw-release-evidence-contract.mjs verify-evidence \
97+ "${evidence_path}/release-evidence.json" "$RELEASE_ID" "$RELEASE_REF" "$PACKAGE_SPEC"
9898 git config user.name "openclaw-release-bot"
9999 git config user.email "release-bot@openclaw.ai"
100100 git add "${evidence_path}"
101+ generated_tree="$(git write-tree)"
102+ expected_tree="$(git rev-parse "${generated_tree}:${evidence_path}")"
103+ changed=0
101104 if git diff --cached --quiet; then
102105 echo "No evidence changes to commit."
103- exit 0
106+ else
107+ git commit -m "Evidence: record ${RELEASE_ID}"
108+ changed=1
104109 fi
105- git commit -m "Evidence: record ${{ inputs.release_id }}"
106- git remote set-url origin "https://x-access-token:${PUSH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git"
107- for attempt in 1 2 3 4 5; do
108- git fetch origin main
109- if git rebase origin/main && git push origin HEAD:main; then
110- exit 0
110+
111+ if [[ "$changed" == "1" ]]; then
112+ pushed=0
113+ for attempt in 1 2 3 4 5; do
114+ git fetch origin main
115+ if git rebase origin/main; then
116+ if [[ "$(git rev-parse "HEAD:${evidence_path}")" != "$expected_tree" ]]; then
117+ echo "::error::Rebase changed the generated evidence directory."
118+ exit 1
119+ fi
120+ if git push origin HEAD:main; then
121+ pushed=1
122+ break
123+ fi
124+ fi
125+ git rebase --abort || true
126+ sleep $((attempt * 5))
127+ done
128+ if [[ "$pushed" != "1" ]]; then
129+ echo "::error::Could not push evidence to main after retries."
130+ exit 1
111131 fi
112- git rebase --abort || true
113- sleep $((attempt * 5))
114- done
115- echo "::error::Could not push evidence to main after retries."
116- exit 1
132+ fi
133+
134+ git fetch origin main
135+ git merge-base --is-ancestor HEAD origin/main || {
136+ echo "::error::Published evidence commit is not reachable from origin/main."
137+ exit 1
138+ }
139+ remote_tree="$(git rev-parse "origin/main:${evidence_path}")"
140+ if [[ "$expected_tree" != "$remote_tree" ]]; then
141+ echo "::error::Remote evidence directory differs from the generated files."
142+ exit 1
143+ fi
144+ remote_json="$RUNNER_TEMP/openclaw-release-evidence/remote-release-evidence.json"
145+ git show "origin/main:${evidence_path}/release-evidence.json" > "$remote_json"
146+ cmp "${evidence_path}/release-evidence.json" "$remote_json" || {
147+ echo "::error::Remote evidence bytes differ from the generated JSON."
148+ exit 1
149+ }
150+ node scripts/openclaw-release-evidence-contract.mjs verify-evidence \
151+ "$remote_json" "$RELEASE_ID" "$RELEASE_REF" "$PACKAGE_SPEC"
0 commit comments