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
6 changes: 6 additions & 0 deletions .github/actionlint.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

self-hosted-runner:
labels:
- ubuntu-26.04
39 changes: 30 additions & 9 deletions .github/workflows/portable-profile-e2e.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,10 @@ concurrency:

jobs:
rootless-linux:
runs-on: ubuntu-latest
runs-on: ubuntu-26.04
Comment thread
senthilr-nv marked this conversation as resolved.
timeout-minutes: 25
env:
PODMAN_APT_VERSION: "5.7.0+ds2-3build1"
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
Expand Down Expand Up @@ -70,14 +72,17 @@ jobs:
run: |
set -euo pipefail
sudo apt-get update
sudo apt-get install --yes fuse-overlayfs passt podman slirp4netns uidmap
package_podman="/usr/bin/podman"
test -x "$package_podman"
runtime_bin="$(mktemp -d "${RUNNER_TEMP}/nemoclaw-rootless-runtime.XXXXXX")"
ln -s "$package_podman" "$runtime_bin/podman"
export PATH="$runtime_bin:$PATH"
printf '%s\n' "$runtime_bin" >> "$GITHUB_PATH"
test "$(readlink -f "$(command -v podman)")" = "$package_podman"
sudo apt-get install --yes \
apparmor \
fuse-overlayfs \
passt \
slirp4netns \
uidmap \
"podman=$PODMAN_APT_VERSION"
package_version="$(dpkg-query --show --showformat='${Version}' podman)"
version="$(podman --version)"
test "$package_version" = "$PODMAN_APT_VERSION"
test "$version" = "podman version 5.7.0"
runtime_dir="/run/user/$(id -u)"
sudo install -d -m 700 -o "$(id -u)" -g "$(id -g)" "$runtime_dir"
if ! grep -q "^${USER}:" /etc/subuid; then
Expand All @@ -90,6 +95,22 @@ jobs:
pasta --version
docker --version

- name: Apply Ubuntu pasta signal policy correction
shell: bash
run: |
set -euo pipefail
pasta_profile="/etc/apparmor.d/usr.bin.pasta"
signal_rule='^[[:space:]]*signal[[:space:]]+\(receive\)[[:space:]]+peer=podman,[[:space:]]*$'
test -f "$pasta_profile"
test "$(grep -Fc 'include <abstractions/pasta>' "$pasta_profile")" -eq 1
if ! grep -Eq "$signal_rule" "$pasta_profile"; then
sudo sed -i \
'/^[[:space:]]*include <abstractions\/pasta>[[:space:]]*$/a\ signal (receive) peer=podman,' \
"$pasta_profile"
fi
test "$(grep -Ec "$signal_rule" "$pasta_profile")" -eq 1
sudo apparmor_parser -r "$pasta_profile"

