Skip to content
Draft
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
65 changes: 65 additions & 0 deletions .github/workflows/e2e-label-dispatch.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
name: E2E Label Dispatch

# When a maintainer applies `test:e2e` or `test:e2e-gpu`, dispatch the matching
# self-hosted workflow against the copy-pr-bot mirror branch. Without this,
# the gated workflow only runs on push to `pull-request/<N>`, so a label
# applied after the mirror was created leaves the gate stuck red until someone
# manually re-runs the workflow.
#
# Pushes to `pull-request/<N>` (whether from an automatic copy-pr-bot sync or
# a maintainer-typed `/ok to test`) already re-trigger the gated workflow on
# their own - only the label-application case needs this dispatcher.
#
# Uses `pull_request_target` so forked PRs get a write-capable token. The job
# never checks out PR code; it only calls the GitHub API.

on:
pull_request_target:
types: [labeled]

permissions: {}

jobs:
dispatch:
name: Dispatch E2E workflow for labeled PR
if: github.event.label.name == 'test:e2e' || github.event.label.name == 'test:e2e-gpu'
runs-on: ubuntu-latest
permissions:
actions: write
pull-requests: write
steps:
- name: Dispatch workflow against the copy-pr-bot mirror
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_REPO: ${{ github.repository }}
PR_NUMBER: ${{ github.event.pull_request.number }}
PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }}
LABEL_NAME: ${{ github.event.label.name }}
shell: bash
run: |
set -euo pipefail

case "$LABEL_NAME" in
test:e2e) workflow=branch-e2e.yml ;;
test:e2e-gpu) workflow=test-gpu.yml ;;
*) echo "Unrecognized label $LABEL_NAME"; exit 1 ;;
esac

mirror_ref="pull-request/$PR_NUMBER"
mirror_sha=$(gh api "repos/$GH_REPO/branches/$mirror_ref" --jq '.commit.sha' 2>/dev/null || echo "")
short_pr=${PR_HEAD_SHA:0:7}

if [ -z "$mirror_sha" ]; then
gh pr comment "$PR_NUMBER" --body "Label \`$LABEL_NAME\` applied, but copy-pr-bot has not mirrored this PR yet. A maintainer needs to comment \`/ok to test $PR_HEAD_SHA\` to start the mirror; re-apply the label once \`$mirror_ref\` exists."
exit 0
fi

if [ "$mirror_sha" != "$PR_HEAD_SHA" ]; then
short_mirror=${mirror_sha:0:7}
gh pr comment "$PR_NUMBER" --body "Label \`$LABEL_NAME\` applied, but \`$mirror_ref\` is at \`$short_mirror\` while the PR head is \`$short_pr\`. Comment \`/ok to test $PR_HEAD_SHA\` to refresh the mirror, then re-apply the label."
exit 0
fi

echo "Dispatching $workflow against $mirror_ref ($short_pr) for label $LABEL_NAME."
gh workflow run "$workflow" --ref "$mirror_ref"
gh pr comment "$PR_NUMBER" --body "Dispatched \`$workflow\` against \`$mirror_ref\` at \`$short_pr\` (label \`$LABEL_NAME\`). Results will post as checks on this PR."
112 changes: 112 additions & 0 deletions CI.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
# CI

This document describes how OpenShell's continuous integration works for pull requests, with a focus on what contributors need to do to get their PR tested.

For local test commands see [TESTING.md](TESTING.md). For PR conventions see [CONTRIBUTING.md](CONTRIBUTING.md).

## Overview

E2E tests run on self-hosted runners (`build-arm64`, GPU runners). To keep untrusted PR code off those runners we use NVIDIA's [copy-pr-bot](https://docs.gha-runners.nvidia.com/platform/apps/copy-pr-bot/), which mirrors trusted PRs to internal `pull-request/<N>` branches. The gated workflows trigger on pushes to those branches, not on the original PR.

Two opt-in labels enable the suites:

- `test:e2e` runs `Branch E2E Checks` (non-GPU E2E)
- `test:e2e-gpu` runs `GPU Test`

Both are required to merge once the corresponding `E2E Gate` checks are marked required in branch protection.

## Commit signing

copy-pr-bot decides whether to mirror a PR automatically based on whether the author is trusted. For org members and collaborators, "trusted" means **all commits in the PR are cryptographically signed**. Unsigned commits, even from an org member, force the bot to wait for a maintainer's `/ok to test <SHA>`.

DCO sign-off (`-s` / `Signed-off-by`) is a separate requirement and does not count as commit signing.

