Skip to content

Commit 429d840

Browse files
authored
Merge branch 'develop' into codex/rename-.github/workflow-to-.github/workflows
2 parents bce83a6 + cb36184 commit 429d840

8 files changed

Lines changed: 317 additions & 10 deletions

File tree

.github/copilot-instructions.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Repository Instructions for Copilot
2+
3+
## NUTs
4+
5+
- https://github.com/cashubtc/nuts/blob/main/00.md
6+
- https://github.com/cashubtc/nuts/blob/main/01.md
7+
- https://github.com/cashubtc/nuts/blob/main/02.md
8+
- https://github.com/cashubtc/nuts/blob/main/03.md
9+
- https://github.com/cashubtc/nuts/blob/main/04.md
10+
- https://github.com/cashubtc/nuts/blob/main/05.md
11+
- https://github.com/cashubtc/nuts/blob/main/06.md
12+
- https://github.com/cashubtc/nuts/blob/main/07.md
13+
- https://github.com/cashubtc/nuts/blob/main/08.md
14+
- https://github.com/cashubtc/nuts/blob/main/09.md
15+
- https://github.com/cashubtc/nuts/blob/main/10.md
16+
- https://github.com/cashubtc/nuts/blob/main/11.md
17+
- https://github.com/cashubtc/nuts/blob/main/12.md
18+
- https://github.com/cashubtc/nuts/blob/main/13.md
19+
- https://github.com/cashubtc/nuts/blob/main/14.md
20+
- https://github.com/cashubtc/nuts/blob/main/15.md
21+
- https://github.com/cashubtc/nuts/blob/main/16.md
22+
- https://github.com/cashubtc/nuts/blob/main/17.md
23+
- https://github.com/cashubtc/nuts/blob/main/18.md
24+
- https://github.com/cashubtc/nuts/blob/main/19.md
25+
- https://github.com/cashubtc/nuts/blob/main/20.md
26+
- https://github.com/cashubtc/nuts/blob/main/21.md
27+
- https://github.com/cashubtc/nuts/blob/main/22.md
28+
- https://github.com/cashubtc/nuts/blob/main/23.md
29+
- https://github.com/cashubtc/nuts/blob/main/24.md
30+
31+
## Guidelines
32+
33+
- All changes must include unit tests and update relevant docs.
34+
- Use clear names and remove unused imports.
35+
- Prefer readable, maintainable code over clever shortcuts.
36+
- Use four spaces for indentation and keep braces on the same line as declarations.
37+
- Run `mvn test` from the repository root and ensure `mvn package` completes without errors before pushing.

.github/labeler.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
bug:
2+
- '(?i)\bbug\b'
3+
feature:
4+
- '(?i)\bfeature\b'
5+
question:
6+
- '(?i)\bquestion\b'
7+
documentation:
8+
- '(?i)doc'
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
name: Assign Copilot Review
2+
3+
on:
4+
pull_request_target:
5+
branches:
6+
- develop
7+
8+
jobs:
9+
assign-copilot:
10+
runs-on: ubuntu-latest
11+
permissions:
12+
pull-requests: write
13+
steps:
14+
- name: Request review from GitHub Copilot
15+
run: |
16+
curl -s -X POST \
17+
-H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
18+
-H "Accept: application/vnd.github+json" \
19+
https://api.github.com/repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/requested_reviewers \
20+
-d '{"reviewers":["github-copilot"]}'

.github/workflows/ci.yml

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,43 @@ name: CI
22

33
on:
44
push:
5-
branches: [ main ]
5+
branches: [main, develop]
66
pull_request:
7-
branches: [ main ]
7+
branches: [main, develop]
88

99
jobs:
1010
build:
1111
runs-on: ubuntu-latest
12-
1312
steps:
14-
- uses: actions/checkout@v3
15-
16-
- name: Set up JDK 21
17-
uses: actions/setup-java@v3
13+
- uses: actions/checkout@v5
14+
- uses: actions/setup-java@v4
1815
with:
1916
java-version: '21'
2017
distribution: 'temurin'
21-
cache: maven
22-
18+
cache: 'maven'
2319
- name: Build with Maven
24-
run: mvn -B package
20+
run: ./mvnw -q verify
21+
- name: Upload surefire reports
22+
if: always()
23+
uses: actions/upload-artifact@v4
24+
with:
25+
name: surefire-reports
26+
path: '**/target/surefire-reports'
27+
if-no-files-found: ignore
28+
- name: Upload JaCoCo coverage
29+
if: always()
30+
uses: actions/upload-artifact@v4
31+
with:
32+
name: jacoco-exec
33+
path: '**/target/jacoco.exec'
34+
if-no-files-found: ignore
35+
- name: Upload coverage to Codecov
36+
uses: codecov/codecov-action@v5
37+
with:
38+
files: '**/target/site/jacoco/jacoco.xml'
39+
token: ${{ secrets.CODECOV_TOKEN }}
40+
- name: Upload test results to Codecov
41+
if: ${{ !cancelled() }}
42+
uses: codecov/test-results-action@v1
43+
with:
44+
token: ${{ secrets.CODECOV_TOKEN }}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
name: CodeQL
2+
3+
# Disable GitHub's default CodeQL setup in the repository settings to avoid
4+
# "advanced configuration" errors when using this workflow.
5+
6+
on:
7+
push:
8+
branches: [main, develop]
9+
pull_request:
10+
branches: [main, develop]
11+
12+
jobs:
13+
analyze:
14+
runs-on: ubuntu-latest
15+
permissions:
16+
actions: read
17+
contents: read
18+
security-events: write
19+
steps:
20+
- uses: actions/checkout@v5
21+
- uses: actions/setup-java@v4
22+
with:
23+
distribution: temurin
24+
java-version: '21'
25+
- uses: github/codeql-action/init@v3
26+
with:
27+
languages: 'java'
28+
- uses: github/codeql-action/autobuild@v3
29+
- uses: github/codeql-action/analyze@v3
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
name: Issue labeler
2+
3+
on:
4+
issues:
5+
types:
6+
- opened
7+
- edited
8+
9+
permissions:
10+
contents: read
11+
12+
jobs:
13+
triage:
14+
runs-on: ubuntu-latest
15+
steps:
16+
- uses: github/issue-labeler@v3
17+
with:
18+
repo-token: "${{ secrets.GITHUB_TOKEN }}"
19+
configuration-path: .github/labeler.yml
20+
include-title: 1
21+
enable-versioned-regex: 0
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
name: PR Quality Gate
2+
3+
on:
4+
pull_request:
5+
branches: [develop]
6+
types: [opened, edited, synchronize, ready_for_review]
7+
8+
permissions:
9+
issues: write
10+
pull-requests: write
11+
12+
jobs:
13+
review:
14+
if: ${{ github.actor != 'dependabot[bot]' }}
15+
runs-on: ubuntu-latest
16+
steps:
17+
- uses: actions/checkout@v3
18+
- name: Verify Copilot instructions
19+
run: test -s .github/copilot-instructions.md
20+
- name: Run expertise standard checks
21+
uses: actions/github-script@v7
22+
with:
23+
script: |
24+
const pr = context.payload.pull_request;
25+
const owner = context.repo.owner;
26+
const repo = context.repo.repo;
27+
// Fetch fresh PR data + files
28+
const { data: prData } = await github.rest.pulls.get({
29+
owner, repo, pull_number: pr.number
30+
});
31+
// Sum changed lines (additions + deletions)
32+
const totalChanged = prData.additions + prData.deletions;
33+
// Pull files for basic heuristics (e.g., tests touched?)
34+
const files = await github.paginate(
35+
github.rest.pulls.listFiles, { owner, repo, pull_number: pr.number }
36+
);
37+
const commits = await github.paginate(
38+
github.rest.pulls.listCommits, { owner, repo, pull_number: pr.number }
39+
);
40+
const extsCode = ['.js','.ts','.tsx','.jsx','.py','.rb','.go','.rs','.java','.kt','.cs','.php','.c','.cc','.cpp','.m','.mm','.swift','.scala','.sh','.yml','.yaml','.json','.toml'];
41+
const extsTests = ['.spec.','.test.','/tests/','/__tests__/'];
42+
const codeTouched = files.some(f =>
43+
extsCode.some(ext => f.filename.includes(ext)));
44+
const testsTouched = files.some(f =>
45+
extsTests.some(tok => f.filename.includes(tok)));
46+
// 1) Scope ≤ 300 lines (from GitHub blog checklist)
47+
const scopeOK = totalChanged <= 300;
48+
// 2) Title and commits follow type: description (verb + object)
49+
const title = prData.title.trim();
50+
const types = ['feat','fix','docs','refactor','test','chore','ci','build','perf','style'];
51+
const naming = `^(${types.join('|')}):\\s+[A-Z][^\\s]*\\s+.+`;
52+
const titleOK = new RegExp(naming).test(title);
53+
const commitsOK = commits.every(c => new RegExp(naming).test(c.commit.message.split('\\n')[0]));
54+
// 3) Description “why now?” + links to issue
55+
const body = (prData.body || '').trim();
56+
const hasIssueLink = /#[0-9]+|https?:\/\/github\.com\/.+\/issues\/[0-9]+/i.test(body);
57+
const mentionsWhy = /\bwhy\b|\bbecause\b|\brationale\b|\bcontext\b/i.test(body);
58+
const descOK = body.length >= 50 && (mentionsWhy || hasIssueLink);
59+
// 4) BREAKING change highlighted
60+
const breakingFlagPresent = /\*\*?BREAKING\*\*?|⚠️\s*BREAKING|BREAKING CHANGE/i.test(title) || /\*\*?BREAKING\*\*?|⚠️\s*BREAKING|BREAKING CHANGE/i.test(body);
61+
// Heuristic: if "breaking" appears anywhere, require emphasis flag; otherwise pass.
62+
const containsBreakingWord = /\bbreaking\b/i.test(title) || /\bbreaking\b/i.test(body);
63+
const breakingOK = containsBreakingWord ? breakingFlagPresent : true;
64+
// 5) Request specific feedback
65+
const feedbackOK = /\b(feedback|review focus|please focus|looking for|need input)\b/i.test(body);
66+
// Soft hint: if code changed but no tests changed, nudge (not blocking per article)
67+
const testsHint = codeTouched && !testsTouched;
68+
// Build result table
69+
function row(name, ok, hint='') {
70+
const status = ok ? '✅' : '❌';
71+
const extra = hint ? ` — ${hint}` : '';
72+
return `| ${status} | ${name}${extra} |`;
73+
}
74+
const report = [
75+
`### PR Quality Gate — AI-Era Expertise Standard`,
76+
`This automated review checks your PR against the five items GitHub recommends for high-quality, human-in-the-loop reviews.`,
77+
``,
78+
`| Pass | Check |`,
79+
`|:----:|:------|`,
80+
row(`Scope ≤ 300 changed lines (current: ${totalChanged})`, scopeOK, scopeOK ? '' : 'Consider splitting into smaller PRs (stacking).'),
81+
row(`Title and commits use type: description (verb + object)`, titleOK && commitsOK),
82+
row(`Description answers "why now?" and links an issue`, descOK, hasIssueLink ? '' : 'Add a linked issue (#123) or URL.'),
83+
row(`Highlight breaking changes with **BREAKING** or ⚠️ BREAKING`, breakingOK, containsBreakingWord && !breakingFlagPresent ? 'Add explicit BREAKING flag.' : ''),
84+
row(`Request specific feedback (e.g., "Concurrency strategy OK?")`, feedbackOK),
85+
``,
86+
testsHint ? `> ℹ️ Heads-up: Code changed but tests weren’t touched. The blog suggests reviewers read tests first—consider adding or updating tests for clarity.` : ``,
87+
``,
88+
`_This gate is derived from GitHub’s “Why developer expertise matters more than ever in the age of AI.”_`
89+
].filter(Boolean).join('\n');
90+
// Determine blocking result (fail if any required check fails)
91+
const failures = [];
92+
if (!scopeOK) failures.push('Scope > 300 lines');
93+
if (!titleOK || !commitsOK) failures.push('Naming format invalid');
94+
if (!descOK) failures.push('Description lacks why/issue link');
95+
if (!breakingOK) failures.push('Missing explicit BREAKING flag');
96+
if (!feedbackOK) failures.push('No specific feedback requested');
97+
const sameRepo = pr.head.repo.full_name === `${owner}/${repo}`;
98+
if (sameRepo) {
99+
try {
100+
// Upsert a single sticky comment
101+
const bot = (await github.rest.users.getAuthenticated()).data.login;
102+
const { data: comments } = await github.rest.issues.listComments({ owner, repo, issue_number: pr.number });
103+
const existing = comments.find(c => c.user?.login === bot && /PR Quality Gate — AI-Era/.test(c.body || ''));
104+
if (existing) {
105+
await github.rest.issues.updateComment({ owner, repo, comment_id: existing.id, body: report });
106+
} else {
107+
await github.rest.issues.createComment({ owner, repo, issue_number: pr.number, body: report });
108+
}
109+
// Add labels for visibility
110+
const addLabel = async (name) => {
111+
await github.rest.issues.addLabels({ owner, repo, issue_number: pr.number, labels: [name] });
112+
};
113+
const removeLabel = async (name) => {
114+
await github.rest.issues.removeLabel({ owner, repo, issue_number: pr.number, name });
115+
};
116+
if (failures.length) {
117+
await addLabel('needs-quality-fixes');
118+
} else {
119+
await removeLabel('needs-quality-fixes');
120+
await addLabel('quality-checked');
121+
}
122+
} catch (error) {
123+
if (error.message && error.message.includes('Resource not accessible by integration')) {
124+
core.warning('Skipping comment and label updates due to insufficient permissions.');
125+
} else {
126+
throw error;
127+
}
128+
}
129+
} else {
130+
core.warning('PR originates from a fork; skipping comment and label updates.');
131+
}
132+
// Fail the job if there are blocking issues
133+
if (failures.length) {
134+
core.setFailed('PR failed the expertise standard: ' + failures.join(', '));
135+
} else {
136+
core.info('PR passes the expertise standard.');
137+
}
138+

.github/workflows/stale.yml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
name: Close Stale Issues and PRs
2+
3+
on:
4+
schedule:
5+
- cron: '0 0 * * *'
6+
workflow_dispatch:
7+
8+
permissions:
9+
issues: write
10+
pull-requests: write
11+
12+
jobs:
13+
stale:
14+
runs-on: ubuntu-latest
15+
steps:
16+
- uses: actions/stale@v9
17+
with:
18+
repo-token: ${{ secrets.GITHUB_TOKEN }}
19+
days-before-stale: 30
20+
days-before-close: 7
21+
exempt-issue-labels: pinned,security
22+
exempt-pr-labels: pinned,security
23+
stale-issue-label: stale
24+
stale-pr-label: stale
25+
stale-issue-message: >-
26+
This issue has been automatically marked as stale because it has not had recent activity.
27+
It will be closed if no further activity occurs.
28+
stale-pr-message: >-
29+
This pull request has been automatically marked as stale because it has not had recent activity.
30+
It will be closed if no further activity occurs.
31+
close-issue-message: >-
32+
Closing this issue due to prolonged inactivity.
33+
close-pr-message: >-
34+
Closing this pull request due to prolonged inactivity.

0 commit comments

Comments
 (0)