Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
359 changes: 359 additions & 0 deletions .github/workflows/ci-no-infra-g1.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,359 @@
name: CI-no-infra-g1

on:
workflow_dispatch:
workflow_call:
inputs:
trigger:
type: string

env:
SHA: ${{ inputs.trigger && fromJson(inputs.trigger).event.workflow_run.head_sha || github.sha }}

jobs:
# ---------------------------------------------------------------------------
# Availability-aware dispatch (PROTOTYPE -- CI-legacy-g3 only for now).
# `runs-on` locks its pool when the job is created and never falls back, so a
# self-hosted-routed job queues behind the 6-VM pool even while GitHub-hosted
# capacity sits idle. This leading job decides the pool up front: eligible
# actor+workflow AND an idle proxysql-ci VM -> self-hosted; pool saturated ->
# SPILL to GitHub-hosted instead of queuing. Listing runners needs repo admin
# (GH_TOKEN_SYSOWN); if that is unavailable we fail SAFE to self-hosted so the
# existing offload behavior is preserved. It is a heuristic (idle-now != idle-
# when-the-job-lands), so it drains the tail to hosted rather than perfectly
# packing the VMs.
# ---------------------------------------------------------------------------
pick-runner:
runs-on: ubuntu-latest
timeout-minutes: 120
permissions:
actions: read # read in-progress runs/jobs (no repo admin needed)
outputs:
runson: ${{ steps.pick.outputs.runson }}
steps:
- name: Pick runner pool
id: pick
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REPO: ${{ github.repository }}
POOL_SIZE: ${{ vars.SELFHOSTED_POOL_SIZE || '6' }}
ELIGIBLE: ${{ (github.actor == 'renecannao' || (inputs.trigger && fromJson(inputs.trigger).event.workflow_run.actor.login == 'renecannao')) && vars.SELFHOSTED_WORKFLOWS && contains(fromJson(vars.SELFHOSTED_WORKFLOWS), github.workflow) }}
run: |
set -uo pipefail
choice='"ubuntu-22.04"'
if [ "${ELIGIBLE:-}" = "true" ]; then
POOL="${POOL_SIZE:-6}"
# Listing runners needs repo admin, which no available token has, so
# infer pool demand from job state the default token CAN read:
# demand = VMs occupied (in-progress jobs whose runner_name starts
# with 'ci-vm') + jobs already queued for the pool
# (status=queued carrying the 'proxysql-ci' label).
# Counting the queue too dampens the cycle-start thundering herd: once
# earlier deciders have queued self-hosted jobs, later deciders see the
# contention and spill. free = POOL - demand; self-hosted iff free>=1.
# Early-exit once demand hits POOL. Scan in-progress AND queued runs.
demand=0; err=0
ids=$( { gh api --paginate "repos/${REPO}/actions/runs?status=in_progress&per_page=100" --jq '.workflow_runs[].id';
gh api --paginate "repos/${REPO}/actions/runs?status=queued&per_page=100" --jq '.workflow_runs[].id'; } 2>e1 ) || err=1
if [ "$err" = "0" ]; then
for rid in ${ids:-}; do
[ "$demand" -ge "$POOL" ] && break
n=$(gh api "repos/${REPO}/actions/runs/${rid}/jobs" --jq '[.jobs[]|select((.status=="in_progress" and ((.runner_name//"")|startswith("ci-vm"))) or (.status=="queued" and (([.labels[]]|index("proxysql-ci")) != null)))]|length' 2>/dev/null) || n=0
demand=$(( demand + ${n:-0} ))
done
free=$(( POOL - demand ))
echo ">>> self-hosted pool: demand=${demand}/${POOL} free=${free} (occupied+queued)"
if [ "${free}" -ge 1 ]; then
choice='["self-hosted","proxysql-ci"]'; echo ">>> capacity available -> self-hosted"
else
choice='"ubuntu-22.04"'; echo ">>> pool at/over capacity -> spill to GitHub-hosted"
fi
else
echo ">>> WARNING: could not read runs:"; sed 's/^/ /' e1
choice='["self-hosted","proxysql-ci"]'; echo ">>> failing SAFE to self-hosted (offload unchanged)"
fi
else
echo ">>> not eligible for self-hosted -> GitHub-hosted"
fi
echo "runson=${choice}" >> "$GITHUB_OUTPUT"
echo ">>> decision: runson=${choice}"

