Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions .github/openclaw/artifacts.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
"""Owned packaging and provenance around the unchanged upstream lane runner."""
"""Owned packaging and provenance around the pinned upstream lane runner."""
import argparse
import hashlib
import json
Expand All @@ -15,6 +15,10 @@
ROOT = Path(__file__).resolve().parents[2]
CONFIG = json.loads((ROOT / '.github/openclaw/variants.json').read_text())
RECIPE = ['.github/scripts/lanes.mjs', 'Dockerfile', 'Dockerfile.musl', 'Dockerfile.macos', 'Dockerfile.windows', 'icu', 'macos-cross']
# Keep the engine comparison BASE; admit only these reviewed runtime and CA updates.
# Tree identities include file modes and every descendant of recipe directories.
RECIPE_REVISION = {'reviewed_source':'670af3032b19a37504d389437c871ebbdf2f35a4',
'tree_sha256':'03e3c253c84d9738a17530c6490325c778f8d995212b598af66a283cc5d9389f'}

def run(*args, **kwargs):
return subprocess.run(args, check=True, text=True, **kwargs)
Expand All @@ -34,7 +38,9 @@ def source():
if not re.fullmatch('[0-9a-f]{40}', sha):
raise ValueError('full source SHA required')
run('git', 'merge-base', '--is-ancestor', BASE, sha)
run('git', 'diff', '--exit-code', BASE, sha, '--', *RECIPE)
recipe = subprocess.check_output(['git', 'ls-tree', '--full-tree', '-z', sha, '--', *RECIPE])
if hashlib.sha256(recipe).hexdigest() != RECIPE_REVISION['tree_sha256']:
raise ValueError('source recipe differs from the reviewed revision')
run('git', 'diff', '--exit-code', 'HEAD')
return sha

