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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
*.swp
.venv
20 changes: 16 additions & 4 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,24 @@ repos:
hooks:
- id: actionlint-docker
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.14.10
rev: v0.15.1
hooks:
- id: ruff
args: ["--fix"]
- id: ruff-check
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

ruff is a deprecated alias for this check, it's now ruff-check

args: ["--fix", "--config=pyproject.toml"]
- id: ruff-format
args: ["--config=pyproject.toml"]
- repo: https://github.com/rapidsai/pre-commit-hooks
rev: v1.2.1
rev: v1.4.3
hooks:
- id: verify-copyright
- repo: https://github.com/pre-commit/mirrors-mypy
rev: 'v1.13.0'
hooks:
- id: mypy
additional_dependencies:
- "requests>=2.32.4"
- "types-requests>=2.32.4"
args:
- "--config-file=pyproject.toml"
- "check_nightly_success/"
pass_filenames: false
121 changes: 121 additions & 0 deletions check_nightly_success/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
# check_nightly_success

Action that can be used to fail CI if a given GitHub Actions workflow hasn't had at least 1 recent succcessful run.

Add it to any GitHub Actions workflow configuration like this:

```yaml
check-nightly-ci:
runs-on: ubuntu-latest
permissions:
actions: read
id-token: write
env:
GH_TOKEN: ${{ github.token }}
Comment on lines +10 to +14
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This isn't tightly coupled to these changes but I'm going to do it as part of this... this action only ever needs to interact with the one repo it's called from, so we don't need to use the over-permissioned, long-lived ${{ secrets.GITHUB_TOKEN }}.

We can use the short-term, tightly-scoped, dynamically-generated one that GItHub gives us 😁

steps:
- name: Get PR Info
id: get-pr-info
uses: nv-gha-runners/get-pr-info@main
- name: Check if nightly CI is passing
uses: rapidsai/shared-actions/check_nightly_success/dispatch@main
with:
repo: ${{ github.repository }}
target-branch: ${{ fromJSON(steps.get-pr-info.outputs.pr-info).base.ref }}
workflow-id: 'test.yaml'
max-days-without-success: 7
```

## Testing

The code for the actions is implemented in Python.

### Case 1: Succeed on recent nightly test successes

Try the following locally to test it.

```shell
python -m venv .venv/
source .venv/bin/activate
python -m pip install requests

GH_TOKEN=$(gh auth token) \
python ./check-nightly-success/check.py \
--repo 'rapidsai/cudf' \
--branch 'main' \
--workflow-id 'test.yaml' \
--max-days-without-success 7
```

If this succeeds, you'll see a `0` exit code and output text similar to the following:

> Found 4 successful runs of workflow 'test.yaml' on branch 'main' in the previous 7 days (most recent: '2026-02-16 06:26:04+00:00'). View logs:
- https://github.com/rapidsai/cudf/actions/runs/22052428055

### Case 2: Fail when branch has 0 runs (of any status)

The check should fail on a repo without any runs of this workflow:

```shell
GH_TOKEN=$(gh auth token) \
python ./check-nightly-success/check.py \
--repo 'rapidsai/build-planning' \
--branch 'main' \
--workflow-id 'test.yaml' \
--max-days-without-success 7
```

That'll return exit code `1` and output similar to this:

> requests.exceptions.RetryError: HTTPSConnectionPool(host='api.github.com', port=443): Max retries exceeded with url: /repos/rapidsai/build-planning/actions/workflows/test.yaml/runs?branch=main&status=success&per_page=100&created=%3E%3D2026-02-10 (Caused by ResponseError('too many 404 error responses'))

### Case 3: Success on new branches with only very-recent runs

Branches with only very-recent runs should be exempted from the check.

