Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
fe2dd3e
fix(sandbox): audit every endpoint in a proposal (#4313)
ericcurtin Oct 8, 2026
b959bb6
fix(policy): share endpoint path matching between Rust and Rego (#4336)
alangou Oct 8, 2026
4f48833
Merge remote-tracking branch 'upstream/main'
moulalis Oct 9, 2026
a991b8e
fix(sandbox): preserve exec capacity under failure and load (#4324)
cjagwani Oct 9, 2026
ca9d6cd
Merge remote-tracking branch 'upstream/main'
moulalis Oct 9, 2026
ef9c33b
Merge remote-tracking branch 'upstream/main'
moulalis Oct 9, 2026
40e0142
fix(helm): improve GatewayClass overrides (#4345)
danehans Oct 9, 2026
e1f3c82
fix(deps): update noyalib and preserve policy null rejection (#4348)
drew Oct 9, 2026
4a9a2a1
Merge remote-tracking branch 'upstream/main'
alexxfan Oct 9, 2026
4f6b3ad
Merge remote-tracking branch 'upstream/main'
alexxfan Oct 9, 2026
a1f88d6
Merge remote-tracking branch 'upstream/main'
moulalis Oct 9, 2026
82bc756
feat(snap): run gateway as a user service by default (#4099)
olivercalder Oct 9, 2026
fa22ccd
fix(ci): publish merge queue E2E statuses (#4370)
elezar Oct 9, 2026
78975ea
chore(test-guest): remove snap reproduction helper (#4368)
elezar Oct 9, 2026
4f0cb1e
Merge remote-tracking branch 'upstream/main'
alexxfan Oct 9, 2026
5ab677e
chore(deps): bump mermaid in /scripts/lint-mermaid (#4369)
dependabot[bot] Oct 9, 2026
f76ea5f
CARRY: fix(konflux): make the OpenClaw image work in Kubernetes sandb…
andre-motta Oct 9, 2026
0015d52
CARRY: feat(odh): add reusable e2e image test foundation
gmenher Oct 7, 2026
a071d8e
CI: update Tekton pipelines
github-actions[bot] Oct 9, 2026
a7c5969
Merge remote-tracking branch 'upstream/main'
moulalis Oct 9, 2026
47dc122
Merge remote-tracking branch 'upstream/main'
moulalis Oct 9, 2026
4c1b16a
feat(server): add operator-only provider credential retrieval (#4357)
sjenning Oct 9, 2026
92d722e
Merge pull request #92 from opendatahub-io/fix/openclaw-sandbox-runti…
andre-motta Oct 9, 2026
a770eb1
CARRY: ci(agentic-ci): lint Rust in the autofix sandbox overlay
andre-motta Oct 9, 2026
72ff906
Merge pull request #94 from opendatahub-io/ci-openshell-1791561292
andre-motta Oct 9, 2026
61d4210
feat(ci): gate merges on maintainer approval via a silent status chec…
purp Oct 9, 2026
0eafb4f
Merge pull request #95 from opendatahub-io/ci/agentic-ci-rust-overlay…
andre-motta Oct 9, 2026
9679615
sync pipelineruns with konflux-central - 787e255
rhods-devops-app[bot] Oct 9, 2026
d789ec6
test(tmachine): use K3s ClusterIP, wait for gateway, add failure diag…
matthewgrossman Oct 9, 2026
ddfa1c7
Merge remote-tracking branch 'upstream/main'
moulalis Oct 9, 2026
8aa5d65
Merge remote-tracking branch 'upstream/main'
moulalis Oct 9, 2026
0d39d20
Merge remote-tracking branch 'upstream/main'
moulalis Oct 10, 2026
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
84 changes: 70 additions & 14 deletions .agentic-ci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,27 @@
# https://github.com/opendatahub-io/agentic-ci, docs/sandbox-profiles.md).
# autofix reads it from the base branch before the agent runs. An overlay
# may set toolchains, setup, validate, skips and env, and open only the
# goproxy, npm and pypi egress presets; anything wider is configured
# centrally in autofix.
# goproxy, npm, pypi and crates egress presets; anything wider is
# configured centrally in autofix.
#
# Toolchains come from the agentic-ci catalog at the versions pinned in
# mise.toml (python reads .python-version). If an upstream sync bumps a
# mise.toml (python reads .python-version, rust reads rust-toolchain.toml). If an upstream sync bumps a
# version in mise.toml, bump it here too: mise does not install it, so
# the validate steps fail until the pins match. Setup installs mise from
# npm, links the catalog toolchains into mise so it never downloads
# them, and installs the mise npm/go tools through the presets. Rust and
# container, cluster and VM tasks cannot run in the sandbox and are
# declared as skips.
# them, and installs the mise npm/go tools through the presets. It also
# fetches the crates of every tracked Cargo.lock through the crates
# preset, so the Rust checks build offline, and runs rust:lint once to
# warm the build cache, so clippy after the agent only rebuilds what it
# changed. mise leaves rust alone (MISE_DISABLE_TOOLS) and the catalog's
# cargo comes first on PATH.
# cargo-deny, nextest and the container, cluster and VM tasks cannot run
# in the sandbox and are declared as skips.

sandbox:
toolchains:
python: auto # reads .python-version
rust: auto # reads rust-toolchain.toml
node: "24.15.0"
go: "1.26.7"
buf: "1.72.0"
Expand All @@ -28,6 +34,7 @@ sandbox:
- goproxy
- npm
- pypi
- crates

env:
MISE_YES: "1"
Expand Down Expand Up @@ -62,12 +69,52 @@ sandbox:
mise link "buf@$(buf --version)" "$(root buf)"
mise link "helm@$(helm version --template '{{.Version}}' | sed 's/^v//')" \
"$(dirname "$(command -v helm)")"
mise install \
npm:markdownlint-cli2 \
go:github.com/golangci/golangci-lint/v2/cmd/golangci-lint \
go:google.golang.org/protobuf/cmd/protoc-gen-go \
go:google.golang.org/grpc/cmd/protoc-gen-go-grpc \
go:golang.org/x/tools/cmd/goimports
# A few go module fetches can fail at once when the sandbox's DNS
# relay times out; mise install is idempotent, so retry it.
ok=""
for attempt in 1 2 3; do
if mise install \
npm:markdownlint-cli2 \
go:github.com/golangci/golangci-lint/v2/cmd/golangci-lint \
go:google.golang.org/protobuf/cmd/protoc-gen-go \
go:google.golang.org/grpc/cmd/protoc-gen-go-grpc \
go:golang.org/x/tools/cmd/goimports; then
ok=1
break
fi
echo "mise install failed (attempt $attempt)" >&2
sleep 15
done
test -n "$ok"

- name: rust-crates
timeout: 1800
run: |
set -euo pipefail
# mise.toml sets RUSTC_WRAPPER=sccache for every `mise run`, and
# sccache is a GitHub download no preset opens. A pass-through
# wrapper runs rustc directly, without a cache.
mkdir -p "$HOME/.local/bin"
printf '#!/bin/sh\nexec "$@"\n' > "$HOME/.local/bin/sccache"
chmod +x "$HOME/.local/bin/sccache"
git ls-files -z -- ':(glob)**/Cargo.lock' |
while IFS= read -r -d '' lockfile; do
cargo fetch --locked --manifest-path "${lockfile%Cargo.lock}Cargo.toml"
done

- name: rust-warm
timeout: 3600
run: |
set -uo pipefail
# Build once before the agent with the exact rust:lint commands, so
# the agent's own clippy runs and the rust-lint validate step only
# rebuild what changed. A lint failure on the base commit does not
# matter here: the build cache is what this step is for.
status=0
mise run rust:lint || status=$?
if [ "$status" -ne 0 ]; then
echo "rust:lint exited $status during the warm-up; continuing, the build cache is what this step is for"
fi

validate:
- {name: python-lint, kind: lint, run: mise run python:lint, timeout: 600}
Expand All @@ -77,13 +124,22 @@ sandbox:
- {name: helm-lint, kind: lint, run: mise run helm:lint, timeout: 600}
- {name: go-format, kind: lint, run: mise run go:format:check, timeout: 600}
- {name: go-lint, kind: lint, run: mise run go:lint, timeout: 900}
- {name: rust-format, kind: lint, run: mise run rust:format:check, timeout: 600}
- {name: rust-lint, kind: lint, run: mise run rust:lint, timeout: 3600}

skips:
- match: "rust:*, cargo, clippy, nextest, cargo-deny"
reason: "No Rust toolchain or C compiler in the sandbox, so Rust builds, tests, lints and dependency checks rely on CI"
- match: "rust:deny, cargo deny, test:rust, cargo nextest, rust:verify:*"
reason: "cargo-deny and nextest are not in the toolchain catalog, and the Rust tests and verify scripts are left to CI"
- match: "docker:*, gateway:*, sandbox:*, vm:*, package:*"
reason: "Image, VM and package builds need a container runtime or privileged features the sandbox blocks"
- match: "e2e:*, test:*, helm:test"
reason: "End-to-end and cluster tests need a container runtime, a running gateway or a live cluster"
- match: "license:check"
reason: "Fails on main today: midstream files such as .tekton/*.yaml have no SPDX header"

# Build output stays in the sandbox and is never downloaded back.
discard_before_download:
- target
- e2e/rust/target
- examples/governance-interceptor/target
- examples/supervisor-middleware-content-guard/target
5 changes: 3 additions & 2 deletions .agents/skills/helm-dev-environment/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -236,8 +236,9 @@ cd ../../..
KUBECONFIG=kubeconfig mise run helm:gateway:apply
```

`values-gateway.yaml` creates a `Gateway` (listener on port 80, class `eg`) and
`GRPCRoute` in the `openshell` namespace. The `high-availability` profile
`values-gateway.yaml` creates the `openshell` `Gateway` (listener on port 80,
class `eg`) and an `openshell` `GRPCRoute` in the `openshell` namespace. The
`high-availability` profile
installs the Envoy Gateway Helm chart and layers both
`values-high-availability.yaml` and `values-gateway.yaml` onto the OpenShell
release.
Expand Down
2 changes: 1 addition & 1 deletion .agents/skills/test-release-canary/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ Loopback registration auto-derives the gateway name to `openshell` if `--name` i
| `macos`/`ubuntu-deb`/`fedora` job fails on `install.sh` | Dev release missing an asset, checksum mismatch, or `install.sh` regression on this branch. | Job log around the `curl … install.sh \| sh` step. |
| Sandbox create or exec fails | Published sandbox and supervisor artifacts are missing, incompatible, or cannot establish the protected runtime channel. | Gateway logs plus Docker, Podman, VM, Snap, or Kubernetes runtime diagnostics for the job. |
| `macos`/`ubuntu-deb`/`fedora` job fails on `openshell status` | Local gateway service did not start (systemd/brew/podman). Often a driver issue. | Service logs in the job log; `OPENSHELL_COMPUTE_DRIVER` env in the "Ensure …" step. |
| `ubuntu-snap-system-docker` fails during `install.sh` | System Docker was unavailable, the edge revision or automatic interfaces were unavailable, or the gateway did not become reachable. | Failure diagnostics dump system Docker, snap service/connection/change state, gateway and snapd journals, snap logs, and port 17670 listeners. |
| `ubuntu-snap-system-docker` fails during `install.sh` | System Docker was unavailable to the runner user, the edge revision or automatic interfaces were unavailable, or the user gateway did not become reachable. | Failure diagnostics dump system Docker, snap service/connection/change state, user and legacy gateway journals, snap logs, and port 17670 listeners. |
| `ubuntu-snap-system-docker` fails during the prover checks | The prover artifact is missing or packaged for the wrong architecture, `openshell.prover` is not exposed or confined to read the test policies, or its solver linkage is not runnable. | The `Verify Snap installation` and `Check a policy boundary with the Snap prover` steps, plus `snap info openshell` and `snap connections openshell`. |
| `ubuntu-snap-docker-preflight` unexpectedly succeeds | The installer no longer fails before installing the OpenShell snap when Docker is absent or supplied by the Docker snap. | Inspect `install.log`, `docker-snap.log`, `snap list`, and snapd changes. |
| `kubernetes` job fails on `helm install --wait` | Chart did not deploy in 5 min — usually image pull failure or readiness probe failing. | "Diagnostics on failure" step dumps `helm status`, manifest, pod describe, pod logs. |
Expand Down
34 changes: 32 additions & 2 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -1,5 +1,35 @@
# Broad ownership — core team reviews everything
## Broad ownership — core team reviews everything
* @NVIDIA/openshell-codeowners @mrunalp @sjenning @derekwaynecarr

# Vouch list — maintainers only (bot commits bypass, but manual edits need review)
## Vouch list — maintainers only (bot commits bypass, but manual edits need review)
.github/VOUCHED.td @NVIDIA/openshell-codeowners

## Merge enforcement — these paths decide who can merge, so they notify the full
# list of maintainers to raise scrutiny on changes here.
#
# PR checks run local to the branch, so unless the check specifically avoids
# using branch-local files, a PR could change its own gates and appear to pass
# them. Covering the required checks here closes that gap.
#
# The whole workflows directory is covered, not just the approval gate, because
# any workflow can claim a required check context or ask for a broader token.
#
# Normally, we'd use a GitHub team to cover all maintainers, but org-level
# policies prevent outside collaborators from being on a GH team at this time
# so we spell them out explicitly.

# Core check mechanisms
/.github/workflows/ @NVIDIA/openshell-codeowners @mrunalp @sjenning @derekwaynecarr
/.github/actions/ @NVIDIA/openshell-codeowners @mrunalp @sjenning @derekwaynecarr
/.github/CODEOWNERS @NVIDIA/openshell-codeowners @mrunalp @sjenning @derekwaynecarr
/.github/zizmor.yml @NVIDIA/openshell-codeowners @mrunalp @sjenning @derekwaynecarr

# `MAINTAINERS.md` maintainer approval checks
/MAINTAINERS.md @NVIDIA/openshell-codeowners @mrunalp @sjenning @derekwaynecarr
/tasks/scripts/check_maintainer_approval.py @NVIDIA/openshell-codeowners @mrunalp @sjenning @derekwaynecarr
/tasks/scripts/alert_maintainer_change.py @NVIDIA/openshell-codeowners @mrunalp @sjenning @derekwaynecarr
/tasks/scripts/check_maintainer_approval_test.py @NVIDIA/openshell-codeowners @mrunalp @sjenning @derekwaynecarr
/tasks/scripts/alert_maintainer_change_test.py @NVIDIA/openshell-codeowners @mrunalp @sjenning @derekwaynecarr

# Auditable SBOM check
/tasks/scripts/verify-image-sbom.sh @NVIDIA/openshell-codeowners @mrunalp @sjenning @derekwaynecarr
9 changes: 9 additions & 0 deletions .github/workflows/integration-runner.yml
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,12 @@ jobs:
# Retry dependency preparation, then execute the test suite once.
nix build --no-link .#tmachine || nix build --no-link .#tmachine
nix run .#tmachine -- test "${ENVIRONMENT}" "${INSTALLER}" "${TESTSUITE}"

- name: Upload tmachine diagnostics
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: tmachine-diagnostics-${{ inputs.category }}-${{ matrix.environment }}-${{ matrix.installer }}-${{ matrix.testsuite }}
path: artifacts/tmachine-diagnostics
if-no-files-found: ignore
retention-days: 7
73 changes: 73 additions & 0 deletions .github/workflows/maintainer-approval.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

name: Maintainer Approval

on:
merge_group:
types: [checks_requested]
pull_request_review:
types: [submitted, dismissed]

permissions:
contents: read
pull-requests: read

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

jobs:
maintainer-approval:
# This name is the required status check context. Changing it silently
# breaks the ruleset entry that gates merges on this job.
name: OpenShell / Maintainer Approval
if: github.repository_owner == 'NVIDIA'
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
# Check out the default branch, never the pull request head. Both the
# maintainer list and the decision helper must come from main: reading
# either from the pull request ref would let a contributor add themselves
# to the list, or rewrite the decision logic, and self-approve.
- name: Check out the maintainer list and helper
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: main
sparse-checkout: |
MAINTAINERS.md
tasks/scripts/check_maintainer_approval.py
sparse-checkout-cone-mode: false
persist-credentials: false

- name: Require an approving review from a maintainer
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_REPO: ${{ github.repository }}
PR_NUMBER: ${{ github.event.pull_request.number }}
# merge_group carries no pull request in its payload, but the queue
# branch names the entry: gh-readonly-queue/main/pr-3027-<base sha>.
MERGE_GROUP_REF: ${{ github.ref_name }}
shell: bash
run: |
set -euo pipefail

if [ -z "${PR_NUMBER:-}" ]; then
if [[ "$MERGE_GROUP_REF" =~ /pr-([0-9]+)-[0-9a-f]+$ ]]; then
PR_NUMBER="${BASH_REMATCH[1]}"
else
# Fail closed: an unrecognised ref must never satisfy the gate.
echo "::error::No pull request resolved from '$MERGE_GROUP_REF'."
exit 1
fi
fi

gh api --paginate "repos/$GH_REPO/pulls/$PR_NUMBER/reviews" --jq '.[]' \
| jq -s '.' > reviews.json

# Exits non-zero when no maintainer's latest decisive review is an
# approval, and when MAINTAINERS.md yields no handles. That exit code
# is the check result; nothing is posted anywhere.
python3 tasks/scripts/check_maintainer_approval.py \
--maintainers MAINTAINERS.md \
--reviews reviews.json
107 changes: 107 additions & 0 deletions .github/workflows/maintainers-change-alert.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

name: Maintainers Change Alert

on:
pull_request_target:
types: [opened, reopened, synchronize]
paths:
- MAINTAINERS.md

permissions:
contents: read
pull-requests: write

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
cancel-in-progress: true

jobs:
describe-change:
name: Comment on the approver set change if MAINTAINERS.md has changed
if: github.repository_owner == 'NVIDIA'
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
# Default branch only. The helper must be the reviewed version, not
# whatever the pull request happens to contain.
- name: Check out the change-alert helper
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: main
sparse-checkout: tasks/scripts/alert_maintainer_change.py
sparse-checkout-cone-mode: false
persist-credentials: false

- name: Post the maintainer delta
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_REPO: ${{ github.repository }}
PR_NUMBER: ${{ github.event.pull_request.number }}
BASE_SHA: ${{ github.event.pull_request.base.sha }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
# Single source of truth for the comment marker: the script emits
# it as the first line of the body, the lookup below matches on it.
COMMENT_MARKER: "<!-- maintainer-approval-delta -->"
shell: bash
run: |
set -euo pipefail

# Fetching file contents is reading data, not executing it. The head
# revision is never checked out or run.
#
# A 404 means the file genuinely does not exist at that revision — a
# pull request that adds or deletes MAINTAINERS.md — and yields an
# empty side of the comparison. Every other failure is fatal: an empty
# file from a rate limit or a 5xx would render as "every maintainer
# was just added" or "nothing changed", both of which mislead the
# reviewer about who can merge code.
fetch_maintainers() {
local ref="$1" out="$2" err
err="$(mktemp)"
if gh api -H "Accept: application/vnd.github.raw" \
"repos/$GH_REPO/contents/MAINTAINERS.md?ref=$ref" > "$out" 2>"$err"; then
rm -f "$err"
return 0
fi
if grep -q 'HTTP 404' "$err"; then
rm -f "$err"
: > "$out"
return 0
fi
echo "::error::Could not fetch MAINTAINERS.md at $ref"
cat "$err" >&2
rm -f "$err"
return 1
}

fetch_maintainers "$BASE_SHA" before.md
fetch_maintainers "$HEAD_SHA" after.md

# An unparseable result exits non-zero, but the comment explaining
# why still has to be posted before this job fails.
status=0
python3 tasks/scripts/alert_maintainer_change.py \
--before before.md --after after.md > body.md || status=$?

# No output means the approver set did not change. A comment saying
# so is noise, so post nothing.
if [ -s body.md ]; then
cat body.md >> "$GITHUB_STEP_SUMMARY"

# Update the existing comment rather than stacking one per push.
COMMENT_ID=$(gh api --paginate "repos/$GH_REPO/issues/$PR_NUMBER/comments" \
--jq '.[] | select(.body | startswith($ENV.COMMENT_MARKER)) | .id' \
| head -n 1)

if [ -n "$COMMENT_ID" ]; then
gh api --method PATCH "repos/$GH_REPO/issues/comments/$COMMENT_ID" \
-F "body=@body.md" >/dev/null
else
gh api --method POST "repos/$GH_REPO/issues/$PR_NUMBER/comments" \
-F "body=@body.md" >/dev/null
fi
fi

exit "$status"
Loading
Loading