Skip to content
Merged
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
111 changes: 110 additions & 1 deletion .buildkite/pinned-runtime-proof.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#!/usr/bin/env python3
"""Opt-in hosted comparison. All agent and HTTP calls use real services."""
import base64
import hashlib
import json
import os
Expand Down Expand Up @@ -127,5 +128,113 @@ def execute(producer):
print("PROOF PASS: same saved plan, real artifacts, real Job API, identity, permissions, status, cold/warm and bad digest", flush=True)


def platforms():
"""Test the plugin before a released importer emits the expanded contract."""
assert os.environ.get("GITHUB_EVENT_NAME") == "workflow_dispatch"
version = os.environ.get("PINNED_PROOF_VERSION", "0.133.0")
assert re.fullmatch(r"[0-9]+\.[0-9]+\.[0-9]+", version)
plugin_ref = os.environ.get("PINNED_PROOF_PLUGIN_REF", os.environ["BUILDKITE_COMMIT"])
assert re.fullmatch(r"[0-9a-f]{40}|v[0-9]+\.[0-9]+\.[0-9]+", plugin_ref)
source = os.environ.get("PINNED_PROOF_PLAN_SOURCE", "producer")
assert source in ("producer", "inline", "env")
namespace = os.environ.get("PINNED_PROOF_NAMESPACE") == "1"
repository = os.environ.get("PINNED_PROOF_REPOSITORY", "buildkite-plugins/github-actions-buildkite-plugin")
assert re.fullmatch(r"[\w.-]+/[\w.-]+", repository)
owner, name = repository.split("/")
producer = os.environ["BUILDKITE_JOB_ID"]
endpoint = os.environ["BUILDKITE_AGENT_ENDPOINT"].rstrip("/")
assert endpoint.startswith("https://")
request = urllib.request.Request(f"{endpoint}/jobs/{producer}/github-actions/releases/tags/v{version}",
headers={"Authorization": "Token " + os.environ["BUILDKITE_AGENT_ACCESS_TOKEN"]})
with urllib.request.urlopen(request) as response:
release = json.load(response)
assert release["tag_name"] == "v" + version
names = {"linux/amd64": "linux_x86_64", "linux/arm64": "linux_arm64",
"darwin/arm64": "darwin_arm64", "windows/amd64": "windows_x86_64.exe"}
assets = {}
for platform, suffix in names.items():
matches = [asset for asset in release["assets"] if asset["name"] == "buildkite-gha_" + suffix]
assert len(matches) == 1
asset = matches[0]
assert asset["browser_download_url"].startswith("https://")
assert re.fullmatch(r"sha256:[0-9a-f]{64}", asset["digest"])
assets[platform] = asset
with tempfile.TemporaryDirectory(prefix="platform-runtime-import-") as temporary:
root = Path(temporary)
binary = root / "buildkite-gha"
with urllib.request.urlopen(assets["linux/amd64"]["browser_download_url"]) as response:
binary.write_bytes(response.read())
assert "sha256:" + hashlib.sha256(binary.read_bytes()).hexdigest() == assets["linux/amd64"]["digest"]
binary.chmod(0o700)
event = root / "event.json"
event.write_text(json.dumps({"provider": "github", "event": "workflow_dispatch",
"repository": {"owner": owner, "name": name,
"clone_url": "https://github.com/" + repository + ".git", "default_branch": "main"},
"ref": "refs/heads/" + os.environ["BUILDKITE_BRANCH"], "sha": os.environ["BUILDKITE_COMMIT"],
"actor": "buildkite-gha-proof", "payload": {"inputs": {"marker": "platform-proof", "namespace": namespace}}}))
os.environ["PROOF_REAL_AGENT"] = shutil.which("buildkite-agent")
os.environ["PROOF_ROOT"] = str(root)
wrapper = root / "buildkite-agent"
wrapper.write_text(f'#!/bin/sh\nexec python3 "{SCRIPT}" capture "$@"\n')
wrapper.chmod(0o700)
env = dict(os.environ, PATH=f'{root}:{os.environ["PATH"]}', BUILDKITE_GHA_PLUGIN_RELEASE=json.dumps(release))
arguments = [str(binary), "upload", "--event-path", str(event)]
for label, key in [("ubuntu-latest", "LINUX"), ("ubuntu-24.04-arm", "ARM"), ("macos-14", "MAC"), ("windows-2022", "WINDOWS")]:
arguments += ["--runner-queue", label + "=" + os.environ["PINNED_PROOF_" + key + "_QUEUE"]]
for platform, asset in assets.items():
if platform == "linux/amd64":
continue
counterpart = root / asset["name"]
with urllib.request.urlopen(asset["browser_download_url"]) as response:
counterpart.write_bytes(response.read())
assert "sha256:" + hashlib.sha256(counterpart.read_bytes()).hexdigest() == asset["digest"]
counterpart.chmod(0o700)
arguments += ["--runtime-distribution", platform + "=" + str(counterpart)]
subprocess.run([*arguments, ".github/workflows/pinned-runtime-platforms.yml"], env=env, check=True)
pipeline = json.loads(agent("pipeline", "upload", "--dry-run", "--no-interpolation", "--format", "json", str(root / "pipeline.yml")))
steps = [job for group in pipeline["steps"] for job in group.get("steps", [group])]
records = []
for step in steps:
digest = step.get("env", {}).get("BUILDKITE_GHA_PLAN_DIGEST")
assert digest, "expected an admitted executable plan"
path = ".buildkite-gha/plans/" + digest.removeprefix("sha256:") + ".json"
agent("artifact", "download", path, str(root), "--step", producer)
plan_bytes = (root / path).read_bytes()
assert "sha256:" + hashlib.sha256(plan_bytes).hexdigest() == digest
plan = json.loads(plan_bytes)
platform = next(platform for platform, asset in assets.items() if asset["digest"] == plan["runtime"]["distribution_digest"])
asset = assets[platform]
run = {"plan": {"digest": digest, "producer": producer},
"runtime": {"platform": platform, "url": asset["browser_download_url"], "sha256": asset["digest"][7:]},
"hosted-tool-cache": "--hosted-tool-cache" in step["command"]}
if namespace and platform == "linux/amd64":
step.pop("image", None)
step["agents"]["nsc-gha-image"] = "ubuntu-24.04"
run.update({"namespace": True, "hosted-tool-cache": False})
if source != "producer":
run["plan"] = {"digest": digest, "inline": base64.b64encode(plan_bytes).decode()}
event_path = ".buildkite-gha/events/" + plan["event"]["payload_digest"][7:] + ".json"
agent("artifact", "download", event_path, str(root), "--step", producer)
payload = base64.b64encode((root / event_path).read_bytes()).decode()
if source == "env":
run["event"] = {"env": "BUILDKITE_GHA_EVENT_PAYLOAD"}
step.setdefault("env", {})["BUILDKITE_GHA_EVENT_PAYLOAD"] = payload
else:
run["event"] = {"inline": payload}
# The plugin-stage proof deliberately changes only delivery. Later
# importer proofs must submit their emitted steps without rewriting.
step.pop("command", None)
step["plugins"] = [{"github-actions#" + plugin_ref: {"run": run}}]
step.setdefault("env", {})["BUILDKITE_PLUGIN_VALIDATION"] = "true"
records.append({"key": step["key"], "platform": platform, "plan": digest, "source": source,
"runtime": asset["digest"], "event": plan["event"]["payload_digest"]})
assert len(records) == 8 and {r["platform"] for r in records} == set(names)
receipt = root / "platform-runtime-plans.json"
receipt.write_text(json.dumps(records, indent=2))
agent("artifact", "upload", receipt.name, cwd=root)
agent("pipeline", "upload", "--no-interpolation", input=json.dumps(pipeline).encode())
print(f"Scheduled {len(records)} native jobs with plugin {plugin_ref}, runtime v{version}, source={source}")


