Skip to content

Commit 9c43ca0

Browse files
Initial commit
0 parents  commit 9c43ca0

File tree

11 files changed

+324
-0
lines changed

11 files changed

+324
-0
lines changed

.github/classroom/autograding.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"tests": [
3+
{
4+
"name": "Grading",
5+
"setup": "",
6+
"run": "mvn test",
7+
"input": "",
8+
"output": "",
9+
"comparison": "included",
10+
"timeout": 10,
11+
"points": 1
12+
}
13+
]
14+
}

.github/dependabot.yml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
version: 2
2+
updates:
3+
# Maintain dependencies for GitHub Actions
4+
- package-ecosystem: "github-actions"
5+
directory: "/"
6+
schedule:
7+
interval: "weekly"
8+
groups:
9+
actions-deps:
10+
patterns:
11+
- "*"
12+
13+
# Maintain dependencies for Maven
14+
- package-ecosystem: "maven"
15+
directory: "/"
16+
schedule:
17+
interval: "weekly"
18+
groups:
19+
maven-deps:
20+
patterns:
21+
- "*"

.github/workflows/classroom.yml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
name: GitHub Classroom Workflow
2+
3+
on: [push]
4+
5+
permissions:
6+
checks: write
7+
actions: read
8+
contents: read
9+
10+
jobs:
11+
build:
12+
name: Autograding
13+
runs-on: ubuntu-latest
14+
steps:
15+
- uses: actions/setup-java@v5
16+
with:
17+
distribution: 'temurin'
18+
java-version: '24'
19+
- uses: actions/checkout@v5
20+
- uses: education/autograding@v1

.github/workflows/feedback.yaml

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
# This is a GitHub Action workflow that automates feedback for Java assignments.
2+
# It is designed to be used with GitHub Classroom.
3+
#
4+
# How it works:
5+
# 1. It triggers when a "feedback" pull request is opened or updated.
6+
# 2. It reads the assignment instructions from the README.md file.
7+
# 3. It gathers all the Java source code from the `src/main/java` directory.
8+
# 4. It gathers all the Java test code from the `src/test/java` directory.
9+
# 5. It sends the instructions, source code, and test code to an LLM on OpenRouter.
10+
# 6. The LLM generates feedback based on the provided information.
11+
# 7. The feedback is then posted as a comment on the pull request.
12+
13+
name: AI-Powered Feedback
14+
15+
on:
16+
pull_request:
17+
types: [ opened, synchronize ]
18+
branches:
19+
- feedback
20+
21+
jobs:
22+
provide_feedback:
23+
runs-on: ubuntu-latest
24+
permissions:
25+
pull-requests: write
26+
env:
27+
OPENROUTER_MODEL: ${{ secrets.OPENROUTER_MODEL }}
28+
SYSTEM_PROMPT: ${{ vars.SYSTEM_PROMPT }}
29+
steps:
30+
- name: Checkout repository
31+
uses: actions/checkout@v5
32+
33+
- name: Read assignment instructions
34+
id: instructions
35+
run: |
36+
# Reads the content of the README.md file into an output variable.
37+
# The `EOF` marker is used to handle multi-line file content.
38+
echo "instructions=$(cat README.md | sed 's/\"/\\\"/g' | sed 's/$/\\n/' | tr -d '\n' | sed 's/\\n/\\\\n/g')" >> $GITHUB_OUTPUT
39+
40+
- name: Read source code
41+
id: source_code
42+
run: |
43+
{
44+
echo 'source_code<<EOF'
45+
find src/main/java -type f -name "*.java" | while read -r file; do
46+
echo "=== File: $file ==="
47+
cat "$file"
48+
echo
49+
done
50+
echo 'EOF'
51+
} >> "$GITHUB_OUTPUT"
52+
53+
- name: Read test code
54+
id: test_code
55+
run: |
56+
{
57+
echo 'test_code<<EOF'
58+
if [ -d "src/test/java" ]; then
59+
find src/test/java -type f -name "*.java" | while read -r file; do
60+
echo "=== File: $file ==="
61+
cat "$file"
62+
echo
63+
done
64+
else
65+
echo "No test code found."
66+
fi
67+
echo 'EOF'
68+
} >> "$GITHUB_OUTPUT"
69+
- name: Generate AI Feedback
70+
id: ai_feedback
71+
run: |
72+
# This step sends the collected data to the OpenRouter API.
73+
INSTRUCTIONS=$(jq -Rs . <<'EOF'
74+
${{ steps.instructions.outputs.instructions }}
75+
EOF
76+
)
77+
SOURCE_CODE=$(jq -Rs . <<'EOF'
78+
${{ steps.source_code.outputs.source_code }}
79+
EOF
80+
)
81+
TEST_CODE=$(jq -Rs . <<'EOF'
82+
${{ steps.test_code.outputs.test_code }}
83+
EOF
84+
)
85+
86+
if [ -z "$INSTRUCTIONS" ] || [ -z "$SOURCE_CODE" ] || [ -z "$TEST_CODE" ]; then
87+
echo "Error: One or more required variables are not set."
88+
exit 1
89+
fi
90+
91+
# Assigning to USER_CONTENT with variable expansion
92+
PAYLOAD="Please provide feedback on the following Java assignment.
93+
94+
--- Assignment Instructions ---
95+
${INSTRUCTIONS}
96+
97+
--- Source files ---
98+
${SOURCE_CODE}
99+
100+
--- Test files ---
101+
${TEST_CODE}"
102+
103+
JSON_CONTENT=$(jq -n \
104+
--arg model "$OPENROUTER_MODEL" \
105+
--arg system_prompt "$SYSTEM_PROMPT" \
106+
--arg payload "$PAYLOAD" \
107+
'{
108+
model: $model,
109+
messages: [
110+
{role: "system", content: $system_prompt},
111+
{role: "user", content: $payload}
112+
]
113+
}')
114+
115+
echo "$JSON_CONTENT"
116+
117+
API_RESPONSE=$(echo "$JSON_CONTENT" | curl https://openrouter.ai/api/v1/chat/completions \
118+
-H "Authorization: Bearer ${{ secrets.OPENROUTER_API_KEY }}" \
119+
-H "Content-Type: application/json" \
120+
-d @-)
121+
122+
echo "$API_RESPONSE"
123+
124+
FEEDBACK_CONTENT=$(echo "$API_RESPONSE" | jq -r '.choices[0].message.content')
125+
echo "feedback<<EOF" >> $GITHUB_OUTPUT
126+
echo "$FEEDBACK_CONTENT" >> $GITHUB_OUTPUT
127+
echo "EOF" >> $GITHUB_OUTPUT
128+
129+
- name: Post Feedback as PR Comment ✍️
130+
uses: actions/github-script@v7
131+
env:
132+
FEEDBACK_BODY: ${{ steps.ai_feedback.outputs.feedback }}
133+
with:
134+
github-token: ${{secrets.GITHUB_TOKEN}}
135+
script: |
136+
// A unique signature to identify our comment
137+
const signature = "";
138+
const body = `${process.env.FEEDBACK_BODY}\n\n${signature}`;
139+
const { owner, repo } = context.repo;
140+
const issue_number = context.issue.number;
141+
142+
// Get all comments on the pull request
143+
const { data: comments } = await github.rest.issues.listComments({
144+
owner,
145+
repo,
146+
issue_number,
147+
});
148+
149+
// Find a previous comment by the action using the signature
150+
const previousComment = comments.find(comment => comment.body.includes(signature));
151+
152+
if (previousComment) {
153+
// If a comment exists, update it
154+
await github.rest.issues.updateComment({
155+
owner,
156+
repo,
157+
comment_id: previousComment.id,
158+
body,
159+
});
160+
console.log('Updated existing feedback comment.');
161+
} else {
162+
// If no comment exists, create a new one
163+
await github.rest.issues.createComment({
164+
owner,
165+
repo,
166+
issue_number,
167+
body,
168+
});
169+
console.log('Posted new feedback comment.');
170+
}

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
target/
2+
/.idea/

