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
51 changes: 46 additions & 5 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ on:
- "v*"

permissions:
contents: write
contents: read

jobs:
cupti-agent:
Expand Down Expand Up @@ -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
Expand All @@ -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}"
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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)

Expand Down
12 changes: 6 additions & 6 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
```

Expand All @@ -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

Expand Down
10 changes: 9 additions & 1 deletion docs/agent-integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
```

Expand Down Expand Up @@ -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
Expand Down
38 changes: 26 additions & 12 deletions docs/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,23 @@ 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

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
Expand All @@ -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
```

Expand All @@ -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

Expand All @@ -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
```

Expand All @@ -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
```

Expand Down
4 changes: 2 additions & 2 deletions install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
1 change: 1 addition & 0 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
37 changes: 37 additions & 0 deletions scripts/check-release-sbom.py
Original file line number Diff line number Diff line change
@@ -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 <sbom.spdx.json>")

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()
22 changes: 21 additions & 1 deletion scripts/verify-public-release.sh
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,37 @@ 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 \
--output "${archive}" "${release_url}/${package}.tar.gz"
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}"

Expand Down Expand Up @@ -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}"
Loading