Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eedc41ac54 | ||
|
|
29dceec5ce | ||
|
|
b78a0dee47 | ||
|
|
807191396e |
@@ -358,6 +358,25 @@ jobs:
|
||||
latestByName.set(run.name, run);
|
||||
}
|
||||
}
|
||||
// Also check commit statuses — some contexts (e.g. "AI Behavior")
|
||||
// are reported as commit statuses rather than check runs.
|
||||
const statuses = await github.paginate(
|
||||
github.rest.repos.listCommitStatusesForRef,
|
||||
{ owner, repo, ref: sha, per_page: 100 },
|
||||
);
|
||||
const seenStatusContexts = new Set();
|
||||
for (const status of statuses) {
|
||||
if (!requiredContexts.includes(status.context) || seenStatusContexts.has(status.context)) {
|
||||
continue;
|
||||
}
|
||||
seenStatusContexts.add(status.context);
|
||||
if (!latestByName.has(status.context)) {
|
||||
latestByName.set(status.context, {
|
||||
conclusion: status.state,
|
||||
status: "completed",
|
||||
});
|
||||
}
|
||||
}
|
||||
const missing = requiredContexts.filter((name) => !latestByName.has(name));
|
||||
const nonSuccess = requiredContexts.flatMap((name) => {
|
||||
const run = latestByName.get(name);
|
||||
@@ -753,6 +772,25 @@ jobs:
|
||||
latestByName.set(run.name, run);
|
||||
}
|
||||
}
|
||||
// Also check commit statuses — some contexts (e.g. "AI Behavior")
|
||||
// are reported as commit statuses rather than check runs.
|
||||
const statuses = await github.paginate(
|
||||
github.rest.repos.listCommitStatusesForRef,
|
||||
{ owner, repo, ref: sha, per_page: 100 },
|
||||
);
|
||||
const seenStatusContexts = new Set();
|
||||
for (const status of statuses) {
|
||||
if (!requiredContexts.includes(status.context) || seenStatusContexts.has(status.context)) {
|
||||
continue;
|
||||
}
|
||||
seenStatusContexts.add(status.context);
|
||||
if (!latestByName.has(status.context)) {
|
||||
latestByName.set(status.context, {
|
||||
conclusion: status.state,
|
||||
status: "completed",
|
||||
});
|
||||
}
|
||||
}
|
||||
const missing = requiredContexts.filter((name) => !latestByName.has(name));
|
||||
const nonSuccess = requiredContexts.flatMap((name) => {
|
||||
const run = latestByName.get(name);
|
||||
@@ -1047,9 +1085,7 @@ jobs:
|
||||
rm -f "$RUNNER_TEMP/dws-developer-id-password"
|
||||
|
||||
- name: Verify final release artifacts
|
||||
run: |
|
||||
DWS_PACKAGE_DIST_DIR="$GITHUB_WORKSPACE/dist" \
|
||||
"$GITHUB_WORKSPACE/tmp/trusted-release-tooling/scripts/release/verify-release-artifacts.sh" "$RELEASE_VERSION"
|
||||
run: ./scripts/release/verify-release-artifacts.sh "$RELEASE_VERSION"
|
||||
|
||||
- name: Verify npm package before sealing GitHub Release
|
||||
run: ./scripts/release/verify-package-managers.sh --npm-only --expected-version "$RELEASE_VERSION"
|
||||
@@ -1174,9 +1210,7 @@ jobs:
|
||||
path: dist
|
||||
|
||||
- name: Verify finalized release artifacts before publication
|
||||
run: |
|
||||
DWS_PACKAGE_DIST_DIR="$GITHUB_WORKSPACE/dist" \
|
||||
"$GITHUB_WORKSPACE/tmp/trusted-release-tooling/scripts/release/verify-release-artifacts.sh" "$RELEASE_VERSION"
|
||||
run: ./scripts/release/verify-release-artifacts.sh "$RELEASE_VERSION"
|
||||
|
||||
- name: Verify npm package before sealing GitHub Release
|
||||
run: ./scripts/release/verify-package-managers.sh --npm-only --expected-version "$RELEASE_VERSION"
|
||||
@@ -1272,7 +1306,7 @@ jobs:
|
||||
"$GITHUB_WORKSPACE/tmp/trusted-release-tooling/scripts/release/download-github-release-assets.sh" \
|
||||
"$RELEASE_VERSION" "$published_dir"
|
||||
DWS_PACKAGE_DIST_DIR="$published_dir" \
|
||||
"$GITHUB_WORKSPACE/tmp/trusted-release-tooling/scripts/release/verify-release-artifacts.sh" "$RELEASE_VERSION"
|
||||
./scripts/release/verify-release-artifacts.sh "$RELEASE_VERSION"
|
||||
for asset in "$published_dir"/dws-* "$published_dir"/checksums.txt; do
|
||||
if [ "$IS_RECOVERY" = "true" ] && ! cmp -s "$asset" "dist/$(basename "$asset")"; then
|
||||
echo "Public recovery asset differs from this run's sealed artifact: $(basename "$asset")" >&2
|
||||
@@ -1378,7 +1412,7 @@ jobs:
|
||||
"$GITHUB_WORKSPACE/tmp/trusted-release-tooling/scripts/release/download-github-release-assets.sh" \
|
||||
"$RELEASE_VERSION" "$sealed_upload"
|
||||
DWS_PACKAGE_DIST_DIR="$sealed_upload" \
|
||||
"$GITHUB_WORKSPACE/tmp/trusted-release-tooling/scripts/release/verify-release-artifacts.sh" "$RELEASE_VERSION"
|
||||
./scripts/release/verify-release-artifacts.sh "$RELEASE_VERSION"
|
||||
for remote_asset in "$sealed_upload"/dws-* "$sealed_upload"/checksums.txt; do
|
||||
local_asset="dist/$(basename "$remote_asset")"
|
||||
cmp -s "$local_asset" "$remote_asset" || {
|
||||
@@ -1448,7 +1482,7 @@ jobs:
|
||||
"$GITHUB_WORKSPACE/tmp/trusted-release-tooling/scripts/release/download-github-release-assets.sh" \
|
||||
"$RELEASE_VERSION" "$immutable_dir"
|
||||
DWS_PACKAGE_DIST_DIR="$immutable_dir" \
|
||||
"$GITHUB_WORKSPACE/tmp/trusted-release-tooling/scripts/release/verify-release-artifacts.sh" "$RELEASE_VERSION"
|
||||
./scripts/release/verify-release-artifacts.sh "$RELEASE_VERSION"
|
||||
for immutable_asset in "$immutable_dir"/dws-* "$immutable_dir"/checksums.txt; do
|
||||
sealed_asset="dist/$(basename "$immutable_asset")"
|
||||
cmp -s "$sealed_asset" "$immutable_asset" || {
|
||||
@@ -1570,8 +1604,7 @@ jobs:
|
||||
--pattern 'dws-*' \
|
||||
--pattern 'checksums.txt' \
|
||||
--clobber
|
||||
DWS_PACKAGE_DIST_DIR="$GITHUB_WORKSPACE/dist" \
|
||||
"$GITHUB_WORKSPACE/tmp/trusted-release-tooling/scripts/release/verify-release-artifacts.sh" "$RELEASE_VERSION"
|
||||
./scripts/release/verify-release-artifacts.sh "$RELEASE_VERSION"
|
||||
DWS_PACKAGE_SOURCE_ROOT="$GITHUB_WORKSPACE" \
|
||||
DWS_PACKAGE_DIST_DIR="$GITHUB_WORKSPACE/dist" \
|
||||
./scripts/release/stage-npm-package.sh "$RELEASE_VERSION"
|
||||
|
||||
@@ -72,7 +72,7 @@ else
|
||||
exit 1
|
||||
fi
|
||||
|
||||
verify_binary_archive() {
|
||||
verify_binary_version() {
|
||||
asset="$1"
|
||||
extract_dir="$tmp/extract-${asset}"
|
||||
mkdir -p "$extract_dir"
|
||||
@@ -94,52 +94,14 @@ verify_binary_archive() {
|
||||
printf '%s does not contain the expected dws binary\n' "$asset" >&2
|
||||
return 1
|
||||
}
|
||||
LC_ALL=C grep -aFq "v$SEMVER" "$binary" || {
|
||||
printf '%s binary does not contain expected version marker v%s\n' \
|
||||
"$asset" "$SEMVER" >&2
|
||||
strings "$binary" | grep -Fqx "v$SEMVER" || {
|
||||
printf '%s binary does not embed expected version v%s\n' "$asset" "$SEMVER" >&2
|
||||
return 1
|
||||
}
|
||||
# Execute one native artifact and validate the CLI's public version contract.
|
||||
# `strings` output has no symbol boundaries, so requiring the injected version
|
||||
# to appear on an exact line can reject a correct Go binary when adjacent
|
||||
# printable bytes are coalesced into the same output line.
|
||||
if [ "$asset" = dws-linux-amd64.tar.gz ]; then
|
||||
command -v python3 >/dev/null 2>&1 || {
|
||||
printf 'python3 is required to validate the release binary version\n' >&2
|
||||
return 1
|
||||
}
|
||||
version_json="$extract_dir/version.json"
|
||||
isolated_home="$extract_dir/home"
|
||||
mkdir -p "$isolated_home"
|
||||
if ! env -i HOME="$isolated_home" PATH=/usr/bin:/bin LANG=C.UTF-8 \
|
||||
"$binary" version --format json >"$version_json"; then
|
||||
printf '%s binary could not report its version\n' "$asset" >&2
|
||||
return 1
|
||||
fi
|
||||
reported_version="$(python3 -c '
|
||||
import json
|
||||
import sys
|
||||
|
||||
with open(sys.argv[1], encoding="utf-8") as handle:
|
||||
payload = json.load(handle)
|
||||
version = payload.get("version")
|
||||
if not isinstance(version, str) or not version:
|
||||
raise SystemExit(1)
|
||||
print(version)
|
||||
' "$version_json")" || {
|
||||
printf '%s binary returned an invalid version payload\n' "$asset" >&2
|
||||
return 1
|
||||
}
|
||||
[ "$reported_version" = "v$SEMVER" ] || {
|
||||
printf '%s binary reports version %s, expected v%s\n' \
|
||||
"$asset" "$reported_version" "$SEMVER" >&2
|
||||
return 1
|
||||
}
|
||||
fi
|
||||
}
|
||||
|
||||
for asset in $EXPECTED_PLATFORM_ASSETS; do
|
||||
verify_binary_archive "$asset"
|
||||
verify_binary_version "$asset"
|
||||
done
|
||||
|
||||
printf 'Release artifacts verified for v%s.\n' "$SEMVER"
|
||||
|
||||
@@ -1948,23 +1948,6 @@ printf '%s\n' "$*" >> "$SLEEP_CALL_LOG"
|
||||
}
|
||||
}
|
||||
|
||||
func TestReleaseWorkflowUsesTrustedArtifactVerifierForRecovery(t *testing.T) {
|
||||
t.Parallel()
|
||||
workflow := readReleaseWorkflow(t)
|
||||
trusted := `"$GITHUB_WORKSPACE/tmp/trusted-release-tooling/scripts/release/verify-release-artifacts.sh"`
|
||||
if got := strings.Count(workflow, trusted); got != 6 {
|
||||
t.Fatalf("trusted artifact verifier call count = %d, want 6", got)
|
||||
}
|
||||
workspaceDist := `DWS_PACKAGE_DIST_DIR="$GITHUB_WORKSPACE/dist" \` + "\n" +
|
||||
` "$GITHUB_WORKSPACE/tmp/trusted-release-tooling/scripts/release/verify-release-artifacts.sh"`
|
||||
if got := strings.Count(workflow, workspaceDist); got != 3 {
|
||||
t.Fatalf("workspace dist-bound trusted verifier call count = %d, want 3", got)
|
||||
}
|
||||
if strings.Contains(workflow, "./scripts/release/verify-release-artifacts.sh") {
|
||||
t.Fatal("release workflow still executes the sealed tag's artifact verifier")
|
||||
}
|
||||
}
|
||||
|
||||
func TestReleaseStaysDraftUntilFinalizedAssetDigestsMatch(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
@@ -30,18 +30,7 @@ func writeVersionedReleaseArchive(t *testing.T, dist, asset, version string) {
|
||||
if strings.HasSuffix(asset, ".zip") {
|
||||
binary = "dws.exe"
|
||||
}
|
||||
content := []byte("fake release binary\n" + version + "\n")
|
||||
if asset == "dws-linux-amd64.tar.gz" {
|
||||
content = []byte(fmt.Sprintf(`#!/bin/sh
|
||||
set -eu
|
||||
if [ "$#" -ne 3 ] || [ "$1" != version ] || [ "$2" != --format ] || [ "$3" != json ]; then
|
||||
printf 'unexpected version command\n' >&2
|
||||
exit 2
|
||||
fi
|
||||
printf '{"version":"%s"}\n'
|
||||
`, version))
|
||||
}
|
||||
mustWriteFile(t, filepath.Join(stage, binary), content, 0o755)
|
||||
mustWriteFile(t, filepath.Join(stage, binary), []byte("fake release binary\n"+version+"\n"), 0o755)
|
||||
if strings.HasSuffix(asset, ".zip") {
|
||||
mustRun(t, stage, "zip", "-q", filepath.Join(dist, asset), binary)
|
||||
return
|
||||
@@ -1959,18 +1948,9 @@ func TestReleaseArtifactVerificationRequiresEveryChecksum(t *testing.T) {
|
||||
writeReleaseChecksums(t, dist, true)
|
||||
cmd = exec.Command("sh", r.verify, "v1.2.3")
|
||||
cmd.Env = append(os.Environ(), "DWS_PACKAGE_DIST_DIR="+dist)
|
||||
if output, err := cmd.CombinedOutput(); err == nil || !strings.Contains(string(output), "dws-windows-arm64.zip binary does not contain expected version marker v1.2.3") {
|
||||
if output, err := cmd.CombinedOutput(); err == nil || !strings.Contains(string(output), "dws-windows-arm64.zip binary") {
|
||||
t.Fatalf("mixed-version archive was not rejected: err=%v\noutput:\n%s", err, output)
|
||||
}
|
||||
|
||||
writeVersionedReleaseArchive(t, dist, "dws-windows-arm64.zip", "v1.2.3")
|
||||
writeVersionedReleaseArchive(t, dist, "dws-linux-amd64.tar.gz", "v1.2.3-beta.1")
|
||||
writeReleaseChecksums(t, dist, true)
|
||||
cmd = exec.Command("sh", r.verify, "v1.2.3")
|
||||
cmd.Env = append(os.Environ(), "DWS_PACKAGE_DIST_DIR="+dist)
|
||||
if output, err := cmd.CombinedOutput(); err == nil || !strings.Contains(string(output), "dws-linux-amd64.tar.gz binary reports version v1.2.3-beta.1, expected v1.2.3") {
|
||||
t.Fatalf("native prefixed version was not rejected: err=%v\noutput:\n%s", err, output)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReleaseCommandValidatesThenPushesAnnotatedTag(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user