mirror of
https://github.com/mvschwarz/openrig.git
synced 2026-11-07 05:56:21 -10:00
test(ci): exercise seat environments on Linux (#2101)
CI now runs the Linux-only seat-environment test in its own job on pull requests and manual dispatch. The macOS package matrix skips it because it reads `/proc/<pid>/environ`, which left #2041's launch paths untested there. The test also checks that new and existing tmux servers give seats the runtime directory, the disabled session-bus default, and tmux's own terminal identity rather than the daemon parent's. The job has read-only contents permission and no secrets, and takes about 90 seconds. The required checks are unchanged. 3 files, +61 -2.
This commit is contained in:
1 parent
2ed390de39
commit
2adde8ffe0
3 files changed
+61
-2
No files matched your search
@@ -3,7 +3,8 @@ name: Tests
|
||||
# Builds, type-checks and runs every test suite on a clean GitHub runner, and records the file
|
||||
# list each published package would contain. Each suite is its own job so one failure does not
|
||||
# hide the others. No provider credentials or operator configuration are supplied.
|
||||
# Package suites deny external networking; scenario runtime has no external route.
|
||||
# Package suites and the Linux seat-environment check deny external networking;
|
||||
# scenario runtime has no external route.
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -76,6 +77,40 @@ jobs:
|
||||
- run: npm ci
|
||||
- run: npm run test:repo
|
||||
|
||||
linux-seat-environment:
|
||||
if: ${{ github.event_name != 'push' }}
|
||||
# This integration test reads /proc/<pid>/environ and is skipped on macOS.
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22
|
||||
cache: npm
|
||||
- name: Install the real terminal and staging tools
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install --yes tmux rsync
|
||||
- run: npm ci
|
||||
- run: npm run build
|
||||
- name: Check seat environments without ambient credentials or external network
|
||||
run: |
|
||||
set -euo pipefail
|
||||
suite_root=$(mktemp -d "$RUNNER_TEMP/opr-seat-env.XXXXXX")
|
||||
mkdir -p "$suite_root/home"
|
||||
# Only loopback exists in this namespace. Drop back to the runner user
|
||||
# before the CLI, private daemon and private tmux server are started.
|
||||
sudo unshare --net -- /bin/bash -s -- "$(id -un)" "$suite_root" "$PATH" <<'SUITE'
|
||||
set -euo pipefail
|
||||
ip link set lo up
|
||||
exec runuser -u "$1" -- env -i HOME="$2/home" PATH="$3" TMPDIR="$2" TMUX_TMPDIR="$2" \
|
||||
CI=true TERM=xterm-256color \
|
||||
npm run test -w packages/daemon -- test/seat-env-parent-session.integration.test.ts --maxWorkers=1 --reporter=verbose
|
||||
SUITE
|
||||
|
||||
package-tests:
|
||||
if: ${{ github.event_name != 'push' }}
|
||||
# macOS, the platform OpenRig runs on: several daemon and cli tests read macOS ps columns,
|
||||
|
||||
@@ -104,8 +104,18 @@ credentials are supplied.
|
||||
| `typecheck` | ubuntu | `npm run lint` |
|
||||
| `repo-checks` | ubuntu | `npm run test:repo` |
|
||||
| `package-tests` | macOS, matrix `daemon`, `cli`, `tui`, `ui` (fail-fast off) | Installs tmux, runs `npm ci` and `npm run build`. Then runs `npm run test -w packages/<pkg>` under `env -i` with a scratch `HOME`/`TMPDIR`/`TMUX_TMPDIR` and a `sandbox-exec` profile that blocks outbound network except localhost and the job's own socket directory. `scripts/ci-macos-process-probe.mjs` runs first. |
|
||||
| `linux-seat-environment` | ubuntu | Builds all packages, then runs `seat-env-parent-session.integration.test.ts` with real CLI, daemon, tmux and stub seats. A network namespace has loopback only; tests run as the unprivileged runner user under `env -i` with scratch `HOME`/`TMPDIR`/`TMUX_TMPDIR`. Runs on pull requests and manual dispatch, like `package-tests`. |
|
||||
| `installed-scenario` | ubuntu | `bash scripts/run-pr-scenarios.sh` (eight container runs, described below). Uploads `dist/pr-scenarios/` as `installed-scenario-evidence`, including on failure. |
|
||||
|
||||
The Linux seat-environment job covers a gap in the macOS matrix: the integration test
|
||||
reads `/proc/<pid>/environ`, so macOS skips it. It checks both a new tmux server and an
|
||||
existing server carrying a parent's session identity. It verifies the scoped environment,
|
||||
the runtime directory and disabled session-bus default, and tmux's terminal identity in
|
||||
the launched panes; the existing owner session stays unchanged. This adds one focused
|
||||
Linux job to the existing suite. Stub seats prove the environment passed to the pane;
|
||||
native Claude image paste and Shift+Enter handling still need separate verification
|
||||
(see [#2041](https://github.com/mvschwarz/openrig/issues/2041)).
|
||||
|
||||
A second workflow, `.github/workflows/portability-report.yml` (job `portability-report`),
|
||||
lists lines your PR adds that contain machine-specific values: credentials, home or temp
|
||||
paths, network addresses, email addresses. It never fails because of a finding. To see the
|
||||
|
||||
@@ -30,6 +30,7 @@ const PARENT_SESSION = Object.fromEntries([...PARENT_SESSION_ENV_KEYS, "CLAUDE_E
|
||||
const SELECTED = {
|
||||
CLAUDE_CONFIG_DIR: "/configured/claude",
|
||||
CODEX_HOME: "/configured/codex",
|
||||
XDG_RUNTIME_DIR: "/configured/runtime",
|
||||
};
|
||||
// Configuration the daemon and the tmux server carry that a seat isn't given: none of it is in the selected baseline,
|
||||
// and this test names no provider credential in recovery.provider_auth_env_allowlist.
|
||||
@@ -56,7 +57,13 @@ describe.runIf(process.platform === "linux")("seat environment from a daemon sta
|
||||
// already carries the parent identity is running before the daemon starts.
|
||||
it.each(["daemon", "existing"] as const)("seats receive none of the parent session's identity variables, the selected configuration, and nothing else the tmux server carries (tmux server: %s)", async (server) => {
|
||||
const scaffold = prepareHermeticEnv({
|
||||
baseEnv: { HOME: process.env.HOME, PATH: process.env.PATH, TERM: "xterm", ...PARENT_SESSION, ...SELECTED, ...NOT_SELECTED },
|
||||
baseEnv: {
|
||||
HOME: process.env.HOME, PATH: process.env.PATH, TERM: "xterm",
|
||||
// These belong to the terminal that started the daemon. A seat must get
|
||||
// the identity tmux supplies to its pane instead (#2041).
|
||||
TERM_PROGRAM: "parent-terminal", TERM_PROGRAM_VERSION: "parent-version",
|
||||
...PARENT_SESSION, ...SELECTED, ...NOT_SELECTED,
|
||||
},
|
||||
});
|
||||
scaffolds.push(scaffold);
|
||||
const tmux = (args: string[], env: Record<string, string | undefined> = process.env) =>
|
||||
@@ -79,6 +86,8 @@ describe.runIf(process.platform === "linux")("seat environment from a daemon sta
|
||||
.trim().split("\n").map((line) => line.split(" ") as [string, string]);
|
||||
const seatPanes = panes.filter(([session]) => session.includes("scn-baton"));
|
||||
expect(seatPanes.length).toBeGreaterThan(0);
|
||||
const tmuxVersion = (await tmux(["display-message", "-p", "#{version}"])).stdout.trim();
|
||||
expect(tmuxVersion).not.toBe("");
|
||||
|
||||
for (const [, pid] of seatPanes) {
|
||||
const env = environOf(pid);
|
||||
@@ -91,6 +100,11 @@ describe.runIf(process.platform === "linux")("seat environment from a daemon sta
|
||||
PATH: scaffold.env.PATH,
|
||||
HOME: scaffold.home,
|
||||
OPENRIG_HOME: scaffold.openrigHome,
|
||||
// Keep the runtime socket directory without discovering the person's
|
||||
// session bus, and retain tmux's own terminal identity through env -i.
|
||||
DBUS_SESSION_BUS_ADDRESS: "disabled:",
|
||||
TERM_PROGRAM: "tmux",
|
||||
TERM_PROGRAM_VERSION: tmuxVersion,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user