Skip to content

AIRA-64: Branch Protection Rules & Core Development Automation #1

AIRA-64: Branch Protection Rules & Core Development Automation

AIRA-64: Branch Protection Rules & Core Development Automation #1

Workflow file for this run

name: PR Validation & Auto-linking
on:
pull_request:
types: [opened, edited, synchronize, ready_for_review]
pull_request_review:
types: [submitted]
jobs:
validate-pr:
runs-on: ubuntu-latest
steps:
- name: Check PR links to issue
uses: actions/github-script@v6
with:
script: |
const prBody = context.payload.pull_request.body || '';
const prTitle = context.payload.pull_request.title || '';
const prNumber = context.payload.pull_request.number;
// Check for issue references
const issuePattern = /(?:closes|fixes|resolves|implements)\s+#?(\d+)|AIRA-(\d+)/gi;
const hasIssueRef = issuePattern.test(prBody + ' ' + prTitle);
if (!hasIssueRef) {
core.setFailed('PR must reference an issue using keywords like "Closes #12" or "AIRA-12"');
return;
}
// Validate PR title format
const titlePattern = /^(feat|fix|docs|style|refactor|test|chore|hotfix):\s.+\s\(AIRA-\d+\)$/;
if (!titlePattern.test(prTitle)) {
core.setFailed('PR title must follow format: "type: description (AIRA-X)"');
return;
}
console.log('✅ PR validation passed');
- name: Validate branch name
run: |
BRANCH_NAME="${{ github.head_ref }}"
if [[ ! $BRANCH_NAME =~ ^AIRA-[0-9]+$ ]] && \
[[ ! $BRANCH_NAME =~ ^(hotfix|docs|experiment)/AIRA-[0-9]+$ ]] && \
[[ ! $BRANCH_NAME =~ ^release/v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "❌ Branch name must follow convention:"
echo " - AIRA-X (feature branches)"
echo " - hotfix/AIRA-X (hotfixes)"
echo " - docs/AIRA-X (documentation)"
echo " - release/vX.Y.Z (releases)"
exit 1
fi
echo "✅ Branch name follows convention: $BRANCH_NAME"
- name: Check PR size
uses: actions/github-script@v6
with:
script: |
const pr = context.payload.pull_request;
const additions = pr.additions;
const deletions = pr.deletions;
const changedFiles = pr.changed_files;
if (additions + deletions > 1000) {
github.rest.issues.createComment({
issue_number: pr.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: `⚠️ **Large PR Warning**
This PR modifies ${additions + deletions} lines across ${changedFiles} files.

Check failure on line 70 in .github/workflows/pr-validation.yml

View workflow run for this annotation

GitHub Actions / .github/workflows/pr-validation.yml

Invalid workflow file

You have an error in your yaml syntax on line 70
Consider breaking it into smaller, focused PRs for easier review.
**PR Stats:**
- **Additions:** ${additions}
- **Deletions:** ${deletions}
- **Files Changed:** ${changedFiles}`
});
}
- name: Auto-assign reviewers
uses: actions/github-script@v6
with:
script: |
const prAuthor = context.payload.pull_request.user.login;
const isExternal = context.payload.pull_request.head.repo.fork;
let reviewers = [];
let teamReviewers = [];
if (isExternal) {
// External contributors need core team review
teamReviewers = ['aira-core'];
reviewers = ['maintainer1', 'maintainer2'];
} else {
// Internal PRs need peer review
const coreTeam = ['dev1', 'dev2', 'dev3', 'dev4'];
reviewers = coreTeam.filter(dev => dev !== prAuthor).slice(0, 1);
}
if (reviewers.length > 0) {
await github.rest.pulls.requestReviewers({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.payload.pull_request.number,
reviewers: reviewers,
team_reviewers: teamReviewers
});
}
test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.9, 3.10, 3.11]
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Cache dependencies
uses: actions/cache@v3
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements*.txt') }}
restore-keys: |
${{ runner.os }}-pip-
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install -r requirements-dev.txt
- name: Run linting
run: |
flake8 aira/ tests/ --max-line-length=88 --extend-ignore=E203,W503
black --check aira/ tests/
isort --check-only aira/ tests/
- name: Run type checking
run: mypy aira/
- name: Run security scan
run: |
bandit -r aira/ -f json -o bandit-report.json
safety check --json --output safety-report.json
- name: Run tests with coverage
run: |
pytest tests/ \
--cov=aira \
--cov-report=xml \
--cov-report=html \
--cov-fail-under=85 \
--junitxml=pytest-report.xml
- name: Upload coverage reports
uses: codecov/codecov-action@v3
with:
file: ./coverage.xml
flags: unittests
name: codecov-umbrella
- name: Upload test results
uses: actions/upload-artifact@v3
if: always()
with:
name: test-results-${{ matrix.python-version }}
path: |
pytest-report.xml
htmlcov/
bandit-report.json
safety-report.json
security-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
scan-type: 'fs'
scan-ref: '.'
format: 'sarif'
output: 'trivy-results.sarif'
- name: Upload Trivy scan results
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: 'trivy-results.sarif'
quality-gate:
runs-on: ubuntu-latest
needs: [test, security-check]
steps:
- name: Quality Gate Check
run: |
echo "✅ All quality checks passed"
echo "- Tests: Passed"
echo "- Security: Passed"
echo "- Coverage: >85%"
echo "- Linting: Passed"