Skip to content

Commit 97b1da2

Browse files
committed
Merge remote-tracking branch 'origin/main' into fix/macos-transport-security
2 parents 8da666c + cf3fc58 commit 97b1da2

16 files changed

Lines changed: 874 additions & 33 deletions

File tree

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
#!/bin/sh
2+
set -eu
3+
4+
if [ "$#" -ne 2 ]; then
5+
echo "usage: release-checksums.sh VERSION DIST_DIRECTORY" >&2
6+
exit 64
7+
fi
8+
9+
VERSION="$1"
10+
DIST_DIRECTORY="$2"
11+
12+
case "$VERSION" in
13+
""|[!0-9A-Za-z]*|*[!0-9A-Za-z.-]*|*..*|*.)
14+
echo "release checksums: invalid version: $VERSION" >&2
15+
exit 64
16+
;;
17+
esac
18+
if [ "${#VERSION}" -gt 64 ]; then
19+
echo "release checksums: version is too long" >&2
20+
exit 64
21+
fi
22+
if [ ! -d "$DIST_DIRECTORY" ]; then
23+
echo "release checksums: dist directory does not exist: $DIST_DIRECTORY" >&2
24+
exit 66
25+
fi
26+
27+
DIST_DIRECTORY="$(CDPATH='' cd -- "$DIST_DIRECTORY" && pwd -P)"
28+
set -- \
29+
"Headless-${VERSION}-macos.zip" \
30+
"headless-${VERSION}-linux-amd64.tar.gz" \
31+
"headless-${VERSION}-linux-arm64.tar.gz"
32+
33+
for asset in "$@"; do
34+
path="$DIST_DIRECTORY/$asset"
35+
if [ ! -f "$path" ] || [ -L "$path" ] || [ ! -s "$path" ]; then
36+
echo "release checksums: missing or unsafe release asset: $asset" >&2
37+
exit 66
38+
fi
39+
done
40+
41+
checksum_files() {
42+
if command -v sha256sum >/dev/null 2>&1; then
43+
sha256sum "$@"
44+
elif command -v shasum >/dev/null 2>&1; then
45+
shasum -a 256 "$@"
46+
else
47+
echo "release checksums: sha256sum or shasum is required" >&2
48+
exit 69
49+
fi
50+
}
51+
52+
verify_manifest() {
53+
if command -v sha256sum >/dev/null 2>&1; then
54+
sha256sum -c SHA256SUMS
55+
else
56+
shasum -a 256 -c SHA256SUMS
57+
fi
58+
}
59+
60+
MANIFEST="$DIST_DIRECTORY/SHA256SUMS"
61+
TEMP_MANIFEST="$(mktemp "$DIST_DIRECTORY/.SHA256SUMS.XXXXXX")"
62+
trap '[ -z "$TEMP_MANIFEST" ] || rm -f "$TEMP_MANIFEST"' EXIT INT TERM
63+
64+
(
65+
cd "$DIST_DIRECTORY"
66+
checksum_files "$@"
67+
) > "$TEMP_MANIFEST"
68+
chmod 0644 "$TEMP_MANIFEST"
69+
mv -f "$TEMP_MANIFEST" "$MANIFEST"
70+
TEMP_MANIFEST=""
71+
72+
(
73+
cd "$DIST_DIRECTORY"
74+
verify_manifest
75+
)

.github/workflows/release.yml

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,22 @@ on:
44
push:
55
tags:
66
- "v*"
7+
pull_request:
8+
paths:
9+
- ".github/workflows/release.yml"
10+
- ".github/scripts/**"
11+
- "apps/headless/Dockerfile.linux"
12+
- "apps/headless/build.sh"
13+
- "apps/headless/build-linux.sh"
14+
- "apps/headless/install-linux.sh"
15+
- "apps/headless/Package.swift"
16+
workflow_dispatch:
17+
inputs:
18+
dry_run:
19+
description: Build and verify release packages without publishing
20+
required: true
21+
default: true
22+
type: boolean
723