```shell
# NOTE: this example requires write access to 'rapidsai/ucxx'
TMP_UCXX=$(mktemp -d)
git clone -o upstream https://github.com/rapidsai/ucxx "${TMP_UCXX}"
pushd "${TMP_UCXX}"
git checkout -b delete-me
git push upstream delete-me
popd

gh workflow run \
--repo rapidsai/ucxx \
--ref delete-me \
test.yaml \
-f branch="delete-me" \
-f date="$(date +%Y-%m-%d)" \
-f sha="$(git rev-parse HEAD)" \
-f build_type=nightly

# (MANUAL - go to https://github.com/rapidsai/ucxx/actions/runs/22109183034 and manually cancel that run)

# run the check
GH_TOKEN=$(gh auth token) \
python ./check-nightly-success/check.py \
--repo 'rapidsai/ucxx' \
--branch 'delete-me' \
--workflow-id 'test.yaml' \
--max-days-without-success 7
```

That'll exit with code `0` and print something like this:

> The oldest run of workflow 'test.yaml' on branch 'delete-me' was 0 days ago (2026-02-17 17:42:05+00:00).
Because the latest run was less than 'max-days-without-success = 7' days ago, this workflow is exempted from check-nightly-success. The check will start failing if there is not a successful run in the next few days.

### Other testing: pagination

Set `--request-page-size` to `1` to test that pagination is working.

```shell
GH_TOKEN=$(gh auth token) \
python ./check-nightly-success/check.py \
--repo 'rapidsai/cudf' \
--branch 'main' \
--workflow-id 'test.yaml' \
--max-days-without-success 30 \
--request-page-size 5
```
40 changes: 24 additions & 16 deletions check_nightly_success/check-nightly-success/action.yaml
Original file line number Diff line number Diff line change
@@ -1,24 +1,26 @@
name: check-nightly-success
description: Check if the nightlies have succeeded recently.

# these inputs should all be 'required: true' without defaults... this action should only
# ever be invoked by check_nightly_success/dispatch
inputs:
repo:
description: "The repository to check"
description: "Repository name with owner (e.g. 'rapidsai/cudf' not 'cudf')"
required: true
type: string
repo_owner:
description: "The org that owns the repo (default: rapidsai)"
required: false
default: "rapidsai"
target-branch:
description: |
Branch the pull request this is running on targets.
Only statuses of nightly runs on that branch will be considered.
required: true
type: string
workflow_id:
workflow-id:
description: "The workflow whose runs to check"
required: false
default: "test.yaml"
required: true
type: string
max_days_without_success:
max-days-without-success:
description: "The number of consecutive days that may go by without a successful CI run"
required: false
default: 7
required: true
type: integer

runs:
Expand All @@ -28,9 +30,15 @@ runs:
shell: bash
env:
REPO: ${{ inputs.repo }}
REPO_OWNER: ${{ inputs.repo_owner }}
WORKFLOW_ID: ${{ inputs.workflow_id }}
MAX_DAYS_WITHOUT_SUCCESS: ${{ inputs.max_days_without_success }}
TARGET_BRANCH: ${{ inputs.target-branch }}
WORKFLOW_ID: ${{ inputs.workflow-id }}
MAX_DAYS_WITHOUT_SUCCESS: ${{ inputs.max-days-without-success }}
run: |
python -m pip install requests
python shared-actions/check_nightly_success/check-nightly-success/check.py ${REPO} --repo-owner ${REPO_OWNER} --workflow-id ${WORKFLOW_ID} --max-days-without-success ${MAX_DAYS_WITHOUT_SUCCESS}
python -m pip install \
--prefer-binary \
'requests>=2.32.4'
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Putting a floor on this just makes solves a little faster and prevents some surprises.

python shared-actions/check_nightly_success/check-nightly-success/check.py \
--repo ${REPO} \
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Notice I switched repo from being positional to keyword, for consistency. This is a minor cosmetic thing, happy to revert it if anyone feels strongly.

--branch ${TARGET_BRANCH} \
--workflow-id ${WORKFLOW_ID} \
--max-days-without-success ${MAX_DAYS_WITHOUT_SUCCESS}
Loading