if __name__ == "__main__":
{"import": prepare, "capture": capture, "probe": probe, "run": lambda: execute(sys.argv[2])}[sys.argv[1]]()
{"import": prepare, "capture": capture, "probe": probe, "platforms": platforms, "run": lambda: execute(sys.argv[2])}[sys.argv[1]]()
13 changes: 11 additions & 2 deletions .buildkite/pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ steps:
plugins:
- shellcheck#v1.4.0:
files:
- hooks/*
- hooks/command
- lib/*
- label: ":pipeline: Load live plugin smoke"
key: "live-plugin-smoke-loader"
if: build.env("PLUGIN_PINNED_RUNTIME_PROOF") != "1"
if: build.env("PLUGIN_PINNED_RUNTIME_PROOF") != "1" && build.env("PLUGIN_PINNED_RUNTIME_PROOF") != "platforms"
agents:
queue: "hosted"
timeout_in_minutes: 15
Expand Down Expand Up @@ -147,3 +147,12 @@ steps:
retry:
automatic: false
command: python3 .buildkite/pinned-runtime-proof.py import
- label: ":test_tube: Schedule native pinned runtime proof"
key: "platform-runtime-proof-importer"
if: build.env("PLUGIN_PINNED_RUNTIME_PROOF") == "platforms"
agents:
queue: "hosted"
timeout_in_minutes: 20
retry:
automatic: false
command: python3 .buildkite/pinned-runtime-proof.py platforms
96 changes: 96 additions & 0 deletions .github/workflows/pinned-runtime-platforms.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
name: Pinned runtime platform proof
on: workflow_dispatch
permissions:
contents: read
jobs:
linux:
runs-on: ubuntu-latest
outputs:
value: ${{ steps.probe.outputs.value }}
steps:
- id: probe
env:
TOKEN: ${{ github.token }}
NAMESPACE: ${{ github.event.inputs.namespace }}
run: |
test -n "$TOKEN"
test -z "${BUILDKITE_AGENT_ACCESS_TOKEN:-}"
test ! -e .git
test "$(id -un)" = runner
test "$HOME" = /home/runner
sudo -n true
test "$RUNNER_ARCH" = X64
if test "$NAMESPACE" = true; then
rustc --version
printf 'FROM scratch\nLABEL proof=namespace\n' | docker build --load --platform linux/amd64 -t pinned-runtime-proof -
docker image inspect pinned-runtime-proof >/dev/null
docker image rm pinned-runtime-proof
fi
echo 'value=41' >> "$GITHUB_OUTPUT"
echo 'PLATFORM_PROOF linux/amd64 value=41 token=present'
arm:
needs: linux
runs-on: ubuntu-24.04-arm
outputs:
value: ${{ steps.probe.outputs.value }}
steps:
- id: probe
env:
VALUE: ${{ needs.linux.outputs.value }}
TOKEN: ${{ github.token }}
run: |
test "$VALUE" = 41
test -n "$TOKEN"
test -z "${BUILDKITE_AGENT_ACCESS_TOKEN:-}"
test ! -e .git
test "$(id -un)" = runner
test "$HOME" = /home/runner
sudo -n true
test "$RUNNER_ARCH" = ARM64
echo 'value=42' >> "$GITHUB_OUTPUT"
echo 'PLATFORM_PROOF linux/arm64 value=42 token=present'
mac:
needs: arm
runs-on: macos-14
outputs:
value: ${{ steps.probe.outputs.value }}
steps:
- id: probe
env:
VALUE: ${{ needs.arm.outputs.value }}
TOKEN: ${{ github.token }}
run: |
test "$VALUE" = 42
test -n "$TOKEN"
test -z "${BUILDKITE_AGENT_ACCESS_TOKEN:-}"
test ! -e .git
test "$RUNNER_OS" = macOS
test "$RUNNER_ARCH" = ARM64
test -w /Users/runner/hostedtoolcache
echo 'value=43' >> "$GITHUB_OUTPUT"
echo 'PLATFORM_PROOF darwin/arm64 value=43 token=present'
windows:
needs: mac
runs-on: windows-2022
steps:
- env:
VALUE: ${{ needs.mac.outputs.value }}
TOKEN: ${{ github.token }}
run: |
if ($env:VALUE -ne '43') { throw 'output transport failed' }
if (-not $env:TOKEN) { throw 'missing provider token' }
if ($env:BUILDKITE_AGENT_ACCESS_TOKEN) { throw 'agent token leaked' }
if (Test-Path .git) { throw 'checkout was not skipped' }
if ($env:RUNNER_OS -ne 'Windows' -or $env:RUNNER_ARCH -ne 'X64') { throw 'wrong platform' }
Write-Output 'PLATFORM_PROOF windows/amd64 value=43 token=present'
failure:
strategy:
matrix:
os: [ubuntu-latest, ubuntu-24.04-arm, macos-14, windows-2022]
runs-on: ${{ matrix.os }}
continue-on-error: true
steps:
- shell: bash
run: |
echo "PLATFORM_PROOF $RUNNER_OS/$RUNNER_ARCH expected failure=37"
exit 37
Loading
Loading