824
permissions:
925
contents: read
@@ -13,9 +29,28 @@ jobs:
1329
runs-on: ubuntu-latest
1430
outputs:
1531
version: ${{ steps.version.outputs.version }}
32+
publish: ${{ steps.version.outputs.publish }}
1633
steps:
1734
- id: version
18-
run: echo "version=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT"
35+
env:
36+
DRY_RUN: ${{ inputs.dry_run }}
37+
run: |
38+
set -eu
39+
if [ "$GITHUB_EVENT_NAME" = "push" ]; then
40+
case "$GITHUB_REF" in
41+
refs/tags/v*) ;;
42+
*) echo "release publishing requires a v* tag" >&2; exit 64 ;;
43+
esac
44+
echo "version=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT"
45+
echo "publish=true" >> "$GITHUB_OUTPUT"
46+
else
47+
if [ "$GITHUB_EVENT_NAME" = "workflow_dispatch" ] && [ "$DRY_RUN" != "true" ]; then
48+
echo "manual release runs must use dry_run=true; publish by pushing a v* tag" >&2
49+
exit 64
50+
fi
51+
echo "version=0.0.${GITHUB_RUN_NUMBER}" >> "$GITHUB_OUTPUT"
52+
echo "publish=false" >> "$GITHUB_OUTPUT"
53+
fi
1954
2055
macos:
2156
needs: version
@@ -36,8 +71,14 @@ jobs:
3671
env:
3772
VERSION: ${{ needs.version.outputs.version }}
3873
run: |
74+
set -eu
3975
cd apps/headless
4076
ditto -c -k --keepParent Headless.app "Headless-${VERSION}-macos.zip"
77+
test -s "Headless-${VERSION}-macos.zip"
78+
unzip -t "Headless-${VERSION}-macos.zip"
79+
unzip -Z1 "Headless-${VERSION}-macos.zip" | grep -qx 'Headless.app/Contents/MacOS/Headless'
80+
unzip -Z1 "Headless-${VERSION}-macos.zip" | grep -qx 'Headless.app/Contents/Resources/bin/headless'
81+
unzip -Z1 "Headless-${VERSION}-macos.zip" | grep -qx 'Headless.app/Contents/Resources/bin/headless-mcp'
4182
- uses: actions/upload-artifact@v7
4283
with:
4384
name: macos
@@ -56,8 +97,15 @@ jobs:
5697
env:
5798
VERSION: ${{ needs.version.outputs.version }}
5899
run: |
100+
set -eu
59101
cp apps/headless/build/headless-linux-amd64.tar.gz \
60102
"apps/headless/build/headless-${VERSION}-linux-amd64.tar.gz"
103+
archive="apps/headless/build/headless-${VERSION}-linux-amd64.tar.gz"
104+
test -s "$archive"
105+
tar -tzf "$archive" > archive-contents.txt
106+
for expected in headless headless-host headless-mcp install-linux.sh Headless_HeadlessProtocol.resources/AgentRuntime.js; do
107+
grep -qx "$expected" archive-contents.txt
108+
done
61109
- uses: actions/upload-artifact@v7
62110
with:
63111
name: linux-amd64
@@ -76,23 +124,37 @@ jobs:
76124
env:
77125
VERSION: ${{ needs.version.outputs.version }}
78126
run: |
127+
set -eu
79128
cp apps/headless/build/headless-linux-arm64.tar.gz \
80129
"apps/headless/build/headless-${VERSION}-linux-arm64.tar.gz"
130+
archive="apps/headless/build/headless-${VERSION}-linux-arm64.tar.gz"
131+
test -s "$archive"
132+
tar -tzf "$archive" > archive-contents.txt
133+
for expected in headless headless-host headless-mcp install-linux.sh Headless_HeadlessProtocol.resources/AgentRuntime.js; do
134+
grep -qx "$expected" archive-contents.txt
135+
done
81136
- uses: actions/upload-artifact@v7
82137
with:
83138
name: linux-arm64
84139
path: apps/headless/build/headless-${{ needs.version.outputs.version }}-linux-arm64.tar.gz
85140