- name: Exercise portable profile in the rootless environment
env:
E2E_ARTIFACT_DIR: ${{ github.workspace }}/e2e-artifacts/portable-profile
Expand Down
2 changes: 1 addition & 1 deletion ci/source-shape-test-budget.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@
},
{
"file": "test/e2e/support/portable-profile-rootless-runtime-workflow.test.ts",
"test": "compiles the managed inference catalogue after dependency installation and before the live E2E test (#9680)",
"test": "keeps actionlint and live E2E on Ubuntu 26.04 and Podman 5.7 (#9006)",
"category": "compatibility"
},
{
Expand Down
75 changes: 46 additions & 29 deletions test/e2e/support/portable-profile-rootless-runtime-workflow.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,48 +3,65 @@

import { describe, expect, it } from "vitest";

import { readYaml, type Workflow, type WorkflowStep } from "../../helpers/e2e-workflow-contract";

function rootlessLinuxStep(name: string): WorkflowStep {
const workflow = readYaml<Workflow>(".github/workflows/portable-profile-e2e.yaml");
const step = workflow.jobs["rootless-linux"]?.steps?.find((candidate) => candidate.name === name);
expect(step).toBeDefined();
return step!;
}
import { readYaml, type Workflow } from "../../helpers/e2e-workflow-contract";

describe("portable profile rootless runtime workflow", () => {
// source-shape-contract: compatibility -- The rootless workflow must compile the exact candidate managed inference catalogue before the live E2E test imports it
it("compiles the managed inference catalogue after dependency installation and before the live E2E test (#9680)", () => {
// source-shape-contract: compatibility -- Actionlint and the workflow must share Ubuntu 26.04 while the workflow compiles the candidate catalogue, pins Podman 5.7, and corrects the pasta AppArmor policy before live E2E
it("keeps actionlint and live E2E on Ubuntu 26.04 and Podman 5.7 (#9006)", () => {
const actionlint = readYaml<{ "self-hosted-runner"?: { labels?: string[] } }>(
".github/actionlint.yaml",
);
Comment thread
senthilr-nv marked this conversation as resolved.
const workflow = readYaml<Workflow>(".github/workflows/portable-profile-e2e.yaml");
const steps = workflow.jobs["rootless-linux"]?.steps ?? [];
const job = workflow.jobs["rootless-linux"];
const steps = job?.steps ?? [];
const provision = steps.find(
(step) => step.name === "Provision restricted rootless Linux runtime",
)?.run;
const policy = steps.find(
(step) => step.name === "Apply Ubuntu pasta signal policy correction",
)?.run;
const dependencyInstallIndex = steps.findIndex(
(step) => step.name === "Install root dependencies",
);
const catalogueCompileIndex = steps.findIndex((step) => step.run === "npm run catalog:compile");
const provisionIndex = steps.findIndex(
(step) => step.name === "Provision restricted rootless Linux runtime",
);
const policyIndex = steps.findIndex(
(step) => step.name === "Apply Ubuntu pasta signal policy correction",
);
const liveTestIndex = steps.findIndex(
(step) => step.name === "Exercise portable profile in the rootless environment",
);
const packageInstallIndex = provision?.indexOf("sudo apt-get install") ?? -1;
const packageVersionIndex = provision?.indexOf("dpkg-query --show") ?? -1;
const runtimeVersionIndex = provision?.indexOf("podman --version") ?? -1;
const actionlintLabels = actionlint["self-hosted-runner"]?.labels;

expect(job?.["runs-on"]).toBe("ubuntu-26.04");
expect(Array.isArray(actionlintLabels)).toBe(true);
expect(actionlintLabels).toContain("ubuntu-26.04");
expect(job?.env?.PODMAN_APT_VERSION).toBe("5.7.0+ds2-3build1");
expect(dependencyInstallIndex).toBeGreaterThanOrEqual(0);
expect(catalogueCompileIndex).toBeGreaterThan(dependencyInstallIndex);
expect(liveTestIndex).toBeGreaterThan(catalogueCompileIndex);
});

it("uses Ubuntu's Podman package with its installed OCI runtime", () => {
const provision = rootlessLinuxStep("Provision restricted rootless Linux runtime").run ?? "";
const packageInstallIndex = provision.indexOf("sudo apt-get install");
const packagePodmanIndex = provision.indexOf('package_podman="/usr/bin/podman"');
const pathExportIndex = provision.indexOf('export PATH="$runtime_bin:$PATH"');
const jobPathIndex = provision.indexOf('printf \'%s\\n\' "$runtime_bin" >> "$GITHUB_PATH"');
const versionIndex = provision.indexOf("podman --version");

expect(provisionIndex).toBeGreaterThan(catalogueCompileIndex);
expect(policyIndex).toBeGreaterThan(provisionIndex);
expect(liveTestIndex).toBeGreaterThan(policyIndex);
expect(packageInstallIndex).toBeGreaterThanOrEqual(0);
expect(provision).toMatch(/sudo apt-get install --yes .*\bpodman\b/);
expect(packagePodmanIndex).toBeGreaterThan(packageInstallIndex);
expect(provision).toContain('ln -s "$package_podman" "$runtime_bin/podman"');
expect(pathExportIndex).toBeGreaterThan(packagePodmanIndex);
expect(jobPathIndex).toBeGreaterThan(pathExportIndex);
expect(versionIndex).toBeGreaterThan(jobPathIndex);
expect(provision).toContain('test "$(readlink -f "$(command -v podman)")" = "$package_podman"');
expect(provision).toContain("apparmor");
expect(provision).toContain('"podman=$PODMAN_APT_VERSION"');
expect(packageVersionIndex).toBeGreaterThan(packageInstallIndex);
expect(runtimeVersionIndex).toBeGreaterThan(packageVersionIndex);
expect(provision).toContain('test "$package_version" = "$PODMAN_APT_VERSION"');
expect(provision).toContain('test "$version" = "podman version 5.7.0"');
expect(policy).toContain("/etc/apparmor.d/usr.bin.pasta");
expect(policy).toContain("signal (receive) peer=podman,");
expect(policy).toContain('test -f "$pasta_profile"');
expect(policy).toContain(
`test "$(grep -Fc 'include <abstractions/pasta>' "$pasta_profile")" -eq 1`,
);
expect(policy).toContain('if ! grep -Eq "$signal_rule" "$pasta_profile"; then');
expect(policy).toContain('test "$(grep -Ec "$signal_rule" "$pasta_profile")" -eq 1');
expect(policy).toContain('apparmor_parser -r "$pasta_profile"');
});
});
Loading