Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
159 changes: 159 additions & 0 deletions .github/workflows/ai-pr-summary.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
name: PR AI Summary

on:
pull_request:
types: [opened, synchronize, reopened]

permissions:
contents: read
pull-requests: write

jobs:
summarize:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Get PR diff
id: diff
run: |
BASE="${{ github.event.pull_request.base.sha }}"
HEAD="${{ github.event.pull_request.head.sha }}"
# Trae exactamente esos commits (evita problemas de merge-base y shallow clones)
git fetch --no-tags --prune --depth=1 origin $BASE $HEAD
git diff $BASE $HEAD > pr.diff
echo "path=pr.diff" >> $GITHUB_OUTPUT

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'

- name: Install deps
run: |
python -m pip install --upgrade pip
pip install openai==1.* # SDK oficial

- name: Generate AI summary (OpenAI)
id: ai
continue-on-error: true
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
MODEL: gpt-4o-mini
run: |
python - << 'PY'
import os
from openai import OpenAI
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))

with open("pr.diff","r",encoding="utf-8") as f:
diff = f.read()[:200000] # tope por costos/ruido

prompt = (
"You are a code reviewer. Summarize this PR in 2-20 bullets. "
"Include WHAT changed, WHY it matters, RISKS, TESTS to add, and any BREAKING CHANGES. "
"Highlight key features or changes. Consider markdown as the default output format."
"Keep in mind the following points:"
"1) If DIFF shows only documentation files (e.g., .md/.mdx/.txt/README), state 'Docs-only change', "
" make clear that the change is included only in documentation files, if that is the case, "
" otherwise explain normally, considering the DIFF changes like normal. "
"2) Include a short list of changed file paths as extracted from DIFF. "
"Keep it concise and actionable.\n\nDIFF:\n" + diff
)

resp = client.chat.completions.create(
model=os.getenv("MODEL","gpt-4o-mini"),
temperature=0.2,
messages=[{"role":"user","content":prompt}],
)
text = resp.choices[0].message.content.strip()
with open("summary.txt","w",encoding="utf-8") as f:
f.write(text)
PY

- name: Heuristic fallback if AI failed
if: ${{ steps.ai.outcome == 'failure' }}
run: |
python - << 'PY'
import re, pathlib
diff = pathlib.Path("pr.diff").read_text(encoding="utf-8")

added = len(re.findall(r"^\\+[^+].*$", diff, flags=re.M))
removed = len(re.findall(r"^\\-[^-].*$", diff, flags=re.M))
files = re.findall(r"^\\+\\+\\+ b/(.+)$", diff, flags=re.M)

lower_paths = [f.lower() for f in files]
DOC_EXT = (".md", ".mdx", ".txt", ".rst", ".adoc")
is_doc = lambda p: p.endswith(DOC_EXT) or "/docs/" in p or "/doc/" in p
docs_only = len(files) > 0 and all(is_doc(p) for p in lower_paths)

# ---------- Doc-only summary ----------
if docs_only:
bullets_changed = []
for f in files[:20]: # evita listas enormes
bullets_changed.append(f"- `{f}`")
doc_summary = [
"## PR Summary",
"",
"### WHAT Changed",
"- **Docs-only change** detected from DIFF.",
f"- Files changed ({len(files)}):",
*bullets_changed,
"",
"### WHY It Matters",
"- Improves documentation/README clarity and onboarding experience.",
"",
"### RISKS",
"- None to runtime behavior (documentation only).",
"",
"### TESTS to Add",
"- N/A (no code changes).",
"",
"### BREAKING CHANGES",
"- None.",
]
pathlib.Path("summary.txt").write_text("\n".join(doc_summary), encoding="utf-8")
raise SystemExit(0)

scopes = set()
for f in files:
fl = f.lower()
if "/controller" in fl: scopes.add("controller")
elif "/service" in fl: scopes.add("service")
elif "/repository" in fl or "jparepository" in diff.lower(): scopes.add("repository")
elif "/entity" in fl or "/model" in fl: scopes.add("entity")
elif "application" in fl and (fl.endswith(".yml") or fl.endswith(".yaml") or fl.endswith(".properties")):
scopes.add("config")
elif fl.endswith("test.java"): scopes.add("test")

scope = ",".join(sorted(scopes)) if scopes else "core"
kind = "refactor"
if added and not removed: kind = "feat"
if removed and not added: kind = "chore"
if re.search(r"@Test", diff): kind = "test"
if re.search(r"fix|bug|exception|stacktrace", diff, re.I): kind = "fix"

