Compare commits

...

1 Commits

Author SHA1 Message Date
Teknium
86465152f2 feat: CI improvements — linting, PR labels, regression test enforcement
Inspired by Ironclaw's CI pipeline. Three new workflows:

1. lint.yml — Ruff lint and format check on PRs
2. pr-labels.yml — Auto-label PRs by size (XS/S/M/L/XL) and scope
   (agent-core, cli, gateway, tools, cron, tests, docs, config)
3. regression-test-check.yml — Warns when PRs modify high-risk files
   (run_agent.py, cli.py, gateway/run.py, etc.) without test changes

Plus .github/labeler.yml for path-based scope label mappings.
2026-03-29 17:59:50 -07:00
4 changed files with 276 additions and 0 deletions

45
.github/labeler.yml vendored Normal file
View File

@@ -0,0 +1,45 @@
# Scope labels for actions/labeler@v5
# Maps labels to file path patterns
agent-core:
- changed-files:
- any-glob-to-any-file:
- run_agent.py
- model_tools.py
cli:
- changed-files:
- any-glob-to-any-file:
- cli.py
- hermes_cli/**
gateway:
- changed-files:
- any-glob-to-any-file:
- gateway/**
tools:
- changed-files:
- any-glob-to-any-file:
- tools/**
cron:
- changed-files:
- any-glob-to-any-file:
- cron/**
tests:
- changed-files:
- any-glob-to-any-file:
- tests/**
docs:
- changed-files:
- any-glob-to-any-file:
- website/**
config:
- changed-files:
- any-glob-to-any-file:
- hermes_cli/config.py
- hermes_cli/setup.py

33
.github/workflows/lint.yml vendored Normal file
View File

@@ -0,0 +1,33 @@
name: Lint
on:
pull_request:
branches: [main]
# Cancel in-progress runs for the same PR
concurrency:
group: lint-${{ github.ref }}
cancel-in-progress: true
jobs:
lint:
name: Ruff Lint & Format Check
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python 3.11
uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Install ruff
run: pip install ruff
- name: Run ruff linter
run: ruff check .
- name: Check ruff formatting
run: ruff format --check .

93
.github/workflows/pr-labels.yml vendored Normal file
View File

@@ -0,0 +1,93 @@
name: PR Labels
on:
pull_request:
branches: [main]
# Cancel in-progress runs for the same PR
concurrency:
group: pr-labels-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
pull-requests: write
jobs:
scope-labels:
name: Scope Labels
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Apply scope labels
uses: actions/labeler@v5
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
configuration-path: .github/labeler.yml
size-label:
name: Size Label
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Label PR by size
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const { data: files } = await github.rest.pulls.listFiles({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.issue.number,
per_page: 100,
});
let totalChanges = 0;
for (const file of files) {
totalChanges += file.additions + file.deletions;
}
let sizeLabel;
if (totalChanges < 10) {
sizeLabel = 'size/XS';
} else if (totalChanges < 50) {
sizeLabel = 'size/S';
} else if (totalChanges < 200) {
sizeLabel = 'size/M';
} else if (totalChanges < 500) {
sizeLabel = 'size/L';
} else {
sizeLabel = 'size/XL';
}
// Remove any existing size labels
const sizeLabels = ['size/XS', 'size/S', 'size/M', 'size/L', 'size/XL'];
const { data: currentLabels } = await github.rest.issues.listLabelsOnIssue({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
});
for (const label of currentLabels) {
if (sizeLabels.includes(label.name) && label.name !== sizeLabel) {
await github.rest.issues.removeLabel({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
name: label.name,
});
}
}
// Add the new size label
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
labels: [sizeLabel],
});
console.log(`PR has ${totalChanges} lines changed → ${sizeLabel}`);

View File

@@ -0,0 +1,105 @@
name: Regression Test Check
on:
pull_request:
branches: [main]
# Cancel in-progress runs for the same PR
concurrency:
group: regression-test-check-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
pull-requests: write
jobs:
check-tests:
name: Check for Regression Tests
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Check high-risk files have corresponding tests
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_NUMBER: ${{ github.event.pull_request.number }}
run: |
# High-risk files that should have test coverage when modified
HIGH_RISK_FILES=(
"run_agent.py"
"cli.py"
"gateway/run.py"
"tools/approval.py"
"tools/terminal_tool.py"
"model_tools.py"
)
# Get the list of changed files in this PR
CHANGED_FILES=$(gh pr diff "$PR_NUMBER" --name-only)
# Check if any high-risk files were modified
HIGH_RISK_MODIFIED=()
for file in "${HIGH_RISK_FILES[@]}"; do
if echo "$CHANGED_FILES" | grep -qx "$file"; then
HIGH_RISK_MODIFIED+=("$file")
fi
done
# If no high-risk files modified, we're good
if [ ${#HIGH_RISK_MODIFIED[@]} -eq 0 ]; then
echo "No high-risk files modified. Skipping test check."
exit 0
fi
echo "High-risk files modified: ${HIGH_RISK_MODIFIED[*]}"
# Check if any test files were added or modified
TEST_FILES_CHANGED=$(echo "$CHANGED_FILES" | grep -E "^tests/" || true)
if [ -z "$TEST_FILES_CHANGED" ]; then
echo "::warning::High-risk files modified without test changes!"
# Build the warning comment
MODIFIED_LIST=""
for file in "${HIGH_RISK_MODIFIED[@]}"; do
MODIFIED_LIST="$MODIFIED_LIST\n- \`$file\`"
done
COMMENT_BODY=$(cat <<EOF
⚠️ **Regression Test Check Warning**
This PR modifies the following high-risk files:
$(for file in "${HIGH_RISK_MODIFIED[@]}"; do echo "- \`$file\`"; done)
However, no test files under \`tests/\` were added or modified.
Please consider adding or updating tests to cover these changes. This helps prevent regressions in critical code paths.
> _This is an automated check. If the changes are trivial or already covered by existing tests, you can disregard this warning._
EOF
)
# Post a comment on the PR (update existing comment if one exists)
EXISTING_COMMENT_ID=$(gh api \
"repos/${{ github.repository }}/issues/${PR_NUMBER}/comments" \
--jq '.[] | select(.body | startswith("⚠️ **Regression Test Check Warning**")) | .id' \
| head -1)
if [ -n "$EXISTING_COMMENT_ID" ]; then
gh api \
"repos/${{ github.repository }}/issues/comments/${EXISTING_COMMENT_ID}" \
-X PATCH \
-f body="$COMMENT_BODY"
echo "Updated existing warning comment."
else
gh pr comment "$PR_NUMBER" --body "$COMMENT_BODY"
echo "Posted warning comment on PR."
fi
else
echo "Test files were modified. Check passed."
echo "Changed test files:"
echo "$TEST_FILES_CHANGED"
fi