Shared, reusable CI building blocks for the RediSearch family of repositories.
These pieces let each repo run a Codex agent with one consistent, hardened security posture instead of every repo copy-pasting (and drifting on) the same plumbing.
Keep everything here generic. No secrets, and no logic specific to a single repository (branch names, prompt text, internal process). Callers pass any repo-specific values in as inputs and secrets.
| Unit | Path | Use it when… |
|---|---|---|
codex-run composite action |
.github/actions/codex-run |
you need to run Codex as a step that can modify the tree (workspace-write), inside a job you already control. |
codex-ci-triage composite action |
.github/actions/codex-ci-triage |
you need Codex to analyze, read-only (CI logs, reports) and hand back its summary as step outputs for a Slack/PR notification. Never fails the job. |
codex-agent reusable workflow |
.github/workflows/codex-agent.yml |
you want a whole job that mints a scoped App token, checks out the caller repo, optionally runs a resolver, then runs Codex — driven by a label/comment trigger. |
common.py helpers |
scripts/ci_common/common.py |
your resolver script needs gh / $GITHUB_OUTPUT / context-file helpers. |
Triage / backport workflows themselves are per-repo, not here. Each repo's triage and backport-agent workflows carry product-specific triggers, CI-report formats, and prompts, so they live in the consuming repo as thin callers of these runners (
codex-runfor write flows like backport,codex-ci-triagefor read-only analysis). The shared, reusable substrate is what lives here.
| Unit | Path | Purpose |
|---|---|---|
checkout-isolated composite action |
.github/actions/checkout-isolated |
A job's first step: bypass a persistent self-hosted runner's leftover global/system git config and drop a reused .git, then check out. Has to be consumed from here — a local ./ copy is resolved from the workspace, which is empty before the checkout it performs. |
slack-notify composite action |
.github/actions/slack-notify |
Post a payload to a Slack webhook — replaces every repo's hand-rolled "notify failure" step. Webhook is a caller secret. |
setup-sccache composite action |
.github/actions/setup-sccache |
Install sccache and, best-effort, point it at an S3 remote cache via AWS OIDC. The job must grant id-token: write; role/bucket/region/prefix are caller inputs. Fork PRs (no OIDC token) and missing inputs fall back to a local-only cache — never fails the job. rustc-wrapper: 'true' also exports RUSTC_WRAPPER=sccache / CARGO_INCREMENTAL=0. |
build-rejson composite action |
.github/actions/build-rejson |
Build RedisJSON for flow tests via RediSearch's tests/deps/setup_rejson.sh and cache rejson.so, keyed on RedisJSON SHA, platform, arch, sanitizer, coverage, C compiler + build flags and the pinned nightly. redisearch-root points at the RediSearch checkout (. or a submodule path). Returns rejson-path; also exports REJSON_PATH unless export-rejson-path: 'false'. Container-safe. |
changed-paths composite action |
.github/actions/changed-paths |
Gate CI on changed paths: tj-actions/changed-files' any_modified (same files / files_ignore inputs) plus submodule pointer moves, which changed-files drops when submodules aren't checked out. Read changed. Needs fetch-depth: 0 and bash 4+. Fails closed (changed=true + warning) on an unresolvable range, forced push, unknown event or untranslatable glob, unlike the best-effort units here that never fail the job: for a skip-CI gate, running CI is the safe failure. |
rerun-action-download-failures composite action |
.github/actions/rerun-action-download-failures |
Re-run a failed PR run's failed jobs, once, when every one of them failed downloading an action in "Set up job" (a transient codeload error the runner does not retry). Call it from a thin on: workflow_run caller of the repo's PR workflow with actions: write and checks: read; no checkout. Skips fork PRs, runs already re-run, superseded runs, and any run with another kind of failure. |
fast-track reusable workflow |
.github/workflows/fast-track.yml |
Approve an author's eligible PR based on its Jira ai-fast-track label. Uses a dedicated caller-scoped App; policy, setup, and rollout. |
pr-size-label reusable workflow |
.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 |
codespell over a PR's changed files. The repo supplies its own .codespell/ config. |
link-check reusable workflow |
.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 |
Mark/close stale issues & PRs (actions/stale). Opt-in — for repos that track work in GitHub issues. |
A shared, REPO/BRANCH-keyed Redis DB of flaky-test marks, so the family's
RLTest suites can quarantine known-flaky tests consistently.
| Unit | Path | Purpose |
|---|---|---|
flaky-mark reusable workflow |
.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 |
Remove a flaky mark. |
flaky_db.py CLI |
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 |
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, and excluded (the dropped ids; empty on fail-open) for runners that deselect. |
flaky-record-results composite action |
.github/actions/flaky-record-results |
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. The pieces — mark/unmark,flaky-filter(skip marked tests), andflaky-record-results(log per-run results) — cover the whole pipeline, so a consuming repo needs no localflaky_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). Passci-common-refmatching the ref you pinned the action to.
openai/codex-actionis pinned to a commit SHA (Sonar S7670).sandbox: workspace-writeconfines the agent to the checkout.- Outbound network is granted only via the codex network override (for
git push/gh) — far narrower thandanger-full-access. safety-strategy: drop-sudoblocks privilege escalation on the runner.
- uses: actions/checkout@v6 # check out the repo to operate on
- name: Configure git
run: |
git config user.name "redis-ci[bot]"
git config user.email "redis-ci[bot]@users.noreply.github.com"
# ... your own steps: create a branch, attempt a merge, etc.
- name: Resolve via Codex
uses: redislabsdev/redisearch-ci-common/.github/actions/codex-run@v1
with:
openai-api-key: ${{ secrets.OPENAI_API_KEY }}
gh-token: ${{ steps.app-token.outputs.token }}
prompt-file: .github/codex/prompts/your-prompt.mdThe agent reads whatever context env vars you set in the job (e.g. a path to a
context JSON your resolver wrote). Keep that file in $RUNNER_TEMP, not the
working tree, so the agent's git add -A can't stage it.
jobs:
resolve:
if: github.event.label.name == 'codex-resolve'
uses: redislabsdev/redisearch-ci-common/.github/workflows/codex-agent.yml@v1
with:
app-id: ${{ vars.GH_CI_APP_ID }}
prompt-file: .github/codex/prompts/your-prompt.md
permission-contents: write
fetch-depth: 0
secrets:
app-private-key: ${{ secrets.GH_CI_PRIVATE_KEY }}
openai-api-key: ${{ secrets.OPENAI_API_KEY }}RediSearch and RSE consume these shared workflows and actions through thin
callers, normally using the floating major tag @v1:
uses: redislabsdev/redisearch-ci-common/...@v1redisearch-ci-common is first-party code owned by Redis. Its v1 tag is a
ref pin, but it is not immutable:
- A backward-compatible release creates an immutable
v1.x.ytag and movesv1after validation. Consumers and release branches already using@v1receive the update on their next workflow run, without dependency-bump or CI-backport PRs. - A breaking release creates a new major tag such as
v2. Updating consumers requires a PR, which Renovate or Dependabot can open where configured. - A branch or commit ref may be used temporarily for end-to-end testing, but should be replaced with the supported major tag before merging.
Do not consume this repository at @main; that would expose consumers to every
merged change before a version is validated and published. A consumer may
instead use an exact commit SHA when required by its security tooling. Such a
consumer needs an explicit ref bump to receive updates.
Actions owned outside the Redis GitHub organizations are third-party
dependencies. For example, codex-run@v1 is first-party and can use the
floating major tag, while the openai/codex-action that it invokes is
third-party and is pinned to a full commit SHA inside codex-run.
(The internal codex-agent → codex-run reference uses @main within this
repository and is updated together with each release.)
This repo lints its own building blocks on every PR
(.github/workflows/ci.yml): actionlint (workflow
syntax, expressions, shellcheck of run: steps), a strict YAML check that
rejects duplicate mapping keys across both workflows and composite actions, and
compileall over the Python helpers, and credential-free fast-track policy tests — so a change here can't silently break a
consumed unit.
Made available under your choice of the Redis Source Available License 2.0
(RSALv2), the Server Side Public License v1 (SSPLv1), or the GNU Affero General
Public License version 3 (AGPLv3) — matching the RediSearch project. See
LICENSE.txt and the licenses/ folder.