ci: edge-cluster-trigger workflow (GH→ADO smoke test)#52
Conversation
Smoke-test workflow that fires SovereignSOC's edge-cluster ADO pipeline via the shared composite action at k8sstormcenter/actions/ trigger-ado-pipeline. workflow_dispatch only for now — once the chain is verified, we can fold it into component-tests.yaml as a pre-step that resets the target edge slot before the 33-test component suite runs against it. Required GH config (one-off): secrets.ADO_PAT (org-level, scope Build:Read & Execute) vars.ADO_ORG = AustrianDataLab vars.ADO_PROJECT = SovereignSOC vars.ADO_PIPELINE_EDGE = 61 A guard step fails fast if any of those are missing, with the exact key names to set. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
📝 WalkthroughWalkthroughThis pull request introduces a new GitHub Actions workflow file ( ChangesEdge Cluster Lifecycle Manual Trigger Workflow
🎯 2 (Simple) | ⏱️ ~10 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/edge-cluster-trigger.yml:
- Around line 13-48: Add a concurrency group to the workflow to prevent
simultaneous lifecycle actions on the same cluster by using the cluster input as
the key; specifically insert a concurrency block after the on: section (before
jobs:) that sets group to something like "edge-cluster-${{ github.workflow
}}-${{ github.event.inputs.cluster }}" (or similar using inputs.cluster) and
optionally set cancel-in-progress: true to let newer runs cancel older ones;
this will serialize runs for the same cluster while still allowing parallel runs
for different clusters and references the existing inputs.cluster and the
workflow's trigger.
- Around line 86-95: The Summary step currently injects step outputs directly
into a shell script which can allow shell metacharacters to be interpreted;
instead, capture the ADO outputs into environment variables (e.g., ADO_RUN_ID,
ADO_RUN_URL, ADO_RESULT from steps.ado.outputs.run_id/run_url/result) and then
append them to GITHUB_STEP_SUMMARY using safe, quoted expansion or printf (e.g.,
use printf '%s\n' "$ADO_RUN_ID") so that backticks/$() are not executed; modify
the "Summary" run block to export/use those env vars and ensure all variable
expansions are quoted when writing to GITHUB_STEP_SUMMARY.
- Around line 73-84: Update the GitHub Action step that defines the "ado" job to
pin the external action instead of using the floating ref: replace the uses
string "k8sstormcenter/actions/trigger-ado-pipeline@main" (in the step with id:
ado) with the commit SHA
"k8sstormcenter/actions/trigger-ado-pipeline@27790fc0392ded46e74bf2d7620c0b790cde2681";
keep the other inputs (org, project, pipeline_id, pat, cluster, action, os,
wait, timeout) unchanged.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: b145d083-a039-4706-8edf-e205e2bacb02
📒 Files selected for processing (1)
.github/workflows/edge-cluster-trigger.yml
| on: | ||
| workflow_dispatch: | ||
| inputs: | ||
| cluster: | ||
| description: 'Edge cluster slot' | ||
| required: true | ||
| default: 'edge4' | ||
| type: choice | ||
| options: | ||
| - edge4 | ||
| - k3s-1 | ||
| - k3s-2 | ||
| - rocky-1 | ||
| action: | ||
| description: 'Lifecycle action' | ||
| required: true | ||
| default: 'plan' | ||
| type: choice | ||
| options: | ||
| - plan | ||
| - create | ||
| - delete | ||
| - reset | ||
| os: | ||
| description: 'Base OS' | ||
| required: true | ||
| default: 'ubuntu' | ||
| type: choice | ||
| options: | ||
| - ubuntu | ||
| - rocky | ||
| wait: | ||
| description: 'Wait for ADO run to complete' | ||
| required: true | ||
| default: 'true' | ||
| type: boolean |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial | ⚡ Quick win
Consider adding concurrency control keyed on cluster input.
The workflow allows parallel manual triggers. If multiple users trigger lifecycle operations (especially reset, create, or delete) on the same cluster simultaneously, they could interfere with each other.
♻️ Proposed addition of concurrency group
Add this block after the on: section and before jobs::
wait:
description: 'Wait for ADO run to complete'
required: true
default: 'true'
type: boolean
+concurrency:
+ group: edge-cluster-${{ inputs.cluster }}
+ cancel-in-progress: false
+
jobs:
trigger:This allows parallel operations on different clusters while serializing operations on the same cluster. Set cancel-in-progress: true if you prefer newer runs to cancel pending ones.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| on: | |
| workflow_dispatch: | |
| inputs: | |
| cluster: | |
| description: 'Edge cluster slot' | |
| required: true | |
| default: 'edge4' | |
| type: choice | |
| options: | |
| - edge4 | |
| - k3s-1 | |
| - k3s-2 | |
| - rocky-1 | |
| action: | |
| description: 'Lifecycle action' | |
| required: true | |
| default: 'plan' | |
| type: choice | |
| options: | |
| - plan | |
| - create | |
| - delete | |
| - reset | |
| os: | |
| description: 'Base OS' | |
| required: true | |
| default: 'ubuntu' | |
| type: choice | |
| options: | |
| - ubuntu | |
| - rocky | |
| wait: | |
| description: 'Wait for ADO run to complete' | |
| required: true | |
| default: 'true' | |
| type: boolean | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| cluster: | |
| description: 'Edge cluster slot' | |
| required: true | |
| default: 'edge4' | |
| type: choice | |
| options: | |
| - edge4 | |
| - k3s-1 | |
| - k3s-2 | |
| - rocky-1 | |
| action: | |
| description: 'Lifecycle action' | |
| required: true | |
| default: 'plan' | |
| type: choice | |
| options: | |
| - plan | |
| - create | |
| - delete | |
| - reset | |
| os: | |
| description: 'Base OS' | |
| required: true | |
| default: 'ubuntu' | |
| type: choice | |
| options: | |
| - ubuntu | |
| - rocky | |
| wait: | |
| description: 'Wait for ADO run to complete' | |
| required: true | |
| default: 'true' | |
| type: boolean | |
| concurrency: | |
| group: edge-cluster-${{ inputs.cluster }} | |
| cancel-in-progress: false | |
| jobs: | |
| trigger: |
🧰 Tools
🪛 zizmor (1.25.2)
[warning] 13-48: insufficient job-level concurrency limits (concurrency-limits): workflow is missing concurrency setting
(concurrency-limits)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/edge-cluster-trigger.yml around lines 13 - 48, Add a
concurrency group to the workflow to prevent simultaneous lifecycle actions on
the same cluster by using the cluster input as the key; specifically insert a
concurrency block after the on: section (before jobs:) that sets group to
something like "edge-cluster-${{ github.workflow }}-${{
github.event.inputs.cluster }}" (or similar using inputs.cluster) and optionally
set cancel-in-progress: true to let newer runs cancel older ones; this will
serialize runs for the same cluster while still allowing parallel runs for
different clusters and references the existing inputs.cluster and the workflow's
trigger.
| - id: ado | ||
| uses: k8sstormcenter/actions/trigger-ado-pipeline@main | ||
| with: | ||
| org: ${{ vars.ADO_ORG }} | ||
| project: ${{ vars.ADO_PROJECT }} | ||
| pipeline_id: ${{ vars.ADO_PIPELINE_EDGE }} | ||
| pat: ${{ secrets.ADO_PAT }} | ||
| cluster: ${{ inputs.cluster }} | ||
| action: ${{ inputs.action }} | ||
| os: ${{ inputs.os }} | ||
| wait: ${{ inputs.wait }} | ||
| timeout: '1800' |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Get the latest commit hash for the action to pin to
gh api repos/k8sstormcenter/actions/commits/main \
--jq '.sha' \
| head -c 7Repository: k8sstormcenter/node-agent
Length of output: 76
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# 1) Show the exact workflow lines around the ADO trigger step
echo "---- Workflow snippet ----"
nl -ba .github/workflows/edge-cluster-trigger.yml | sed -n '60,95p'
# 2) Get full SHA for k8sstormcenter/actions@main
echo "---- GitHub: full SHA for repos/k8sstormcenter/actions@main ----"
FULL_SHA="$(gh api repos/k8sstormcenter/actions/commits/main --jq '.sha')"
echo "$FULL_SHA"
# 3) Verify that trigger-ado-pipeline path exists in that repo at that ref
# (GitHub Actions expects an action.yml/metadata inside the directory for the path portion.)
echo "---- GitHub: check contents/trigger-ado-pipeline exists at ref ----"
gh api "repos/k8sstormcenter/actions/contents/trigger-ado-pipeline?ref=$FULL_SHA" --jq '.[].path' || \
gh api "repos/k8sstormcenter/actions/contents/trigger-ado-pipeline?ref=$FULL_SHA" --jq '.path'Repository: k8sstormcenter/node-agent
Length of output: 157
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "---- Workflow snippet (lines 65-95) ----"
awk 'NR>=65 && NR<=95 {printf "%d:%s\n", NR, $0}' .github/workflows/edge-cluster-trigger.yml
echo "---- GitHub: full SHA for repos/k8sstormcenter/actions@main ----"
FULL_SHA="$(gh api repos/k8sstormcenter/actions/commits/main --jq '.sha')"
SHORT_SHA="$(echo "$FULL_SHA" | head -c 7)"
echo "FULL_SHA=$FULL_SHA"
echo "SHORT_SHA=$SHORT_SHA"
echo "---- GitHub: check trigger-ado-pipeline directory exists at that ref ----"
gh api "repos/k8sstormcenter/actions/contents/trigger-ado-pipeline?ref=$FULL_SHA" --jq '{path, type, name}' || true
echo "---- GitHub: check action entry files exist at that ref ----"
gh api "repos/k8sstormcenter/actions/contents/trigger-ado-pipeline/action.yml?ref=$FULL_SHA" --jq '.path' || true
gh api "repos/k8sstormcenter/actions/contents/trigger-ado-pipeline/action.yaml?ref=$FULL_SHA" --jq '.path' || true
gh api "repos/k8sstormcenter/actions/contents/trigger-ado-pipeline/metadata.json?ref=$FULL_SHA" --jq '.path' || trueRepository: k8sstormcenter/node-agent
Length of output: 2318
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FULL_SHA="$(gh api repos/k8sstormcenter/actions/commits/main --jq '.sha')"
echo "FULL_SHA=$FULL_SHA"
echo "---- Find any paths containing 'trigger-ado' in that ref ----"
gh api "repos/k8sstormcenter/actions/git/trees/$FULL_SHA?recursive=1" --jq '
.tree[]
| select(.path | test("trigger-ado"; "i"))
| {path, type}
' | head -n 50
echo "---- Find exact 'trigger-ado-pipeline' matches ----"
gh api "repos/k8sstormcenter/actions/git/trees/$FULL_SHA?recursive=1" --jq '
.tree[]
| select(.path == "trigger-ado-pipeline" or .path | test("trigger-ado-pipeline"; "i"))
| {path, type}
' | head -n 50Repository: k8sstormcenter/node-agent
Length of output: 180
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "---- contents: trigger-ado-pipeline at ref=main ----"
gh api "repos/k8sstormcenter/actions/contents/trigger-ado-pipeline?ref=main" --jq '
if type=="array" then map({name, path, type}) else {path, type} end
' || true
echo "---- contents: trigger-ado-pipeline at ref=SHORT_SHA=27790fc (if needed) ----"
gh api "repos/k8sstormcenter/actions/contents/trigger-ado-pipeline?ref=27790fc" --jq '
if type=="array" then map({name, path, type}) else {path, type} end
' || trueRepository: k8sstormcenter/node-agent
Length of output: 508
Pin trigger-ado-pipeline to a commit SHA (remove @main).
uses: k8sstormcenter/actions/trigger-ado-pipeline@main lets the workflow automatically pick up whatever main points to next. Pin it to 27790fc0392ded46e74bf2d7620c0b790cde2681 (e.g., update the uses: line around line 74). [Optional: keep timeout: '1800' as-is since it’s reasonable for a smoke test.]
🧰 Tools
🪛 zizmor (1.25.2)
[error] 74-74: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/edge-cluster-trigger.yml around lines 73 - 84, Update the
GitHub Action step that defines the "ado" job to pin the external action instead
of using the floating ref: replace the uses string
"k8sstormcenter/actions/trigger-ado-pipeline@main" (in the step with id: ado)
with the commit SHA
"k8sstormcenter/actions/trigger-ado-pipeline@27790fc0392ded46e74bf2d7620c0b790cde2681";
keep the other inputs (org, project, pipeline_id, pat, cluster, action, os,
wait, timeout) unchanged.
| - name: Summary | ||
| if: always() | ||
| run: | | ||
| echo "### ADO run" >> "$GITHUB_STEP_SUMMARY" | ||
| echo "" >> "$GITHUB_STEP_SUMMARY" | ||
| echo "- cluster: \`${{ inputs.cluster }}\`" >> "$GITHUB_STEP_SUMMARY" | ||
| echo "- action: \`${{ inputs.action }}\`" >> "$GITHUB_STEP_SUMMARY" | ||
| echo "- run id: ${{ steps.ado.outputs.run_id }}" >> "$GITHUB_STEP_SUMMARY" | ||
| echo "- url: ${{ steps.ado.outputs.run_url }}" >> "$GITHUB_STEP_SUMMARY" | ||
| echo "- result: ${{ steps.ado.outputs.result }}" >> "$GITHUB_STEP_SUMMARY" |
There was a problem hiding this comment.
Mitigate potential code injection via step outputs.
Lines 93-95 expand step outputs directly in the shell script. While the outputs come from a trusted internal action, if they contain shell metacharacters (backticks, $(), etc.), they could execute unintended code. Lines 91-92 are safe because workflow inputs are constrained by the choice type.
🛡️ Proposed fix using environment variables
- name: Summary
if: always()
+ env:
+ RUN_ID: ${{ steps.ado.outputs.run_id }}
+ RUN_URL: ${{ steps.ado.outputs.run_url }}
+ RESULT: ${{ steps.ado.outputs.result }}
run: |
echo "### ADO run" >> "$GITHUB_STEP_SUMMARY"
echo "" >> "$GITHUB_STEP_SUMMARY"
echo "- cluster: \`${{ inputs.cluster }}\`" >> "$GITHUB_STEP_SUMMARY"
echo "- action: \`${{ inputs.action }}\`" >> "$GITHUB_STEP_SUMMARY"
- echo "- run id: ${{ steps.ado.outputs.run_id }}" >> "$GITHUB_STEP_SUMMARY"
- echo "- url: ${{ steps.ado.outputs.run_url }}" >> "$GITHUB_STEP_SUMMARY"
- echo "- result: ${{ steps.ado.outputs.result }}" >> "$GITHUB_STEP_SUMMARY"
+ echo "- run id: $RUN_ID" >> "$GITHUB_STEP_SUMMARY"
+ echo "- url: $RUN_URL" >> "$GITHUB_STEP_SUMMARY"
+ echo "- result: $RESULT" >> "$GITHUB_STEP_SUMMARY"🧰 Tools
🪛 zizmor (1.25.2)
[warning] 91-91: code injection via template expansion (template-injection): may expand into attacker-controllable code
(template-injection)
[warning] 92-92: code injection via template expansion (template-injection): may expand into attacker-controllable code
(template-injection)
[info] 93-93: code injection via template expansion (template-injection): may expand into attacker-controllable code
(template-injection)
[info] 94-94: code injection via template expansion (template-injection): may expand into attacker-controllable code
(template-injection)
[info] 95-95: code injection via template expansion (template-injection): may expand into attacker-controllable code
(template-injection)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/edge-cluster-trigger.yml around lines 86 - 95, The Summary
step currently injects step outputs directly into a shell script which can allow
shell metacharacters to be interpreted; instead, capture the ADO outputs into
environment variables (e.g., ADO_RUN_ID, ADO_RUN_URL, ADO_RESULT from
steps.ado.outputs.run_id/run_url/result) and then append them to
GITHUB_STEP_SUMMARY using safe, quoted expansion or printf (e.g., use printf
'%s\n' "$ADO_RUN_ID") so that backticks/$() are not executed; modify the
"Summary" run block to export/use those env vars and ensure all variable
expansions are quoted when writing to GITHUB_STEP_SUMMARY.
Summary
Adds
.github/workflows/edge-cluster-trigger.yml— aworkflow_dispatch-onlyworkflow that exercises the GitHub→Azure DevOps trigger path for the
SovereignSOC edge-cluster pipeline. Uses the shared composite action at
k8sstormcenter/actions/trigger-ado-pipeline@main.Once verified, the same call can be folded into
component-tests.yamlas apre-step that resets the target edge slot before the 33-test component
suite runs against it (current setup uses ephemeral Kind).
Required GH config (already in place)
ADO_PATADO_ORGAustrianDataLabADO_PROJECTSovereignSOCADO_PIPELINE_EDGE61The workflow guards on these and prints exact missing keys if any are unset.
Test plan
action=plan, cluster=edge4(safe — no infra mutation)action=resetagainstedge4for a real lifecycle test🤖 Generated with Claude Code