Skip to content
Merged
Show file tree
Hide file tree
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
134 changes: 134 additions & 0 deletions .github/actions/flaky-filter/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
name: Filter flaky tests from a test list
description: |
Fetch active flaky marks for the current branch from the REPO/BRANCH-keyed
flaky DB and filter them out of a caller-provided test-id list, emitting the
path to a filtered TESTFILE the runner can consume.

The caller enumerates its own tests (that step is product-specific, e.g.
`LIST=1 make pytest`) and passes the list as `tests`. This action only does
the generic fetch + filter, so it stays framework/product agnostic.

Fail-open: emits an empty `testfile` when there are no active marks, when the
DB is unset/unreachable, or on any error — callers treat empty as "run the
full suite". When marks exist and they filter out *every* enumerated test, the
`all-quarantined` output is `true` so the caller can choose to skip the shard
entirely (an empty TESTFILE alone can't express "run nothing").

Container-safe: the DB CLI is fetched into the (mounted) workspace via checkout,
since github.action_path is not mounted in container jobs.

inputs:
redis-url:
description: "Redis connection URL (rediss://...). No marks fetched if empty."
required: true
tests:
description: "Path to the enumerated test-id list (one RLTest id per line)."
required: true
output:
description: "Path to write the filtered TESTFILE."
required: true
branch:
description: "Branch to fetch marks for. Defaults to the PR / merge-queue target."
required: false
default: ""
ci-common-ref:
description: "Ref of redisearch-ci-common to fetch the DB CLI from (match your pin)."
required: false
default: v1

outputs:
testfile:
description: "Filtered TESTFILE path; empty if no marks or any step failed."
value: ${{ steps.filter.outputs.testfile }}
all-quarantined:
description: >
'true' when active marks filtered out every enumerated test (the filtered
set is empty). Callers can skip the test step entirely in that case.
'false' otherwise (no marks, partial filter, or any error).
value: ${{ steps.filter.outputs.all-quarantined }}

runs:
using: composite
steps:
- name: Resolve target branch
shell: bash -l -eo pipefail {0}
env:
OVERRIDE: ${{ inputs.branch }}
MERGE_GROUP_BASE: ${{ github.event.merge_group.base_ref }}
BASE_REF: ${{ github.base_ref }}
REF_NAME: ${{ github.ref_name }}
run: |
if [ -n "$OVERRIDE" ]; then
branch="$OVERRIDE"
elif [ -n "$MERGE_GROUP_BASE" ]; then
branch="${MERGE_GROUP_BASE#refs/heads/}"
elif [ -n "$BASE_REF" ]; then
branch="$BASE_REF"
else
branch="$REF_NAME"
fi
echo "Resolved flaky target branch: $branch"
echo "FLAKY_BRANCH=$branch" >> "$GITHUB_ENV"

- name: Fetch shared flaky DB CLI
uses: actions/checkout@v6
with:
repository: redislabsdev/redisearch-ci-common
ref: ${{ inputs.ci-common-ref }}
path: .redisearch-ci-common
persist-credentials: false

- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.x"

- name: Install dependencies
shell: bash -l -eo pipefail {0}
run: python3 -m pip install 'redis>=6,<7'

- name: Fetch marks and filter
id: filter
shell: bash -l -eo pipefail {0}
env:
REDIS_URL: ${{ inputs.redis-url }}
REPO: ${{ github.repository }}
BRANCH: ${{ env.FLAKY_BRANCH }}
TESTS: ${{ inputs.tests }}
OUTPUT: ${{ inputs.output }}
run: |
cli="$GITHUB_WORKSPACE/.redisearch-ci-common/scripts/ci_common/flaky_db.py"
marks="$RUNNER_TEMP/flaky_marks.txt"
# Fail-open: a DB outage must not break the test pipeline.
python3 "$cli" fetch --output-json "$RUNNER_TEMP/flaky_marks.json" --output-list "$marks" || \
echo "::warning::flaky fetch failed; running the full suite"
: > "$OUTPUT"
all_quarantined=false
if [ -s "$marks" ]; then
if python3 "$cli" filter --tests "$TESTS" --marks "$marks" --output "$OUTPUT"; then
if [ ! -s "$OUTPUT" ]; then
# Output empty: distinguish "every valid test was quarantined" from
# "the input had no valid test ids at all". Re-filter with no marks
# (same code path, so the same id-validation applies) to get the set
# of tests that *would* run; only call it all-quarantined if that
# baseline was non-empty.
empty_marks="$RUNNER_TEMP/flaky_empty_marks.txt"; : > "$empty_marks"
baseline="$RUNNER_TEMP/flaky_baseline.txt"
python3 "$cli" filter --tests "$TESTS" --marks "$empty_marks" --output "$baseline" || : > "$baseline"
if [ -s "$baseline" ]; then
all_quarantined=true
echo "::notice::All enumerated tests are quarantined for this shard."
fi
fi
else
: > "$OUTPUT" # filter error ⇒ fail-open (full suite)
fi
else
echo "No active flaky marks — running the full suite"
fi
if [ -s "$OUTPUT" ]; then
echo "testfile=$OUTPUT" >> "$GITHUB_OUTPUT"
else
echo "testfile=" >> "$GITHUB_OUTPUT"
fi
Comment thread
kei-nan marked this conversation as resolved.
echo "all-quarantined=$all_quarantined" >> "$GITHUB_OUTPUT"
29 changes: 22 additions & 7 deletions .github/actions/flaky-record-results/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ description: |

This action never fails the job: it warns on connection errors so a flaky-DB
outage cannot break the test pipeline. The calling step should set
`if: always()` so failures are recorded even when tests fail. The DB CLI
(scripts/ci_common/flaky_db.py) is bundled in this repo, so the action is
self-contained.
`if: always()` so failures are recorded even when tests fail.

Container-safe: the DB CLI is fetched into the (mounted) workspace via a
checkout, since `github.action_path` points at the runner host and is not
mounted inside container jobs.

inputs:
redis-url:
Expand All @@ -33,6 +35,12 @@ inputs:
description: "Short label for this job/shard, recorded with each result"
required: false
default: ""
ci-common-ref:
description: >
Ref of redisearch-ci-common to fetch the DB CLI from. Set this to the same
ref you pinned this action to (e.g. v1).
required: false
default: v1

runs:
using: composite
Expand All @@ -59,14 +67,23 @@ runs:
echo "Resolved flaky target branch: $branch"
echo "FLAKY_BRANCH=$branch" >> "$GITHUB_ENV"

- name: Fetch shared flaky DB CLI
# Into the workspace (mounted in container jobs), not action_path (host).
uses: actions/checkout@v6
with:
repository: redislabsdev/redisearch-ci-common
ref: ${{ inputs.ci-common-ref }}
path: .redisearch-ci-common
persist-credentials: false

- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.x"

- name: Install dependencies
shell: bash -l -eo pipefail {0}
run: pip install 'redis>=6,<7'
run: python3 -m pip install 'redis>=6,<7'

- name: Record results
shell: bash -l -eo pipefail {0}
Expand All @@ -80,6 +97,4 @@ runs:
JOB_NAME: ${{ inputs.job-name != '' && inputs.job-name || github.job }}
FAILED_FILE: ${{ inputs.failed-file }}
PASSED_FILE: ${{ inputs.passed-file }}
# flaky_db.py is bundled in this repo; reference it relative to the action
# (action_path -> .github/actions/flaky-record-results, so go up 3 to root).
run: python3 "${{ github.action_path }}/../../../scripts/ci_common/flaky_db.py" record
run: python3 "$GITHUB_WORKSPACE/.redisearch-ci-common/scripts/ci_common/flaky_db.py" record
40 changes: 40 additions & 0 deletions .github/workflows/stale.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: Stale issues and PRs (reusable)

# Reusable wrapper around actions/stale with sensible defaults. Opt-in: only
# repos that track work in GitHub issues/PRs (e.g. the public repo) need call it
# via a thin `on: schedule` caller. Tunables are overridable via inputs.

on:
workflow_call:
inputs:
days-before-stale:
type: number
default: 60
days-before-close:
type: number
default: -1
operations-per-run:
type: number
default: 1000
runs-on:
type: string
default: ubuntu-latest

jobs:
stale:
runs-on: ${{ inputs.runs-on }}
permissions:
issues: write
pull-requests: write
steps:
- uses: actions/stale@v10
with:
days-before-stale: ${{ inputs.days-before-stale }}
days-before-close: ${{ inputs.days-before-close }}
stale-issue-label: "stale"
stale-issue-message: "This issue is stale because it has been open for ${{ inputs.days-before-stale }} days with no activity."
close-issue-message: "This issue was closed because it has been inactive for ${{ inputs.days-before-close }} days since being marked as stale."
stale-pr-label: "stale"
stale-pr-message: "This pull request is stale because it has been open for ${{ inputs.days-before-stale }} days with no activity."
close-pr-message: "This pull request was closed because it has been inactive for ${{ inputs.days-before-close }} days since being marked as stale."
operations-per-run: ${{ inputs.operations-per-run }}
14 changes: 10 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ plumbing.
| `pr-size-label` reusable workflow | [`.github/workflows/pr-size-label.yml`](.github/workflows/pr-size-label.yml) | Label a PR by diff size. Add a thin `on: pull_request` caller. |
| `spellcheck` reusable workflow | [`.github/workflows/spellcheck.yml`](.github/workflows/spellcheck.yml) | codespell over a PR's changed files. The repo supplies its own `.codespell/` config. |
| `link-check` reusable workflow | [`.github/workflows/link-check.yml`](.github/workflows/link-check.yml) | Validate Markdown links/anchors. Self-contained — bundles `scripts/ci_common/check_links.py`. |
| `stale` reusable workflow | [`.github/workflows/stale.yml`](.github/workflows/stale.yml) | Mark/close stale issues & PRs (actions/stale). Opt-in — for repos that track work in GitHub issues. |

### Flaky-test DB

Expand All @@ -46,15 +47,20 @@ RLTest suites can quarantine known-flaky tests consistently.
| `flaky-mark` reusable workflow | [`.github/workflows/flaky-mark.yml`](.github/workflows/flaky-mark.yml) | Add a flaky mark (test id, reason, Jira key, expiry). Add a thin `on: workflow_dispatch` caller. |
| `flaky-unmark` reusable workflow | [`.github/workflows/flaky-unmark.yml`](.github/workflows/flaky-unmark.yml) | Remove a flaky mark. |
| `flaky_db.py` CLI | [`scripts/ci_common/flaky_db.py`](scripts/ci_common/flaky_db.py) | `mark`/`unmark`/`fetch`/`filter`/`record`. No-op when `REDIS_URL` is unset (keeps fork-PR CI green). |
| `flaky-filter` composite action | [`.github/actions/flaky-filter`](.github/actions/flaky-filter/action.yml) | Fetch marks and filter them out of a caller-provided test list → filtered TESTFILE. Caller enumerates its own tests (product-specific); empty output = run full suite. Also emits `all-quarantined` so a caller can skip a fully-quarantined shard. |
| `flaky-record-results` composite action | [`.github/actions/flaky-record-results`](.github/actions/flaky-record-results/action.yml) | Record an RLTest run's failed/passed test ids to the DB from inside a test job (`if: always()`). Never fails the job. |

> Unlike the units above (which are test-framework agnostic), the flaky tooling
> is **RLTest-shaped**: it expects test ids in RLTest's
> `<test_file>:<test_name>[variant]` form — which is what the consuming suites
> emit. Marks only take effect once the consuming repo's **test pipeline** calls
> `flaky_db.py fetch`/`filter` to skip marked tests (and `flaky-record-results`
> to log per-run results); that wiring is product-specific and lives in each
> repo's own test workflow — intentionally **not** here.
> emit. The pieces — mark/unmark, `flaky-filter` (skip marked tests), and
> `flaky-record-results` (log per-run results) — cover the whole pipeline, so a
> consuming repo needs no local `flaky_db.py`. Only the *test enumeration* (e.g.
> `LIST=1 make pytest`) stays product-specific in the caller.
>
> **Container-safe:** the two composite actions fetch the DB CLI into the mounted
> workspace via a checkout (not `github.action_path`, which isn't mounted inside
> container jobs). Pass `ci-common-ref` matching the ref you pinned the action to.

### Security posture (baked into `codex-run`)

Expand Down