-
Notifications
You must be signed in to change notification settings - Fork 95
test: trigger OKP quality gate #2051
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,186 @@ | ||
| name: OKP Quality Gate | ||
|
|
||
| on: | ||
| pull_request: | ||
| types: [opened, synchronize, reopened] | ||
| paths: | ||
| - 'src/llama_stack_configuration.py' | ||
| - 'src/constants.py' | ||
| - 'src/configuration.py' | ||
| - 'src/models/config.py' | ||
| - 'src/utils/vector_search.py' | ||
| - 'src/utils/reranker.py' | ||
| - 'src/app/endpoints/query.py' | ||
| - 'src/app/endpoints/streaming_query.py' | ||
| - 'src/app/endpoints/responses.py' | ||
| - 'src/app/endpoints/rags.py' | ||
| - 'src/client.py' | ||
| - 'providers/**/solr_vector_io/**' | ||
| - 'deploy/lightspeed-stack/Containerfile' | ||
| - 'docker-compose-library.yaml' | ||
|
|
||
| permissions: | ||
| pull-requests: write | ||
|
|
||
| jobs: | ||
| okp-quality-gate: | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 120 | ||
| steps: | ||
| - name: Trigger GitLab evaluation pipeline | ||
| id: trigger | ||
| env: | ||
| GITLAB_TRIGGER_TOKEN: ${{ secrets.GITLAB_TRIGGER_TOKEN }} | ||
| GITLAB_PROJECT_ID: ${{ vars.GITLAB_PROJECT_ID }} | ||
| run: | | ||
| RESPONSE=$(curl --silent --show-error --fail \ | ||
| --request POST \ | ||
| --form "token=${GITLAB_TRIGGER_TOKEN}" \ | ||
| --form "ref=ci-trigger-test" \ | ||
| --form "variables[PR_REPO]=${{ github.event.pull_request.head.repo.full_name }}" \ | ||
| --form "variables[PR_BRANCH]=${{ github.event.pull_request.head.ref }}" \ | ||
| --form "variables[PR_SHA]=${{ github.event.pull_request.head.sha }}" \ | ||
| "https://gitlab.cee.redhat.com/api/v4/projects/${GITLAB_PROJECT_ID}/trigger/pipeline") | ||
|
Comment on lines
+36
to
+43
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🔴 Critical | ⚡ Quick win Script injection via untrusted PR branch ref in shell
The 🔒 Proposed fix using intermediate env vars - name: Trigger GitLab evaluation pipeline
id: trigger
env:
GITLAB_TRIGGER_TOKEN: ${{ secrets.GITLAB_TRIGGER_TOKEN }}
GITLAB_PROJECT_ID: ${{ vars.GITLAB_PROJECT_ID }}
+ PR_REPO: ${{ github.event.pull_request.head.repo.full_name }}
+ PR_BRANCH: ${{ github.event.pull_request.head.ref }}
+ PR_SHA: ${{ github.event.pull_request.head.sha }}
run: |
RESPONSE=$(curl --silent --show-error --fail \
--request POST \
--form "token=${GITLAB_TRIGGER_TOKEN}" \
--form "ref=ci-trigger-test" \
- --form "variables[PR_REPO]=${{ github.event.pull_request.head.repo.full_name }}" \
- --form "variables[PR_BRANCH]=${{ github.event.pull_request.head.ref }}" \
- --form "variables[PR_SHA]=${{ github.event.pull_request.head.sha }}" \
+ --form "variables[PR_REPO]=${PR_REPO}" \
+ --form "variables[PR_BRANCH]=${PR_BRANCH}" \
+ --form "variables[PR_SHA]=${PR_SHA}" \
"https://gitlab.cee.redhat.com/api/v4/projects/${GITLAB_PROJECT_ID}/trigger/pipeline") - name: Wait for GitLab pipeline to complete
env:
GITLAB_API_TOKEN: ${{ secrets.GITLAB_API_TOKEN }}
GITLAB_PROJECT_ID: ${{ vars.GITLAB_PROJECT_ID }}
+ PIPELINE_ID: ${{ steps.trigger.outputs.pipeline_id }}
run: |
- PIPELINE_ID=${{ steps.trigger.outputs.pipeline_id }}
echo "Waiting for pipeline #${PIPELINE_ID}..."Also applies to: 59-59 🧰 Tools🪛 zizmor (1.26.1)[error] 37-37: code injection via template expansion (template-injection): may expand into attacker-controllable code (template-injection) [error] 38-38: code injection via template expansion (template-injection): may expand into attacker-controllable code (template-injection) [warning] 39-39: code injection via template expansion (template-injection): may expand into attacker-controllable code (template-injection) 🤖 Prompt for AI AgentsSource: Linters/SAST tools |
||
|
|
||
| PIPELINE_ID=$(echo "$RESPONSE" | jq -r '.id') | ||
| PIPELINE_URL=$(echo "$RESPONSE" | jq -r '.web_url') | ||
|
|
||
| if [ "$PIPELINE_ID" = "null" ] || [ -z "$PIPELINE_ID" ]; then | ||
| echo "Failed to trigger pipeline. Response:" | ||
| echo "$RESPONSE" | ||
| exit 1 | ||
| fi | ||
|
|
||
| echo "pipeline_id=${PIPELINE_ID}" >> "$GITHUB_OUTPUT" | ||
| echo "pipeline_url=${PIPELINE_URL}" >> "$GITHUB_OUTPUT" | ||
| echo "Triggered GitLab pipeline #${PIPELINE_ID}: ${PIPELINE_URL}" | ||
|
|
||
| - name: Wait for GitLab pipeline to complete | ||
| id: wait | ||
| env: | ||
| GITLAB_API_TOKEN: ${{ secrets.GITLAB_API_TOKEN }} | ||
| GITLAB_PROJECT_ID: ${{ vars.GITLAB_PROJECT_ID }} | ||
| run: | | ||
| PIPELINE_ID=${{ steps.trigger.outputs.pipeline_id }} | ||
| echo "Waiting for pipeline #${PIPELINE_ID}..." | ||
|
|
||
| while true; do | ||
| RESPONSE=$(curl --silent --show-error \ | ||
| --header "PRIVATE-TOKEN: ${GITLAB_API_TOKEN}" \ | ||
| "https://gitlab.cee.redhat.com/api/v4/projects/${GITLAB_PROJECT_ID}/pipelines/${PIPELINE_ID}") | ||
|
Comment on lines
+68
to
+70
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value Add a request timeout to Neither 🤖 Prompt for AI Agents |
||
|
|
||
| STATUS=$(echo "$RESPONSE" | jq -r '.status') | ||
|
|
||
| case "$STATUS" in | ||
| success) | ||
| echo "Pipeline #${PIPELINE_ID} passed!" | ||
| echo "status=success" >> "$GITHUB_OUTPUT" | ||
| break | ||
| ;; | ||
| failed) | ||
| echo "Pipeline #${PIPELINE_ID} failed." | ||
| echo "status=failed" >> "$GITHUB_OUTPUT" | ||
| break | ||
| ;; | ||
| canceled) | ||
| echo "Pipeline #${PIPELINE_ID} was canceled." | ||
| echo "status=canceled" >> "$GITHUB_OUTPUT" | ||
| break | ||
| ;; | ||
| created|waiting_for_resource|preparing|pending|running) | ||
| echo "Status: ${STATUS}. Checking again in 60s..." | ||
| sleep 60 | ||
| ;; | ||
| *) | ||
| echo "Unexpected status: ${STATUS}. Checking again in 60s..." | ||
| sleep 60 | ||
| ;; | ||
| esac | ||
| done | ||
|
|
||
| - name: Fetch evaluation results from GitLab | ||
| id: results | ||
| if: always() && steps.wait.outputs.status != 'canceled' | ||
| env: | ||
| GITLAB_API_TOKEN: ${{ secrets.GITLAB_API_TOKEN }} | ||
| GITLAB_PROJECT_ID: ${{ vars.GITLAB_PROJECT_ID }} | ||
| run: | | ||
| PIPELINE_ID=${{ steps.trigger.outputs.pipeline_id }} | ||
|
|
||
| JOB_ID=$(curl --silent --show-error \ | ||
| --header "PRIVATE-TOKEN: ${GITLAB_API_TOKEN}" \ | ||
| "https://gitlab.cee.redhat.com/api/v4/projects/${GITLAB_PROJECT_ID}/pipelines/${PIPELINE_ID}/jobs" \ | ||
| | jq -r '[.[] | select(.name == "stack_test")][0].id') | ||
|
|
||
| if [ "$JOB_ID" = "null" ] || [ -z "$JOB_ID" ]; then | ||
| echo "Could not find stack_test job in pipeline" | ||
| echo "has_report=false" >> "$GITHUB_OUTPUT" | ||
| exit 0 | ||
| fi | ||
|
|
||
| HTTP_CODE=$(curl --silent --output regression_report.md --write-out "%{http_code}" \ | ||
| --header "PRIVATE-TOKEN: ${GITLAB_API_TOKEN}" \ | ||
| "https://gitlab.cee.redhat.com/api/v4/projects/${GITLAB_PROJECT_ID}/jobs/${JOB_ID}/artifacts/lightspeed-evaluation/eval_output/ci_run/regression_report.md") | ||
|
|
||
| if [ "$HTTP_CODE" = "200" ] && [ -s regression_report.md ]; then | ||
| echo "has_report=true" >> "$GITHUB_OUTPUT" | ||
| else | ||
| echo "No regression report artifact found (HTTP ${HTTP_CODE})" | ||
| echo "has_report=false" >> "$GITHUB_OUTPUT" | ||
| fi | ||
|
|
||
| - name: Post results to PR | ||
| if: always() && steps.wait.outputs.status != '' | ||
| uses: actions/github-script@v7 | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Fetch the latest commit SHA for actions/github-script@v7
curl -s https://api.github.com/repos/actions/github-script/git/refs/tags/v7 | jq -r '.object.sha'Repository: lightspeed-core/lightspeed-stack Length of output: 175 🏁 Script executed: #!/bin/bash
set -euo pipefail
# Inspect the workflow context around the reported line
nl -ba .github/workflows/okp-quality-gate.yaml | sed -n '110,155p'
# Resolve the v7 tag correctly (dereference annotated tags if needed)
python3 - <<'PY'
import json, urllib.request
repo = "actions/github-script"
ref_url = f"https://api.github.com/repos/{repo}/git/refs/tags/v7"
req = urllib.request.Request(ref_url, headers={"Accept": "application/vnd.github+json", "User-Agent": "code-rabbit"})
with urllib.request.urlopen(req) as r:
ref = json.load(r)
print("ref_object_type:", ref.get("object", {}).get("type"))
print("ref_object_sha:", ref.get("object", {}).get("sha"))
if ref.get("object", {}).get("type") == "tag":
tag_sha = ref["object"]["sha"]
tag_url = f"https://api.github.com/repos/{repo}/git/tags/{tag_sha}"
req = urllib.request.Request(tag_url, headers={"Accept": "application/vnd.github+json", "User-Agent": "code-rabbit"})
with urllib.request.urlopen(req) as r:
tag = json.load(r)
print("tag_object_type:", tag.get("object", {}).get("type"))
print("tag_commit_sha:", tag.get("object", {}).get("sha"))
PYRepository: lightspeed-core/lightspeed-stack Length of output: 210 🏁 Script executed: #!/bin/bash
set -euo pipefail
# Inspect the workflow context around the reported line without nl
awk 'NR>=110 && NR<=155 { print NR ":" $0 }' .github/workflows/okp-quality-gate.yaml
# Resolve actions/github-script@v7 to a commit if the API is reachable
python3 - <<'PY'
import json, urllib.request, sys
repo = "actions/github-script"
headers = {"Accept": "application/vnd.github+json", "User-Agent": "code-rabbit"}
def get(url):
req = urllib.request.Request(url, headers=headers)
with urllib.request.urlopen(req, timeout=20) as r:
return json.load(r)
try:
ref = get(f"https://api.github.com/repos/{repo}/git/refs/tags/v7")
print("ref_json:", json.dumps(ref, sort_keys=True))
obj = ref.get("object", {})
if obj.get("type") == "tag":
tag = get(f"https://api.github.com/repos/{repo}/git/tags/{obj['sha']}")
print("tag_json:", json.dumps(tag, sort_keys=True))
elif obj.get("type") == "commit":
print("commit_sha:", obj.get("sha"))
except Exception as e:
print("ERROR:", type(e).__name__, e)
sys.exit(0)
PYRepository: lightspeed-core/lightspeed-stack Length of output: 2821 🏁 Script executed: #!/bin/bash
set -euo pipefail
# Show the workflow header and permissions/event context
awk 'NR>=1 && NR<=60 { print NR ":" $0 }' .github/workflows/okp-quality-gate.yamlRepository: lightspeed-core/lightspeed-stack Length of output: 2447 🏁 Script executed: #!/bin/bash
set -euo pipefail
python3 - <<'PY'
import json, ssl, urllib.request, sys
repo = "actions/github-script"
headers = {"Accept": "application/vnd.github+json", "User-Agent": "code-rabbit"}
ctx = ssl._create_unverified_context()
def get(url):
req = urllib.request.Request(url, headers=headers)
with urllib.request.urlopen(req, context=ctx, timeout=20) as r:
return json.load(r)
ref = get(f"https://api.github.com/repos/{repo}/git/refs/tags/v7")
print("ref_type:", ref.get("object", {}).get("type"))
print("ref_sha:", ref.get("object", {}).get("sha"))
obj = ref.get("object", {})
if obj.get("type") == "tag":
tag = get(f"https://api.github.com/repos/{repo}/git/tags/{obj['sha']}")
print("tag_type:", tag.get("object", {}).get("type"))
print("commit_sha:", tag.get("object", {}).get("sha"))
else:
print("commit_sha:", obj.get("sha"))
sha = (tag.get("object", {}).get("sha") if obj.get("type") == "tag" else obj.get("sha"))
if sha:
commit = get(f"https://api.github.com/repos/{repo}/commits/{sha}")
print("commit_exists:", commit.get("sha") == sha)
PYRepository: lightspeed-core/lightspeed-stack Length of output: 1166 Pin 🧰 Tools🪛 zizmor (1.26.1)[error] 134-134: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy) (unpinned-uses) 🤖 Prompt for AI AgentsSource: Linters/SAST tools |
||
| env: | ||
| PIPELINE_STATUS: ${{ steps.wait.outputs.status }} | ||
| PIPELINE_URL: ${{ steps.trigger.outputs.pipeline_url }} | ||
| HAS_REPORT: ${{ steps.results.outputs.has_report }} | ||
| with: | ||
| script: | | ||
| const fs = require('fs'); | ||
| const status = process.env.PIPELINE_STATUS; | ||
| const pipelineUrl = process.env.PIPELINE_URL; | ||
| const hasReport = process.env.HAS_REPORT === 'true'; | ||
|
|
||
| const statusEmoji = status === 'success' ? '✅' : status === 'failed' ? '❌' : '⚠️'; | ||
| let body = `## ${statusEmoji} OKP Quality Gate: ${status}\n\n`; | ||
| body += `[GitLab Pipeline](${pipelineUrl})\n\n`; | ||
|
|
||
| if (hasReport) { | ||
| const report = fs.readFileSync('regression_report.md', 'utf8'); | ||
| body += `<details>\n<summary>Regression Report</summary>\n\n${report}\n\n</details>\n`; | ||
| } else if (status === 'failed') { | ||
| body += `No regression report was generated. Check the [pipeline logs](${pipelineUrl}) for details.\n`; | ||
| } | ||
|
|
||
| const { data: comments } = await github.rest.issues.listComments({ | ||
| owner: context.repo.owner, | ||
| repo: context.repo.repo, | ||
| issue_number: context.issue.number, | ||
| }); | ||
| const existing = comments.find(c => | ||
| c.user.type === 'Bot' && c.body.includes('OKP Quality Gate') | ||
| ); | ||
|
|
||
| if (existing) { | ||
| await github.rest.issues.updateComment({ | ||
| owner: context.repo.owner, | ||
| repo: context.repo.repo, | ||
| comment_id: existing.id, | ||
| body, | ||
| }); | ||
| } else { | ||
| await github.rest.issues.createComment({ | ||
| owner: context.repo.owner, | ||
| repo: context.repo.repo, | ||
| issue_number: context.issue.number, | ||
| body, | ||
| }); | ||
| } | ||
|
|
||
| - name: Fail if pipeline did not succeed | ||
| if: steps.wait.outputs.status != 'success' | ||
| run: | | ||
| echo "Pipeline status: ${{ steps.wait.outputs.status }}" | ||
| exit 1 | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win
Add explicit least-privilege
permissionsand aconcurrencygroup.The job has no
permissions:block (defaults to broadGITHUB_TOKENscopes) and noconcurrencygroup, so repeated pushes to the same PR (synchronize) can trigger multiple overlapping GitLab pipelines instead of canceling the stale one.🛡️ Proposed fix
📝 Committable suggestion
🧰 Tools
🪛 zizmor (1.26.1)
[info] 23-23: workflow or action definition without a name (anonymous-definition): this job
(anonymous-definition)
🤖 Prompt for AI Agents
Source: Linters/SAST tools