This repository contains GitHub Actions workflows to automate common development tasks for team projects.
- Features
- 1. Keeper: unified triage management ✅ Implemented
- 2. Keeper: stale PR detector ✅ Implemented
- 3. Keeper: auto-label release and backport ✅ Implemented
- 4. Keeper: closed PR label cleanup 📝 Planned
- 5. Keeper: feature branch auto-labeling ✅ Implemented
- 6. Keeper: enhanced triage label management 📝 Planned
- Workflow Structure
- Fork Compatibility
- Prerequisites
- Usage
- Testing
- Development
- License
.github/workflows/
├── keeper-triage.yml # Unified triage management: auto-add, protection, and ready-for-review labeling
├── keeper-stale-pr-detector.yml # Marks inactive PRs as stale
├── keeper-auto-label-release-backport.yml # Auto-labels PRs based on YAML frontmatter
├── keeper-closed-pr-label-cleanup.yml # Removes "ready for review" label from closed PRs
├── keeper-feature-branch-auto-labeling.yml # Auto-labels PRs as "feature-branch" based on YAML frontmatter
├── keeper-enhanced-triage-management.yml # Enhanced triage label management with release/ready conditions
└── keeper-trigger.yml # Data collection workflow (fork-compatible)
- Copy the workflows under the
.github/workflows/
dir to your repository - Make sure to update the
if: github.repository == 'thenets/repo-automations'
to match your repository
Unified workflow that handles triage labeling, label protection, and ready-for-review labeling in a single, intelligent system.
File: .github/workflows/keeper-triage.yml
Trigger: issues.opened
, workflow_run
(from keeper-trigger.yml
)
Behavior:
For Issues:
- Automatically adds "triage" label to newly created issues
- Protects "triage" label from removal (re-adds if removed without release/backport labels)
For Pull Requests:
- Waits 10 seconds for other workflows to complete
- Smart Conditional Labeling:
- If PR has "release *" label AND is not in draft mode → Adds "ready for review" label
- Otherwise → Adds "triage" label
- Label Protection: Prevents "triage" label removal unless release/backport labels are present
- Draft Handling: Skips labeling for draft PRs entirely
Integration Benefits:
- Unified Logic: All triage-related labeling in one workflow
- Intelligent Decision Making: Context-aware labeling based on PR state
- Reduced Overhead: Single workflow instead of three separate ones
- Fork Compatible: Works seamlessly with external contributor PRs
flowchart TD
A[New Issue] --> B[Add 'triage' Label]
C[New PR] --> D{Is Draft?}
D -->|Yes| E[Skip Labeling]
D -->|No| F[Wait 10 seconds]
F --> G{Has 'release *' Label?}
G -->|Yes| H[Add 'ready for review' Label]
G -->|No| I[Add 'triage' Label]
J[Label Removed] --> K{Is 'triage' Label?}
K -->|Yes| L{Has release/backport?}
L -->|No| M[Re-add 'triage' Label]
L -->|Yes| N[Allow Removal]
K -->|No| O[No Action]
Automatically marks pull requests as stale when they have no activity for more than 1 day.
File: .github/workflows/keeper-stale-pr-detector.yml
Trigger: schedule
(daily cron job)
Behavior:
- Runs daily to check all open pull requests
- Identifies PRs with no activity (comments, commits, or label changes) for more than 1 day
- Adds "stale" label to inactive PRs
- Skips PRs that already have the "stale" label
- Considers these activities as "activity":
- New commits pushed to the PR branch
- Comments added to the PR
- Labels added or removed
- PR reviews submitted
- PR status changes (ready for review, draft, etc.)
flowchart LR
A[Daily Cron Job] --> B[Check Open PRs]
B --> C{PR inactive >1 day?}
C -->|Yes| D[Add 'stale' Label]
C -->|No| E[Skip PR]
D --> F[✅ Complete]
E --> F
Automatically adds release and backport labels to pull requests based on YAML code blocks in the PR description.
File: .github/workflows/keeper-auto-label-release-backport.yml
Trigger: pull_request.opened
, pull_request.synchronize
Behavior:
- Scans the PR description for YAML code blocks
- Parses
release
andbackport
values from YAML - Validates values against accepted lists (see below)
- Adds corresponding labels (e.g.,
release 1.5
,backport 1.4
) - Ignores comments after
#
in YAML values - Only processes the first YAML block found
Validation Rules:
- Empty values (e.g.,
release:
with no value) → Workflow exits gracefully - Existing labels → Skips assignment to preserve manually set labels
- Valid values (from accepted lists) → Labels are added successfully
- Invalid values (not in accepted lists) → Workflow fails with error
- Quotes → Both single and double quotes are supported and automatically removed
- Case sensitivity → Values are case-sensitive for release/backport (e.g.,
devel
notDevel
)
Accepted Values:
- Release tags:
1.0
,1.1
,1.2
,1.3
,1.4
,1.5
,1.6
,2.0
,2.1
,2.2
,devel
,main
- Backport tags:
1.0
,1.1
,1.2
,1.3
,1.4
,1.5
,1.6
,2.0
,2.1
,2.2
,main
Note: Using values not in these lists will cause the workflow to fail, alerting you to fix the invalid value.
Supported YAML format:
Include a YAML code block in your PR description:
release: 1.5 # Creates "release 1.5" label
backport: 1.4 # Creates "backport 1.4" label
release: "devel" # Quotes are supported (single or double)
backport: 'main' # Both single and double quotes work
Example PR description:
This PR adds new feature X.
release: "1.5" # Quotes are optional backport: 1.4 # Works with or without quotesThe changes are backward compatible.
flowchart LR
A[PR Created/Updated] --> B[Scan Changed Files]
B --> C{YAML Found?}
C -->|Yes| D[Parse release/backport]
C -->|No| E[✅ Skip]
D --> F[Add Labels]
F --> G[✅ Complete]
Automatically removes the "ready for review" label from closed pull requests during nightly runs.
File: .github/workflows/keeper-closed-pr-label-cleanup.yml
Trigger: schedule
(nightly cron job)
Behavior:
- Runs nightly to check all closed pull requests
- Identifies closed PRs that still have the "ready for review" label
- Removes the "ready for review" label from these PRs
- Helps maintain clean label states by removing review-related labels that are no longer relevant
- Only processes PRs that are in closed state (merged or closed without merge)
flowchart LR
A[Nightly Cron Job] --> B[Check Closed PRs]
B --> C{Has 'ready for review' label?}
C -->|Yes| D[Remove Label]
C -->|No| E[Skip PR]
D --> F[✅ Complete]
E --> F
Automatically adds "feature-branch" label to pull requests based on YAML code blocks in the PR description.
File: .github/workflows/keeper-feature-branch-auto-labeling.yml
Trigger: pull_request.opened
, pull_request.synchronize
Behavior:
- Scans the PR description for YAML code blocks
- Parses
needs_feature_branch
value from YAML (Boolean:true
orfalse
) - If
needs_feature_branch: true
, adds "feature-branch" label to the PR - If
needs_feature_branch: false
, no action is taken - Ignores comments after
#
in YAML values - Only processes the first YAML block found
Validation Rules:
- Empty values (e.g.,
needs_feature_branch:
with no value) → Workflow exits gracefully - Existing labels → Skips assignment to preserve manually set labels
- Valid values (
true
,false
) → Labels are added successfully iftrue
- Invalid values (not boolean) → Workflow fails with error
- Quotes → Both single and double quotes are supported and automatically removed
- Case insensitive → Accepts
true
,True
,TRUE
,false
,False
,FALSE
, etc.
Supported YAML format:
Include a YAML code block in your PR description:
needs_feature_branch: true # Creates "feature-branch" label
needs_feature_branch: True # Case-insensitive: True, FALSE, etc.
needs_feature_branch: "false" # Quotes are supported (single or double)
release: 1.5 # Creates "release 1.5" label (existing feature)
backport: 1.4 # Creates "backport 1.4" label (existing feature)
Example PR description:
This PR adds new feature X that requires a feature branch.
needs_feature_branch: True # Case-insensitive release: "1.5" # Quotes are optional backport: 1.4The changes require coordination with multiple teams.
flowchart LR
A[PR Created/Updated] --> B[Scan PR Description]
B --> C{YAML Found?}
C -->|Yes| D[Parse needs_feature_branch]
C -->|No| E[✅ Skip]
D --> F{Value is true?}
F -->|Yes| G[Add feature-branch Label]
F -->|No| H[✅ Skip]
G --> I[✅ Complete]
H --> I
Enhances the existing triage label protection by automatically removing the "triage" label when specific conditions are met.
File: .github/workflows/keeper-enhanced-triage-management.yml
Trigger: pull_request.labeled
, pull_request.unlabeled
Behavior:
- Monitors when labels are added or removed from PRs
- Automatically removes "triage" label when ALL conditions are met:
- PR has any label starting with "release " (e.g., "release 1.5", "release main")
- PR has "ready for review" label
- This indicates the PR is properly categorized and ready for review, so triage is no longer needed
- Works in conjunction with existing triage protection workflow
Logic Flow:
- Label Change Detected → Check if PR has both "release *" and "ready for review" labels
- Conditions Met → Remove "triage" label (if present)
- Conditions Not Met → No action taken (existing protection workflow handles re-adding if needed)
Integration with Existing Workflows:
- Works with
keeper-triage.yml
for complete triage label lifecycle management - Works with
keeper-auto-label-release-backport.yml
for complete label management - Respects manual label changes by only acting on specific conditions
flowchart LR
A[PR Label Change] --> B{Has release label?}
B -->|Yes| C{Has ready for review?}
B -->|No| D[✅ No Action]
C -->|Yes| E[Remove triage Label]
C -->|No| F[✅ No Action]
E --> G[✅ Complete]
This repository implements a fork-compatible architecture that allows external contributors to trigger labeling workflows seamlessly. The system uses a two-workflow pattern to separate data collection from privileged operations.
Traditional GitHub Actions workflows fail when triggered by pull requests from forks because:
- Forked repositories don't have access to the original repository's secrets
- GitHub's default
GITHUB_TOKEN
has limited permissions for external contributors - Workflows cannot add labels to pull requests from forks without elevated permissions
We implement a two-workflow pattern that separates data collection from privileged operations:
- Data Collection Workflow (
keeper-trigger
): Runs on any repository (including forks), collects ALL PR metadata as-is - Action Workflows (
keeper-*
): Triggered by data collection completion, run only on target repository with full permissions
flowchart TD
A[PR Created/Updated on Fork] --> FT
subgraph FT ["🔄 keeper-trigger.yml (Runs on Fork)"]
direction TB
B[Collect PR Metadata]
C[Extract: title, body, draft, etc.]
D[Store as pr-metadata.json]
E[Upload Artifact]
B --> C --> D --> E
end
FT --> TG[Trigger Action Workflows]
subgraph AW ["🎯 Action Workflows (Run on Target Repo)"]
direction TB
subgraph FB ["keeper-feature-branch-auto-labeling.yml"]
F1[Download Artifact] --> F2[Parse YAML from prData.body] --> F3[Apply feature-branch Label]
end
subgraph RB ["keeper-auto-label-release-backport.yml"]
R1[Download Artifact] --> R2[Parse YAML from prData.body] --> R3[Apply release/backport Labels]
end
subgraph UT ["keeper-triage.yml"]
U1[Download Artifact] --> U2[Check draft & labels] --> U3[Apply triage OR ready-for-review Label]
end
end
TG --> FB
TG --> RB
TG --> UT
FB --> Z[✅ Complete]
RB --> Z
UT --> Z
style A fill:#e1f5fe
style FT fill:#fff3e0
style AW fill:#f3e5f5
style Z fill:#e8f5e8
Workflow | Fork Compatible | Status | Notes |
---|---|---|---|
keeper-trigger.yml | ✅ N/A | ✅ Working | Central data collection workflow |
keeper-triage.yml | ✅ Yes | ✅ Complete | Unified triage management with full artifact consumption |
keeper-auto-label-release-backport.yml | ✅ Yes | ✅ Complete | Full artifact consumption |
keeper-feature-branch-auto-labeling.yml | ✅ Yes | ✅ Complete | Full artifact consumption |
keeper-stale-pr-detector.yml | ✅ N/A | ✅ No changes needed | Uses schedule/dispatch |
- External Contributor Friendly: PRs from forks trigger workflows seamlessly
- Security: Privileged operations only run on target repository
- Minimal Changes: Existing logic preserved, just data source changed
- Performance: Complete workflow chain executes in ~1-2 minutes
- Backward Compatible: All existing functionality preserved
When external contributors (non-collaborators) create pull requests or issues, the default GITHUB_TOKEN
has limited permissions and may cause workflows to fail with permission errors. To enable all workflows to function properly for external contributors, repository administrators should create a fine-grained personal access token.
Repository Access:
- This repository (or "All repositories" for organization-wide use)
Repository Permissions:
- Issues: Write - Required to add labels to issues
- Issues: Read - Required to access issue comments and timeline events
- Pull requests: Write - Required to add labels to pull requests
- Pull requests: Read - Required to list and read pull request details
- Metadata: Read - Required to access repository information
- Create Token: Go to GitHub Settings > Personal Access Tokens (Beta)
- Configure Access: Select the target repository or choose "All repositories" for organization-wide use
- Set Permissions: Grant all the permissions listed above
- Add to Repository: Add the token as a repository secret named
CUSTOM_GITHUB_TOKEN
- Go to your repository Settings > Secrets and variables > Actions
- Click "New repository secret"
- Name:
CUSTOM_GITHUB_TOKEN
- Value: Your generated token
- With Custom Token: All workflows will function properly for external contributors
- Without Custom Token: Workflows may fail for external contributors with clear error messages explaining the setup required
- Backward Compatible: Existing setups continue to work without any changes required
- Repository must have "triage", "stale", and "ready for review" labels created
- GitHub Actions must have write permissions for issues and pull requests
- Workflows require
GITHUB_TOKEN
with appropriate scopes
- Copy workflow files to
.github/workflows/
directory - Ensure required labels exist in repository settings:
- "triage" (for new issues/PRs)
- "stale" (for inactive PRs)
- "ready for review" (for PRs ready for team review)
- Verify GitHub Actions permissions include:
issues: write
pull-requests: write
- Fork this repository to your GitHub account
- Update repository references in all workflow files:
- Change
if: github.repository == 'thenets/repo-automations'
toif: github.repository == 'your-username/repo-automations'
- This line appears in all
keeper-*.yml
workflow files under.github/workflows/
- Change
The test suite is organized with shared fixtures and utilities in a centralized structure:
test/
├── conftest.py # Shared fixtures and utilities for all tests
├── test_triage_auto_add.py # Triage label management and stale PR detection tests
├── test_feature_branch_labeler.py # Feature branch labeling tests (basic + error reporting)
├── test_release_backport_labeler.py # Release/backport labeling tests (basic + error reporting)
├── test_ready_for_review_labeling.py # Ready-for-review label workflow tests
├── test_yaml_variations.py # YAML parsing edge cases and format variations
├── test_label_validation.py # Label validation and error handling tests
└── test_basic_functionality.py # Basic smoke tests
Key Components:
conftest.py
: ContainsGitHubTestManager
andGitHubFixtures
classes with all shared test utilities (Git operations, PR/issue management, label operations, polling utilities)- Integration Tests: All test classes inherit from
GitHubFixtures
and use real GitHub API calls - Error Reporting Tests: Dedicated test classes for validation error comment lifecycle (creation, auto-cleanup)
- Parallel Execution: Tests use thread-safe unique naming for parallel test execution
Run the test suite:
# Run all tests (Warning: Takes 1+ minutes)
make test
# Run specific test file
./venv/bin/pytest test/test_feature_branch_labeler.py -v
# Run specific test method
./venv/bin/pytest -k test_stale_pr_detection_manual_trigger -v
# Run specific test class
./venv/bin/pytest test/test_feature_branch_labeler.py::TestFeatureBranchErrorReporting -v
When adding a new automation workflow, follow these guidelines:
All new workflows should follow the fork-compatible two-workflow pattern:
- Data Collection: Use
keeper-trigger.yml
to collect PR/issue metadata - Action Workflow: Create your new workflow triggered by
workflow_run
fromkeeper-trigger
Use keeper-auto-label-release-backport.yml
as your reference template:
name: "Your New Workflow"
on:
workflow_run:
workflows: ["Keeper: Trigger Data Collection"]
types: [completed]
jobs:
your-job:
if: >
github.event.workflow_run.conclusion == 'success' &&
github.repository == 'thenets/repo-automations'
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
steps:
- name: Download artifact
uses: actions/download-artifact@v4
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
run-id: ${{ github.event.workflow_run.id }}
name: pr-metadata
# Your workflow logic here
- Trigger: Use
workflow_run
fromkeeper-trigger.yml
- Repository Check: Include
if: github.repository == 'your-org/your-repo'
condition - Permissions: Add required
issues: write
andpull-requests: write
permissions - Artifact Download: Download
pr-metadata
artifact for PR/issue data - Error Handling: Implement error reporting with comments and check runs (see release/backport workflow)
- Fork Compatibility: Test with external contributor PRs
- File Naming: Use
keeper-{feature-name}.yml
convention
For workflows that validate YAML or user input, implement error reporting:
- name: Post validation error comment
if: failure()
uses: actions/github-script@v7
with:
script: |
const comment = `🚨 YAML Validation Error: your feature
**Invalid value found**: "${invalidValue}"
**How to fix:**
1. Update your PR description
2. Use valid values from the accepted list
Valid YAML format:
\`\`\`yaml
your_field: valid_value
\`\`\``;
// Post comment and create check run
When adding tests for new workflows or features, follow these guidelines:
Before writing new test utilities, review test/conftest.py
:
# Available fixtures and utilities:
class GitHubTestManager:
# Git operations: create_branch(), git_commit_and_push(), push_branch()
# PR/Issue management: create_pr(), create_issue(), close_pr()
# Label operations: create_label(), get_pr_labels(), pr_has_label()
# Comment operations: get_pr_comments(), pr_has_comment_containing()
# Polling utilities: poll_until_condition()
class GitHubFixtures:
# test_repo: Temporary repository with basic labels
# integration_manager: GitHubTestManager instance
# github_manager: Function-scoped GitHubTestManager
Follow test_release_backport_labeler.py
as your template:
"""
Test suite for your new workflow.
This test validates that the workflow does X, Y, and Z.
"""
import pytest
from .conftest import GitHubTestManager, GitHubFixtures
@pytest.mark.integration
class TestYourWorkflow(GitHubFixtures):
"""Integration test cases for your workflow."""
def test_basic_functionality(self, test_repo, integration_manager):
"""Test basic workflow functionality.
Steps:
1. Create a new branch
2. Create file changes and commit
3. Create PR with test configuration
4. Wait for workflow to process
5. Verify expected behavior
6. Cleanup PR
"""
repo_path = test_repo
# Setup required labels
integration_manager.create_label(repo_path, "your-label", "FF0000", "Description")
# Create test branch and changes
branch_name = f"test-your-feature-{int(time.time())}"
integration_manager.create_branch(repo_path, branch_name)
# Test implementation...
@pytest.mark.integration
class TestYourWorkflowErrorReporting(GitHubFixtures):
"""Test error reporting functionality."""
def test_validation_error_comment_lifecycle(self, test_repo, integration_manager):
"""Test error comment creation and auto-cleanup."""
# Follow error reporting test pattern from reference file
- File naming:
test_{workflow_name}.py
- Class organization: Separate basic functionality from error reporting
- Integration tests: Use
@pytest.mark.integration
decorator - Real GitHub API: All tests use real GitHub operations (not mocked)
- Cleanup: Always cleanup PRs/branches in test teardown
- Thread safety: Use
int(time.time())
for unique naming in parallel execution
- Descriptive names: Use clear test and branch names
- Comprehensive steps: Document test steps in docstrings
- Polling pattern: Use
poll_until_condition()
for async workflow results - Error scenarios: Test both success and failure cases
- Label verification: Always verify expected labels are present/absent
- Comment verification: Test error comment posting and cleanup
# Run your specific test file
./venv/bin/pytest test/test_your_workflow.py -v
# Run specific test method
./venv/bin/pytest test/test_your_workflow.py::TestYourWorkflow::test_basic_functionality -v
# Run error reporting tests
./venv/bin/pytest test/test_your_workflow.py::TestYourWorkflowErrorReporting -v
This project is licensed under the MIT License. See the LICENSE file for details.