From c79cc6b9af65c80a2fdee88fd8c9be4b74e7f8d9 Mon Sep 17 00:00:00 2001 From: itdevwu Date: Sun, 16 Aug 2026 23:52:56 +0800 Subject: [PATCH 1/3] =?UTF-8?q?=F0=9F=A7=AD=20feat:=20guide=20bounded=20co?= =?UTF-8?q?ntainer=20profiling?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- skills/xprobe-measure-latency/SKILL.md | 9 +++++++ .../examples/request-to-first-kernel.json | 2 +- .../references/investigation.md | 8 +++++- .../references/setup.md | 27 +++++++++++++++++-- .../fixtures/workflow-routes.json | 5 ++++ tests/agent-contract/test_contract.py | 7 ++++- tests/agent-contract/test_workflow_routes.py | 2 ++ 7 files changed, 55 insertions(+), 5 deletions(-) diff --git a/skills/xprobe-measure-latency/SKILL.md b/skills/xprobe-measure-latency/SKILL.md index 4894d50..2b829e1 100644 --- a/skills/xprobe-measure-latency/SKILL.md +++ b/skills/xprobe-measure-latency/SKILL.md @@ -34,6 +34,11 @@ not run a fixed checklist or collect every source. [references/multi-process.md](references/multi-process.md). Select explicit PID/start-time identities. Inventory one representative per defensible worker class, then run independent narrow commands concurrently where useful. +- **Containerized live target**: Also read + [references/setup.md](references/setup.md). Run the matching CLI in the + target's PID and mount namespaces, then reacquire the namespace-local PID plus + procfs start time before validation. Resolve the container from explicit + workload evidence; do not guess a pod, container, or PID. - **Setup or repair**: Read [references/setup.md](references/setup.md) only when a required live command is absent, incompatible, or unhealthy. Existing artifacts do not require a local collector. @@ -77,6 +82,10 @@ justify unrelated collection. validation and attachment. Never substitute a reused PID. - Bound every capture by duration or samples, timeout, and record/group/thread capacity. Preserve each command's stdout, stderr, status, and artifact. +- For one narrow exact hypothesis, start with modest record headroom over the + requested samples and always write the bounded event artifact. Increase + `--max-events` only from observed unmatched records or capacity pressure, not + as a default substitute for narrower selectors. - Before CUDA injection, disclose that `measure` will ptrace the process and leave the CUPTI shared object mapped. `startup_required` NVTX work must restart with the matching Agent before the first NVTX call. diff --git a/skills/xprobe-measure-latency/examples/request-to-first-kernel.json b/skills/xprobe-measure-latency/examples/request-to-first-kernel.json index f0289e2..2841abf 100644 --- a/skills/xprobe-measure-latency/examples/request-to-first-kernel.json +++ b/skills/xprobe-measure-latency/examples/request-to-first-kernel.json @@ -11,5 +11,5 @@ "samples": 100, "duration_ms": null, "timeout_ms": 30000, - "max_events": 100000 + "max_events": 2000 } diff --git a/skills/xprobe-measure-latency/references/investigation.md b/skills/xprobe-measure-latency/references/investigation.md index e548142..392479b 100644 --- a/skills/xprobe-measure-latency/references/investigation.md +++ b/skills/xprobe-measure-latency/references/investigation.md @@ -198,11 +198,17 @@ inventory cannot be re-correlated because it intentionally contains no events. Collect one exact artifact for the selected hypothesis, then use `measure --input` when only selectors or policy change. +For 100 requested narrow pairs, begin with about 10x record capacity rather +than a six-figure default. Two records per pair is the theoretical minimum; +the remaining headroom covers unmatched boundaries and collector stop latency. +Increase capacity only when the quality fields show it is necessary and the +selectors cannot be narrowed without changing the hypothesis. + ```bash xprobe measure --pid "$PID" \ --from 'cuda:kernel_start:name~^selected_kernel$' \ --to 'cuda:kernel_end:name~^selected_kernel$' \ - --match exact --samples 100 --max-events 200000 \ + --match exact --samples 100 --max-events 1000 \ --events-out selected-kernel.jsonl --format jsonl \ --json --non-interactive --no-color diff --git a/skills/xprobe-measure-latency/references/setup.md b/skills/xprobe-measure-latency/references/setup.md index dc6c130..8040537 100644 --- a/skills/xprobe-measure-latency/references/setup.md +++ b/skills/xprobe-measure-latency/references/setup.md @@ -7,7 +7,7 @@ command unless their environment prevents the agent from writing a usable prefix ## Check and bootstrap xprobe -For live work, check the executable first. This Skill supports xprobe `0.4.x` +For live work, check the executable first. This Skill supports xprobe `0.5.x` with schema version `2.0`; install the current release when the CLI is absent, outside that range, or fails its required capability checks. Offline analysis of an existing schema-v2 artifact does not require an installed CLI. @@ -38,6 +38,29 @@ PATH, permission, driver, CUDA, or CUPTI failures explicitly and adjust from the reported detail; do not continue to measurement on an unverified installation. The CLI needs no Node.js. CUDA is optional until a GPU selector is measured. +## Run with a containerized live target + +A host-side `measure` cannot safely inject when the target has a different +mount namespace. Do not retry with a guessed Agent path. Run the matching +xprobe release in the application container itself, or have an explicitly +privileged launcher enter both the target PID and mount namespaces. `docker +exec` or `kubectl exec` is suitable only after the caller has identified the +exact container from workload evidence. An adjacent sidecar or ephemeral +container normally has a different mount namespace and is not equivalent. + +Bootstrap xprobe under a writable prefix in that namespace, then resolve the +target again from its procfs view. Record the namespace-local PID and +`/proc/PID/stat` start time, and rerun `doctor` and read-only `validate` there +before measurement. Never carry a host PID or an earlier process identity into +the container command. Keep stdout, stderr, status, and artifacts outside the +container through an explicit writable or shared path. + +Entering a namespace does not add capabilities that the running container was +not granted. Surface missing ptrace, perf, BPF, NVIDIA device, or CUPTI access +as an environment failure; do not silently fall back to a host-side injection +or a different container. For NVTX, the matching Agent must still be configured +in the application container before its first NVTX API call. + ## Build locally when the release is unsuitable The `glibc 2.34` requirement applies to the precompiled release archive. When @@ -84,7 +107,7 @@ online injection as a fallback for an already initialized NVTX process. ## Repair the Skill only when needed The user normally installed this Skill before invoking the agent. When its files -are missing or incompatible with xprobe `0.4.x`, install the complete current +are missing or incompatible with xprobe `0.5.x`, install the complete current release directory through the Agent Skills CLI: ```bash diff --git a/tests/agent-contract/fixtures/workflow-routes.json b/tests/agent-contract/fixtures/workflow-routes.json index f9788d5..76a0967 100644 --- a/tests/agent-contract/fixtures/workflow-routes.json +++ b/tests/agent-contract/fixtures/workflow-routes.json @@ -29,6 +29,11 @@ "name": "unsupported_python_runtime", "sources": ["skill", "investigation"], "required": ["`inactive` or `unsupported`", "native fallback", "do not claim xprobe identifies python semantics"] + }, + { + "name": "container_namespace", + "sources": ["skill", "setup"], + "required": ["containerized live target", "PID and mount namespaces", "namespace-local PID", "do not guess a pod, container, or PID", "does not add capabilities"] } ] } diff --git a/tests/agent-contract/test_contract.py b/tests/agent-contract/test_contract.py index 13c16af..bb7b87c 100755 --- a/tests/agent-contract/test_contract.py +++ b/tests/agent-contract/test_contract.py @@ -56,6 +56,7 @@ def check_skill(workspace: pathlib.Path) -> None: "Unknown CPU or Python workload", "Unknown GPU or mixed workload", "Multiple processes", + "Containerized live target", "Setup or repair", ): assert route in normalized_skill @@ -67,6 +68,7 @@ def check_skill(workspace: pathlib.Path) -> None: "Start with bounded `--cpu-sample` evidence", "Add `--syscall-aggregate` only for a kernel-facing hypothesis", "inventories can run concurrently", + "modest record headroom", "Existing artifacts do not require a local collector", "Run `doctor` when capability is unknown", ): @@ -223,13 +225,16 @@ def check_skill(workspace: pathlib.Path) -> None: assert required in normalized_cli_contract for required in ( "v0.5.0/install.sh", - "xprobe `0.4.x`", + "xprobe `0.5.x`", "npx skills@1 add", "xprobe --version", "xprobe doctor", "host glibc", "CUDA/CUPTI majors other than 12 or 13 are not supported", "scripts/package-release.sh", + "PID and mount namespaces", + "namespace-local PID", + "does not add capabilities", ): assert required in normalized_setup diff --git a/tests/agent-contract/test_workflow_routes.py b/tests/agent-contract/test_workflow_routes.py index 12569b4..3c9cf36 100644 --- a/tests/agent-contract/test_workflow_routes.py +++ b/tests/agent-contract/test_workflow_routes.py @@ -16,6 +16,7 @@ def main() -> None: "investigation": normalize( (skill_root / "references/investigation.md").read_text() ), + "setup": normalize((skill_root / "references/setup.md").read_text()), } fixture = json.loads( (workspace / "tests/agent-contract/fixtures/workflow-routes.json").read_text() @@ -33,6 +34,7 @@ def main() -> None: "python_semantics", "mixed_cpu_gpu", "unsupported_python_runtime", + "container_namespace", } print(json.dumps({"schema_version": "2.0", "ok": True, "routes": sorted(covered)})) From fc9a1aa5dd17a0e632bf18f33f31034e48d6ecd7 Mon Sep 17 00:00:00 2001 From: itdevwu Date: Sun, 16 Aug 2026 23:56:07 +0800 Subject: [PATCH 2/3] =?UTF-8?q?=F0=9F=94=90=20feat:=20attest=20release=20a?= =?UTF-8?q?rtifacts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/release.yml | 51 +++++++++++++++++++++++++++--- justfile | 1 + scripts/check-release-sbom.py | 37 ++++++++++++++++++++++ scripts/verify-public-release.sh | 22 ++++++++++++- tests/install/test_release_sbom.py | 34 ++++++++++++++++++++ 5 files changed, 139 insertions(+), 6 deletions(-) create mode 100755 scripts/check-release-sbom.py create mode 100755 tests/install/test_release_sbom.py diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ea074cf..10aeab9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -6,7 +6,7 @@ on: - "v*" permissions: - contents: write + contents: read jobs: cupti-agent: @@ -54,6 +54,11 @@ jobs: linux-x86_64: needs: cupti-agent runs-on: ubuntu-22.04 + permissions: + actions: read + attestations: write + contents: write + id-token: write steps: - uses: actions/checkout@v6 - name: Install build tools @@ -75,18 +80,54 @@ jobs: name: xprobe-cupti-cuda13-linux-x86_64 path: build/cuda13/cupti - name: Build release archive - run: scripts/package-release.sh + id: package + run: | + version=$(sed -n 's/^version = "\([^"]*\)"/\1/p' Cargo.toml | head -n 1) + test "$GITHUB_REF_NAME" = "v$version" + echo "name=xprobe-${version}-linux-x86_64" >> "$GITHUB_OUTPUT" + scripts/package-release.sh - name: Verify release installation - run: tests/install/test_install.sh dist/xprobe-*-linux-x86_64.tar.gz + run: >- + tests/install/test_install.sh + dist/${{ steps.package.outputs.name }}.tar.gz + - name: Generate SPDX SBOM + uses: anchore/sbom-action@e22c389904149dbc22b58101806040fa8d37a610 # v0 + with: + file: dist/${{ steps.package.outputs.name }}.tar.gz + format: spdx-json + output-file: dist/${{ steps.package.outputs.name }}.spdx.json + upload-artifact: false + upload-release-assets: false + - name: Verify SPDX SBOM + run: >- + scripts/check-release-sbom.py + dist/${{ steps.package.outputs.name }}.spdx.json + - name: Attest build provenance + uses: actions/attest@1e69f48acb82d1966a394da916b4c1698aa569d6 # v4 + with: + subject-path: dist/${{ steps.package.outputs.name }}.tar.gz + - name: Attest SPDX SBOM + uses: actions/attest@1e69f48acb82d1966a394da916b4c1698aa569d6 # v4 + with: + subject-path: dist/${{ steps.package.outputs.name }}.tar.gz + sbom-path: dist/${{ steps.package.outputs.name }}.spdx.json - uses: actions/upload-artifact@v7 with: name: xprobe-linux-x86_64 - path: dist/*.tar.gz* + path: | + dist/${{ steps.package.outputs.name }}.tar.gz + dist/${{ steps.package.outputs.name }}.tar.gz.sha256 + dist/${{ steps.package.outputs.name }}.spdx.json - uses: softprops/action-gh-release@v3 with: - files: dist/*.tar.gz* + files: | + dist/${{ steps.package.outputs.name }}.tar.gz + dist/${{ steps.package.outputs.name }}.tar.gz.sha256 + dist/${{ steps.package.outputs.name }}.spdx.json generate_release_notes: true - name: Verify public release archive env: + GH_TOKEN: ${{ github.token }} + XPROBE_VERIFY_ATTESTATIONS: "1" XPROBE_RELEASE_REPOSITORY: ${{ github.repository }} run: scripts/verify-public-release.sh "${GITHUB_REF_NAME#v}" diff --git a/justfile b/justfile index e9882cb..526555c 100644 --- a/justfile +++ b/justfile @@ -35,6 +35,7 @@ test-skill-install: test-install: sh -n install.sh tests/install/test_install.sh bash -n scripts/check-glibc-ceiling.sh scripts/verify-public-release.sh tests/install/test_glibc_ceiling.sh + python3 tests/install/test_release_sbom.py tests/install/test_glibc_ceiling.sh tests/install/test_install.sh diff --git a/scripts/check-release-sbom.py b/scripts/check-release-sbom.py new file mode 100755 index 0000000..9cfdff3 --- /dev/null +++ b/scripts/check-release-sbom.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 +import json +import pathlib +import sys + + +def main() -> None: + if len(sys.argv) != 2: + raise SystemExit("usage: check-release-sbom.py ") + + path = pathlib.Path(sys.argv[1]) + document = json.loads(path.read_text()) + if not str(document.get("spdxVersion", "")).startswith("SPDX-2."): + raise SystemExit(f"{path} is not an SPDX 2.x document") + if document.get("SPDXID") != "SPDXRef-DOCUMENT": + raise SystemExit(f"{path} has no SPDX document identifier") + if document.get("dataLicense") != "CC0-1.0": + raise SystemExit(f"{path} has an unexpected SPDX data license") + if not document.get("documentNamespace"): + raise SystemExit(f"{path} has no document namespace") + if not document.get("creationInfo", {}).get("created"): + raise SystemExit(f"{path} has no creation timestamp") + + packages = document.get("packages") + if not isinstance(packages, list) or not packages: + raise SystemExit(f"{path} contains no software packages") + for package in packages: + if not package.get("name") or not str(package.get("SPDXID", "")).startswith( + "SPDXRef-" + ): + raise SystemExit(f"{path} contains an invalid package entry") + + print(f"Verified SPDX SBOM with {len(packages)} packages") + + +if __name__ == "__main__": + main() diff --git a/scripts/verify-public-release.sh b/scripts/verify-public-release.sh index 7315f3e..e378abd 100755 --- a/scripts/verify-public-release.sh +++ b/scripts/verify-public-release.sh @@ -21,6 +21,7 @@ trap 'rm -rf "${temporary}"' EXIT HUP INT TERM archive=${temporary}/${package}.tar.gz checksum=${archive}.sha256 +sbom=${temporary}/${package}.spdx.json curl --fail --location --proto '=https' --tlsv1.2 \ --retry 5 --retry-delay 2 --retry-all-errors \ @@ -28,10 +29,29 @@ curl --fail --location --proto '=https' --tlsv1.2 \ curl --fail --location --proto '=https' --tlsv1.2 \ --retry 5 --retry-delay 2 --retry-all-errors \ --output "${checksum}" "${release_url}/${package}.tar.gz.sha256" +curl --fail --location --proto '=https' --tlsv1.2 \ + --retry 5 --retry-delay 2 --retry-all-errors \ + --output "${sbom}" "${release_url}/${package}.spdx.json" ( cd "${temporary}" sha256sum --check "$(basename "${checksum}")" ) +"${root}/scripts/check-release-sbom.py" "${sbom}" + +if [[ ${XPROBE_VERIFY_ATTESTATIONS:-0} == 1 ]]; then + command -v gh >/dev/null || { + echo "gh is required to verify release attestations" >&2 + exit 1 + } + attestation_policy=( + --repo "${repository}" + --signer-workflow "${repository}/.github/workflows/release.yml" + --deny-self-hosted-runners + ) + gh attestation verify "${archive}" "${attestation_policy[@]}" + gh attestation verify "${archive}" "${attestation_policy[@]}" \ + --predicate-type https://spdx.dev/Document +fi "${root}/tests/install/test_install.sh" "${archive}" @@ -89,5 +109,5 @@ verify_agent "${cuda13}" 13 exit 1 } -printf 'Verified public xprobe %s archive, installation, and 3 shipped ELFs\n' \ +printf 'Verified public xprobe %s archive, SBOM, installation, and 3 shipped ELFs\n' \ "${version}" diff --git a/tests/install/test_release_sbom.py b/tests/install/test_release_sbom.py new file mode 100755 index 0000000..05bd290 --- /dev/null +++ b/tests/install/test_release_sbom.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 +import json +import pathlib +import subprocess +import tempfile + + +def main() -> None: + workspace = pathlib.Path(__file__).resolve().parents[2] + checker = workspace / "scripts/check-release-sbom.py" + document = { + "spdxVersion": "SPDX-2.3", + "dataLicense": "CC0-1.0", + "SPDXID": "SPDXRef-DOCUMENT", + "name": "xprobe-release", + "documentNamespace": "https://example.invalid/xprobe/test", + "creationInfo": {"created": "2026-08-16T00:00:00Z", "creators": ["Tool: test"]}, + "packages": [ + { + "name": "xprobe-cli", + "SPDXID": "SPDXRef-Package-xprobe-cli", + "downloadLocation": "NOASSERTION", + "filesAnalyzed": False, + } + ], + } + with tempfile.TemporaryDirectory(prefix="xprobe-sbom-") as directory: + path = pathlib.Path(directory) / "release.spdx.json" + path.write_text(json.dumps(document)) + subprocess.run([checker, path], check=True, capture_output=True, text=True) + + +if __name__ == "__main__": + main() From 0dd6005beba5d53c7966c4849a27d74985bba90f Mon Sep 17 00:00:00 2001 From: itdevwu Date: Sun, 16 Aug 2026 23:57:45 +0800 Subject: [PATCH 3/3] =?UTF-8?q?=F0=9F=94=96=20chore:=20prepare=20version?= =?UTF-8?q?=200.5.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 2 +- Cargo.lock | 12 +++--- Cargo.toml | 2 +- README.md | 5 ++- docs/agent-integration.md | 10 ++++- docs/installation.md | 38 +++++++++++++------ install.sh | 4 +- .../references/setup.md | 8 ++-- tests/agent-contract/test_contract.py | 2 +- 9 files changed, 53 insertions(+), 30 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 813d883..4873901 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.20) -project(xprobe VERSION 0.5.0 LANGUAGES C) +project(xprobe VERSION 0.5.1 LANGUAGES C) include(CTest) diff --git a/Cargo.lock b/Cargo.lock index e7e2def..128e728 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -732,7 +732,7 @@ dependencies = [ [[package]] name = "xprobe-cli" -version = "0.5.0" +version = "0.5.1" dependencies = [ "clap", "serde_json", @@ -745,7 +745,7 @@ dependencies = [ [[package]] name = "xprobe-collector" -version = "0.5.0" +version = "0.5.1" dependencies = [ "libbpf-rs", "perf-event-open", @@ -755,7 +755,7 @@ dependencies = [ [[package]] name = "xprobe-core" -version = "0.5.0" +version = "0.5.1" dependencies = [ "cpp_demangle", "nix", @@ -766,7 +766,7 @@ dependencies = [ [[package]] name = "xprobe-correlator" -version = "0.5.0" +version = "0.5.1" dependencies = [ "regex", "serde_json", @@ -775,7 +775,7 @@ dependencies = [ [[package]] name = "xprobe-exporter" -version = "0.5.0" +version = "0.5.1" dependencies = [ "serde_json", "xprobe-protocol", @@ -783,7 +783,7 @@ dependencies = [ [[package]] name = "xprobe-protocol" -version = "0.5.0" +version = "0.5.1" dependencies = [ "schemars", "serde", diff --git a/Cargo.toml b/Cargo.toml index e28ad6b..f5a2558 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ members = [ resolver = "2" [workspace.package] -version = "0.5.0" +version = "0.5.1" edition = "2024" license = "Apache-2.0" repository = "https://github.com/itdevwu/xprobe" diff --git a/README.md b/README.md index e950b14..81cb6d1 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ only installation action required from the user: ```bash npx skills@1 add \ - https://github.com/itdevwu/xprobe/tree/v0.5.0/skills/xprobe-measure-latency \ + https://github.com/itdevwu/xprobe/tree/v0.5.1/skills/xprobe-measure-latency \ --global ``` @@ -32,7 +32,8 @@ When invoked, the Skill checks for the matching `xprobe` CLI and installs or repairs it under a writable prefix before profiling. It can then diagnose and adjust path, permission, NVIDIA, CUDA, or CUPTI problems from live evidence. Node.js is only needed for Skill installation, not for xprobe itself. See -[Installation](docs/installation.md) for direct CLI use and archive verification. +[Installation](docs/installation.md) for direct CLI use, SPDX SBOMs, and +GitHub-hosted build attestation verification. ## Measure diff --git a/docs/agent-integration.md b/docs/agent-integration.md index a9fa126..6184b8c 100644 --- a/docs/agent-integration.md +++ b/docs/agent-integration.md @@ -17,7 +17,7 @@ repairs the matching xprobe CLI itself: ```bash npx skills@1 add \ - https://github.com/itdevwu/xprobe/tree/v0.5.0/skills/xprobe-measure-latency \ + https://github.com/itdevwu/xprobe/tree/v0.5.1/skills/xprobe-measure-latency \ --global ``` @@ -52,6 +52,14 @@ read-only `validate`. The Agent must inspect the quality fields specific to each schema and cannot equate sample proportions, aggregate duration shares, or overlapping capture windows with exact causality. +For containerized live targets, the Skill keeps orchestration in the caller. It +resolves an explicit application container, runs xprobe in the same PID and +mount namespaces, reacquires PID plus procfs start time there, and preserves +per-command artifacts outside the container. It never substitutes a sidecar or +host PID, and reports capabilities that cannot be added to an already-running +container. Narrow exact captures begin with bounded record headroom and an +`--events-out` artifact instead of defaulting to six-figure capacity. + ## Contract test ```bash diff --git a/docs/installation.md b/docs/installation.md index 32bf86d..66e3f31 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -13,14 +13,15 @@ user needs to run: ```bash npx skills@1 add \ - https://github.com/itdevwu/xprobe/tree/v0.5.0/skills/xprobe-measure-latency \ + https://github.com/itdevwu/xprobe/tree/v0.5.1/skills/xprobe-measure-latency \ --global ``` -The Skill verifies `xprobe --version`, installs the matching release when needed, -then runs `doctor` before it profiles. It has the live context needed to adjust -PATH, prefix, permission, NVIDIA, CUDA, and CUPTI issues. Node.js is required -only for Skill installation. +The Skill verifies `xprobe --version`, installs the matching release when +needed, and runs `doctor` when capabilities are unknown or an environment check +fails. It has the live context needed to adjust PATH, prefix, namespace, +permission, NVIDIA, CUDA, and CUPTI issues. Node.js is required only for Skill +installation. ## Direct CLI installation @@ -28,7 +29,7 @@ The versioned bootstrap installs to `~/.local` without root access: ```bash curl --proto '=https' --tlsv1.2 -fsSL \ - https://raw.githubusercontent.com/itdevwu/xprobe/v0.5.0/install.sh | sh + https://raw.githubusercontent.com/itdevwu/xprobe/v0.5.1/install.sh | sh ``` The bootstrap downloads the release archive and its SHA256 file, verifies the @@ -47,7 +48,7 @@ prefix, download the script and pass `--prefix`: ```bash curl --proto '=https' --tlsv1.2 -fsSLO \ - https://raw.githubusercontent.com/itdevwu/xprobe/v0.5.0/install.sh + https://raw.githubusercontent.com/itdevwu/xprobe/v0.5.1/install.sh sh install.sh --prefix /opt/xprobe ``` @@ -59,19 +60,32 @@ script with `sudo`. The installer never elevates privileges itself. For a fully explicit archive workflow: ```bash -version=0.5.0 +version=0.5.1 base=https://github.com/itdevwu/xprobe/releases/download/v$version archive=xprobe-$version-linux-x86_64.tar.gz curl --proto '=https' --tlsv1.2 -fLO "$base/$archive" curl --proto '=https' --tlsv1.2 -fLO "$base/$archive.sha256" +curl --proto '=https' --tlsv1.2 -fLO "$base/${archive%.tar.gz}.spdx.json" sha256sum --check "$archive.sha256" +gh attestation verify "$archive" \ + --repo itdevwu/xprobe \ + --signer-workflow itdevwu/xprobe/.github/workflows/release.yml \ + --deny-self-hosted-runners +gh attestation verify "$archive" \ + --repo itdevwu/xprobe \ + --signer-workflow itdevwu/xprobe/.github/workflows/release.yml \ + --deny-self-hosted-runners \ + --predicate-type https://spdx.dev/Document tar -xzf "$archive" "./xprobe-$version-linux-x86_64/install.sh" ``` -The unpacked package can also be run in place as long as its `bin` and `lib` -layout remains together. +The checksum detects archive corruption. The two `gh` commands additionally +require signed provenance and SPDX claims from this repository's release +workflow on a GitHub-hosted runner. The downloaded SPDX JSON is the inspectable +release SBOM. The unpacked package can also be run in place as long as its +`bin` and `lib` layout remains together. ## Upgrade and removal @@ -92,7 +106,7 @@ missing or damaged, manually install the complete version-matched directory: ```bash npx skills@1 add \ - https://github.com/itdevwu/xprobe/tree/v0.5.0/skills/xprobe-measure-latency \ + https://github.com/itdevwu/xprobe/tree/v0.5.1/skills/xprobe-measure-latency \ --global ``` @@ -101,7 +115,7 @@ installation names the target explicitly: ```bash npx --yes skills@1 add \ - https://github.com/itdevwu/xprobe/tree/v0.5.0/skills/xprobe-measure-latency \ + https://github.com/itdevwu/xprobe/tree/v0.5.1/skills/xprobe-measure-latency \ --agent codex --global --copy --yes ``` diff --git a/install.sh b/install.sh index cddd7fa..3fe0d1c 100755 --- a/install.sh +++ b/install.sh @@ -2,7 +2,7 @@ set -eu repository=${XPROBE_REPOSITORY:-itdevwu/xprobe} -version=${XPROBE_VERSION:-0.5.0} +version=${XPROBE_VERSION:-0.5.1} if [ -n "${XPROBE_PREFIX:-}" ]; then prefix=$XPROBE_PREFIX elif [ -n "${HOME:-}" ]; then @@ -19,7 +19,7 @@ Install a released xprobe binary and its CUDA Agents. Usage: install.sh [--version VERSION] [--prefix DIR] [--uninstall] Options: - --version VERSION Release to install (default: 0.5.0) + --version VERSION Release to install (default: 0.5.1) --prefix DIR Installation prefix (default: $HOME/.local) --uninstall Remove xprobe from the selected prefix -h, --help Show this help diff --git a/skills/xprobe-measure-latency/references/setup.md b/skills/xprobe-measure-latency/references/setup.md index 8040537..31348a4 100644 --- a/skills/xprobe-measure-latency/references/setup.md +++ b/skills/xprobe-measure-latency/references/setup.md @@ -23,7 +23,7 @@ installing under `~/.local`: ```bash curl --proto '=https' --tlsv1.2 -fsSL \ - https://raw.githubusercontent.com/itdevwu/xprobe/v0.5.0/install.sh \ + https://raw.githubusercontent.com/itdevwu/xprobe/v0.5.1/install.sh \ -o /tmp/xprobe-install.sh sh /tmp/xprobe-install.sh export PATH="$HOME/.local/bin:$PATH" @@ -69,7 +69,7 @@ host glibc instead. This is a local-use fallback, not permission to weaken the release package's `GLIBC_2.34` ceiling. ```bash -git clone --depth 1 --branch v0.5.0 https://github.com/itdevwu/xprobe.git +git clone --depth 1 --branch v0.5.1 https://github.com/itdevwu/xprobe.git cd xprobe mamba env create --file environment.yml mamba run -n xprobe-dev just build @@ -112,7 +112,7 @@ release directory through the Agent Skills CLI: ```bash npx skills@1 add \ - https://github.com/itdevwu/xprobe/tree/v0.5.0/skills/xprobe-measure-latency \ + https://github.com/itdevwu/xprobe/tree/v0.5.1/skills/xprobe-measure-latency \ --global ``` @@ -120,7 +120,7 @@ For non-interactive automation, select the host explicitly: ```bash npx --yes skills@1 add \ - https://github.com/itdevwu/xprobe/tree/v0.5.0/skills/xprobe-measure-latency \ + https://github.com/itdevwu/xprobe/tree/v0.5.1/skills/xprobe-measure-latency \ --agent codex --global --copy --yes ``` diff --git a/tests/agent-contract/test_contract.py b/tests/agent-contract/test_contract.py index bb7b87c..c5933bb 100755 --- a/tests/agent-contract/test_contract.py +++ b/tests/agent-contract/test_contract.py @@ -224,7 +224,7 @@ def check_skill(workspace: pathlib.Path) -> None: ): assert required in normalized_cli_contract for required in ( - "v0.5.0/install.sh", + "v0.5.1/install.sh", "xprobe `0.5.x`", "npx skills@1 add", "xprobe --version",