86141
publish:
87142
needs: [version, macos, linux-amd64, linux-arm64]
143+
if: >-
144+
github.event_name == 'push' &&
145+
startsWith(github.ref, 'refs/tags/v') &&
146+
needs.version.outputs.publish == 'true'
88147
runs-on: ubuntu-latest
89148
permissions:
90149
contents: write
91150
steps:
151+
- uses: actions/checkout@v7
92152
- uses: actions/download-artifact@v8
93153
with:
94154
path: dist
95155
merge-multiple: true
156+
- name: Generate and verify checksums
157+
run: ./.github/scripts/release-checksums.sh "${{ needs.version.outputs.version }}" dist
96158
- name: Create GitHub Release
97159
uses: softprops/action-gh-release@v3
98160
with:
@@ -115,4 +177,5 @@ jobs:
115177
dist/Headless-${{ needs.version.outputs.version }}-macos.zip
116178
dist/headless-${{ needs.version.outputs.version }}-linux-amd64.tar.gz
117179
dist/headless-${{ needs.version.outputs.version }}-linux-arm64.tar.gz
180+
dist/SHA256SUMS
118181
fail_on_unmatched_files: true

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,17 @@ Cutting that release is tracked in
2323

2424
### Added
2525

26+
- Tagged releases now include a verified `SHA256SUMS` manifest covering every
27+
downloadable macOS and Linux package.
2628
- A single cross-engine conformance scenario now runs against real WKWebView
2729
and Chromium hosts, locking shared response shapes and declared capability
2830
errors without mirrored platform assertions.
2931
- A generated WebKit/Chromium capability matrix now declares exhaustive
3032
command support and intentional engine differences; host ping responses
3133
include the active engine profile.
34+
- The comparison benchmark now emits a validated JSON results artifact with
35+
raw samples and medians, and its refreshed Headless workflows include
36+
task-aware action inspection.
3237
- The single MCP tool now accurately declares its mutating, destructive,
3338
non-idempotent, open-world behavior; integration tests lock the metadata and
3439
deliberate `stop` / `session close` exposure.
@@ -72,6 +77,8 @@ Cutting that release is tracked in
7277

7378
### Changed
7479

80+
- The release workflow can now exercise and verify every package without
81+
publishing, manually or on pull requests that change packaging inputs.
7582
- macOS WebKit and Linux Chromium now share one `HostCore` dispatcher and
7683
lifecycle implementation behind small engine adapters, eliminating the two
7784
divergent copies of flow, capture, recording, report, trace, and error logic.

README.md

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -210,9 +210,22 @@ git tag v1.0.0
210210
git push origin v1.0.0
211211
```
212212

213-
Assets: macOS `Headless.app` zip, Linux amd64/arm64 tarballs. See the Actions
214-
`Release` workflow and the release notes on each tag for install caveats
215-
(Gatekeeper; Linux Chromium/FFmpeg).
213+
Assets: macOS `Headless.app` zip, Linux amd64/arm64 tarballs, and
214+
`SHA256SUMS`. Download the manifest beside the selected package and verify it
215+
before installing:
216+
217+
```sh
218+
sha256sum --ignore-missing -c SHA256SUMS # Linux
219+
shasum -a 256 --ignore-missing -c SHA256SUMS # macOS
220+
```
221+
222+
See the Actions `Release` workflow and the release notes on each tag for
223+
install caveats (Gatekeeper; Linux Chromium/FFmpeg).
224+
225+
The `Release` workflow can also be run manually with `dry_run` enabled. That
226+
builds, verifies, and uploads all three workflow artifacts without creating a
227+
GitHub Release. Pull requests that change release packaging run the same dry
228+
run automatically.
216229

217230
## Tests
218231

apps/headless/Tests/Benchmarks/headless.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ headless start >/dev/null
66
headless session create bench >/dev/null
77
headless --session bench visit "$BENCH_URL" >/dev/null
88
headless --session bench record start --fps 5 >/dev/null
9+
headless --session bench inspect --context actions --task 'continue to designer details' --limit 8 --budget 700 >/dev/null
910
headless --session bench tour --full-page --pace 5000 >/dev/null
1011
headless --session bench click --role button --name Continue >/dev/null
1112
headless --session bench wait --url /next --text 'Designer details' --settled --timeout 10000 >/dev/null

apps/headless/Tests/Benchmarks/headless_warm.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ set -eu
33

44
headless --session bench visit "$BENCH_URL" >/dev/null
55
headless --session bench record start --fps 5 >/dev/null
6+
headless --session bench inspect --context actions --task 'continue to designer details' --limit 8 --budget 700 >/dev/null
67
headless --session bench tour --full-page --pace 5000 >/dev/null
78
headless --session bench click --role button --name Continue >/dev/null
89
headless --session bench wait --url /next --text 'Designer details' --settled --timeout 10000 >/dev/null

0 commit comments

Comments
 (0)