From 1354267a5bf571fb61d8ddbeee1992fb0f430790 Mon Sep 17 00:00:00 2001 From: Yuriy Andamasov Date: Mon, 11 May 2026 00:57:18 +0300 Subject: ci: address Context7 LTS variants via tag field, not branch field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Post-#1961, the rolling auto-refresh (default variant, no field) succeeds, but workflow_dispatch for branch=circinus/sagitta still 404s. Further empirical curls against the live API revealed: POST /api/v1/refresh {"libraryName":"/vyos/vyos-documentation","branch":"circinus"} → HTTP 404 {"error":"branch_not_found","message":"Branch 'circinus' not found"} POST /api/v1/refresh {"libraryName":"/vyos/vyos-documentation","tag":"1.5"} → HTTP 200 {"message":"Refresh started successfully"} So Context7's API addresses variants by their REGISTRATION TYPE on the dashboard: - default variant (rolling) → omit both 'branch' and 'tag' - tag-backed variants (1.5/1.4) → 'tag' field - branch-backed non-default → 'branch' field The dashboard shows 'rolling' with a branch icon and '1.5'/'1.4' with tag icons. The 'branch' field only addresses entries registered as branches; 'tag' addresses entries registered as tags. This is undocumented in the public GitHub Actions integration page but works against the live API. Changes: - Restore the variant mapping (circinus → 1.5, sagitta → 1.4) — that matches the actual dashboard variant names. #1961 had dropped this in favor of branch-name passthrough, which only worked for the default. - Switch the non-default payload from 'branch: ' to 'tag: '. - workflow_dispatch input renamed back from 'branch' to 'variant'; choices back to [rolling, '1.5', '1.4']. - Restore the variant-keyed concurrency expression (with rewrite chain). - Update the documentation comment to record the empirical API semantics. Spec: ~/.claude/specs/2026-05-10-context7-github-actions-integration-design.md 🤖 Generated by [robots](https://vyos.io) --- .github/workflows/context7-refresh.yml | 49 ++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/.github/workflows/context7-refresh.yml b/.github/workflows/context7-refresh.yml index 86d92735..67a51cca 100644 --- a/.github/workflows/context7-refresh.yml +++ b/.github/workflows/context7-refresh.yml @@ -5,9 +5,12 @@ name: Context7 refresh # workflow is matched by name — DO NOT rename `Update version tags` without # updating the `workflows:` field below. # -# Context7 API addresses variants by their underlying Git branch name -# (rolling/circinus/sagitta), not by their display name (rolling/1.5/1.4). -# For the default variant (rolling) the `branch` field is OMITTED. +# Context7 API addresses variants by their TYPE: +# - default variant (rolling) → omit both `branch` and `tag` +# - tag-backed variants (1.5/1.4) → `tag: "1.5"` / `tag: "1.4"` +# - branch-backed non-default → `branch: ""` (none used here) +# `branch: "1.5"` returns 404 branch_not_found; `branch: "rolling"` returns +# 400 branch-not-found. Empirically verified 2026-05-10 against the live API. # # OPERATOR NOTE: do NOT use GitHub's "Re-run jobs" on `Update version tags`. # Re-runs replay the original SHA, which would move the version tag @@ -21,16 +24,22 @@ on: types: [completed] workflow_dispatch: inputs: - branch: - description: "Source branch whose Context7 variant to refresh" + variant: + description: "Context7 variant to refresh (rolling = default; 1.5 = circinus; 1.4 = sagitta)" type: choice - options: [rolling, circinus, sagitta] + options: [rolling, "1.5", "1.4"] default: rolling permissions: {} concurrency: - group: context7-refresh-${{ inputs.branch || github.event.workflow_run.head_branch }} + group: >- + context7-refresh-${{ + inputs.variant + || (github.event.workflow_run.head_branch == 'circinus' && '1.5') + || (github.event.workflow_run.head_branch == 'sagitta' && '1.4') + || github.event.workflow_run.head_branch + }} cancel-in-progress: true jobs: @@ -46,7 +55,7 @@ jobs: env: CONTEXT7_API_KEY: ${{ secrets.CONTEXT7_API_KEY }} HEAD_BRANCH: ${{ github.event.workflow_run.head_branch }} - DISPATCH_BRANCH: ${{ inputs.branch }} + DISPATCH_VARIANT: ${{ inputs.variant }} run: | set -euo pipefail if [[ -z "${CONTEXT7_API_KEY:-}" ]]; then @@ -54,23 +63,23 @@ jobs: exit 1 fi if [[ "${GITHUB_EVENT_NAME}" == "workflow_dispatch" ]]; then - BRANCH="$DISPATCH_BRANCH" + VARIANT="$DISPATCH_VARIANT" else - BRANCH="$HEAD_BRANCH" + case "$HEAD_BRANCH" in + rolling) VARIANT=rolling ;; + circinus) VARIANT=1.5 ;; + sagitta) VARIANT=1.4 ;; + *) echo "Unexpected head_branch: $HEAD_BRANCH" >&2; exit 1 ;; + esac fi - case "$BRANCH" in - rolling|circinus|sagitta) ;; - *) echo "Unexpected branch: $BRANCH" >&2; exit 1 ;; - esac - echo "Refreshing Context7 variant for branch: $BRANCH" - # Default variant (rolling) refreshes when `branch` is omitted; - # non-default variants address by their Git branch name. - if [[ "$BRANCH" == "rolling" ]]; then + echo "Refreshing Context7 variant: $VARIANT" + # rolling = default variant (no field). 1.5/1.4 = tag-backed. + if [[ "$VARIANT" == "rolling" ]]; then PAYLOAD="$(jq -nc --arg lib "/${{ github.repository }}" \ '{libraryName: $lib}')" else - PAYLOAD="$(jq -nc --arg lib "/${{ github.repository }}" --arg br "$BRANCH" \ - '{libraryName: $lib, branch: $br}')" + PAYLOAD="$(jq -nc --arg lib "/${{ github.repository }}" --arg tg "$VARIANT" \ + '{libraryName: $lib, tag: $tg}')" fi curl -fsSL \ --connect-timeout 10 \ -- cgit v1.2.3 From ac0b1e8d8ea866c0314d693443bf29b76f093822 Mon Sep 17 00:00:00 2001 From: Yuriy Andamasov Date: Mon, 11 May 2026 01:02:47 +0300 Subject: ci: validate workflow_dispatch variant input against allowlist MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Copilot review on #1962 flagged that the type:choice UI constraint on workflow_dispatch.inputs.variant is bypassable when invoked via API (gh workflow run -f variant=…). An empty or arbitrary value would: - Generate a malformed concurrency key (context7-refresh- with empty suffix, since inputs.variant || head_branch || '' both go falsy) - Pass garbage to Context7's API (404s gracefully, but still a wasted runner minute and noisy) Add an explicit allowlist case after VARIANT is computed. Fails loud with a clear message before any downstream call. 🤖 Generated by [robots](https://vyos.io) --- .github/workflows/context7-refresh.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/context7-refresh.yml b/.github/workflows/context7-refresh.yml index 67a51cca..67ed7bd4 100644 --- a/.github/workflows/context7-refresh.yml +++ b/.github/workflows/context7-refresh.yml @@ -72,6 +72,12 @@ jobs: *) echo "Unexpected head_branch: $HEAD_BRANCH" >&2; exit 1 ;; esac fi + # Defense-in-depth: type:choice UI constraint is bypassable when + # workflow_dispatch is invoked via API (`gh workflow run -f variant=…`). + case "$VARIANT" in + rolling|1.5|1.4) ;; + *) echo "Invalid variant: '${VARIANT}'. Expected one of: rolling, 1.5, 1.4." >&2; exit 1 ;; + esac echo "Refreshing Context7 variant: $VARIANT" # rolling = default variant (no field). 1.5/1.4 = tag-backed. if [[ "$VARIANT" == "rolling" ]]; then -- cgit v1.2.3 From 0685e742b125d49271de9a96f45042c79e963458 Mon Sep 17 00:00:00 2001 From: Yuriy Andamasov Date: Mon, 11 May 2026 01:58:23 +0300 Subject: ci: sanitize concurrency.group against arbitrary variant input MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CodeRabbit review on #1962 flagged that concurrency.group is evaluated at workflow scheduling time, BEFORE the shell-level allowlist case in the job runs. An API-triggered workflow_dispatch with arbitrary variant value (e.g. 'gh workflow run -f variant=foobar') would produce concurrency key 'context7-refresh-foobar' and bypass dedupe with legitimate runs. Gate inputs.variant in the expression against the same allowlist ('rolling'/'1.5'/'1.4'). Map head_branch values to their canonical variant names so the workflow_run path also resolves cleanly. Fall through to 'invalid' if neither path matches — the shell allowlist then fails the run loud. 🤖 Generated by [robots](https://vyos.io) --- .github/workflows/context7-refresh.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/context7-refresh.yml b/.github/workflows/context7-refresh.yml index 67ed7bd4..b21124d8 100644 --- a/.github/workflows/context7-refresh.yml +++ b/.github/workflows/context7-refresh.yml @@ -35,10 +35,11 @@ permissions: {} concurrency: group: >- context7-refresh-${{ - inputs.variant + (contains(fromJSON('["rolling","1.5","1.4"]'), inputs.variant) && inputs.variant) || (github.event.workflow_run.head_branch == 'circinus' && '1.5') || (github.event.workflow_run.head_branch == 'sagitta' && '1.4') - || github.event.workflow_run.head_branch + || (github.event.workflow_run.head_branch == 'rolling' && 'rolling') + || 'invalid' }} cancel-in-progress: true -- cgit v1.2.3