mirror of
https://github.com/openclaw/openclaw.git
synced 2026-10-03 01:29:56 +00:00
fix: Telegram QA rejects frozen candidates after release branch advances (#151039)
* fix: Telegram QA rejects frozen candidates after release branch advances * refactor(release): share frozen candidate context checks
This commit is contained in:
parent
d1b4d871a2
commit
f8703ed512
7 changed files with 164 additions and 189 deletions
59
.github/workflows/openclaw-release-checks.yml
vendored
59
.github/workflows/openclaw-release-checks.yml
vendored
|
|
@ -246,10 +246,8 @@ jobs:
|
|||
refs/tags/*) normalized_context_ref="${normalized_context_ref#refs/tags/}" ;;
|
||||
esac
|
||||
if [[ "$normalized_context_ref" =~ ^(release/[0-9]{4}\.([1-9]|1[0-2])\.[1-9][0-9]*(-[1-9][0-9]*)?|extended-stable/[0-9]{4}\.([1-9]|1[0-2])\.33)$ ]]; then
|
||||
context_kind=branch
|
||||
fetch_ref="refs/heads/${normalized_context_ref}"
|
||||
elif [[ "$normalized_context_ref" =~ ^v[0-9]{4}\.([1-9]|1[0-2])\.[1-9][0-9]*((-(alpha|beta)\.[1-9][0-9]*)|(-[1-9][0-9]*))?$ ]]; then
|
||||
context_kind=tag
|
||||
fetch_ref="refs/tags/${normalized_context_ref}"
|
||||
else
|
||||
echo "target_context_ref must be a canonical OpenClaw release branch or tag." >&2
|
||||
|
|
@ -259,52 +257,8 @@ jobs:
|
|||
echo "target_context_ref requires ref to be a full 40-character commit SHA." >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "normalized_ref=${normalized_context_ref}" >> "$GITHUB_OUTPUT"
|
||||
echo "context_kind=${context_kind}" >> "$GITHUB_OUTPUT"
|
||||
echo "fetch_ref=${fetch_ref}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Validate trusted QA tooling eligibility
|
||||
id: trusted_qa_tooling
|
||||
if: steps.trusted_qa_context.outputs.fetch_ref != ''
|
||||
env:
|
||||
CONTEXT_FETCH_REF: ${{ steps.trusted_qa_context.outputs.fetch_ref }}
|
||||
CONTEXT_KIND: ${{ steps.trusted_qa_context.outputs.context_kind }}
|
||||
CONTEXT_REF: ${{ steps.trusted_qa_context.outputs.normalized_ref }}
|
||||
TARGET_REF: ${{ inputs.ref }}
|
||||
TRUSTED_REPOSITORY_URL: https://github.com/${{ github.repository }}.git
|
||||
run: |
|
||||
set -euo pipefail
|
||||
context_repo="$(mktemp -d)"
|
||||
trap 'rm -rf -- "$context_repo"' EXIT
|
||||
git init --bare --quiet "$context_repo"
|
||||
if ! GIT_TERMINAL_PROMPT=0 git -C "$context_repo" fetch --quiet --no-tags --filter=blob:none \
|
||||
"$TRUSTED_REPOSITORY_URL" "$CONTEXT_FETCH_REF"; then
|
||||
echo "Failed to fetch trusted QA tooling context ${CONTEXT_REF}." >&2
|
||||
exit 1
|
||||
fi
|
||||
context_sha="$(git -C "$context_repo" rev-parse --verify 'FETCH_HEAD^{commit}')"
|
||||
normalized_context_sha="$(printf '%s' "$context_sha" | tr '[:upper:]' '[:lower:]')"
|
||||
normalized_target_sha="$(printf '%s' "$TARGET_REF" | tr '[:upper:]' '[:lower:]')"
|
||||
case "$CONTEXT_KIND" in
|
||||
tag)
|
||||
if [[ "$normalized_context_sha" != "$normalized_target_sha" ]]; then
|
||||
echo "Trusted QA tooling tag ${CONTEXT_REF} resolves to ${context_sha} and does not match target ${TARGET_REF}." >&2
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
branch)
|
||||
if ! git -C "$context_repo" merge-base --is-ancestor "$normalized_target_sha" "$normalized_context_sha" 2>/dev/null; then
|
||||
echo "Target ${TARGET_REF} is not reachable from branch ${CONTEXT_REF} at ${context_sha}." >&2
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
echo "Trusted QA tooling context kind is invalid: ${CONTEXT_KIND}." >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
echo "eligible=true" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Checkout trusted workflow helper
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
|
|
@ -313,6 +267,19 @@ jobs:
|
|||
path: workflow
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Validate trusted QA tooling eligibility
|
||||
id: trusted_qa_tooling
|
||||
if: steps.trusted_qa_context.outputs.fetch_ref != ''
|
||||
env:
|
||||
CONTEXT_FETCH_REF: ${{ steps.trusted_qa_context.outputs.fetch_ref }}
|
||||
TARGET_REF: ${{ inputs.ref }}
|
||||
TRUSTED_REPOSITORY_URL: https://github.com/${{ github.repository }}.git
|
||||
run: |
|
||||
set -euo pipefail
|
||||
bash "${GITHUB_WORKSPACE}/workflow/scripts/release-context-contains.sh" \
|
||||
"$TRUSTED_REPOSITORY_URL" "$CONTEXT_FETCH_REF" "$TARGET_REF" ancestor
|
||||
echo "eligible=true" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Setup admission Node.js
|
||||
env:
|
||||
REQUESTED_NODE_VERSION: "24.x"
|
||||
|
|
|
|||
|
|
@ -176,3 +176,9 @@ that selects Telegram, conflicts with the waiver and is rejected. The declaratio
|
|||
target version bind the immutable execution plan, manifest, and reuse identity;
|
||||
the publisher carries the waiver into release verification notes. The beta-only
|
||||
package deferral above remains unchanged.
|
||||
|
||||
Source Telegram QA uses the release checks' shared context check: an exact candidate
|
||||
SHA must remain an ancestor of its canonical branch, or equal its release tag.
|
||||
Both build and execution admission independently repeat that check and retain
|
||||
candidate-version, signature/merge-attribution, and live maintainer checks.
|
||||
Advancing a release branch does not select a new candidate or invalidate the old one.
|
||||
|
|
|
|||
33
scripts/release-context-contains.sh
Normal file
33
scripts/release-context-contains.sh
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Compare against one fetched snapshot, without executing candidate code.
|
||||
repository_url="$1"
|
||||
context_ref="$2"
|
||||
target_sha="$3"
|
||||
relationship="${4:-exact}"
|
||||
context_repo="$(mktemp -d)"
|
||||
trap 'rm -rf -- "$context_repo"' EXIT
|
||||
git init --bare --quiet "$context_repo"
|
||||
if ! GIT_TERMINAL_PROMPT=0 git -C "$context_repo" fetch --quiet --no-tags --filter=blob:none \
|
||||
"$repository_url" "$context_ref"; then
|
||||
echo "Failed to fetch trusted QA tooling context ${context_ref}." >&2
|
||||
exit 1
|
||||
fi
|
||||
context_sha="$(git -C "$context_repo" rev-parse --verify 'FETCH_HEAD^{commit}')"
|
||||
target_sha="$(printf '%s' "$target_sha" | tr '[:upper:]' '[:lower:]')"
|
||||
case "$context_ref:$relationship" in
|
||||
refs/heads/*:ancestor)
|
||||
if ! git -C "$context_repo" merge-base --is-ancestor "$target_sha" "$context_sha" 2>/dev/null; then
|
||||
echo "Target ${target_sha} is not reachable from branch ${context_ref} at ${context_sha}." >&2
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
refs/heads/*:exact|refs/tags/*:exact|refs/tags/*:ancestor)
|
||||
if [[ "$context_sha" != "$target_sha" ]]; then
|
||||
echo "Release context ${context_ref} resolves to ${context_sha} and does not match target ${target_sha}." >&2
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
*) exit 1 ;;
|
||||
esac
|
||||
|
|
@ -120,72 +120,51 @@ compare_status="$(
|
|||
)"
|
||||
trusted_reason=""
|
||||
trusted_release_branch=""
|
||||
if [[ -n "$context_release_branch" ]]; then
|
||||
branch_sha="$(
|
||||
git -C "$remote_git_dir" ls-remote --exit-code --refs origin \
|
||||
"refs/heads/${context_release_branch}" |
|
||||
awk 'NR == 1 { print $1 } END { if (NR != 1) exit 1 }' ||
|
||||
true
|
||||
)"
|
||||
if [[ "$branch_sha" == "$candidate_sha" ]]; then
|
||||
trusted_reason="release-branch-head"
|
||||
trusted_release_branch="$context_release_branch"
|
||||
release_ref="${context_release_branch:-$context_release_tag}"
|
||||
relationship=exact
|
||||
if [[ -n "$release_ref" ]]; then
|
||||
if [[ "$TARGET_REF" =~ ^[a-f0-9]{40}$ && "$TARGET_REF" == "$candidate_sha" ]]; then
|
||||
relationship=ancestor
|
||||
fi
|
||||
elif [[ -n "$context_release_tag" ]]; then
|
||||
tag_refs="$(
|
||||
git -C "$remote_git_dir" ls-remote --exit-code origin \
|
||||
"refs/tags/${context_release_tag}" "refs/tags/${context_release_tag}^{}"
|
||||
)"
|
||||
awk -v sha="$candidate_sha" '$1 == sha { found = 1 } END { exit(found ? 0 : 1) }' \
|
||||
<<<"$tag_refs"
|
||||
trusted_reason="release-tag"
|
||||
elif [[ "$compare_status" == "ahead" || "$compare_status" == "identical" ]]; then
|
||||
trusted_reason="main-ancestor"
|
||||
else
|
||||
normalized_ref="${TARGET_REF#refs/heads/}"
|
||||
if [[ "$normalized_ref" =~ ^(release/[0-9]{4}\.[1-9][0-9]*\.[1-9][0-9]*|extended-stable/[0-9]{4}\.[1-9][0-9]*\.33)$ ]]; then
|
||||
branch_sha="$(
|
||||
git -C "$remote_git_dir" ls-remote --exit-code --refs origin \
|
||||
"refs/heads/${normalized_ref}" |
|
||||
awk 'NR == 1 { print $1 } END { if (NR != 1) exit 1 }'
|
||||
)"
|
||||
[[ "$branch_sha" == "$candidate_sha" ]]
|
||||
trusted_reason="release-branch-head"
|
||||
trusted_release_branch="$normalized_ref"
|
||||
elif [[ "$TARGET_REF" =~ ^refs/tags/v ]] || [[ "$TARGET_REF" =~ ^v ]]; then
|
||||
normalized_tag="${TARGET_REF#refs/tags/}"
|
||||
tag_refs="$(
|
||||
git -C "$remote_git_dir" ls-remote --exit-code origin \
|
||||
"refs/tags/${normalized_tag}" "refs/tags/${normalized_tag}^{}"
|
||||
)"
|
||||
awk -v sha="$candidate_sha" '$1 == sha { found = 1 } END { exit(found ? 0 : 1) }' \
|
||||
<<<"$tag_refs"
|
||||
trusted_reason="release-tag"
|
||||
elif [[ "$TARGET_REF" =~ ^[a-f0-9]{40}$ && "$TARGET_REF" == "$candidate_sha" ]]; then
|
||||
matching_release_branches="$(
|
||||
release_ref="${TARGET_REF#refs/heads/}"
|
||||
release_ref="${release_ref#refs/tags/}"
|
||||
if [[ "$TARGET_REF" =~ ^[a-f0-9]{40}$ && "$TARGET_REF" == "$candidate_sha" ]]; then
|
||||
release_ref="$(
|
||||
gh_with_retry api --paginate \
|
||||
"repos/${GITHUB_REPOSITORY}/commits/${candidate_sha}/branches-where-head" \
|
||||
--jq '.[].name' |
|
||||
awk '$0 ~ /^release\/[0-9]{4}\.[1-9][0-9]*\.[1-9][0-9]*$/ ||
|
||||
$0 ~ /^extended-stable\/[0-9]{4}\.[1-9][0-9]*\.33$/ { print }'
|
||||
$0 ~ /^extended-stable\/[0-9]{4}\.[1-9][0-9]*\.33$/ { refs[++n] = $0 }
|
||||
END { if (n == 1) print refs[1] }'
|
||||
)"
|
||||
if [[ "$(wc -l <<<"$matching_release_branches" | tr -d ' ')" == "1" &&
|
||||
-n "$matching_release_branches" ]]; then
|
||||
trusted_reason="release-branch-head"
|
||||
trusted_release_branch="$matching_release_branches"
|
||||
else
|
||||
matching_release_tags="$(
|
||||
if [[ -z "$release_ref" ]]; then
|
||||
release_ref="$(
|
||||
git -C "$remote_git_dir" ls-remote origin 'refs/tags/v*' |
|
||||
awk -v sha="$candidate_sha" '$1 == sha { sub(/\^\{\}$/, "", $2); print $2 }' |
|
||||
sort -u
|
||||
sort -u | head -n 1
|
||||
)"
|
||||
if [[ -n "$matching_release_tags" ]]; then
|
||||
trusted_reason="release-tag"
|
||||
fi
|
||||
release_ref="${release_ref#refs/tags/}"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
fetch_ref=""
|
||||
if [[ "$release_ref" =~ ^(release/[0-9]{4}\.[1-9][0-9]*\.[1-9][0-9]*|extended-stable/[0-9]{4}\.[1-9][0-9]*\.33)$ ]]; then
|
||||
fetch_ref="refs/heads/${release_ref}"
|
||||
reason=release-branch
|
||||
elif [[ "$release_ref" == v* ]]; then
|
||||
fetch_ref="refs/tags/${release_ref}"
|
||||
reason=release-tag
|
||||
fi
|
||||
if [[ -n "$fetch_ref" ]] && bash "${GITHUB_WORKSPACE}/scripts/release-context-contains.sh" \
|
||||
"https://github.com/${GITHUB_REPOSITORY}.git" "$fetch_ref" "$candidate_sha" "$relationship" 2>/dev/null; then
|
||||
trusted_reason="$reason"
|
||||
trusted_release_branch="$release_ref"
|
||||
fi
|
||||
|
||||
if [[ -z "$trusted_reason" && -n "$frozen_release_branch_pattern" &&
|
||||
"$TARGET_REF" =~ ^[a-f0-9]{40}$ && "$TARGET_REF" == "$candidate_sha" ]]; then
|
||||
matching_frozen_release_branches="$(
|
||||
|
|
@ -231,8 +210,8 @@ if [[ "$trusted_reason" != "main-ancestor" ]]; then
|
|||
fi
|
||||
permission_actor="$signer"
|
||||
if [[ "$signature_status" == "missing" || "$signer" == "web-flow" ]]; then
|
||||
if [[ "$trusted_reason" != "release-branch-head" || -z "$trusted_release_branch" ]]; then
|
||||
echo "Unsigned or GitHub web-flow candidates require an exact release branch head." >&2
|
||||
if [[ "$trusted_reason" != "release-branch" || -z "$trusted_release_branch" ]]; then
|
||||
echo "Unsigned or GitHub web-flow candidates require canonical release branch provenance." >&2
|
||||
exit 1
|
||||
fi
|
||||
merge_pr_candidates="$(jq -c '.data.repository.object.associatedPullRequests.nodes' <<<"$candidate_metadata_json")"
|
||||
|
|
|
|||
|
|
@ -18901,7 +18901,6 @@ fi
|
|||
const fullReleaseWorkflow = readWorkflow(".github/workflows/full-release-validation.yml");
|
||||
const releaseWorkflow = readReleaseChecksWorkflow();
|
||||
const telegramWorkflow = readWorkflow(".github/workflows/openclaw-release-telegram-qa.yml");
|
||||
const telegramProvenanceHelper = readFileSync("scripts/release-telegram-provenance.sh", "utf8");
|
||||
const fullReleaseDispatchStep = fullReleaseWorkflow.jobs.release_checks_candidate.steps.find(
|
||||
(step: WorkflowStep) => step.name === "Dispatch release checks candidate phase",
|
||||
);
|
||||
|
|
@ -18957,44 +18956,6 @@ fi
|
|||
'bash "${GITHUB_WORKSPACE}/scripts/release-telegram-provenance.sh"',
|
||||
);
|
||||
}
|
||||
expect(telegramProvenanceHelper).toContain(
|
||||
'if [[ "$candidate_version" == "$release_version" ]]; then',
|
||||
);
|
||||
expect(telegramProvenanceHelper).toContain(
|
||||
'elif [[ "$candidate_version" =~ ^${release_version_pattern}-beta\\.[0-9]+$ ]]; then',
|
||||
);
|
||||
expect(telegramProvenanceHelper).toContain(
|
||||
'frozen_release_branch_pattern="^release/${candidate_version_pattern}-code-frozen(-r[1-9][0-9]*)?$"',
|
||||
);
|
||||
expect(telegramProvenanceHelper).toContain(
|
||||
'"$TARGET_REF" =~ ^[a-f0-9]{40}$ && "$TARGET_REF" == "$candidate_sha"',
|
||||
);
|
||||
expect(telegramProvenanceHelper).toContain('trusted_reason="frozen-release-branch-head"');
|
||||
expect(telegramProvenanceHelper).toContain(
|
||||
'"$signature_status" != "valid" || "$signer" == "web-flow"',
|
||||
);
|
||||
expect(telegramProvenanceHelper).toContain('context_release_branch="$normalized_context_ref"');
|
||||
expect(telegramProvenanceHelper).toContain('context_release_tag="$normalized_context_ref"');
|
||||
expect(telegramProvenanceHelper).toContain(
|
||||
"Telegram candidate version ${candidate_version} does not belong to release ${release_version}.",
|
||||
);
|
||||
expect(telegramProvenanceHelper).toContain(
|
||||
"Telegram candidate version ${candidate_version} does not match context ${normalized_context_ref}.",
|
||||
);
|
||||
expect(telegramProvenanceHelper).toContain(
|
||||
'select(.state == "OPEN" and .headRepository.nameWithOwner == $repo and',
|
||||
);
|
||||
expect(telegramProvenanceHelper).toContain(
|
||||
'select(.state == "MERGED" and .baseRepository.nameWithOwner == $repo and',
|
||||
);
|
||||
expect(telegramProvenanceHelper).toContain(".mergeCommit.oid == $sha)]");
|
||||
expect(telegramProvenanceHelper).toContain(
|
||||
'if [[ "$(jq \'length\' <<<"$matching_merge_prs")" != "1" ]]; then',
|
||||
);
|
||||
expect(telegramProvenanceHelper).toContain(
|
||||
'if [[ "$permission" != "admin" && "$role_name" != "maintain" ]]; then',
|
||||
);
|
||||
expect(telegramProvenanceHelper).not.toContain(".baseRefName ==");
|
||||
});
|
||||
|
||||
it("checks out the complete trusted Release Decision scripts tree", () => {
|
||||
|
|
|
|||
|
|
@ -226,6 +226,7 @@ function runCandidateProvenance(
|
|||
provenanceBlock: ProvenanceBlock,
|
||||
params: {
|
||||
branchHeads?: string[];
|
||||
releaseCompareStatus?: "ahead" | "behind" | "diverged" | "identical";
|
||||
candidateVersion?: string;
|
||||
messageHeadline?: string;
|
||||
directPullRequest?: {
|
||||
|
|
@ -326,6 +327,11 @@ exit 64
|
|||
join(fakeBin, "git"),
|
||||
`#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
if [[ "$*" == *"init --bare"* || "$*" == *"fetch --quiet"* ]]; then [[ -n "$FAKE_REMOTE_SHA" ]]; exit; fi
|
||||
if [[ "$*" == *"FETCH_HEAD^{commit}"* ]]; then printf '%s\\n' "$FAKE_REMOTE_SHA"; exit 0; fi
|
||||
if [[ "$*" == *"merge-base --is-ancestor"* ]]; then
|
||||
[[ "$TARGET_SHA" == "$FAKE_REMOTE_SHA" || "$FAKE_RELEASE_COMPARE_STATUS" == ahead ]]; exit
|
||||
fi
|
||||
if [[ "$*" == *"rev-parse HEAD"* ]]; then printf '%s\\n' "$TARGET_SHA"; exit 0; fi
|
||||
if [[ "$*" == *"ls-remote"* ]]; then
|
||||
if [[ "$*" == *"refs/tags/"* && "$FAKE_REMOTE_REF" != refs/tags/* ]]; then exit 0; fi
|
||||
|
|
@ -363,6 +369,7 @@ exit 64
|
|||
permission: params.permission === "admin" ? "admin" : "write",
|
||||
role_name: params.permission ?? "maintain",
|
||||
}),
|
||||
FAKE_RELEASE_COMPARE_STATUS: params.releaseCompareStatus ?? "diverged",
|
||||
FAKE_REMOTE_REF: remoteRef,
|
||||
FAKE_REMOTE_SHA: params.remoteSha ?? candidateSha,
|
||||
CANDIDATE_GIT_DIR:
|
||||
|
|
@ -575,6 +582,56 @@ describe("release Telegram QA workflow", () => {
|
|||
}
|
||||
});
|
||||
|
||||
it("keeps exact release candidates trusted when the canonical branch advances", () => {
|
||||
for (const provenanceBlock of PROVENANCE_BLOCKS) {
|
||||
for (const targetContextRef of ["release/2026.7.35", "extended-stable/2026.7.33"]) {
|
||||
for (const signature of ["maintainer", "web-flow", "missing"] as const) {
|
||||
const result = runCandidateProvenance(provenanceBlock, {
|
||||
candidateVersion: "2026.7.35",
|
||||
remoteSha: "b".repeat(40),
|
||||
releaseCompareStatus: "ahead",
|
||||
targetContextRef,
|
||||
signature,
|
||||
mergedPullRequests: [{ baseRefName: targetContextRef }],
|
||||
});
|
||||
expect(result.status, `${provenanceBlock.stepName}/${signature}: ${result.stderr}`).toBe(
|
||||
0,
|
||||
);
|
||||
expect(result.stdout).toContain("Telegram candidate trust reason: release-branch");
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it("rejects untrusted or uncontained candidates after the release branch advances", () => {
|
||||
const cases = [
|
||||
{ releaseCompareStatus: "behind" as const },
|
||||
{ releaseCompareStatus: "diverged" as const },
|
||||
{ targetRef: "extended-stable/2026.7.33" },
|
||||
{ remoteSha: "" },
|
||||
{ openPr: true },
|
||||
{ signature: "invalid" as const },
|
||||
{ permission: "write" as const },
|
||||
{ mergedPullRequests: [] },
|
||||
{ mergedPullRequests: [{ mergeCommitOid: "c".repeat(40) }] },
|
||||
{ mergedPullRequests: [{ baseRepository: "fork/openclaw" }] },
|
||||
];
|
||||
for (const provenanceBlock of PROVENANCE_BLOCKS) {
|
||||
for (const params of cases) {
|
||||
const result = runCandidateProvenance(provenanceBlock, {
|
||||
candidateVersion: "2026.7.35",
|
||||
remoteSha: "b".repeat(40),
|
||||
releaseCompareStatus: "ahead",
|
||||
targetContextRef: "extended-stable/2026.7.33",
|
||||
signature: "web-flow",
|
||||
mergedPullRequests: [{ baseRefName: "extended-stable/2026.7.33" }],
|
||||
...params,
|
||||
});
|
||||
expect(result.status, `${provenanceBlock.stepName}: ${JSON.stringify(params)}`).not.toBe(0);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it("accepts only strict signed frozen beta branch heads in both provenance blocks", () => {
|
||||
for (const provenanceBlock of PROVENANCE_BLOCKS) {
|
||||
const frozen = runCandidateProvenance(provenanceBlock, {
|
||||
|
|
@ -669,51 +726,21 @@ describe("release Telegram QA workflow", () => {
|
|||
});
|
||||
|
||||
it("attributes web-flow release heads through a unique integration-base merge", () => {
|
||||
const results = PROVENANCE_BLOCKS.flatMap((provenanceBlock) =>
|
||||
["2026.7.1", "2026.7.1-beta.3"].map((candidateVersion) => ({
|
||||
candidateVersion,
|
||||
provenanceBlock,
|
||||
result: runCandidateProvenance(provenanceBlock, {
|
||||
for (const provenanceBlock of PROVENANCE_BLOCKS) {
|
||||
for (const candidateVersion of ["2026.7.1", "2026.7.1-beta.3"]) {
|
||||
const result = runCandidateProvenance(provenanceBlock, {
|
||||
candidateVersion,
|
||||
mergedPullRequests: [{ baseRefName: "release-integration/2026.7.1-repair-2" }],
|
||||
signature: "web-flow",
|
||||
targetContextRef: "release/2026.7.1",
|
||||
}),
|
||||
})),
|
||||
);
|
||||
expect(
|
||||
results.map(({ candidateVersion, provenanceBlock, result }) => ({
|
||||
block: provenanceBlock.stepName,
|
||||
candidateVersion,
|
||||
status: result.status,
|
||||
stderr: result.stderr,
|
||||
})),
|
||||
).toEqual([
|
||||
{
|
||||
block: "Validate candidate release provenance",
|
||||
candidateVersion: "2026.7.1",
|
||||
status: 0,
|
||||
stderr: "",
|
||||
},
|
||||
{
|
||||
block: "Validate candidate release provenance",
|
||||
candidateVersion: "2026.7.1-beta.3",
|
||||
status: 0,
|
||||
stderr: "",
|
||||
},
|
||||
{
|
||||
block: "Revalidate candidate release provenance",
|
||||
candidateVersion: "2026.7.1",
|
||||
status: 0,
|
||||
stderr: "",
|
||||
},
|
||||
{
|
||||
block: "Revalidate candidate release provenance",
|
||||
candidateVersion: "2026.7.1-beta.3",
|
||||
status: 0,
|
||||
stderr: "",
|
||||
},
|
||||
]);
|
||||
});
|
||||
expect(
|
||||
result.status,
|
||||
`${provenanceBlock.stepName}/${candidateVersion}: ${result.stderr}`,
|
||||
).toBe(0);
|
||||
expect(result.stderr).toBe("");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it("verifies an exact merged PR directly when commit associations are missing", () => {
|
||||
|
|
@ -727,7 +754,7 @@ describe("release Telegram QA workflow", () => {
|
|||
directPullRequest: {},
|
||||
});
|
||||
expect(result.status, result.stderr).toBe(0);
|
||||
expect(result.stdout).toContain("Telegram candidate trust reason: release-branch-head");
|
||||
expect(result.stdout).toContain("Telegram candidate trust reason: release-branch");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -3119,6 +3119,11 @@ function runReleaseChecksShellStep(
|
|||
workdir = tempDirs.make("release-checks-shell-step-"),
|
||||
) {
|
||||
const step = workflowStep(workflowJob(RELEASE_CHECKS_WORKFLOW, "resolve_target"), stepName);
|
||||
mkdirSync(join(workdir, "workflow", "scripts"), { recursive: true });
|
||||
copyFileSync(
|
||||
"scripts/release-context-contains.sh",
|
||||
join(workdir, "workflow", "scripts", "release-context-contains.sh"),
|
||||
);
|
||||
const outputPath = resolve(workdir, "github-output");
|
||||
writeFileSync(outputPath, "", "utf8");
|
||||
const result = spawnSync("bash", ["-c", step.run ?? ""], {
|
||||
|
|
@ -3126,6 +3131,7 @@ function runReleaseChecksShellStep(
|
|||
encoding: "utf8",
|
||||
env: {
|
||||
...env,
|
||||
GITHUB_WORKSPACE: workdir,
|
||||
GITHUB_OUTPUT: outputPath,
|
||||
PATH: process.env.PATH,
|
||||
},
|
||||
|
|
@ -12024,12 +12030,9 @@ printf '%s\\n' "$DEEPSEEK_API_KEY" "$DEEPINFRA_API_KEY"`,
|
|||
expect(eligibility.env?.TRUSTED_REPOSITORY_URL).toBe(
|
||||
"https://github.com/${{ github.repository }}.git",
|
||||
);
|
||||
expect(eligibility.run).toContain('context_repo="$(mktemp -d)"');
|
||||
expect(eligibility.run).toContain("git init --bare --quiet");
|
||||
expect(eligibility.run).toContain("--filter=blob:none");
|
||||
expect(eligibility.run).toContain("FETCH_HEAD^{commit}");
|
||||
expect(eligibility.run).not.toContain("git checkout");
|
||||
expect(eligibility.run).not.toContain("git worktree");
|
||||
expect(resolveStepNames.indexOf("Checkout trusted workflow helper")).toBeLessThan(
|
||||
resolveStepNames.indexOf("Validate trusted QA tooling eligibility"),
|
||||
);
|
||||
|
||||
for (const contextRef of [
|
||||
"release/2026.8.1",
|
||||
|
|
@ -12045,8 +12048,9 @@ printf '%s\\n' "$DEEPSEEK_API_KEY" "$DEEPINFRA_API_KEY"`,
|
|||
TARGET_REF: targetSha,
|
||||
});
|
||||
expect(result.status, `${contextRef}: ${result.stderr}`).toBe(0);
|
||||
expect(output, contextRef).toContain(
|
||||
`normalized_ref=${contextRef.replace(/^refs\/(heads|tags)\//u, "")}\n`,
|
||||
const normalizedRef = contextRef.replace(/^refs\/(heads|tags)\//u, "");
|
||||
expect(output, contextRef).toBe(
|
||||
`fetch_ref=refs/${normalizedRef.startsWith("v") ? "tags" : "heads"}/${normalizedRef}\n`,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -12103,9 +12107,7 @@ printf '%s\\n' "$DEEPSEEK_API_KEY" "$DEEPINFRA_API_KEY"`,
|
|||
const { output, result } = runReleaseChecksShellStep(
|
||||
"Validate trusted QA tooling eligibility",
|
||||
{
|
||||
CONTEXT_KIND: contextKind,
|
||||
CONTEXT_FETCH_REF: fetchRef,
|
||||
CONTEXT_REF: fetchRef.replace(/^refs\/(heads|tags)\//u, ""),
|
||||
TARGET_REF: targetRef,
|
||||
TRUSTED_REPOSITORY_URL: fixture.repoUrl,
|
||||
},
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue