1- name : PR Validation & Auto-linking
1+ name : PR Validation
22
33on :
44 pull_request :
55 types : [opened, edited, synchronize, ready_for_review]
6- pull_request_review :
7- types : [submitted]
86
97jobs :
108 validate-pr :
@@ -13,195 +11,73 @@ jobs:
1311 - name : Check PR links to issue
1412 uses : actions/github-script@v6
1513 with :
14+ github-token : ${{ secrets.GITHUB_TOKEN }}
1615 script : |
1716 const prBody = context.payload.pull_request.body || '';
1817 const prTitle = context.payload.pull_request.title || '';
19- const prNumber = context.payload.pull_request.number;
2018
2119 // Check for issue references
22- const issuePattern = /(?:closes|fixes|resolves|implements)\s+#? (\d+)|AIRA-(\d+)/gi;
20+ const issuePattern = /(?:closes|fixes|resolves|implements)\s+#(\d+)|AIRA-(\d+)/gi;
2321 const hasIssueRef = issuePattern.test(prBody + ' ' + prTitle);
2422
2523 if (!hasIssueRef) {
26- core.setFailed('PR must reference an issue using keywords like "Closes #12" or "AIRA-12"');
24+ core.setFailed('❌ PR must reference an issue using "Closes #12" or mention "AIRA-12"');
2725 return;
2826 }
2927
30- // Validate PR title format
31- const titlePattern = /^(feat|fix|docs|style|refactor|test|chore|hotfix):\s.+\s\(AIRA-\d+\)$/;
32- if (!titlePattern.test(prTitle)) {
33- core.setFailed('PR title must follow format: "type: description (AIRA-X)"');
34- return;
35- }
36-
37- console.log('✅ PR validation passed');
28+ console.log('✅ PR references an issue');
3829
3930 - name : Validate branch name
4031 run : |
4132 BRANCH_NAME="${{ github.head_ref }}"
4233 if [[ ! $BRANCH_NAME =~ ^AIRA-[0-9]+$ ]] && \
43- [[ ! $BRANCH_NAME =~ ^(hotfix|docs|experiment)/AIRA-[0-9]+$ ]] && \
44- [[ ! $BRANCH_NAME =~ ^release/v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
45- echo "❌ Branch name must follow convention:"
34+ [[ ! $BRANCH_NAME =~ ^(hotfix|docs)/AIRA-[0-9]+$ ]]; then
35+ echo "❌ Branch name must follow pattern:"
4636 echo " - AIRA-X (feature branches)"
4737 echo " - hotfix/AIRA-X (hotfixes)"
4838 echo " - docs/AIRA-X (documentation)"
49- echo " - release/vX.Y.Z (releases)"
5039 exit 1
5140 fi
52- echo "✅ Branch name follows convention: $BRANCH_NAME"
53-
54- - name : Check PR size
55- uses : actions/github-script@v6
56- with :
57- script : |
58- const pr = context.payload.pull_request;
59- const additions = pr.additions;
60- const deletions = pr.deletions;
61- const changedFiles = pr.changed_files;
62-
63- if (additions + deletions > 1000) {
64- github.rest.issues.createComment({
65- issue_number: pr.number,
66- owner: context.repo.owner,
67- repo: context.repo.repo,
68- body: `⚠️ **Large PR Warning**
69-
70- This PR modifies ${additions + deletions} lines across ${changedFiles} files.
71- Consider breaking it into smaller, focused PRs for easier review.
72-
73- **PR Stats:**
74- - **Additions:** ${additions}
75- - **Deletions:** ${deletions}
76- - **Files Changed:** ${changedFiles}`
77- });
78- }
79-
80- - name : Auto-assign reviewers
81- uses : actions/github-script@v6
82- with :
83- script : |
84- const prAuthor = context.payload.pull_request.user.login;
85- const isExternal = context.payload.pull_request.head.repo.fork;
86-
87- let reviewers = [];
88- let teamReviewers = [];
89-
90- if (isExternal) {
91- // External contributors need core team review
92- teamReviewers = ['aira-core'];
93- reviewers = ['maintainer1', 'maintainer2'];
94- } else {
95- // Internal PRs need peer review
96- const coreTeam = ['dev1', 'dev2', 'dev3', 'dev4'];
97- reviewers = coreTeam.filter(dev => dev !== prAuthor).slice(0, 1);
98- }
99-
100- if (reviewers.length > 0) {
101- await github.rest.pulls.requestReviewers({
102- owner: context.repo.owner,
103- repo: context.repo.repo,
104- pull_number: context.payload.pull_request.number,
105- reviewers: reviewers,
106- team_reviewers: teamReviewers
107- });
108- }
41+ echo "✅ Branch name valid: $BRANCH_NAME"
10942
11043 test :
11144 runs-on : ubuntu-latest
112- strategy :
113- matrix :
114- python-version : [3.9, 3.10, 3.11]
115-
11645 steps :
11746 - uses : actions/checkout@v4
11847
119- - name : Set up Python ${{ matrix.python-version }}
48+ - name : Set up Python
12049 uses : actions/setup-python@v4
12150 with :
122- python-version : ${{ matrix.python-version }}
51+ python-version : ' 3.11 '
12352
124- - name : Cache dependencies
125- uses : actions/cache@v3
126- with :
127- path : ~/.cache/pip
128- key : ${{ runner.os }}-pip-${{ hashFiles('**/requirements*.txt') }}
129- restore-keys : |
130- ${{ runner.os }}-pip-
131-
13253 - name : Install dependencies
13354 run : |
13455 python -m pip install --upgrade pip
13556 pip install -r requirements.txt
136- pip install -r requirements-dev.txt
137-
138- - name : Run linting
139- run : |
140- flake8 aira/ tests/ --max-line-length=88 --extend-ignore=E203,W503
141- black --check aira/ tests/
142- isort --check-only aira/ tests/
57+ if [ -f requirements-dev.txt ]; then pip install -r requirements-dev.txt; fi
14358
144- - name : Run type checking
145- run : mypy aira/
146-
147- - name : Run security scan
59+ - name : Run tests
14860 run : |
149- bandit -r aira/ -f json -o bandit-report.json
150- safety check --json --output safety-report.json
151-
152- - name : Run tests with coverage
153- run : |
154- pytest tests/ \
155- --cov=aira \
156- --cov-report=xml \
157- --cov-report=html \
158- --cov-fail-under=85 \
159- --junitxml=pytest-report.xml
160-
161- - name : Upload coverage reports
162- uses : codecov/codecov-action@v3
163- with :
164- file : ./coverage.xml
165- flags : unittests
166- name : codecov-umbrella
167-
168- - name : Upload test results
169- uses : actions/upload-artifact@v3
170- if : always()
171- with :
172- name : test-results-${{ matrix.python-version }}
173- path : |
174- pytest-report.xml
175- htmlcov/
176- bandit-report.json
177- safety-report.json
61+ # Run pytest if tests exist, otherwise just import check
62+ if [ -d "tests" ]; then
63+ pytest tests/ -v
64+ else
65+ python -c "import aira; print('✅ Package imports successfully')"
66+ fi
17867
17968 security-check :
18069 runs-on : ubuntu-latest
18170 steps :
18271 - uses : actions/checkout@v4
18372
184- - name : Run Trivy vulnerability scanner
185- uses : aquasecurity/trivy-action@master
186- with :
187- scan-type : ' fs'
188- scan-ref : ' .'
189- format : ' sarif'
190- output : ' trivy-results.sarif'
73+ - name : Run Bandit Security Scan
74+ run : |
75+ pip install bandit
76+ bandit -r . -f json -o bandit-report.json || true
19177
192- - name : Upload Trivy scan results
193- uses : github/codeql-action/upload-sarif@v2
78+ - name : Check for secrets
79+ uses : trufflesecurity/trufflehog@main
19480 with :
195- sarif_file : ' trivy-results.sarif'
196-
197- quality-gate :
198- runs-on : ubuntu-latest
199- needs : [test, security-check]
200- steps :
201- - name : Quality Gate Check
202- run : |
203- echo "✅ All quality checks passed"
204- echo "- Tests: Passed"
205- echo "- Security: Passed"
206- echo "- Coverage: >85%"
207- echo "- Linting: Passed"
81+ path : ./
82+ base : main
83+ head : HEAD
0 commit comments