Expand Down Expand Up @@ -65,7 +71,7 @@ def build(label, destination):
provenance = destination / 'provenance'
provenance.mkdir()
command = json.loads(capture('node', '.github/scripts/lanes.mjs', 'build', label, '--output', str(artifact), '--dry-run'))
# Build the unchanged base locally. No image registry write or inherited workflow is used.
# Build the pinned recipe base locally. No image registry write or inherited workflow is used.
base_tag = 'openclaw-webkit-toolchain:' + hashlib.sha256(lane['image'].encode()).hexdigest()[:24]
base_command = command[:command.index('--target=artifact')] + ['--target=base', '--load', '--tag', base_tag, '--metadata-file', str(provenance/'toolchain-build.json'), '.']
run('docker', 'buildx', 'use', 'default')
Expand All @@ -92,7 +98,7 @@ def build(label, destination):
for commit in capture('git','rev-list','--reverse',BASE+'..'+sha).splitlines():
patch = subprocess.check_output(['git','show','--format=','--binary',commit])
patches.append({'commit':commit,'subject':capture('git','show','-s','--format=%s',commit),'patch_sha256':hashlib.sha256(patch).hexdigest()})
manifest = {'schema_version':1,'source':sha,'base':BASE,'lane':lane,'patches':patches,
manifest = {'schema_version':1,'source':sha,'base':BASE,'recipe_revision':RECIPE_REVISION,'lane':lane,'patches':patches,
'recipe_sha256':{p:digest(ROOT/p) for p in paths},'toolchain_image_id':image['Id'],
'commands':{'base':base_command,'artifact':build_command},
'tool_versions':tool_versions,'docker_version':capture('docker','version'),'buildx_version':capture('docker','buildx','version'),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
diff --git a/test/js/node/process/process.test.js b/test/js/node/process/process.test.js
--- a/test/js/node/process/process.test.js
+++ b/test/js/node/process/process.test.js
@@ -1056,3 +1056,3 @@
let [out, exited] = await Promise.all([new Response(subprocess.stdout).text(), subprocess.exited]);
- expect(out.trim()).toEqual("v26.3.0");
+ expect(out.trim()).toEqual("v26.12.1");
expect(exited).toBe(0);
diff --git a/test/js/node/tls/node-tls-client-wrap-fixture.mjs b/test/js/node/tls/node-tls-client-wrap-fixture.mjs
--- a/test/js/node/tls/node-tls-client-wrap-fixture.mjs
+++ b/test/js/node/tls/node-tls-client-wrap-fixture.mjs
@@ -305,3 +305,4 @@
const host = "127.0.0.1";
- const options = { session, rejectUnauthorized: false };
+ // Node binds reusable sessions to the peer identity, including over an existing socket.
+ const options = { session, host, rejectUnauthorized: false };
// The listener is attached in the turn that makes the socket: a wrap starts its handshake at once.
13 changes: 11 additions & 2 deletions .github/openclaw/qualify.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ git apply "$inputs/patches/000-checksums.patch"
# Apply openclaw/bun#151 equally to both frozen-source arms.
git apply "$inputs/patches/012-vm-finalization-fixture.patch"
sha256sum "$inputs/patches/012-vm-finalization-fixture.patch" > "$QUALIFICATION_DIR/vm-finalization-fixture.sha256"
# Match the qualified host Node and preserve its authenticated session identity in both arms.
git apply "$inputs/patches/013-node-reference-fixtures.patch"
sha256sum "$inputs/patches/013-node-reference-fixtures.patch" > "$QUALIFICATION_DIR/node-reference-fixtures.sha256"
bootstrap=$(command -v bun)
"$bootstrap" install --frozen-lockfile
(cd test && "$bootstrap" install --frozen-lockfile)
Expand Down Expand Up @@ -58,9 +61,14 @@ for arm in baseline candidate; do
cp "$inputs/selected.json" build/openclaw-ci/selected.json
cp "$inputs/selected.json" "$results/selected.json"
rm -f build/openclaw-ci/results.json
env -u BUN_WEBKIT_ARTIFACT_MANIFEST -u BUN_BUILD_CACHE_DIR -u BUN_BUILD_PREFETCH_DIR \
# Retain both arms when a reference fails; the strict all-pass gate below still rejects failures.
if env -u BUN_WEBKIT_ARTIFACT_MANIFEST -u BUN_BUILD_CACHE_DIR -u BUN_BUILD_PREFETCH_DIR \
HOME="$results/home" TMPDIR="$results/tmp" PATH="$results/bin:$PATH" OPENCLAW_CI_PLATFORM=linux \
"$candidate" scripts/openclaw-ci/tests.ts test > "$results/tests.log" 2>&1
"$candidate" scripts/openclaw-ci/tests.ts test > "$results/tests.log" 2>&1; then
printf '0\n' > "$results/exit-code.log"
else
printf '%s\n' "$?" > "$results/exit-code.log"
fi
cp build/openclaw-ci/results.json "$results/results.json"
done
python3 "$inputs/compare-fork-ci.py" "$QUALIFICATION_DIR/baseline" "$QUALIFICATION_DIR/candidate" --output "$QUALIFICATION_DIR/faithfulness.json"
Expand Down Expand Up @@ -96,6 +104,7 @@ from pathlib import Path
root=Path(sys.argv[1])
faith=json.loads((root/'faithfulness.json').read_text())
assert faith['identical_file_outcomes'] and faith['all_passed']
assert all((root/arm/'exit-code.log').read_text().strip() == '0' for arm in ['baseline','candidate']), 'fork test runner failed'
assert faith['selected_files']==44 and faith['result_files']==46
for name in ['als-plugin','als-native']:
rows=json.loads((root/(name+'.json')).read_text())['results']
Expand Down
50 changes: 50 additions & 0 deletions .github/openclaw/test_release.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import hashlib
import importlib.util
import json
import os
from pathlib import Path
import subprocess
import tempfile
Expand All @@ -9,6 +11,8 @@

spec=importlib.util.spec_from_file_location('release',Path(__file__).with_name('release.py'))
release=importlib.util.module_from_spec(spec);spec.loader.exec_module(release)
spec=importlib.util.spec_from_file_location('artifacts',Path(__file__).with_name('artifacts.py'))
artifacts=importlib.util.module_from_spec(spec);spec.loader.exec_module(artifacts)
SOURCE='a'*40

class ReleaseIntegrity(unittest.TestCase):
Expand Down Expand Up @@ -100,4 +104,50 @@ def test_authorization_rejects_other_branch(self):
with self.assertRaisesRegex(ValueError,'reviewed source'):release.authorize(SOURCE,42)
execute.assert_not_called()

class ArtifactRecipe(unittest.TestCase):
def setUp(self):
self.temp=tempfile.TemporaryDirectory();self.addCleanup(self.temp.cleanup)
self.root=Path(self.temp.name)
self.addCleanup(os.chdir,Path.cwd());os.chdir(self.root)
self.git('init','-q')
self.git('config','user.name','Recipe fixture');self.git('config','user.email','fixture@example.invalid')
self.git('config','commit.gpgsign','false')
for name in artifacts.RECIPE:
p=self.root/name
if name in ('icu','macos-cross'):p=p/'input.txt'
p.parent.mkdir(parents=True,exist_ok=True);p.write_text('reviewed input\n')
(self.root/'Dockerfile').write_text('ARG NODE_VERSION=24.16.0\n')
(self.root/'source.txt').write_text('engine source\n')
self.commit('upstream base');self.base=self.git('rev-parse','HEAD')
(self.root/'Dockerfile').write_text('ARG NODE_VERSION=24.22.1\n')
self.commit('reviewed Node security update')
recipe=subprocess.check_output(['git','ls-tree','--full-tree','-z','HEAD','--',*artifacts.RECIPE])
revision={'reviewed_source':self.git('rev-parse','HEAD'),'tree_sha256':hashlib.sha256(recipe).hexdigest()}
for name,value in [('BASE',self.base),('RECIPE_REVISION',revision)]:
p=patch.object(artifacts,name,value,create=True);p.start();self.addCleanup(p.stop)
def git(self,*args):
return subprocess.check_output(['git',*args],text=True).strip()
def commit(self,message):
self.git('add','.');self.git('commit','-qm',message)
def test_reviewed_recipe_survives_squash_and_later_engine_commits(self):
self.git('reset','--hard',self.base)
(self.root/'Dockerfile').write_text('ARG NODE_VERSION=24.22.1\n')
self.commit('squashed reviewed update')
self.assertEqual(artifacts.source(),self.git('rev-parse','HEAD'))
(self.root/'source.txt').write_text('security-patched engine source\n')
self.commit('engine change after reviewed recipe')
self.assertEqual(artifacts.source(),self.git('rev-parse','HEAD'))
def test_unreviewed_recipe_and_dirty_source_are_rejected(self):
(self.root/'source.txt').write_text('uncommitted engine change\n')
with self.assertRaises(subprocess.CalledProcessError):artifacts.source()
self.git('checkout','--','source.txt')
for name,mode in [('Dockerfile',False),('icu/input.txt',False),('Dockerfile',True)]:
with self.subTest(path=name,mode=mode):
p=self.root/name
if mode:p.chmod(0o755)
else:p.write_text('unreviewed recipe input\n')
self.commit('unreviewed recipe change')
with self.assertRaisesRegex(ValueError,'recipe differs'):artifacts.source()
self.git('reset','--hard','HEAD^')

if __name__=='__main__':unittest.main()
10 changes: 5 additions & 5 deletions .github/workflows/openclaw-artifacts.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ jobs:
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
fetch-depth: 16
fetch-depth: 0
persist-credentials: false
sparse-checkout-cone-mode: false
sparse-checkout: |
Expand All @@ -38,7 +38,7 @@ jobs:
!/WebDriverTests/
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020
with:
node-version: '26.3.0'
node-version: '26.12.1'
- name: Test publication integrity gates
run: python3 .github/openclaw/test_release.py
- name: Measure runner capacity and Docker support
Expand All @@ -59,7 +59,7 @@ jobs:
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
fetch-depth: 16
fetch-depth: 0
persist-credentials: false
sparse-checkout-cone-mode: false
sparse-checkout: |
Expand All @@ -72,7 +72,7 @@ jobs:
!/WebDriverTests/
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020
with:
node-version: '26.3.0'
node-version: '26.12.1'
- name: Build unchanged upstream lane and record provenance
env:
LANE: ${{ matrix.lane }}
Expand All @@ -96,7 +96,7 @@ jobs:
sparse-checkout: .github
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020
with:
node-version: '26.3.0'
node-version: '26.12.1'
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c
with:
name: bun-webkit-linux-amd64
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/openclaw-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jobs:
/JSTests/stress/
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020
with:
node-version: '26.3.0'
node-version: '26.12.1'
- name: Verify pipeline integrity and runner support
run: |
python3 .github/openclaw/test_release.py
Expand Down Expand Up @@ -152,6 +152,6 @@ jobs:
!/WebDriverTests/
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020
with:
node-version: '26.3.0'
node-version: '26.12.1'
- name: Build the macOS fork artifact on Linux without publishing
run: python3 .github/openclaw/artifacts.py build --lane bun-webkit-macos-arm64 --output "$RUNNER_TEMP/macos-fork-artifact"
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ RUN curl -fsSL "https://github.com/facebook/zstd/releases/download/v${ZSTD_VERSI
&& zstd --version

# Install Node (for icu/compress-data.ts; needs >=23.6 for default type stripping)
ARG NODE_VERSION=24.16.0
ARG NODE_VERSION=24.22.1
RUN curl -fsSL "https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-$(uname -m | sed 's/x86_64/x64/;s/aarch64/arm64/').tar.xz" \
| tar -xJ -C /usr/local --strip-components=1 \
&& node --version
Expand Down
1 change: 1 addition & 0 deletions Dockerfile.musl
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ ARG LINUX_ARCH="x86_64"
# `--build-context base=docker-image://...`, which replaces this stage (.github/workflows/ci.yml, the `image` job).
# Without that, it is built here like any other stage. Lane settings belong in `lane` below.
FROM alpine:3.23 as base
RUN apk add --no-cache --upgrade ca-certificates-bundle=20261020-r0

# LLVM 23 is in Alpine edge only. `@edge` is a tagged repository: apk takes a package from it only when the package is
# asked for with that tag, or when a tagged package needs something 3.23 does not have. So musl, libstdc++ and gcc stay
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile.windows
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ RUN for pair in "x86_64 x64" "aarch64 arm64"; do \
# (>= 23.6 for type stripping), and patch for the udata.cpp hook. zstd's lib/ sources stay, at /zstd/lib: jsc.exe builds
# the decoder from them to read the repacked data (shell/CMakeLists.txt, BUN_ICU_ZSTD_SOURCE_DIR).
ARG ZSTD_VERSION=1.5.7
ARG NODE_VERSION=24.16.0
ARG NODE_VERSION=24.22.1
RUN apt-get update && apt-get install -y --no-install-recommends patch && rm -rf /var/lib/apt/lists/* && \
curl -fsSL "https://github.com/facebook/zstd/releases/download/v${ZSTD_VERSION}/zstd-${ZSTD_VERSION}.tar.gz" | tar xz -C /tmp && \
make -C /tmp/zstd-${ZSTD_VERSION}/programs zstd -j$(nproc) CC=clang-${LLVM_VERSION} && \
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -363,31 +363,6 @@ LBfQdCVp9/5rPJS+TUtBjE7ic9DjkCJzQ83z7+pzzkWKsKZJ/0x9nXGIxHYdkFsd
# MD5 Fingerprint: 49:79:04:b0:eb:87:19:ac:47:b0:bc:11:51:9b:74:d0
# SHA1 Fingerprint: d1:eb:23:a4:6d:17:d6:8f:d9:25:64:c2:f1:f1:60:17:64:d8:e3:49
# SHA256 Fingerprint: d7:a7:a0:fb:5d:7e:27:31:d7:71:e9:48:4e:bc:de:f7:1d:5f:0c:3e:0a:29:48:78:2b:c8:3e:e0:ea:69:9e:f4
-----BEGIN CERTIFICATE-----
MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEb
MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow
GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmlj
YXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAwMFoXDTI4MTIzMTIzNTk1OVowezEL
MAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE
BwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNVBAMM
GEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEP
ADCCAQoCggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686tdUIoWMQua
BtDFcCLNSS1UY8y2bmhGC1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe
3M/vg4aijJRPn2jymJBGhCfHdr/jzDUsi14HZGWCwEiwqJH5YZ92IFCokcdmtet4
YgNW8IoaE+oxox6gmf049vYnMlhvB/VruPsUK6+3qszWY19zjNoFmag4qMsXeDZR
rOme9Hg6jc8P2ULimAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjHYpy+g8cm
ez6KJcfA3Z3mNWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQU
oBEKIz6W8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF
MAMBAf8wewYDVR0fBHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20v
QUFBQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29t
b2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2VzLmNybDANBgkqhkiG9w0BAQUF
AAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm7l3sAg9g1o1Q
GE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLz
Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2
G9w84FoVxp7Z8VlIMCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsi
l2D4kF501KKaU73yqWjgom7C12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3
smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg==
-----END CERTIFICATE-----

# Issuer: CN=QuoVadis Root Certification Authority O=QuoVadis Limited OU=Root Certification Authority
# Subject: CN=QuoVadis Root Certification Authority O=QuoVadis Limited OU=Root Certification Authority
Expand Down
Loading
Loading