From 4d3ef899176efc08b9416838c282f52bf6a7cf79 Mon Sep 17 00:00:00 2001 From: donpetry-bot <281750570+donpetry-bot@users.noreply.github.com> Date: Mon, 3 Aug 2026 10:31:39 +0000 Subject: [PATCH 1/4] =?UTF-8?q?feat:=20implement=20issue=20#935=20?= =?UTF-8?q?=E2=80=94=20[Fleet=20Monitor]=20petry-projects/.github=20?= =?UTF-8?q?=E2=80=94=20.github/workflows/ci.yml?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 37 ++++++++++++++- test/workflows/ci/install-resilience.bats | 55 +++++++++++++++++++++++ 2 files changed, 90 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f6e63c3bd..a45fc01a0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,7 +36,11 @@ jobs: - name: Lint YAML if: always() run: | - pip install --quiet --require-hashes --only-binary :all: -r scripts/ci-yamllint-requirements.txt + # Bounded retries + a per-attempt timeout so a transient PyPI/index + # blip retries instead of reding the Lint job (guards the fleet + # failure-rate metric — see test/workflows/ci/install-resilience.bats, + # issue #935). + pip install --quiet --retries 3 --timeout 30 --require-hashes --only-binary :all: -r scripts/ci-yamllint-requirements.txt yamllint -c <(cat <<'YAMLLINTRC' extends: default rules: @@ -107,6 +111,16 @@ jobs: node-version: '20' - name: Run AgentShield + # npm's default of 2 fetch retries is thin for the npx package download. + # Bump it via npm's native fetch-retries config so a transient npm-registry + # blip retries instead of reding the job (guards the fleet failure-rate + # metric — see test/workflows/ci/install-resilience.bats, issue #935). + # An env var (not a shell loop) so a genuine high-severity finding still + # fails on the first run instead of being retried. + env: + npm_config_fetch_retries: '3' + npm_config_fetch_retry_mintimeout: '2000' + npm_config_fetch_retry_maxtimeout: '30000' run: | npx --ignore-scripts ecc-agentshield@1.4.0 scan --path . --format json --min-severity high @@ -125,7 +139,26 @@ jobs: env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - gh release download v8.30.1 --repo gitleaks/gitleaks --pattern 'gitleaks_8.30.1_linux_x64.tar.gz' --output /tmp/gitleaks.tar.gz + set -euo pipefail + # `gh release download` pulls from GitHub Releases (redirects to + # objects.githubusercontent.com) and has no native retry — the same + # transient-blip failure mode #894 fixed for the actionlint curl. Wrap + # in a bounded retry loop with backoff so a transient error retries + # instead of reding the Secret scan job (guards the fleet failure-rate + # metric — see test/workflows/ci/install-resilience.bats, issue #935). + downloaded=0 + for attempt in 1 2 3; do + if gh release download v8.30.1 --repo gitleaks/gitleaks --pattern 'gitleaks_8.30.1_linux_x64.tar.gz' --output /tmp/gitleaks.tar.gz --clobber; then + downloaded=1 + break + fi + echo "gitleaks download attempt ${attempt} failed" >&2 + [ "${attempt}" -lt 3 ] && sleep $((attempt * 5)) + done + if [ "${downloaded}" -ne 1 ]; then + echo "gitleaks download failed after 3 attempts" >&2 + exit 1 + fi echo "551f6fc83ea457d62a0d98237cbad105af8d557003051f41f3e7ca7b3f2470eb /tmp/gitleaks.tar.gz" | sha256sum -c tar -xzf /tmp/gitleaks.tar.gz -C /tmp gitleaks sudo mv /tmp/gitleaks /usr/local/bin/gitleaks diff --git a/test/workflows/ci/install-resilience.bats b/test/workflows/ci/install-resilience.bats index 7ba36330b..f0e6db90d 100644 --- a/test/workflows/ci/install-resilience.bats +++ b/test/workflows/ci/install-resilience.bats @@ -13,6 +13,27 @@ load 'helpers/setup' +# Emit the `run:` step block whose text contains the given marker substring. +# Steps in ci.yml start at a 6-space "- " (the `steps:` child indent); every +# deeper-indented line belongs to that step. Isolating a step lets each test +# assert resilience on the one fetch it cares about without matching flags that +# happen to appear in a neighbouring step. +step_block_with() { + local marker="$1" + local block="" out="" + while IFS= read -r line || [[ -n "$line" ]]; do + line="${line%$'\r'}" + if [[ "$line" =~ ^\ \ \ \ \ \ -\ ]]; then + [[ "$block" == *"$marker"* ]] && out="$block" + block="$line"$'\n' + else + block+="$line"$'\n' + fi + done < "$TT_WORKFLOW" + [[ "$block" == *"$marker"* ]] && out="$block" + printf '%s' "$out" +} + @test "install: the ci workflow exists" { [ -f "$TT_WORKFLOW" ] } @@ -50,3 +71,37 @@ load 'helpers/setup' [[ "$cmd" == *"--retry-all-errors"* ]] done } + +@test "install: the yamllint pip install carries a bounded retry budget" { + # pip fetches wheels from PyPI/the configured index; a transient index blip + # would otherwise red the Lint job. An explicit --retries pins the budget so a + # future edit can't drop resilience and reintroduce a bare install. + local block + block="$(step_block_with 'pip install')" + [ -n "$block" ] + [[ "$block" =~ --retries[[:space:]=][1-9] ]] +} + +@test "install: the AgentShield npx fetch retries transient registry errors" { + # `npx ecc-agentshield` downloads the package from the npm registry before + # scanning; npm's default of 2 fetch retries is thin. Bump it via npm's native + # fetch-retries config (env var, NOT a shell loop) so a transient registry + # error retries while a genuine high-severity finding still fails on the first + # run instead of being retried three times. + local block + block="$(step_block_with 'npx ')" + [ -n "$block" ] + [[ "$block" == *"npm_config_fetch_retries"* ]] +} + +@test "install: the gitleaks release download retries on transient failure" { + # `gh release download` pulls gitleaks from GitHub Releases (which redirects to + # objects.githubusercontent.com) and has no native retry — the exact failure + # mode #894 fixed for the actionlint curl. Wrap it in a bounded retry loop with + # backoff so a transient blip retries instead of reding the Secret scan job. + local block + block="$(step_block_with 'gh release download')" + [ -n "$block" ] + [[ "$block" == *"for attempt in"* ]] + [[ "$block" == *"sleep"* ]] +} From 2d0fe31a30d595ca61dc67522501fbf910722f6e Mon Sep 17 00:00:00 2001 From: donpetry-bot <281750570+donpetry-bot@users.noreply.github.com> Date: Mon, 3 Aug 2026 10:41:16 +0000 Subject: [PATCH 2/4] fix(reviews): address review comments [skip ci-relay] --- test/workflows/ci/install-resilience.bats | 25 +++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/test/workflows/ci/install-resilience.bats b/test/workflows/ci/install-resilience.bats index f0e6db90d..b2a8b9327 100644 --- a/test/workflows/ci/install-resilience.bats +++ b/test/workflows/ci/install-resilience.bats @@ -20,17 +20,23 @@ load 'helpers/setup' # happen to appear in a neighbouring step. step_block_with() { local marker="$1" - local block="" out="" + local block="" out="" line="" while IFS= read -r line || [[ -n "$line" ]]; do line="${line%$'\r'}" - if [[ "$line" =~ ^\ \ \ \ \ \ -\ ]]; then - [[ "$block" == *"$marker"* ]] && out="$block" + if [[ "$line" == " - "* ]]; then + if [[ "$block" == " - "* ]]; then + [[ "$block" == *"$marker"* ]] && out="$block" + fi block="$line"$'\n' else - block+="$line"$'\n' + if [[ -n "$block" ]]; then + block+="$line"$'\n' + fi fi done < "$TT_WORKFLOW" - [[ "$block" == *"$marker"* ]] && out="$block" + if [[ "$block" == " - "* ]]; then + [[ "$block" == *"$marker"* ]] && out="$block" + fi printf '%s' "$out" } @@ -79,7 +85,10 @@ step_block_with() { local block block="$(step_block_with 'pip install')" [ -n "$block" ] - [[ "$block" =~ --retries[[:space:]=][1-9] ]] + local pip_cmd + pip_cmd="$(printf '%s' "$block" | grep -E '^[[:space:]]+pip install' | head -1)" + [ -n "$pip_cmd" ] + [[ "$pip_cmd" =~ --retries[[:space:]=][1-9] ]] } @test "install: the AgentShield npx fetch retries transient registry errors" { @@ -89,9 +98,9 @@ step_block_with() { # error retries while a genuine high-severity finding still fails on the first # run instead of being retried three times. local block - block="$(step_block_with 'npx ')" + block="$(step_block_with 'ecc-agentshield')" [ -n "$block" ] - [[ "$block" == *"npm_config_fetch_retries"* ]] + [[ "$block" =~ npm_config_fetch_retries:[[:space:]]*\'?[1-9] ]] } @test "install: the gitleaks release download retries on transient failure" { From 9a86bca5a72b6ab5d5f8090a01bcecaf0b24b6a7 Mon Sep 17 00:00:00 2001 From: donpetry-bot <281750570+donpetry-bot@users.noreply.github.com> Date: Mon, 3 Aug 2026 10:44:22 +0000 Subject: [PATCH 3/4] fix(bot): address bot feedback [skip ci-relay] --- .github/workflows/ci.yml | 3 ++- test/workflows/ci/install-resilience.bats | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a45fc01a0..6b329c407 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -121,6 +121,7 @@ jobs: npm_config_fetch_retries: '3' npm_config_fetch_retry_mintimeout: '2000' npm_config_fetch_retry_maxtimeout: '30000' + npm_config_fetch_timeout: '60' run: | npx --ignore-scripts ecc-agentshield@1.4.0 scan --path . --format json --min-severity high @@ -148,7 +149,7 @@ jobs: # metric — see test/workflows/ci/install-resilience.bats, issue #935). downloaded=0 for attempt in 1 2 3; do - if gh release download v8.30.1 --repo gitleaks/gitleaks --pattern 'gitleaks_8.30.1_linux_x64.tar.gz' --output /tmp/gitleaks.tar.gz --clobber; then + if timeout 30 gh release download v8.30.1 --repo gitleaks/gitleaks --pattern 'gitleaks_8.30.1_linux_x64.tar.gz' --output /tmp/gitleaks.tar.gz --clobber; then downloaded=1 break fi diff --git a/test/workflows/ci/install-resilience.bats b/test/workflows/ci/install-resilience.bats index b2a8b9327..44f8a4b1b 100644 --- a/test/workflows/ci/install-resilience.bats +++ b/test/workflows/ci/install-resilience.bats @@ -101,6 +101,7 @@ step_block_with() { block="$(step_block_with 'ecc-agentshield')" [ -n "$block" ] [[ "$block" =~ npm_config_fetch_retries:[[:space:]]*\'?[1-9] ]] + [[ "$block" =~ npm_config_fetch_timeout:[[:space:]]*\'?[1-9] ]] } @test "install: the gitleaks release download retries on transient failure" { @@ -113,4 +114,5 @@ step_block_with() { [ -n "$block" ] [[ "$block" == *"for attempt in"* ]] [[ "$block" == *"sleep"* ]] + [[ "$block" == *"timeout"* ]] } From 3c9a0600b4ac2fac9d9e93fc16570b9939897e1d Mon Sep 17 00:00:00 2001 From: donpetry-bot <281750570+donpetry-bot@users.noreply.github.com> Date: Fri, 7 Aug 2026 00:00:27 +0000 Subject: [PATCH 4/4] chore: dev-lead update (review-changes) [skip ci-relay] --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6b329c407..c6ebb34d4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -121,7 +121,7 @@ jobs: npm_config_fetch_retries: '3' npm_config_fetch_retry_mintimeout: '2000' npm_config_fetch_retry_maxtimeout: '30000' - npm_config_fetch_timeout: '60' + npm_config_fetch_timeout: '60000' run: | npx --ignore-scripts ecc-agentshield@1.4.0 scan --path . --format json --min-severity high