tests:
needs: pick-runner
runs-on: ${{ fromJson(needs.pick-runner.outputs.runson) }}
timeout-minutes: 120
# `write-all` grants every default GITHUB_TOKEN scope plus the
# id-token:write scope codecov-action@v4 needs to mint a GitHub OIDC
# token for `use_oidc: true`. Caller workflow on v3.0 must also
# declare write-all for the permissions to propagate through the
# reusable call (permissions are intersected caller-callee).
permissions: write-all

Check warning on line 90 in .github/workflows/ci-no-infra-g1.yml

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Replace "write-all" with specific permissions (e.g., "contents: write").

See more on https://sonarcloud.io/project/issues?id=sysown_proxysql&issues=AZ_mVZ1GO9VMtsP3-0yb&open=AZ_mVZ1GO9VMtsP3-0yb&pullRequest=6008

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== workflow excerpt =="
sed -n '1,140p' .github/workflows/ci-no-infra-g1.yml
echo

echo "== permission occurrences =="
rg -n "permissions:|github_token_permissions|write-all|id-token|actions:|checks:|contents:|packages:" .github/workflows/ci-no-infra-g1.yml || true

echo "== codecov references =="
rg -n "codecov|gh api|checks-action|checkout|set --pull-request" .github/workflows/ci-no-infra-g1.yml || true

echo "== related workflows permissions =="
rg -n "permissions:\s*write-all|id-token:" .github/workflows || true

Repository: sysown/proxysql

