From f6ef27d087a4cae08b122e73b7c46771d1a6764b Mon Sep 17 00:00:00 2001 From: Yuriy Andamasov Date: Mon, 11 May 2026 01:11:54 +0300 Subject: ci(ai-validation): skip Pass 1/2 when PR has no .md changes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit actions/upload-artifact silently omits empty directories. On a PR that changes no docs/**/*.md (workflow tweaks, README edits, config), the prepare job's _changed_md/ ends up empty and is dropped from the artifact. The validate job downloads the artifact, then "Pass 1 โ€” deterministic checks" tries to start with `working-directory: _changed_md`, which doesn't exist, and bash fails before any in-step short-circuit can run. Surface a `has_md_changes` output from prepare based on whether changed-md.txt is non-empty, and gate Pass 1 + Pass 2 on it. When the flag is false, both review steps skip cleanly with no failure noise. Affects all infrastructure-only PRs (CI changes, workflow updates, config tweaks). Same failure was visible on the in-flight backports of update-version-tags hardening (#1965, #1966) which are pure workflow changes. ๐Ÿค– Generated by [robots](https://vyos.io) --- .github/workflows/ai-validation.yml | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) (limited to '.github/workflows') diff --git a/.github/workflows/ai-validation.yml b/.github/workflows/ai-validation.yml index 44952092..738faf60 100644 --- a/.github/workflows/ai-validation.yml +++ b/.github/workflows/ai-validation.yml @@ -49,6 +49,14 @@ jobs: runs-on: ubuntu-latest permissions: contents: read + outputs: + # Surface whether the PR touched any docs/**/*.md so validate's review + # steps can skip on infrastructure-only PRs (workflow/config/README + # changes). actions/upload-artifact silently omits empty directories + # โ€” when no .md files change, _changed_md/ isn't uploaded, and + # validate's working-directory: _changed_md would otherwise fail + # before any in-step short-circuit can run. + has_md_changes: ${{ steps.changes.outputs.has_md_changes }} steps: - name: Checkout PR merge ref (NO credentials) uses: actions/checkout@v6 @@ -58,6 +66,7 @@ jobs: fetch-depth: 2 - name: Compute changed files and bundle .md content + id: changes run: | set -euo pipefail git fetch --depth=1 origin "${{ github.event.pull_request.base.ref }}" @@ -122,6 +131,12 @@ jobs: git show "HEAD:$path" > "_changed_md/$path" done < changed-md.z + if [ -s changed-md.txt ]; then + echo "has_md_changes=true" >> "$GITHUB_OUTPUT" + else + echo "has_md_changes=false" >> "$GITHUB_OUTPUT" + fi + - name: Upload PR input artifact uses: actions/upload-artifact@v4 with: @@ -312,7 +327,7 @@ jobs: # Pass 1 would emit zero findings โ€” a silent failure mode the # ยง3.6 fail-closed gate cannot catch when the DB is present. - name: Pass 1 โ€” deterministic checks - if: steps.secrets-check.outputs.skip != 'true' && steps.download-db.outcome == 'success' + if: steps.secrets-check.outputs.skip != 'true' && steps.download-db.outcome == 'success' && needs.prepare.outputs.has_md_changes == 'true' working-directory: _changed_md run: | vyos-doc-review pass1 \ @@ -321,7 +336,7 @@ jobs: --output ../pass1-findings.json - name: Pass 2 โ€” Claude review - if: steps.secrets-check.outputs.skip != 'true' + if: steps.secrets-check.outputs.skip != 'true' && needs.prepare.outputs.has_md_changes == 'true' uses: anthropics/claude-code-action@v1 with: anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} -- cgit v1.2.3 From aba878f54096704710a3899ffca9737d5a992a0c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 10 May 2026 22:14:19 +0000 Subject: ci(ai-validation): fetch base branch ref unambiguously Agent-Logs-Url: https://github.com/vyos/vyos-documentation/sessions/b505c475-0c95-470c-bb8e-3391741f8e4e Co-authored-by: andamasov <12631358+andamasov@users.noreply.github.com> --- .github/workflows/ai-validation.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to '.github/workflows') diff --git a/.github/workflows/ai-validation.yml b/.github/workflows/ai-validation.yml index 738faf60..c6d7c664 100644 --- a/.github/workflows/ai-validation.yml +++ b/.github/workflows/ai-validation.yml @@ -69,8 +69,10 @@ jobs: id: changes run: | set -euo pipefail - git fetch --depth=1 origin "${{ github.event.pull_request.base.ref }}" - BASE="origin/${{ github.event.pull_request.base.ref }}" + # Fetch the base branch explicitly by refname to avoid ambiguity with + # same-named tags (e.g., a `rolling` tag), then diff against FETCH_HEAD. + git fetch --no-tags --depth=1 origin "refs/heads/${{ github.event.pull_request.base.ref }}" + BASE="FETCH_HEAD" # --diff-filter=ACMRT excludes Deleted entries so the bundling # loop below (`git show HEAD:`) doesn't try to extract # blobs for files that no longer exist in the merge ref. -- cgit v1.2.3 From ea41c7f3fc6137c1b0a5cabb1455ad03f6cc6e1b Mon Sep 17 00:00:00 2001 From: Yuriy Andamasov Date: Mon, 11 May 2026 01:18:45 +0300 Subject: ci(ai-validation): hoist has_md_changes gate to validate job level MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per Copilot review on PR #1968: with the per-step gates, the validate job still ran the expensive setup chain (artifact download, GitHub App token, reviewer checkout/install, reference-DB download/extract, uv setup) on infrastructure-only PRs even though both Pass 1 and Pass 2 were guaranteed to skip. CI minutes wasted, no value produced. Move the gate to the job level โ€” `if: needs.prepare.outputs.has_md_changes == 'true'` on validate. The whole job (including setup) skips cleanly when no .md files changed, and the per-step gates become redundant (removed in the same commit). ๐Ÿค– Generated by [robots](https://vyos.io) --- .github/workflows/ai-validation.yml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to '.github/workflows') diff --git a/.github/workflows/ai-validation.yml b/.github/workflows/ai-validation.yml index c6d7c664..5319023e 100644 --- a/.github/workflows/ai-validation.yml +++ b/.github/workflows/ai-validation.yml @@ -151,6 +151,11 @@ jobs: validate: needs: [prepare] + # Skip the entire job on infrastructure-only PRs. Otherwise the + # expensive setup chain (artifact download, GitHub App token, reviewer + # checkout/install, reference-DB download/extract, uv setup) runs even + # though Pass 1 + Pass 2 are guaranteed to no-op. + if: needs.prepare.outputs.has_md_changes == 'true' runs-on: ubuntu-latest permissions: contents: read @@ -329,7 +334,7 @@ jobs: # Pass 1 would emit zero findings โ€” a silent failure mode the # ยง3.6 fail-closed gate cannot catch when the DB is present. - name: Pass 1 โ€” deterministic checks - if: steps.secrets-check.outputs.skip != 'true' && steps.download-db.outcome == 'success' && needs.prepare.outputs.has_md_changes == 'true' + if: steps.secrets-check.outputs.skip != 'true' && steps.download-db.outcome == 'success' working-directory: _changed_md run: | vyos-doc-review pass1 \ @@ -338,7 +343,7 @@ jobs: --output ../pass1-findings.json - name: Pass 2 โ€” Claude review - if: steps.secrets-check.outputs.skip != 'true' && needs.prepare.outputs.has_md_changes == 'true' + if: steps.secrets-check.outputs.skip != 'true' uses: anthropics/claude-code-action@v1 with: anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} -- cgit v1.2.3 From 5e2f6ab327ed24d6ea66ed91e0d6506524778230 Mon Sep 17 00:00:00 2001 From: Yuriy Andamasov Date: Mon, 11 May 2026 01:25:48 +0300 Subject: ci(ai-validation): cover deletion-only Markdown PRs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CodeRabbit caught a real Major issue on this PR's first version: has_md_changes was derived from changed-md.txt, which is built with --diff-filter=ACMRT (excludes Deletes). A PR that only deletes docs/**/*.md files would set has_md_changes=false and skip validate entirely โ€” but Pass 1 reviews the diff (via --pr-diff ../diff-md.patch), not just the post-image files in _changed_md/, so deletes are legitimate review targets. Two fixes that go together: 1. Derive has_md_changes from diff-md.patch (the unfiltered git diff already computed) instead of changed-md.txt. diff-md.patch includes D entries; changed-md.txt does not. 2. Add a defensive `mkdir -p _changed_md` after artifact download. actions/upload-artifact silently omits empty directories โ€” on a deletion-only PR the artifact carries no _changed_md/ at all, and Pass 1's `working-directory: _changed_md` would fail. The directory is recreated empty; Pass 1 still operates on the diff. ๐Ÿค– Generated by [robots](https://vyos.io) --- .github/workflows/ai-validation.yml | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) (limited to '.github/workflows') diff --git a/.github/workflows/ai-validation.yml b/.github/workflows/ai-validation.yml index 5319023e..f75081dc 100644 --- a/.github/workflows/ai-validation.yml +++ b/.github/workflows/ai-validation.yml @@ -133,7 +133,12 @@ jobs: git show "HEAD:$path" > "_changed_md/$path" done < changed-md.z - if [ -s changed-md.txt ]; then + # Use diff-md.patch (unfiltered git diff) rather than changed-md.txt + # (--diff-filter=ACMRT) so deletion-only PRs still trigger validate. + # Pass 1 reviews the diff, not just the post-image files in + # _changed_md/, so deletes are legitimate review targets even though + # they produce no entries in _changed_md/. + if [ -s diff-md.patch ]; then echo "has_md_changes=true" >> "$GITHUB_OUTPUT" else echo "has_md_changes=false" >> "$GITHUB_OUTPUT" @@ -208,6 +213,16 @@ jobs: with: name: pr-input + - name: Ensure _changed_md exists (handles deletion-only PRs) + if: steps.secrets-check.outputs.skip != 'true' + # actions/upload-artifact silently omits empty directories. On a + # deletion-only PR, prepare's _changed_md/ holds no files and never + # makes it across the artifact boundary โ€” Pass 1's + # working-directory: _changed_md would then fail. Recreate the + # directory unconditionally; Pass 1 still operates on the diff + # via --pr-diff ../diff-md.patch, which is the source of truth. + run: mkdir -p _changed_md + - name: Generate GitHub App token if: steps.secrets-check.outputs.skip != 'true' id: app -- cgit v1.2.3