Skip to content

Commit 54b5f89

Browse files
author
Avwerosuo Oghenesode
committed
2 parents b06e96f + d518311 commit 54b5f89

5 files changed

Lines changed: 330 additions & 4 deletions

File tree

.github/PULL_REQUEST_TEMPLATE.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
## Description
2+
3+
<!-- Please describe your change clearly. If it fixes a bug, link the issue. -->
4+
5+
Fixes # (issue)
6+
7+
## Type of change
8+
9+
- [ ] Bug fix
10+
- [ ] New feature
11+
- [ ] Documentation update
12+
- [ ] Dependency upgrade
13+
14+
## Checklist
15+
16+
- [ ] I have read the [CONTRIBUTING](../CONTRIBUTING.md) guide
17+
- [ ] **This is NOT coursework, a student assignment, or homework** — I understand that forks of this repo are widely used in courses and that student PRs will be closed without review
18+
- [ ] My change targets the `main` branch of **spring-petclinic/spring-petclinic-microservices**, not my own fork
19+
- [ ] I have tested this change locally
20+
- [ ] Existing tests pass (`./mvnw test`)
21+
22+
> ⚠️ **Are you a student?**
23+
> If you forked this repo for a course or bootcamp, your changes belong in **your own fork**, not here.
24+
> PRs that are part of a course assignment will be labelled `invalid` and closed immediately.
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
name: Check PR template completeness
2+
3+
on:
4+
pull_request_target:
5+
types: [opened, reopened, edited]
6+
schedule:
7+
- cron: '0 8 * * *' # every day at 08:00 UTC
8+
9+
permissions:
10+
pull-requests: write
11+
issues: write
12+
13+
jobs:
14+
# ── 1. On every PR open/edit: check that the template was filled in ──────────
15+
check-template:
16+
if: ${{ github.event_name == 'pull_request_target' }}
17+
runs-on: ubuntu-latest
18+
steps:
19+
- name: Ensure needs-information label exists
20+
uses: actions/github-script@v7
21+
with:
22+
script: |
23+
try {
24+
await github.rest.issues.createLabel({
25+
owner: context.repo.owner,
26+
repo: context.repo.repo,
27+
name: 'needs-information',
28+
color: 'e4e669',
29+
description: 'More information is needed before this can be reviewed',
30+
});
31+
} catch (e) {
32+
if (e.status !== 422) throw e; // 422 = already exists, ignore
33+
}
34+
35+
- name: Detect incomplete template
36+
uses: actions/github-script@v7
37+
with:
38+
script: |
39+
const { data: pr } = await github.rest.pulls.get({
40+
owner: context.repo.owner,
41+
repo: context.repo.repo,
42+
pull_number: context.payload.pull_request.number,
43+
});
44+
45+
const body = pr.body || '';
46+
47+
function isIncomplete(body) {
48+
if (body.trim().length < 30) return true;
49+
// Template HTML comment still present → body was never edited
50+
if (body.includes('<!-- Please describe your change')) return true;
51+
// The placeholder issue reference was not replaced
52+
if (/Fixes\s*#\s*\(issue\)/.test(body)) return true;
53+
// All checklist items still unchecked
54+
const unchecked = (body.match(/- \[ \]/g) || []).length;
55+
const checked = (body.match(/- \[x\]/gi) || []).length;
56+
if (unchecked >= 3 && checked === 0) return true;
57+
return false;
58+
}
59+
60+
const labels = pr.labels.map(l => l.name);
61+
const alreadyFlagged = labels.includes('needs-information');
62+
63+
if (isIncomplete(body)) {
64+
if (!alreadyFlagged) {
65+
await github.rest.issues.addLabels({
66+
owner: context.repo.owner,
67+
repo: context.repo.repo,
68+
issue_number: pr.number,
69+
labels: ['needs-information'],
70+
});
71+
72+
await github.rest.issues.createComment({
73+
owner: context.repo.owner,
74+
repo: context.repo.repo,
75+
issue_number: pr.number,
76+
body: [
77+
`## 📋 Please complete the PR template, @${pr.user.login}`,
78+
'',
79+
'It looks like the Pull Request description is **empty or hasn\'t been filled in** yet.',
80+
'',
81+
'To help maintainers review your contribution, please:',
82+
'',
83+
'1. Edit this PR and fill in the description template:',
84+
' - Describe **what** your change does and **why**',
85+
' - Link the related issue (e.g. `Fixes #123`)',
86+
' - Tick the checklist items that apply',
87+
'',
88+
'2. Once the template is complete, the `needs-information` label will be removed automatically.',
89+
'',
90+
'> **Note:** If this PR is not updated within **7 days**, it will be closed automatically. You are welcome to re-open it once the description is complete.',
91+
'',
92+
'_This is an automated message._',
93+
].join('\n'),
94+
});
95+
console.log(`PR #${pr.number} flagged as needs-information.`);
96+
} else {
97+
console.log(`PR #${pr.number} still incomplete, already flagged.`);
98+
}
99+
} else {
100+
// Template is now complete — remove the label if it was set
101+
if (alreadyFlagged) {
102+
await github.rest.issues.removeLabel({
103+
owner: context.repo.owner,
104+
repo: context.repo.repo,
105+
issue_number: pr.number,
106+
name: 'needs-information',
107+
});
108+
await github.rest.issues.createComment({
109+
owner: context.repo.owner,
110+
repo: context.repo.repo,
111+
issue_number: pr.number,
112+
body: `Thanks for completing the PR template, @${pr.user.login}! The \`needs-information\` label has been removed. A maintainer will review your PR shortly. 🙏`,
113+
});
114+
console.log(`PR #${pr.number} template now complete, label removed.`);
115+
} else {
116+
console.log(`PR #${pr.number} template looks complete.`);
117+
}
118+
}
119+
120+
# ── 2. Daily: close PRs still labelled needs-information after 7 days ─────────
121+
close-stale-incomplete:
122+
if: ${{ github.event_name == 'schedule' }}
123+
runs-on: ubuntu-latest
124+
steps:
125+
- name: Close PRs with incomplete template after 7 days of inactivity
126+
uses: actions/github-script@v7
127+
with:
128+
script: |
129+
const sevenDaysAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000);
130+
131+
// Fetch all open PRs labelled needs-information (paginate if needed)
132+
const prs = await github.paginate(github.rest.pulls.list, {
133+
owner: context.repo.owner,
134+
repo: context.repo.repo,
135+
state: 'open',
136+
per_page: 100,
137+
});
138+
139+
for (const pr of prs) {
140+
const hasLabel = pr.labels.some(l => l.name === 'needs-information');
141+
if (!hasLabel) continue;
142+
143+
const lastUpdate = new Date(pr.updated_at);
144+
if (lastUpdate >= sevenDaysAgo) {
145+
console.log(`PR #${pr.number} flagged but still recent (${pr.updated_at}), skipping.`);
146+
continue;
147+
}
148+
149+
console.log(`Closing PR #${pr.number} — no activity for 7+ days with incomplete template.`);
150+
151+
await github.rest.issues.createComment({
152+
owner: context.repo.owner,
153+
repo: context.repo.repo,
154+
issue_number: pr.number,
155+
body: [
156+
'## PR closed — template not completed',
157+
'',
158+
`Hi @${pr.user.login},`,
159+
'',
160+
'This Pull Request has been **automatically closed** because the description template was not completed within 7 days.',
161+
'',
162+
'You are welcome to **re-open it** once the template is fully filled in. A complete description helps maintainers understand the context and intent of your change.',
163+
'',
164+
`See our [CONTRIBUTING guide](https://github.com/${context.repo.owner}/${context.repo.repo}/blob/${context.payload.repository.default_branch}/CONTRIBUTING.md) for details.`,
165+
'',
166+
'_This is an automated message._',
167+
].join('\n'),
168+
});
169+
170+
await github.rest.pulls.update({
171+
owner: context.repo.owner,
172+
repo: context.repo.repo,
173+
pull_number: pr.number,
174+
state: 'closed',
175+
});
176+
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
name: Close invalid PRs from student assignments
2+
3+
on:
4+
pull_request_target:
5+
types: [opened, reopened, edited]
6+
7+
permissions:
8+
issues: write
9+
pull-requests: write
10+
11+
jobs:
12+
detect-student-pr:
13+
runs-on: ubuntu-latest
14+
steps:
15+
- name: Check for student/course assignment patterns
16+
uses: actions/github-script@v7
17+
with:
18+
script: |
19+
const { data: pr } = await github.rest.pulls.get({
20+
owner: context.repo.owner,
21+
repo: context.repo.repo,
22+
pull_number: context.payload.pull_request.number,
23+
});
24+
25+
const title = pr.title || '';
26+
const body = pr.body || '';
27+
const branch = pr.head.ref || '';
28+
const combined = `${title} ${body} ${branch}`.toLowerCase();
29+
30+
// Patterns commonly seen in student course submissions
31+
const studentPatterns = [
32+
/\bspc-\d+/i, // SPC-001, SPC-003-T1 …
33+
/add\s+(jenkins|gitlab|github\s+actions)\s+(ci|pipeline)/i,
34+
/add\s+ci\s+(workflow|pipeline)/i,
35+
/push\s+(image|docker)\s+to\s+(ecr|dockerhub|registry)/i,
36+
/add\s+(kubernetes|k8s)\s+manifests?/i,
37+
/add\s+monitoring\s+(stack|dashboard)/i,
38+
/\bdevops\s+(project|assignment|lab|homework|tp)\b/i,
39+
/\b(lab|tp|homework|assignment|exercise)\s*[#\d]/i,
40+
/\bbootcamp\b/i,
41+
];
42+
43+
const matched = studentPatterns.find((pattern) => pattern.test(combined));
44+
45+
if (matched) {
46+
console.log(`Pattern matched: ${matched}`);
47+
48+
await github.rest.issues.addLabels({
49+
owner: context.repo.owner,
50+
repo: context.repo.repo,
51+
issue_number: pr.number,
52+
labels: ['invalid'],
53+
});
54+
55+
await github.rest.issues.createComment({
56+
owner: context.repo.owner,
57+
repo: context.repo.repo,
58+
issue_number: pr.number,
59+
body: [
60+
`## 👋 Hi @${pr.user.login},`,
61+
'',
62+
'It looks like this Pull Request may be part of a **course assignment or bootcamp exercise**.',
63+
'',
64+
'This repository is widely used as a training project, but PRs that are part of coursework are **out of scope** for the upstream project and will be closed.',
65+
'',
66+
'Your changes belong in **your own fork** — that is where your instructor will review them.',
67+
'',
68+
`Please read our [CONTRIBUTING guide](https://github.com/${context.repo.owner}/${context.repo.repo}/blob/${context.payload.repository.default_branch}/CONTRIBUTING.md) for details on what kinds of contributions we accept.`,
69+
'',
70+
'_This is an automated message. If you believe this was a mistake, please re-open and leave a comment explaining why your PR is not course-related._',
71+
].join('\n'),
72+
});
73+
74+
await github.rest.pulls.update({
75+
owner: context.repo.owner,
76+
repo: context.repo.repo,
77+
pull_number: pr.number,
78+
state: 'closed',
79+
});
80+
81+
console.log(`PR #${pr.number} closed as student assignment.`);
82+
} else {
83+
console.log('No student patterns detected — PR looks legitimate.');
84+
}

CONTRIBUTING.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# Contributing to Spring PetClinic Microservices
2+
3+
Thank you for your interest in contributing!
4+
5+
## ⚠️ Are you a student or course participant?
6+
7+
This repository is widely used as a **training exercise** in DevOps, cloud, and Java courses.
8+
If you forked this repo as part of a course, bootcamp, or homework assignment:
9+
10+
- **Your changes belong in your own fork**, not here.
11+
- Pull Requests that are part of a course assignment (CI pipelines, Kubernetes manifests, monitoring additions, etc.) will be labelled `invalid` and **closed without review**.
12+
- This is not personal. It is simply not the scope of this project.
13+
14+
## What we accept
15+
16+
- Bug fixes with a linked issue
17+
- Dependency upgrades (Spring Boot, Spring Cloud ...)
18+
- Documentation improvements
19+
- Features discussed and agreed upon in an issue first
20+
21+
## What we do NOT accept
22+
23+
- Course assignments or homework submissions
24+
- PRs adding Jenkins/GitLab CI, ECR pipelines, or cloud-specific infrastructure for educational purposes
25+
- PRs that duplicate already-open contributions
26+
- Changes without tests
27+
28+
## How to contribute
29+
30+
1. **Open an issue first** to discuss the change you want to make.
31+
2. Fork the repository and create a branch: `git checkout -b bugfix/my-bug-fix`
32+
3. Make your changes and add tests if applicable.
33+
4. Run the test suite: `./mvnw test`
34+
5. Open a Pull Request using the provided template and fill it out completely.
35+
36+
## Reporting a bug
37+
38+
Open an issue with:
39+
- A clear description of the bug
40+
- Steps to reproduce
41+
- Expected vs actual behaviour
42+
- Spring Boot / Java version

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ are usually not enough and make the `docker-compose up` painfully slow.*
6666
## Starting services locally with docker-compose and Java
6767
If you experience issues with running the system via docker-compose you can try running the `./scripts/run_all.sh` script that will start the infrastructure services via docker-compose and all the Java based applications via standard `nohup java -jar ...` command. The logs will be available under `${ROOT}/target/nameoftheapp.log`.
6868

69-
Each of the java based applications is started with the `chaos-monkey` profile in order to interact with Spring Boot Chaos Monkey. You can check out the (README)[scripts/chaos/README.md] for more information about how to use the `./scripts/chaos/call_chaos.sh` helper script to enable assaults.
69+
Each of the java based applications is started with the `chaos-monkey` profile in order to interact with Spring Boot Chaos Monkey. You can check out the [README](scripts/chaos/README.md) for more information about how to use the `./scripts/chaos/call_chaos.sh` helper script to enable assaults.
7070

7171
## Understanding the Spring Petclinic application
7272

@@ -110,12 +110,12 @@ Spring Petclinic integrates a Chatbot that allows you to interact with the appli
110110

111111
![Screenshot of the chat dialog](docs/spring-ai.png)
112112

113-
This `spring-petlinic-genai-service` microservice currently supports **OpenAI** (default) or **Azure's OpenAI** as the LLM provider.
113+
This `spring-petclinic-genai-service` microservice currently supports **OpenAI** (default) or **Azure's OpenAI** as the LLM provider.
114114
In order to start the microservice, perform the following steps:
115115

116116
1. Decide which provider you want to use. By default, the `spring-ai-starter-model-openai` dependency is enabled.
117117
You can change it to `spring-ai-starter-model-azure-openai`in the `pom.xml`.
118-
2. Create an OpenAI API key or a Azure OpenAI resource in your Azure Portal.
118+
2. Create an OpenAI API key or an Azure OpenAI resource in your Azure Portal.
119119
Refer to the [OpenAI's quickstart](https://platform.openai.com/docs/quickstart) or [Azure's documentation](https://learn.microsoft.com/en-us/azure/ai-services/openai/) for further information on how to obtain these.
120120
You only need to populate the provider you're using - either openai, or azure-openai.
121121
If you don't have your own OpenAI API key, don't worry!
@@ -161,7 +161,7 @@ By default, at startup, database schema will be created and data will be populat
161161
You may also manually create the PetClinic database and data by executing the `"db/mysql/{schema,data}.sql"` scripts of each 3 microservices.
162162
In the `application.yml` of the [Configuration repository], set the `initialization-mode` to `never`.
163163

164-
If you are running the microservices with Docker, you have to add the `mysql` profile into the (Dockerfile)[docker/Dockerfile]:
164+
If you are running the microservices with Docker, you have to add the `mysql` profile into the [Dockerfile](docker/Dockerfile):
165165
```
166166
ENV SPRING_PROFILES_ACTIVE docker,mysql
167167
```

0 commit comments

Comments
 (0)