Skip to content
Open
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
7 changes: 6 additions & 1 deletion .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,12 @@ By opening a pull request you confirm that, unless explicitly stated otherwise,
- are all your own work, and you have the right to contribute them.
- are contributed solely under the terms and conditions of the Apache License 2.0 (see section 5 of the license for more information).

Please make sure (eg. `git log`) that all commits have a valid name and email address for you in the Author field.
### LLMs, Commit messages and PR description:

- Please make sure (eg. `git log`) that all commits have a valid name and email address for you in the Author field.
- LLM assisted commits should be attributed with an `Assisted-by: MODEL_NAME MODEL_VERSION` line appended to the commit message.
- Please mention coding assistance in the PR description too (eg. by adding the same `Assisted-by` line from above)
- Please describe the changes in your own words - we'd like to know you understand the changes being made!

If you're a first time contributor, see the Contributing guidelines for more information.

Expand Down
53 changes: 42 additions & 11 deletions .github/scripts/CommitHeaderChecker.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,10 @@

import static java.util.stream.Gatherers.fold;
import static java.util.stream.Gatherers.scan;
import static java.util.stream.Gatherers.windowSliding;

record Commit(int index, String from, String date, String subject, String blank) {}
static final Pattern robots = Pattern.compile("(gpt|claude|gemini|llama|mistral|command|grok|qwen|deepseek)");

record Commit(int index, String from, String date, String subject, List<String> msg) {}
record Result(int total, boolean green) {}

// checks commit headers for valid author, email and commit msg formatting
Expand Down Expand Up @@ -66,14 +67,32 @@ void main(String[] args) throws IOException, InterruptedException {
.followRedirects(Redirect.NORMAL).build()) {

result = client.send(request, BodyHandlers.ofLines()).body()
// 5 line window, From/Date/Subject and extra line for blank line / overflow check
// "From" can be two lines if the name is very long
.gather(windowSliding(5))
// gather commit header + message as List of lines
.gather(
Gatherer.<String, List<String>, List<String>>ofSequential(
ArrayList::new,
(window, line, downstream) -> {
if (line.startsWith("From: ")) { // window start
window.add(line);
} else if (!window.isEmpty()) {
if (line.equals("---")) { // window end (separator after message)
downstream.push(List.copyOf(window));
window.clear();
} else {
window.add(line);
}
}
return true;
}
)
)
.filter(w -> isCommitHeader(w))
// map to indexed commits
.gather(scan(
() -> new Commit(-1, "", "", "", ""),
() -> new Commit(-1, "", "", "", List.of()),
(c, w) -> createCommit(c.index+1, w)))
.peek(System.out::println)
// check commits and store as result
.gather(fold(
() -> new Result(0, true),
(r, c) -> new Result(r.total+1, r.green & checkCommit(c))))
Expand All @@ -90,7 +109,7 @@ void main(String[] args) throws IOException, InterruptedException {
// Subject: [PATCH] Mail Validator
private static boolean isCommitHeader(List<String> lines) {
int i = 0;
return lines.size() == 5
return lines.size() >= 4
&& lines.get(i++).startsWith("From: ") // "From" can be two lines in some cases
&&(lines.get(i++).startsWith("Date: ") || lines.get(i++).startsWith("Date: "))
&& lines.get(i++).startsWith("Subject: ");
Expand All @@ -99,14 +118,15 @@ private static boolean isCommitHeader(List<String> lines) {
private static Commit createCommit(int index, List<String> lines) {
int i = 0;
return lines.get(1).startsWith("Date: ") // "From" can be two lines in some cases
? new Commit(index, lines.get(i++), lines.get(i++), lines.get(i++), lines.get(i++))
: new Commit(index, lines.get(i++) + lines.get(i++), lines.get(i++), lines.get(i++), lines.get(i++));
? new Commit(index, lines.get(i++), lines.get(i++), lines.get(i++), lines.subList(i, lines.size()))
: new Commit(index, lines.get(i++) + lines.get(i++), lines.get(i++), lines.get(i++), lines.subList(i, lines.size()));
}

boolean checkCommit(Commit c) {
return checkNameAndEmail(c.index, c.from)
& checkSubject(c.index, c.subject)
& checkBlankLineAfterSubject(c.index, c.blank);
& checkBlankLineAfterSubject(c.index, c.msg)
& checkHumanCoAuthors(c.index, c.msg);
}

boolean checkNameAndEmail(int i, String from) {
Expand Down Expand Up @@ -152,7 +172,7 @@ boolean checkSubject(int i, String subject) {
}

// there should be a blank line after the subject line, some subjects can overflow though.
boolean checkBlankLineAfterSubject(int i, String blank) {
boolean checkBlankLineAfterSubject(int i, List<String> msg) {
// disabled since this would produce too many warnings due to overflowing subject lines
// if (!blank.isBlank()) {
// log("::warning::blank line after subject recommended in commit " + i + " (is subject over 50 char limit?)");
Expand All @@ -161,6 +181,17 @@ boolean checkBlankLineAfterSubject(int i, String blank) {
return true;
}

boolean checkHumanCoAuthors(int i, List<String> msg) {
for (String line : msg) {
String lower = line.toLowerCase(Locale.ROOT);
if ((lower.startsWith("co-authored-by:") || lower.startsWith("generated-by:")) && robots.matcher(lower).find()) {
log("::error::please use 'Assisted-by: MODEL_NAME MODEL_VERSION' in commit " + i);
return false;
}
}
return true;
}

void log(String msg) {
System.out.println(msg);
}