subject = f"[Fallback] {kind}({scope}): {len(files)} file(s), +{added}/-{removed}"

bullets = []
bullets.append(f"- Files changed: {len(files)}")
bullets.append(f"- Lines: +{added} / -{removed}")
if scopes:
bullets.append(f"- Layers: {', '.join(sorted(scopes))}")
if re.search(r"@Transactional", diff): bullets.append("- Touches transactional boundaries")
if re.search(r"@RestController|@Controller", diff): bullets.append("- Controller changes present")
if re.search(r"@Service", diff): bullets.append("- Service-layer changes present")
if re.search(r"@Repository|JpaRepository", diff): bullets.append("- Repository-layer changes present")
if re.search(r"todo|fixme", diff, re.I): bullets.append("- Contains TODO/FIXME markers")

text = subject + "\\n\\n" + "\\n".join(bullets)
pathlib.Path("summary.txt").write_text(text, encoding="utf-8")
PY

- name: Comment on PR
uses: marocchino/sticky-pull-request-comment@v2
with:
header: ai-pr-summary
recreate: true
path: summary.txt
18 changes: 3 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# InventoryManagerBC
Breakable Toy 1 - Inventory Manager
Breakable Toy 1 (Gen AI Augmented) - Inventory Manager

# Inventory Management Application

Expand Down Expand Up @@ -57,18 +57,14 @@ This is a Spring Boot-based inventory management application designed to help ma
| GET | `/products/categories` | Retrieves all the categories available |


### Storage

Currently, product data is stored in a local database using docker.

---

## Tech Stack

- **Language:** Java
- **Framework:** Spring Boot
- **Build Tool:** Maven
- **Data Storage:** Oracle DB
- **Data Storage:** H2 local Runtime via JDBC

---

Expand All @@ -82,13 +78,5 @@ Currently, product data is stored in a local database using docker.
### Running the Application

```bash
docker run -d \
--name oracle-xe \
-e ORACLE_PASSWORD=admin \
-p 1521:1521 \
-p 5500:5500 \
oracle-xe-inventory-manager:1.0
```
```bash
mvn spring-boot:run
mvn spring-boot:run
```
19 changes: 11 additions & 8 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.5.0</version>
<version>3.5.6</version>
<relativePath/>
</parent>
<groupId>com.encorazone</groupId>
Expand All @@ -27,12 +27,14 @@
<url/>
</scm>
<properties>
<logback.version>1.5.19</logback.version>
<java.version>17</java.version>
<ojdbc.version>21.3.0.0</ojdbc.version>
<springdoc.version>2.3.0</springdoc.version>
<springdoc.version>2.8.13</springdoc.version>
<lombok.version>1.18.38</lombok.version>
<mapstruct.version>1.5.5.Final</mapstruct.version>
<maven-compiler.version>3.14.0</maven-compiler.version>
<commons-lang3.version>3.18.0</commons-lang3.version>
</properties>
<dependencies>
<dependency>
Expand Down Expand Up @@ -86,11 +88,6 @@
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
Expand Down Expand Up @@ -127,11 +124,17 @@
</annotationProcessorPaths>
</configuration>
</plugin>

<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>-XX:+EnableDynamicAgentLoading -Xshare:off</argLine>
</configuration>
</plugin>
</plugins>
</build>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import java.util.List;
import java.util.UUID;

@CrossOrigin(origins = "http://localhost:3000")
@CrossOrigin(origins = "http://localhost:8080")
@RestController
@RequestMapping("/products")
final class InventoryManagerController {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ public interface ProductRepository extends JpaRepository<Product, UUID>, JpaSpec
@Query("SELECT DISTINCT p.category FROM Product p")
Optional<List<String>> findDistinctCategories();

@Query("SELECT p.category AS category, COUNT(p) AS productsInStock, " +
"SUM(p.unitPrice) AS valueInStock, AVG(p.unitPrice) AS averageValue " +
@Query("SELECT p.category AS category, SUM(p.stockQuantity) AS productsInStock, " +
"SUM(p.unitPrice * p.stockQuantity) AS valueInStock, AVG(p.unitPrice) AS averageValue " +
"FROM Product p GROUP BY p.category")
List<InventorySummaryInterface> findCategoriesSummary();
}

This file was deleted.

4 changes: 3 additions & 1 deletion src/main/resources/application.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
server:
port: 9090

spring:
application:
name: inventory-manager-spark
Expand All @@ -15,4 +18,3 @@ spring:
jpa:
hibernate:
ddl-auto: update
show-sql: true
1 change: 0 additions & 1 deletion src/main/resources/templates/hello.html

This file was deleted.

Loading