Length of output: 17849


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== call sites for ci-no-infra-g1 =="
rg -n "ci-no-infra-g1|needs:.*CI|uses:.*/main|on:\n  workflow_call|permissions:\s*write-all" .github/workflows/*.yml .github/ 2>/dev/null || true

echo

echo "== workflow_call permissions in caller-like workflows near call site =="
for f in .github/workflows/ci-no-infra-g1.yml .github/workflows/ci-mysql84-gr-g1.yml; do
  [ -f "$f" ] || continue
  echo "-- $f"
  sed -n '1,120p' "$f"
done

Repository: sysown/proxysql

Length of output: 11746


🌐 Web query:

Codecov GitHub Actions oidc GITHUB_TOKEN permissions id-token write

💡 Result:

To use OpenID Connect (OIDC) with the Codecov GitHub Action, you must explicitly grant the id-token: write permission within your GitHub Actions workflow [1][2][3]. This configuration allows the action to request a short-lived OIDC token to authenticate with Codecov, eliminating the need for a long-lived repository secret [1][4]. To implement this, add the following configuration to your workflow or job [1][3]: permissions: id-token: write # Ensure you also have read permissions if you have overridden defaults contents: read Then, configure the codecov-action to use OIDC by setting the use_oidc argument to true [1][2][3]: - uses: codecov/codecov-action@v5 with: use_oidc: true Important considerations: - Permission Scope: When you define a permissions block at the workflow or job level, it overrides all default permissions [5]. If you override these defaults, ensure you include contents: read so that steps like actions/checkout can still function [5]. - Token Behavior: If use_oidc: true is set, any token or CODECOV_TOKEN provided to the action will be ignored, as the action will default to using the OIDC token for authentication [1][2][3]. - Forks: OIDC authentication is generally not applicable to pull requests from forks [6][7]. The action handles tokenless uploads for forks automatically, which remains the standard approach for those scenarios [6][8].

Citations:


Replace write-all with the required permissions.

This job only needs actions: read, contents: read, id-token: write, packages: read, and checks: write; write-all grants every supported GITHUB_TOKEN write scope.

Proposed fix
-    permissions: write-all
+    permissions:
+      actions: read
+      checks: write
+      contents: read
+      id-token: write
+      packages: read

</细节>

Run this workflow after the change to verify artifact retrieval, GHCR authentication, GitHub Check updates, and Codecov upload.

📝 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.

Suggested change
permissions: write-all
permissions:
actions: read
checks: write
contents: read
id-token: write
packages: read
🧰 Tools
🪛 zizmor (1.29.0)

[error] 90-90: overly broad permissions (excessive-permissions): uses write-all permissions

(excessive-permissions)

🤖 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/ci-no-infra-g1.yml at line 90, Replace the workflow job’s
permissions: write-all setting with explicit permissions for actions: read,
contents: read, id-token: write, packages: read, and checks: write. Keep the
job’s existing behavior for artifact retrieval, GHCR authentication, GitHub
Check updates, and Codecov upload.

Source: Linters/SAST tools

strategy:
fail-fast: false
matrix:
infradb: [ 'no-infra' ]
env:
BLDCACHE: ${{ inputs.trigger && fromJson(inputs.trigger).event.workflow_run.head_sha || github.sha }}_ubuntu24-tap-genai-gcov_src
MATRIX: '(${{ matrix.infradb }},genai-gcov)'

steps:

- uses: LouisBrunner/checks-action@v2.0.0

Check failure on line 101 in .github/workflows/ci-no-infra-g1.yml

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use full commit SHA hash for this dependency.

See more on https://sonarcloud.io/project/issues?id=sysown_proxysql&issues=AZ_mVZ1GO9VMtsP3-0yc&open=AZ_mVZ1GO9VMtsP3-0yc&pullRequest=6008
id: checks
continue-on-error: true
if: always()
with:
token: ${{ secrets.GITHUB_TOKEN }}
name: '${{ github.workflow }} / ${{ github.job }} ${{ env.MATRIX }}'
repo: ${{ github.repository }}
sha: ${{ env.SHA }}
status: 'in_progress'
details_url: 'https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}'

- name: Checkout repository
uses: actions/checkout@v4
with:
repository: ${{ github.repository }}
ref: ${{ env.SHA }}
path: 'proxysql'
# include/lib/src required so Codecov can resolve LCOV SF: paths
# (repo-root lib/*.cpp, include/*). Without them sparse checkout
# omits daemon sources, codecov-cli drops those SF: entries, and
# Codecov shows only test/+src/ -- the regression after #5967.
sparse-checkout: |
include
lib
src
test/infra
test/tap
test/scripts

- name: Download build handoff
# Build payload arrives as workflow artifacts, not an Actions cache:
# GitHub made cache writes read-only for workflow_run/untrusted triggers
# (changelog 2026-06-26). CI-builds uploads per-type artifacts named
# ci-builds-handoff-<sha>-<variant>-<type>; download the types this job
# used to restore and unpack them into proxysql/. SHA is the real head
# SHA (identical to CI-builds' SHA), so the artifact names line up.
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REPO: ${{ github.repository }}
SHA: ${{ inputs.trigger && fromJson(inputs.trigger).event.workflow_run.head_sha || github.sha }}
BUILD_RUN_ID: ${{ inputs.trigger && fromJson(inputs.trigger).event.workflow_run.id || '' }}
HANDOFF_VARIANT: ubuntu24-tap-genai-gcov
HANDOFF_TYPES: src test
run: |
set -uo pipefail
command -v zstd >/dev/null || sudo apt-get install -y zstd
# Resolve the build-handoff artifact robustly. Self-hosted runners can
# pick up this job within ~1 min of CI-builds finishing -- earlier than
# GitHub-hosted runners queue -- which lands inside the window where
# GitHub's GLOBAL name-filtered artifact index (artifacts?name=) still
# lags behind creation, returning empty for an artifact that already
# exists. Prefer resolving via the triggering CI-builds run's OWN
# artifact list (run-scoped: populated as soon as the artifact is
# attached, not subject to the global-index lag); fall back to the
# global name query. Retry on a bounded budget and surface the real gh
# error, so a genuine build failure is distinguishable from transient lag.
: "${HANDOFF_MAX_ATTEMPTS:=20}" # bounded: 20 x 15s = up to ~5 min
ERRLOG="$(mktemp)"
resolve_aid() {
local name="$1" out=""
if [ -n "${BUILD_RUN_ID:-}" ]; then
out=$(gh api "repos/${REPO}/actions/runs/${BUILD_RUN_ID}/artifacts?per_page=100" \
--jq "[.artifacts[]|select(.name==\"${name}\" and .expired==false)]|sort_by(.created_at)|last|.id // empty" 2>>"$ERRLOG") || true
[ -n "$out" ] && { printf '%s' "$out"; return 0; }
fi
out=$(gh api "repos/${REPO}/actions/artifacts?name=${name}&per_page=100" \
--jq '[.artifacts[]|select(.expired==false)]|sort_by(.created_at)|last|.id // empty' 2>>"$ERRLOG") || true
[ -n "$out" ] && { printf '%s' "$out"; return 0; }
return 1
}
for t in $HANDOFF_TYPES; do
name="ci-builds-handoff-${SHA}-${HANDOFF_VARIANT}-${t}"
aid=""
for attempt in $(seq 1 "$HANDOFF_MAX_ATTEMPTS"); do
aid=$(resolve_aid "$name") && [ -n "$aid" ] && break
aid=""
echo ">>> ${name} not resolvable yet (${attempt}/${HANDOFF_MAX_ATTEMPTS}); sleeping 15s"; sleep 15
done
if [ -z "$aid" ]; then
echo "ERROR: handoff artifact ${name} not found after ${HANDOFF_MAX_ATTEMPTS} attempts (build_run=${BUILD_RUN_ID:-n/a}) -- did CI-builds succeed for ${SHA}?" >&2
echo "--- last gh errors ---" >&2; tail -n 20 "$ERRLOG" >&2
exit 1
fi
echo ">>> downloading ${name} (id=${aid})"
gh api "repos/${REPO}/actions/artifacts/${aid}/zip" > "handoff-${t}.zip" || { echo "ERROR: download ${name} failed" >&2; exit 1; }
unzip -o "handoff-${t}.zip" || { echo "ERROR: unzip ${name} failed" >&2; exit 1; }
done
mkdir -p proxysql
cd proxysql/
for tb in ../cache_*.tar.zst; do
[ -e "$tb" ] || continue
echo ">>> unpacking $(basename "$tb")"
zstd -d < "$tb" | tar -xf - || { echo "ERROR: unpack $tb failed" >&2; exit 1; }
done
rm -f ../cache_*.tar.zst ../handoff-*.zip

- name: Verify binary
run: |
chmod +x proxysql/src/proxysql
file proxysql/src/proxysql

- name: Log in to GHCR and pull CI base image
# Both `docker login` and `docker pull` against ghcr.io have been
# observed to fail transiently with `net/http: request canceled
# (Client.Timeout exceeded while awaiting headers)`. Wrap both in
# a short retry loop with linear backoff so a single network blip
# does not red the whole TAP group.
env:
GHCR_USER: ${{ github.actor }}
GHCR_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set +e
attempt=0
max_attempts=5
while [ $attempt -lt $max_attempts ]; do
attempt=$((attempt + 1))
echo ">>> GHCR login+pull attempt ${attempt}/${max_attempts}"
if echo "$GHCR_TOKEN" | docker login ghcr.io \
-u "$GHCR_USER" --password-stdin \
&& docker pull ghcr.io/sysown/proxysql-ci-base:latest; then
echo ">>> GHCR login+pull OK on attempt ${attempt}"
docker tag ghcr.io/sysown/proxysql-ci-base:latest \
proxysql-ci-base:latest
exit 0
fi
if [ $attempt -lt $max_attempts ]; then
sleep_for=$((attempt * 10))
echo ">>> attempt ${attempt} failed; sleeping ${sleep_for}s"
sleep $sleep_for
fi
done
echo ">>> all ${max_attempts} GHCR attempts failed"
exit 1

- name: Start infrastructure
run: |
cd proxysql
export INFRA_ID="ci-no-infra-g1"
export TAP_GROUP="no-infra-g1"
export SKIP_CLUSTER_START=1
test/infra/control/ensure-infras.bash

- name: Run no-infra-g1 tests
run: |
cd proxysql
export INFRA_ID="ci-no-infra-g1"
export TAP_GROUP="no-infra-g1"
export SKIP_CLUSTER_START=1
export COVERAGE=1
test/infra/control/run-tests-isolated.bash

- name: Cleanup
if: always()
run: |
set +e
if [ ! -d proxysql ]; then
echo "proxysql checkout not present; skipping cleanup."
exit 0
fi
cd proxysql
export INFRA_ID="ci-no-infra-g1"
export TAP_GROUP="no-infra-g1"
docker logs proxysql.ci-no-infra-g1 2>&1 | tail -50 || true
test/infra/control/stop-proxysql-isolated.bash || true
test/infra/control/destroy-infras.bash || true

- name: Fix artifact permissions
if: ${{ failure() && !cancelled() }}
run: |
# actions/upload-artifact dies with EACCES when it scandirs into
# directories under ci_*_logs/ that were created inside docker
# build containers (root-owned). Make everything readable by the
# runner user before upload. sudo required because files are
# root-owned; 2>/dev/null + || true because the path may not
# exist on all failure paths (e.g. a cache-restore failure before
# any test even runs).
sudo chmod -R a+rX proxysql/ci_*_logs/ 2>/dev/null || true

- name: Archive artifacts logs
if: ${{ failure() && !cancelled() }}
uses: actions/upload-artifact@v4
with:
name: ${{ github.workflow }}-${{ env.SHA }}-logs-run#${{ github.run_number }}
path: |
proxysql/ci_*_logs/

- name: Archive coverage report
if: ${{ !cancelled() }}
uses: actions/upload-artifact@v4
with:
name: ${{ github.workflow }}-${{ env.SHA }}-coverage-run#${{ github.run_number }}
path: |
proxysql/ci_infra_logs/ci-no-infra-g1/coverage-report/
if-no-files-found: ignore

- name: Report missing coverage file
if: ${{ !cancelled() && hashFiles('proxysql/ci_infra_logs/**/coverage-report/*.info') == '' }}
run: |
echo "No coverage report was produced; skipping Codecov upload."

- name: Upload coverage to Codecov
# Send the LCOV .info file fastcov produces in run-tests-isolated.bash
# (under proxysql/ci_infra_logs/ci-no-infra-g1/coverage-report/) to
# Codecov. The proxysql binary was built WITHGCOV=1 (-tap-genai-gcov
# build cache) and was the live daemon answering the no-infra-g1 TAP
# suite, so the report reflects production code paths exercised by
# integration tests, not just the lib-only unit-test slice.
#
# integration-tests merges regular-daemon TAP shards for this commit.
# The group-specific name remains available for diagnostics.
# `use_oidc: true` is mandatory -- the
# repo has no CODECOV_TOKEN secret and Codecov rejects tokenless
# legacy uploads with "branch is protected" HTTP 400.
#
# `fail_ci_if_error: false` so a Codecov outage never fails the
# whole TAP group; `!cancelled()` so coverage uploads even when
# the test step itself reported a failure (partial coverage is
# still useful diagnostically).
if: ${{ !cancelled() && hashFiles('proxysql/ci_infra_logs/**/coverage-report/*.info') != '' }}
uses: codecov/codecov-action@v4

Check failure on line 321 in .github/workflows/ci-no-infra-g1.yml

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use full commit SHA hash for this dependency.

See more on https://sonarcloud.io/project/issues?id=sysown_proxysql&issues=AZ_mVZ1GO9VMtsP3-0yd&open=AZ_mVZ1GO9VMtsP3-0yd&pullRequest=6008
with:
codecov_yml_path: ${{ github.workspace }}/proxysql/codecov.yml
override_commit: ${{ inputs.trigger && fromJson(inputs.trigger).event.workflow_run.head_sha || github.sha }}
files: proxysql/ci_infra_logs/ci-no-infra-g1/coverage-report/ci-no-infra-g1.info
flags: integration-tests
name: tap-no-infra-g1-coverage
use_oidc: true
# Upload ONLY the explicit LCOV .info file.
# - disable_search + plugins:noop: default plugins include gcov,
# which auto-discovers leftover .gcno from the build handoff
# (lib/obj/*.gcno) and polluted uploads ("Found 33 coverage
# files" instead of 1).
# - no network_prefix: TAP LCOV SF: paths are repo-root-relative
# (lib/, src/, test/tap/). network_prefix:proxysql/ created a
# phantom proxysql/ namespace that never merged with unit-tests
# lib/, leaving lib/MySQL_Monitor.cpp at 0% despite real hits.
# - root_dir:proxysql: checkout is at path:proxysql; point
# codecov-cli network listing at the git root so lib/X matches.
# - disable_file_fixes:true: codecov-cli _get_file_fixes crashes
# FileNotFoundError when sparse checkout lacks sources referenced
# in the report. Aggregate coverage is unaffected.
disable_search: true
plugins: noop
root_dir: proxysql
disable_file_fixes: true
fail_ci_if_error: false
verbose: true

- uses: LouisBrunner/checks-action@v2.0.0

Check failure on line 350 in .github/workflows/ci-no-infra-g1.yml

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use full commit SHA hash for this dependency.

See more on https://sonarcloud.io/project/issues?id=sysown_proxysql&issues=AZ_mVZ1GO9VMtsP3-0ye&open=AZ_mVZ1GO9VMtsP3-0ye&pullRequest=6008
continue-on-error: true
if: ${{ always() && steps.checks.outputs.check_id != '' }}
with:
token: ${{ secrets.GITHUB_TOKEN }}
check_id: ${{ steps.checks.outputs.check_id }}
repo: ${{ github.repository }}
sha: ${{ env.SHA }}
conclusion: ${{ job.status }}
details_url: 'https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}'