Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eedc41ac54 | ||
|
|
29dceec5ce | ||
|
|
b78a0dee47 | ||
|
|
807191396e |
No files matched your search
@@ -358,6 +358,25 @@ jobs:
|
|||||||
latestByName.set(run.name, run);
|
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 missing = requiredContexts.filter((name) => !latestByName.has(name));
|
||||||
const nonSuccess = requiredContexts.flatMap((name) => {
|
const nonSuccess = requiredContexts.flatMap((name) => {
|
||||||
const run = latestByName.get(name);
|
const run = latestByName.get(name);
|
||||||
@@ -753,6 +772,25 @@ jobs:
|
|||||||
latestByName.set(run.name, run);
|
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 missing = requiredContexts.filter((name) => !latestByName.has(name));
|
||||||
const nonSuccess = requiredContexts.flatMap((name) => {
|
const nonSuccess = requiredContexts.flatMap((name) => {
|
||||||
const run = latestByName.get(name);
|
const run = latestByName.get(name);
|
||||||
@@ -1047,9 +1085,7 @@ jobs:
|
|||||||
rm -f "$RUNNER_TEMP/dws-developer-id-password"
|
rm -f "$RUNNER_TEMP/dws-developer-id-password"
|
||||||
|
|
||||||
- name: Verify final release artifacts
|
- name: Verify final release artifacts
|
||||||
run: |
|
run: ./scripts/release/verify-release-artifacts.sh "$RELEASE_VERSION"
|
||||||
DWS_PACKAGE_DIST_DIR="$GITHUB_WORKSPACE/dist" \
|
|
||||||
"$GITHUB_WORKSPACE/tmp/trusted-release-tooling/scripts/release/verify-release-artifacts.sh" "$RELEASE_VERSION"
|
|
||||||
|
|
||||||
- name: Verify npm package before sealing GitHub Release
|
- name: Verify npm package before sealing GitHub Release
|
||||||
run: ./scripts/release/verify-package-managers.sh --npm-only --expected-version "$RELEASE_VERSION"
|
run: ./scripts/release/verify-package-managers.sh --npm-only --expected-version "$RELEASE_VERSION"
|
||||||
@@ -1174,9 +1210,7 @@ jobs:
|
|||||||
path: dist
|
path: dist
|
||||||
|
|
||||||
- name: Verify finalized release artifacts before publication
|
- name: Verify finalized release artifacts before publication
|
||||||
run: |
|
run: ./scripts/release/verify-release-artifacts.sh "$RELEASE_VERSION"
|
||||||
DWS_PACKAGE_DIST_DIR="$GITHUB_WORKSPACE/dist" \
|
|
||||||
"$GITHUB_WORKSPACE/tmp/trusted-release-tooling/scripts/release/verify-release-artifacts.sh" "$RELEASE_VERSION"
|
|
||||||
|
|
||||||
- name: Verify npm package before sealing GitHub Release
|
- name: Verify npm package before sealing GitHub Release
|
||||||
run: ./scripts/release/verify-package-managers.sh --npm-only --expected-version "$RELEASE_VERSION"
|
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" \
|
"$GITHUB_WORKSPACE/tmp/trusted-release-tooling/scripts/release/download-github-release-assets.sh" \
|
||||||
"$RELEASE_VERSION" "$published_dir"
|
"$RELEASE_VERSION" "$published_dir"
|
||||||
DWS_PACKAGE_DIST_DIR="$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
|
for asset in "$published_dir"/dws-* "$published_dir"/checksums.txt; do
|
||||||
if [ "$IS_RECOVERY" = "true" ] && ! cmp -s "$asset" "dist/$(basename "$asset")"; then
|
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
|
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" \
|
"$GITHUB_WORKSPACE/tmp/trusted-release-tooling/scripts/release/download-github-release-assets.sh" \
|
||||||
"$RELEASE_VERSION" "$sealed_upload"
|
"$RELEASE_VERSION" "$sealed_upload"
|
||||||
DWS_PACKAGE_DIST_DIR="$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
|
for remote_asset in "$sealed_upload"/dws-* "$sealed_upload"/checksums.txt; do
|
||||||
local_asset="dist/$(basename "$remote_asset")"
|
local_asset="dist/$(basename "$remote_asset")"
|
||||||
cmp -s "$local_asset" "$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" \
|
"$GITHUB_WORKSPACE/tmp/trusted-release-tooling/scripts/release/download-github-release-assets.sh" \
|
||||||
"$RELEASE_VERSION" "$immutable_dir"
|
"$RELEASE_VERSION" "$immutable_dir"
|
||||||
DWS_PACKAGE_DIST_DIR="$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
|
for immutable_asset in "$immutable_dir"/dws-* "$immutable_dir"/checksums.txt; do
|
||||||
sealed_asset="dist/$(basename "$immutable_asset")"
|
sealed_asset="dist/$(basename "$immutable_asset")"
|
||||||
cmp -s "$sealed_asset" "$immutable_asset" || {
|
cmp -s "$sealed_asset" "$immutable_asset" || {
|
||||||
@@ -1570,8 +1604,7 @@ jobs:
|
|||||||
--pattern 'dws-*' \
|
--pattern 'dws-*' \
|
||||||
--pattern 'checksums.txt' \
|
--pattern 'checksums.txt' \
|
||||||
--clobber
|
--clobber
|
||||||
DWS_PACKAGE_DIST_DIR="$GITHUB_WORKSPACE/dist" \
|
./scripts/release/verify-release-artifacts.sh "$RELEASE_VERSION"
|
||||||
"$GITHUB_WORKSPACE/tmp/trusted-release-tooling/scripts/release/verify-release-artifacts.sh" "$RELEASE_VERSION"
|
|
||||||
DWS_PACKAGE_SOURCE_ROOT="$GITHUB_WORKSPACE" \
|
DWS_PACKAGE_SOURCE_ROOT="$GITHUB_WORKSPACE" \
|
||||||
DWS_PACKAGE_DIST_DIR="$GITHUB_WORKSPACE/dist" \
|
DWS_PACKAGE_DIST_DIR="$GITHUB_WORKSPACE/dist" \
|
||||||
./scripts/release/stage-npm-package.sh "$RELEASE_VERSION"
|
./scripts/release/stage-npm-package.sh "$RELEASE_VERSION"
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ else
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
verify_binary_archive() {
|
verify_binary_version() {
|
||||||
asset="$1"
|
asset="$1"
|
||||||
extract_dir="$tmp/extract-${asset}"
|
extract_dir="$tmp/extract-${asset}"
|
||||||
mkdir -p "$extract_dir"
|
mkdir -p "$extract_dir"
|
||||||
@@ -94,52 +94,14 @@ verify_binary_archive() {
|
|||||||
printf '%s does not contain the expected dws binary\n' "$asset" >&2
|
printf '%s does not contain the expected dws binary\n' "$asset" >&2
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
LC_ALL=C grep -aFq "v$SEMVER" "$binary" || {
|
strings "$binary" | grep -Fqx "v$SEMVER" || {
|
||||||
printf '%s binary does not contain expected version marker v%s\n' \
|
printf '%s binary does not embed expected version v%s\n' "$asset" "$SEMVER" >&2
|
||||||
"$asset" "$SEMVER" >&2
|
|
||||||
return 1
|
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
|
for asset in $EXPECTED_PLATFORM_ASSETS; do
|
||||||
verify_binary_archive "$asset"
|
verify_binary_version "$asset"
|
||||||
done
|
done
|
||||||
|
|
||||||
printf 'Release artifacts verified for v%s.\n' "$SEMVER"
|
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) {
|
func TestReleaseStaysDraftUntilFinalizedAssetDigestsMatch(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
|||||||
@@ -30,18 +30,7 @@ func writeVersionedReleaseArchive(t *testing.T, dist, asset, version string) {
|
|||||||
if strings.HasSuffix(asset, ".zip") {
|
if strings.HasSuffix(asset, ".zip") {
|
||||||
binary = "dws.exe"
|
binary = "dws.exe"
|
||||||
}
|
}
|
||||||
content := []byte("fake release binary\n" + version + "\n")
|
mustWriteFile(t, filepath.Join(stage, binary), []byte("fake release binary\n"+version+"\n"), 0o755)
|
||||||
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)
|
|
||||||
if strings.HasSuffix(asset, ".zip") {
|
if strings.HasSuffix(asset, ".zip") {
|
||||||
mustRun(t, stage, "zip", "-q", filepath.Join(dist, asset), binary)
|
mustRun(t, stage, "zip", "-q", filepath.Join(dist, asset), binary)
|
||||||
return
|
return
|
||||||
@@ -1959,18 +1948,9 @@ func TestReleaseArtifactVerificationRequiresEveryChecksum(t *testing.T) {
|
|||||||
writeReleaseChecksums(t, dist, true)
|
writeReleaseChecksums(t, dist, true)
|
||||||
cmd = exec.Command("sh", r.verify, "v1.2.3")
|
cmd = exec.Command("sh", r.verify, "v1.2.3")
|
||||||
cmd.Env = append(os.Environ(), "DWS_PACKAGE_DIST_DIR="+dist)
|
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)
|
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) {
|
func TestReleaseCommandValidatesThenPushesAnnotatedTag(t *testing.T) {
|
||||||
|
|||||||
Reference in new issue
Block a user