### One-time setup with an SSH key

If you already use an SSH key for `git push`, you can reuse it as a signing key. (You can also generate a separate one - GitHub allows the same SSH key as both auth and signing.)

1. Generate a key (skip if reusing your existing SSH key):

```shell
ssh-keygen -t ed25519 -C "you@example.com" -f ~/.ssh/id_ed25519_signing
```

2. Add the **public** key at <https://github.com/settings/keys> using **New SSH key**, and set **Key type: Signing Key** (not Authentication). Signing keys are managed separately from authentication keys, even when they reuse the same key material - you have to add the entry once for each role.

3. Configure git globally:

```shell
git config --global gpg.format ssh
git config --global user.signingkey ~/.ssh/id_ed25519_signing.pub
git config --global commit.gpgsign true
git config --global tag.gpgsign true
```

4. (Recommended) Tell git to verify your own signatures locally by listing your key as trusted:

```shell
echo "$(git config user.email) $(cat ~/.ssh/id_ed25519_signing.pub)" \
>> ~/.ssh/allowed_signers
git config --global gpg.ssh.allowedSignersFile ~/.ssh/allowed_signers
```

5. Verify on a test commit:

```shell
git commit --allow-empty -s -m "test: signing"
git log --show-signature -1
```

You should see `Good "git" signature for you@example.com`.

If you have older unsigned commits on a branch, sign them by rebasing:

```shell
git rebase --exec 'git commit --amend --no-edit -S' origin/main
```

## Pull request flows

### Internal contributor PR

Prerequisites:

- Org member or collaborator on the repo.
- All commits cryptographically signed (see [Commit signing](#commit-signing)).
- All commits include a DCO sign-off (`git commit -s`).

Flow:

1. Open the PR. copy-pr-bot mirrors it to `pull-request/<N>` automatically.
2. A maintainer applies `test:e2e` and/or `test:e2e-gpu`.
3. `E2E Label Dispatch` detects the label and triggers the matching workflow against the mirror.
4. Results post as checks on your PR head SHA.
5. New commits push to the mirror automatically; gated workflows re-run on their own. No re-labeling needed.

### Forked PR

Prerequisites:

- DCO sign-off (`git commit -s`) on every commit. Commit signing is not required for forks - copy-pr-bot trusts forks based on maintainer review, not signing.
- A maintainer must vouch you. See the [Vouch System](AGENTS.md#vouch-system).

Flow:

1. Open the PR. The vouch check confirms you are vouched (otherwise the PR is auto-closed).
2. copy-pr-bot does not mirror forks automatically. A maintainer reviews the diff and comments `/ok to test <SHA>` with your latest commit SHA.
3. After `/ok to test`, copy-pr-bot mirrors to `pull-request/<N>`.
4. A maintainer applies `test:e2e` / `test:e2e-gpu`. The dispatcher runs the matching workflow against the mirror.
5. Results post as checks on your PR.

Important: every new commit you push requires another `/ok to test <new-SHA>` from a maintainer before E2E will run on it. If a label is applied while the mirror is stale, `E2E Label Dispatch` will post a comment explaining what's needed.

## Workflow files

| File | Role |
|---|---|
| `.github/workflows/branch-e2e.yml` | Non-GPU E2E. Triggers on `push: pull-request/[0-9]+`. |
| `.github/workflows/test-gpu.yml` | GPU E2E. Triggers on `push: pull-request/[0-9]+`. |
| `.github/actions/pr-gate/action.yml` | Composite action that resolves PR metadata and verifies the required label is set. |
| `.github/workflows/e2e-gate.yml` | Posts the required `E2E Gate` check on the PR. Re-evaluates after the gated workflow completes. |
| `.github/workflows/e2e-gate-check.yml` | Reusable gate logic shared by E2E and GPU E2E. |
| `.github/workflows/e2e-label-dispatch.yml` | Triggers gated workflows when a `test:e2e*` label is applied. Posts a comment if the mirror is missing or stale. |
6 changes: 6 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -293,3 +293,9 @@ All contributions must include a `Signed-off-by` line in each commit message. Th
```bash
git commit -s -m "feat(sandbox): add new capability"
```

DCO sign-off is separate from cryptographic commit signing. CI requires signing for org members so that copy-pr-bot can mirror your PR automatically; see [CI.md](CI.md#commit-signing) for setup.

## CI

How E2E runs in CI, the `test:e2e` / `test:e2e-gpu` labels, copy-pr-bot, and commit-signing setup are documented in [CI.md](CI.md).
Loading