Skip to content

Commit

Permalink
Merge pull request #32 from rapidsai/feat/check_nightly_ci
Browse files Browse the repository at this point in the history
Add an action to check whether nightlies have succeeded recently
  • Loading branch information
vyasr authored Dec 17, 2024
2 parents c2d3cdc + a5b4916 commit 62ed1b3
Show file tree
Hide file tree
Showing 7 changed files with 197 additions and 1 deletion.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.swp
6 changes: 6 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,9 @@ repos:
rev: v1.7.4
hooks:
- id: actionlint-docker
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.8.3
hooks:
- id: ruff
args: ["--fix"]
- id: ruff-format
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,4 +81,4 @@ jobs:
id: telemetry-setup
# DO NOT change this in PRs
uses: rapidsai/shared-actions/dispatch-script@main
```
```
36 changes: 36 additions & 0 deletions check_nightly_success/check-nightly-success/action.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: check-nightly-success
description: Check if the nightlies have succeeded recently.
inputs:
repo:
description: "The repository to check"
required: true
type: string
repo_owner:
description: "The org that owns the repo (default: rapidsai)"
required: false
default: "rapidsai"
type: string
workflow_id:
description: "The workflow whose runs to check"
required: false
default: "test.yaml"
type: string
max_days_without_success:
description: "The number of consecutive days that may go by without a successful CI run"
required: false
default: 7
type: integer

runs:
using: composite
steps:
- name: Run the Python script
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 }}
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}
97 changes: 97 additions & 0 deletions check_nightly_success/check-nightly-success/check.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
"""Check whether a GHA workflow has run successfully in the last N days."""
# ruff: noqa: INP001

import argparse
import itertools
import os
import re
import sys
from datetime import datetime

import requests

# Constants
GITHUB_TOKEN = os.environ["RAPIDS_GH_TOKEN"]
GOOD_STATUSES = {"success"}


def main(
repo: str,
repo_owner: str,
workflow_id: str,
max_days_without_success: int,
num_attempts: int = 5,
) -> bool:
"""Check whether a GHA workflow has run successfully in the last N days."""
headers = {"Authorization": f"token {GITHUB_TOKEN}"}
url = f"https://api.github.com/repos/{repo_owner}/{repo}/actions/workflows/{workflow_id}/runs"
exceptions = []
for _ in range(num_attempts):
try:
response = requests.get(url, headers=headers, timeout=10)
response.raise_for_status()
break
except requests.RequestException as e:
exceptions.append(e)
else:
sep = "\n\t"
msg = (
f"Failed to fetch {url} after {num_attempts} attempts with the following "
f"errors: {sep}{'{sep}'.join(exceptions)}"
)
raise RuntimeError(msg)

runs = response.json()["workflow_runs"]
tz = datetime.fromisoformat(runs[0]["run_started_at"]).tzinfo
now = datetime.now(tz=tz)

branch_ok = {}
for branch, branch_runs in itertools.groupby(runs, key=lambda r: r["head_branch"]):
if not re.match("branch-[0-9]{2}.[0-9]{2}", branch):
continue

branch_ok[branch] = False
for run in sorted(branch_runs, key=lambda r: r["run_started_at"], reverse=True):
if (
now - datetime.fromisoformat(run["run_started_at"])
).days > max_days_without_success:
break
if run["conclusion"] in GOOD_STATUSES:
branch_ok[branch] = True
break

failed_branches = [k for k, v in branch_ok.items() if not v]
if failed_branches:
print( # noqa: T201
f"Branches with no successful runs of {workflow_id} in the last "
f"{max_days_without_success} days: "
f"{', '.join(failed_branches)}",
)
return len(failed_branches) > 0


if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("repo", type=str, help="Repository name")
parser.add_argument(
"--repo-owner",
default="rapidsai",
help="Repository organization/owner",
)
parser.add_argument("--workflow-id", default="test.yaml", help="Workflow ID")
parser.add_argument(
"--max-days-without-success",
type=int,
default=7,
help="Maximum number of days without a successful run",
)
args = parser.parse_args()

sys.exit(
main(
args.repo,
args.repo_owner,
args.workflow_id,
args.max_days_without_success,
),
)
39 changes: 39 additions & 0 deletions check_nightly_success/dispatch/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
name: dispatch-check-nightly-success
description: Clone shared-actions and dispatch to the check-nightly-success action.
inputs:
repo:
description: "The repository to check"
required: true
type: string
repo_owner:
description: "The org that owns the repo (default: rapidsai)"
required: false
default: "rapidsai"
type: string
workflow_id:
description: "The workflow whose runs to check"
required: false
default: "test.yaml"
type: string
max_days_without_success:
description: "The number of consecutive days that may go by without a successful CI run"
required: false
default: 7
type: integer

runs:
using: 'composite'
steps:
- name: Clone shared-actions repo
uses: actions/checkout@v4
with:
repository: ${{ env.SHARED_ACTIONS_REPO || 'rapidsai/shared-actions' }}
ref: ${{ env.SHARED_ACTIONS_REF || 'main' }}
path: ./shared-actions
- name: Run check-nightly-success
uses: ./shared-actions/check_nightly_success/check-nightly-success
with:
repo: ${{ inputs.repo }}
repo_owner: ${{ inputs.repo_owner }}
workflow_id: ${{ inputs.workflow_id }}
max_days_without_success: ${{ inputs.max_days_without_success }}
17 changes: 17 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Copyright (c) 2024, NVIDIA CORPORATION.

[tool.ruff]
target-version = "py310"

[tool.ruff.lint]
select = ["ALL"]
ignore = [
# Incompatible with D211
"D203",
# Incompatible with D213
"D213",
# Incompatible with ruff-format
"COM812",
"ISC001",
]
fixable = ["ALL"]

0 comments on commit 62ed1b3

Please sign in to comment.