README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# 🚀 Requirements
2+
3+
Add requirements here
4+
5+
Maven Goals
6+
=========
7+
1. Clean:
8+
* mvn clean
9+
10+
2. Build:
11+
* mvn compile
12+
* mvn test
13+
*

pom.xml

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
7+
<groupId>org.example</groupId>
8+
<artifactId>JavaTemplate</artifactId>
9+
<version>1.0-SNAPSHOT</version>
10+
11+
<properties>
12+
<maven.compiler.release>24</maven.compiler.release>
13+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
14+
<junit.jupiter.version>5.13.4</junit.jupiter.version>
15+
<assertj.core.version>3.27.4</assertj.core.version>
16+
<mockito.version>5.19.0</mockito.version>
17+
</properties>
18+
<dependencies>
19+
<dependency>
20+
<groupId>org.junit.jupiter</groupId>
21+
<artifactId>junit-jupiter</artifactId>
22+
<version>${junit.jupiter.version}</version>
23+
<scope>test</scope>
24+
</dependency>
25+
<dependency>
26+
<groupId>org.assertj</groupId>
27+
<artifactId>assertj-core</artifactId>
28+
<version>${assertj.core.version}</version>
29+
<scope>test</scope>
30+
</dependency>
31+
<dependency>
32+
<groupId>org.mockito</groupId>
33+
<artifactId>mockito-junit-jupiter</artifactId>
34+
<version>${mockito.version}</version>
35+
<scope>test</scope>
36+
</dependency>
37+
</dependencies>
38+
<build>
39+
<plugins>
40+
<plugin>
41+
<groupId>org.jacoco</groupId>
42+
<artifactId>jacoco-maven-plugin</artifactId>
43+
<version>0.8.13</version>
44+
<configuration>
45+
<skip>true</skip>
46+
</configuration>
47+
<executions>
48+
<execution>
49+
<id>default-prepare-agent</id>
50+
<goals>
51+
<goal>prepare-agent</goal>
52+
</goals>
53+
</execution>
54+
<execution>
55+
<id>default-report</id>
56+
<phase>prepare-package</phase>
57+
<goals>
58+
<goal>report</goal>
59+
</goals>
60+
</execution>
61+
</executions>
62+
</plugin>
63+
</plugins>
64+
</build>
65+
</project>
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.example;
2+
3+
public class Main {
4+
public static void main(String[] args) {
5+
6+
}
7+
}

src/main/resources/.gitkeep

Whitespace-only changes.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package org.example;
2+
3+
import org.junit.jupiter.api.Test;
4+
5+
import static org.assertj.core.api.Assertions.assertThat;
6+
7+
class MainTest {
8+
@Test
9+
void test() {
10+
assertThat(true).isTrue();
11+
}
12+
}

0 commit comments

Comments
 (0)