mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-12 21:53:00 -06:00
refactor: make Telegram release workflow tests behavioral (#114023)
* refactor: make Telegram release workflow tests behavioral * test: fix Telegram release workflow CI
This commit is contained in:
@@ -2772,6 +2772,16 @@ jobs:
|
||||
actions: read
|
||||
contents: read
|
||||
steps:
|
||||
# Identity binds this checkout to the workflow definition that received
|
||||
# the OIDC token. Do not execute status code from the release candidate.
|
||||
- name: Checkout trusted Telegram status helper
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
repository: ${{ needs.trusted_identity.outputs.workflow_repository }}
|
||||
ref: ${{ needs.trusted_identity.outputs.workflow_sha }}
|
||||
fetch-depth: 1
|
||||
persist-credentials: false
|
||||
|
||||
- name: Record advisory status
|
||||
id: record_status
|
||||
env:
|
||||
@@ -2796,119 +2806,7 @@ jobs:
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
status=failure
|
||||
if [[ "$IDENTITY_RESULT" == "cancelled" ||
|
||||
"$BUILD_RESULT" == "cancelled" ||
|
||||
"$ATTESTATION_RESULT" == "cancelled" ||
|
||||
"$RUN_RESULT" == "cancelled" ]]; then
|
||||
status=cancelled
|
||||
elif [[ "$IDENTITY_STATUS" == "success" &&
|
||||
"$BUILD_STATUS" == "success" &&
|
||||
"$ATTESTATION_STATUS" == "success" &&
|
||||
"$EXECUTION_STATUS" == "success" ]]; then
|
||||
status=success
|
||||
fi
|
||||
|
||||
candidate_artifact=null
|
||||
if [[ "$CANDIDATE_ARTIFACT_ID" =~ ^[1-9][0-9]*$ &&
|
||||
"$CANDIDATE_ARTIFACT_DIGEST" =~ ^[a-f0-9]{64}$ &&
|
||||
"$ARCHIVE_NAME" == "release-telegram-candidate-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}-${TARGET_SHA}.tar.zst" &&
|
||||
"$ARCHIVE_SHA256" =~ ^[a-f0-9]{64}$ &&
|
||||
-n "$CANDIDATE_VERSION" ]]; then
|
||||
candidate_artifact="$(
|
||||
jq -cn \
|
||||
--arg id "$CANDIDATE_ARTIFACT_ID" \
|
||||
--arg name "$ARCHIVE_NAME" \
|
||||
--arg digest "$CANDIDATE_ARTIFACT_DIGEST" \
|
||||
--arg runId "$GITHUB_RUN_ID" \
|
||||
--argjson runAttempt "$GITHUB_RUN_ATTEMPT" \
|
||||
--arg fileName "$ARCHIVE_NAME" \
|
||||
--arg sha256 "$ARCHIVE_SHA256" \
|
||||
--arg sourceSha "$TARGET_SHA" \
|
||||
--arg version "$CANDIDATE_VERSION" \
|
||||
'{
|
||||
id: $id,
|
||||
name: $name,
|
||||
digest: $digest,
|
||||
runId: $runId,
|
||||
runAttempt: $runAttempt,
|
||||
fileName: $fileName,
|
||||
sha256: $sha256,
|
||||
sourceSha: $sourceSha,
|
||||
version: $version
|
||||
}'
|
||||
)"
|
||||
fi
|
||||
if [[ "$status" == "success" ]]; then
|
||||
[[ "$candidate_artifact" != "null" &&
|
||||
"$WORKFLOW_SHA" =~ ^[a-f0-9]{40}$ &&
|
||||
"$TARGET_SHA" =~ ^[a-f0-9]{40}$ &&
|
||||
"$EVIDENCE_ARTIFACT_ID" =~ ^[1-9][0-9]*$ &&
|
||||
"$EVIDENCE_ARTIFACT_DIGEST" =~ ^[a-f0-9]{64}$ &&
|
||||
"$EVIDENCE_ARTIFACT_NAME" == "release-qa-live-telegram-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}-${TARGET_SHA}" ]]
|
||||
fi
|
||||
|
||||
status_dir=".artifacts/release-check-status"
|
||||
status_file="${status_dir}/qa_live_telegram_release_checks-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}.env"
|
||||
evidence_file="${status_dir}/qa_live_telegram_release_checks-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}.json"
|
||||
mkdir -p "$status_dir"
|
||||
step_outcomes="identity:${IDENTITY_STATUS} build:${BUILD_STATUS} attest:${ATTESTATION_STATUS} execute:${EXECUTION_STATUS}"
|
||||
{
|
||||
printf 'run_id=%s\n' "$GITHUB_RUN_ID"
|
||||
printf 'run_attempt=%s\n' "$GITHUB_RUN_ATTEMPT"
|
||||
printf 'target_sha=%s\n' "$TARGET_SHA"
|
||||
printf 'workflow_sha=%s\n' "$WORKFLOW_SHA"
|
||||
printf 'job=%s\n' "qa_live_telegram_release_checks"
|
||||
printf 'variant=\n'
|
||||
printf 'status=%s\n' "$status"
|
||||
printf 'job_status=%s\n' "$RUN_RESULT"
|
||||
printf 'step_outcomes=%s\n' "$step_outcomes"
|
||||
} >"$status_file"
|
||||
jq -n \
|
||||
--arg status "$status" \
|
||||
--arg jobStatus "$RUN_RESULT" \
|
||||
--arg identityOutcome "identity:${IDENTITY_STATUS}" \
|
||||
--arg buildOutcome "build:${BUILD_STATUS}" \
|
||||
--arg attestOutcome "attest:${ATTESTATION_STATUS}" \
|
||||
--arg executeOutcome "execute:${EXECUTION_STATUS}" \
|
||||
--arg runId "$GITHUB_RUN_ID" \
|
||||
--argjson runAttempt "$GITHUB_RUN_ATTEMPT" \
|
||||
--arg workflowSha "$WORKFLOW_SHA" \
|
||||
--arg targetSha "$TARGET_SHA" \
|
||||
--arg evidenceId "$EVIDENCE_ARTIFACT_ID" \
|
||||
--arg evidenceName "$EVIDENCE_ARTIFACT_NAME" \
|
||||
--arg evidenceDigest "$EVIDENCE_ARTIFACT_DIGEST" \
|
||||
--argjson candidateArtifact "$candidate_artifact" \
|
||||
'{
|
||||
version: 1,
|
||||
kind: "release-check-status",
|
||||
job: "qa_live_telegram_release_checks",
|
||||
status: $status,
|
||||
jobStatus: $jobStatus,
|
||||
stepOutcomes: [
|
||||
$identityOutcome,
|
||||
$buildOutcome,
|
||||
$attestOutcome,
|
||||
$executeOutcome
|
||||
],
|
||||
runId: $runId,
|
||||
runAttempt: $runAttempt,
|
||||
workflowSha: $workflowSha,
|
||||
targetSha: $targetSha,
|
||||
evidenceArtifact: {
|
||||
id: $evidenceId,
|
||||
name: $evidenceName,
|
||||
digest: $evidenceDigest,
|
||||
runId: $runId,
|
||||
runAttempt: $runAttempt
|
||||
},
|
||||
candidateArtifact: $candidateArtifact
|
||||
}' >"$evidence_file"
|
||||
{
|
||||
echo "status=$status"
|
||||
echo "status_file=$status_file"
|
||||
echo "evidence_file=$evidence_file"
|
||||
} >>"$GITHUB_OUTPUT"
|
||||
node scripts/release-telegram-qa.mjs advisory-status
|
||||
|
||||
- name: Upload advisory status
|
||||
if: always()
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
#!/usr/bin/env node
|
||||
// Trusted helpers for the release Telegram workflow. Keep policy here so it can
|
||||
// be exercised directly rather than inferred from a workflow shell snippet.
|
||||
import { appendFileSync, mkdirSync, writeFileSync } from "node:fs";
|
||||
|
||||
const SHA = /^[a-f0-9]{40}$/u;
|
||||
const DIGEST = /^[a-f0-9]{64}$/u;
|
||||
const POSITIVE_ID = /^[1-9][0-9]*$/u;
|
||||
|
||||
function required(name) {
|
||||
const value = process.env[name];
|
||||
if (value === undefined) {
|
||||
throw new Error(`${name} is required`);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function appendOutput(lines) {
|
||||
appendFileSync(required("GITHUB_OUTPUT"), `${lines.join("\n")}\n`, "utf8");
|
||||
}
|
||||
|
||||
function advisoryStatus() {
|
||||
const env = process.env;
|
||||
const cancelled = [
|
||||
env.IDENTITY_RESULT,
|
||||
env.BUILD_RESULT,
|
||||
env.ATTESTATION_RESULT,
|
||||
env.RUN_RESULT,
|
||||
].includes("cancelled");
|
||||
const succeeded = [
|
||||
env.IDENTITY_STATUS,
|
||||
env.BUILD_STATUS,
|
||||
env.ATTESTATION_STATUS,
|
||||
env.EXECUTION_STATUS,
|
||||
].every((value) => value === "success");
|
||||
const status = cancelled ? "cancelled" : succeeded ? "success" : "failure";
|
||||
const runId = required("GITHUB_RUN_ID");
|
||||
const runAttempt = required("GITHUB_RUN_ATTEMPT");
|
||||
const targetSha = required("TARGET_SHA");
|
||||
const candidateArtifact =
|
||||
POSITIVE_ID.test(env.CANDIDATE_ARTIFACT_ID ?? "") &&
|
||||
DIGEST.test(env.CANDIDATE_ARTIFACT_DIGEST ?? "") &&
|
||||
env.ARCHIVE_NAME === `release-telegram-candidate-${runId}-${runAttempt}-${targetSha}.tar.zst` &&
|
||||
DIGEST.test(env.ARCHIVE_SHA256 ?? "") &&
|
||||
Boolean(env.CANDIDATE_VERSION)
|
||||
? {
|
||||
id: env.CANDIDATE_ARTIFACT_ID,
|
||||
name: env.ARCHIVE_NAME,
|
||||
digest: env.CANDIDATE_ARTIFACT_DIGEST,
|
||||
runId,
|
||||
runAttempt: Number(runAttempt),
|
||||
fileName: env.ARCHIVE_NAME,
|
||||
sha256: env.ARCHIVE_SHA256,
|
||||
sourceSha: targetSha,
|
||||
version: env.CANDIDATE_VERSION,
|
||||
}
|
||||
: null;
|
||||
if (
|
||||
status === "success" &&
|
||||
(!candidateArtifact ||
|
||||
!SHA.test(env.WORKFLOW_SHA ?? "") ||
|
||||
!SHA.test(targetSha) ||
|
||||
!POSITIVE_ID.test(env.EVIDENCE_ARTIFACT_ID ?? "") ||
|
||||
!DIGEST.test(env.EVIDENCE_ARTIFACT_DIGEST ?? "") ||
|
||||
env.EVIDENCE_ARTIFACT_NAME !== `release-qa-live-telegram-${runId}-${runAttempt}-${targetSha}`)
|
||||
) {
|
||||
throw new Error("Successful Telegram release status has incomplete evidence.");
|
||||
}
|
||||
const statusDir = ".artifacts/release-check-status";
|
||||
const fileStem = `qa_live_telegram_release_checks-${runId}-${runAttempt}`;
|
||||
const statusFile = `${statusDir}/${fileStem}.env`;
|
||||
const evidenceFile = `${statusDir}/${fileStem}.json`;
|
||||
const stepOutcomes = [
|
||||
`identity:${env.IDENTITY_STATUS ?? ""}`,
|
||||
`build:${env.BUILD_STATUS ?? ""}`,
|
||||
`attest:${env.ATTESTATION_STATUS ?? ""}`,
|
||||
`execute:${env.EXECUTION_STATUS ?? ""}`,
|
||||
];
|
||||
mkdirSync(statusDir, { recursive: true });
|
||||
writeFileSync(
|
||||
statusFile,
|
||||
[
|
||||
`run_id=${runId}`,
|
||||
`run_attempt=${runAttempt}`,
|
||||
`target_sha=${targetSha}`,
|
||||
`workflow_sha=${env.WORKFLOW_SHA ?? ""}`,
|
||||
"job=qa_live_telegram_release_checks",
|
||||
"variant=",
|
||||
`status=${status}`,
|
||||
`job_status=${env.RUN_RESULT ?? ""}`,
|
||||
`step_outcomes=${stepOutcomes.join(" ")}`,
|
||||
"",
|
||||
].join("\n"),
|
||||
"utf8",
|
||||
);
|
||||
writeFileSync(
|
||||
evidenceFile,
|
||||
`${JSON.stringify(
|
||||
{
|
||||
version: 1,
|
||||
kind: "release-check-status",
|
||||
job: "qa_live_telegram_release_checks",
|
||||
status,
|
||||
jobStatus: env.RUN_RESULT ?? "",
|
||||
stepOutcomes,
|
||||
runId,
|
||||
runAttempt: Number(runAttempt),
|
||||
workflowSha: env.WORKFLOW_SHA ?? "",
|
||||
targetSha,
|
||||
evidenceArtifact: {
|
||||
id: env.EVIDENCE_ARTIFACT_ID ?? "",
|
||||
name: env.EVIDENCE_ARTIFACT_NAME ?? "",
|
||||
digest: env.EVIDENCE_ARTIFACT_DIGEST ?? "",
|
||||
runId,
|
||||
runAttempt: Number(runAttempt),
|
||||
},
|
||||
candidateArtifact,
|
||||
},
|
||||
null,
|
||||
2,
|
||||
)}\n`,
|
||||
"utf8",
|
||||
);
|
||||
appendOutput([`status=${status}`, `status_file=${statusFile}`, `evidence_file=${evidenceFile}`]);
|
||||
}
|
||||
|
||||
const command = process.argv[2];
|
||||
try {
|
||||
if (command === "advisory-status") {
|
||||
advisoryStatus();
|
||||
} else {
|
||||
throw new Error(`Unknown release Telegram QA command: ${command ?? ""}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error instanceof Error ? error.message : String(error));
|
||||
process.exitCode = 1;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3417,17 +3417,9 @@ describe("package artifact reuse", () => {
|
||||
const telegramStatus = workflowJob(RELEASE_TELEGRAM_QA_WORKFLOW, "advisory_status");
|
||||
expect(telegramStatus["continue-on-error"]).toBeUndefined();
|
||||
const telegramRecord = workflowStep(telegramStatus, "Record advisory status");
|
||||
expectTextToIncludeAll(telegramRecord.run, [
|
||||
"run_id=",
|
||||
"run_attempt=",
|
||||
"target_sha=",
|
||||
"workflow_sha=",
|
||||
"job=",
|
||||
"variant=",
|
||||
"status=",
|
||||
"job_status=",
|
||||
"step_outcomes=",
|
||||
]);
|
||||
expect(telegramRecord.run?.trim()).toBe(
|
||||
"set -euo pipefail\nnode scripts/release-telegram-qa.mjs advisory-status",
|
||||
);
|
||||
const telegramStatusUpload = workflowStep(telegramStatus, "Upload advisory status");
|
||||
expect(telegramStatusUpload.if).toBe("always()");
|
||||
expect(telegramStatusUpload.uses).toBe(UPLOAD_ARTIFACT_V7);
|
||||
|
||||
Reference in New Issue
Block a user