diff options
| author | Yuriy Andamasov <yuriy@vyos.io> | 2026-05-11 00:18:12 +0300 |
|---|---|---|
| committer | Yuriy Andamasov <yuriy@vyos.io> | 2026-05-11 00:18:12 +0300 |
| commit | 6aa6d83cdd95e514f0fa06258a8c1ba298a5dfd8 (patch) | |
| tree | 81a98f1606b3bb7f744e925e5ce1188e3be06a8b /.github | |
| parent | d15e22a0bbd12b457f0d12d20e58e702c19302b7 (diff) | |
| download | vyos-documentation-6aa6d83cdd95e514f0fa06258a8c1ba298a5dfd8.tar.gz vyos-documentation-6aa6d83cdd95e514f0fa06258a8c1ba298a5dfd8.zip | |
ci(ai-validation): switch to GitHub-hosted ubuntu-latest runners
The `vyos` org does not have self-hosted runners labeled `web` (those
live in the VyOS-Networks org pool and only serve repos there). Every
AI Validation run queued since #1947 merged sat in `queued` state
indefinitely with no runner picking it up — observed across all recent
PRs (#1955 mergify backports, #1956, plus several yuriy/* branches).
Switching both `prepare` and `validate` jobs to `runs-on: ubuntu-latest`:
* Removes the host-isolation half of the prepare-job rationale comment
and replaces it with the ephemeral-VM rationale (cross-run state
leakage is impossible on a fresh GitHub-hosted VM).
* Removes both `atos-actions/clean-self-hosted-runner` cleanup steps —
GitHub-hosted runners are ephemeral, the action is a no-op there at
best and a failure mode at worst (it expects self-hosted workspace
patterns that don't exist on hosted runners).
* Tweaks one comment that mentioned `/proc/<pid>/cmdline on the self-
hosted runner` to be runner-agnostic.
Also removes `.github/actionlint.yaml`. It was added in #1947 to silence
actionlint's "label 'web' is unknown" false positive — with no workflow
in this repo now using `[self-hosted, web]`, the file is dead code.
The canonical reference at `VyOS-Networks/vyos-docs-opus-reviewer/scripts/
ai-validation.yml` intentionally diverges: that repo IS in VyOS-Networks
and has access to the `web` self-hosted pool, so its canonical keeps
`runs-on: [self-hosted, web]` and the cleanup steps. The deployed
file's REFERENCE COPY header comment block in the reviewer repo will be
updated in a follow-up to note that the deployed file may use different
runners per host repo's pool availability.
No security regression — the trust boundary on prepare is enforced by
no-fork-code-execution, no-secrets-referenced, persist-credentials:false,
and the split-job artifact, all of which are unchanged. Adds the implicit
host-ephemerality guarantee of GitHub-hosted runners.
Diffstat (limited to '.github')
| -rw-r--r-- | .github/actionlint.yaml | 12 | ||||
| -rw-r--r-- | .github/workflows/ai-validation.yml | 70 |
2 files changed, 27 insertions, 55 deletions
diff --git a/.github/actionlint.yaml b/.github/actionlint.yaml deleted file mode 100644 index 04e9cefe..00000000 --- a/.github/actionlint.yaml +++ /dev/null @@ -1,12 +0,0 @@ -# actionlint configuration for vyos/vyos-documentation -# -# Declares the custom labels used by the org-managed self-hosted runner -# pool so actionlint stops emitting "label 'web' is unknown" false -# positives on `runs-on: [self-hosted, web]` lines. -# -# The `web` label is the org-level VyOS Networks self-hosted Debian 12 -# runner pool used by .github/workflows/ai-validation.yml (both the -# `prepare` and `validate` jobs). -self-hosted-runner: - labels: - - web diff --git a/.github/workflows/ai-validation.yml b/.github/workflows/ai-validation.yml index 4c1984e6..1cad9e58 100644 --- a/.github/workflows/ai-validation.yml +++ b/.github/workflows/ai-validation.yml @@ -25,10 +25,13 @@ jobs: # expanding the attack surface to any future shell change in this job. # The validate job below performs the secrets-availability check and # skips with a notice if any are missing. - # Untrusted prepare runs on the same self-hosted Debian 12 pool as - # validate. GitHub-hosted ubuntu-latest is not available in this - # environment. The trust boundary on prepare is enforced WITHOUT host - # isolation: + # Untrusted prepare runs on GitHub-hosted ubuntu-latest. The `vyos` org + # does not have self-hosted runners labeled `web` (those live in the + # VyOS-Networks org and only serve repos there); `vyos/vyos-documentation` + # therefore uses GitHub-hosted runners for the AI Validation workflow. + # The split-job artifact still bridges the trust boundary to validate; + # validate is the only place where secrets are referenced. Defense in + # depth on prepare: # - No fork code is executed: prepare only does # git fetch / git diff / git show / file reads. # There is no `pip install` from the fork, no `npm install`, no @@ -40,13 +43,10 @@ jobs: # - persist-credentials: false on the merge-ref checkout means the # default GITHUB_TOKEN is not available to fork-controlled file # content. - # - atos-actions/clean-self-hosted-runner step (`if: always()`) at - # the end of the job wipes the workspace regardless of how prepare - # exits. - # The split-job artifact still bridges the trust boundary to validate; - # validate is the only place where secrets are referenced. + # - GitHub-hosted runners are ephemeral — every run starts on a fresh + # VM, so cross-run state leakage is not possible. prepare: - runs-on: [self-hosted, web] + runs-on: ubuntu-latest permissions: contents: read steps: @@ -73,17 +73,18 @@ jobs: git diff "$BASE...HEAD" -- 'docs/**/*.md' > diff-md.patch # Bundle .md files via git's blob store (NOT the filesystem). # The fork's merge ref can contain symlinks (mode 120000) committed - # to docs/**/*.md that resolve to absolute paths on this self-hosted - # runner. `cp` would dereference and copy the target's content - # (/etc/passwd, runner secrets, ssh keys) into the artifact, - # exfiltrating runner state to the validate job's claude-code-action - # input. `git show HEAD:<path>` returns the blob directly from the - # object database; for a symlink-mode entry it returns the textual - # target path, never the target's content. + # to docs/**/*.md that resolve to absolute paths on the runner. + # `cp` would dereference and copy the target's content (/etc/passwd, + # any cached state, ssh keys etc) into the artifact, exfiltrating + # runner state to the validate job's claude-code-action input. + # `git show HEAD:<path>` returns the blob directly from the object + # database; for a symlink-mode entry it returns the textual target + # path, never the target's content. The runner being ephemeral + # (GitHub-hosted) limits the blast radius further, but the blob- + # extraction approach is the actual mitigation and is portable. # Idempotent: a previous run cancelled by concurrency.cancel-in-progress - # may have left _changed_md/ behind on the self-hosted runner if the - # job was killed before the post-job cleanup ran. rm -rf + mkdir -p - # guarantees a clean target regardless of prior state. + # may have left _changed_md/ behind. rm -rf + mkdir -p guarantees a + # clean target regardless of prior state. rm -rf _changed_md && mkdir -p _changed_md while IFS= read -r -d '' path; do # Path-traversal hardening: even though git's tree machinery @@ -130,17 +131,9 @@ jobs: diff-md.patch _changed_md/ - # Self-hosted-runner workspace cleanup. Composite action; the upstream - # already wraps its own logic in `if: ${{ always() && ... }}`, but the - # outer step also needs `if: always()` so it runs even after a prior - # step fails. - - name: Clean self-hosted runner workspace - if: always() - uses: atos-actions/clean-self-hosted-runner@c6ce136031329a4435508e02b3f97fd85353f744 # v1.4.34 - validate: needs: [prepare] - runs-on: [self-hosted, web] + runs-on: ubuntu-latest permissions: contents: read pull-requests: write @@ -289,12 +282,11 @@ jobs: # Check out the reviewer source instead of installing via # `uv pip install git+https://x-access-token:<TOK>@...` — the URL form - # puts the App token in process argv (visible through - # /proc/<pid>/cmdline on the self-hosted runner while uv or git is - # running). actions/checkout writes the token as a transient http - # extraheader instead, and persist-credentials:false ensures it does - # not linger in reviewer-src/.git/config where the Pass 2 LLM step - # could read it. + # puts the App token in process argv (visible through /proc/<pid>/cmdline + # to any other process on the runner while uv or git is running). + # actions/checkout writes the token as a transient http extraheader + # instead, and persist-credentials:false ensures it does not linger in + # reviewer-src/.git/config where the Pass 2 LLM step could read it. - name: Checkout reviewer package source (pinned to REVIEWER_REF) if: steps.secrets-check.outputs.skip != 'true' uses: actions/checkout@v6 @@ -410,11 +402,3 @@ jobs: claude_args: | --model claude-opus-4-7 --allowedTools "mcp__github_inline_comment__create_inline_comment,Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*),Read,Glob,Grep" - - # Self-hosted-runner workspace cleanup. Composite action; the upstream - # already wraps its own logic in `if: ${{ always() && ... }}`, but the - # outer step also needs `if: always()` so it runs even after a prior - # step fails. - - name: Clean self-hosted runner workspace - if: always() - uses: atos-actions/clean-self-hosted-runner@c6ce136031329a4435508e02b3f97fd85353f744 # v1.4.34 |
