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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ See <https://blog.jabref.org/2024/04/03/JabRef5-13/#special-thanks> for real-wor
E.g.,
- Linux/macOS: `curl -Ls https://sh.jbang.dev | bash -s - app setup` or
- Windows (Powershell): `iex "& { $(iwr -useb https://ps.jbang.dev) } app setup"`
2. Add `oauth=...` to `~/.github` with `...` being your [GitHub personal access token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-personal-access-token-classic). See [GitHub API for Java](https://github-api.kohsuke.org/) for details.
2. Add `oauth=...` to `~/.github` with `...` being your [GitHub personal access token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-personal-access-token-classic). See [GitHub API for Java](https://hub4j.github.io/github-api/) for details.
3. `cd` to the repository you want to analyze.
4. `jbang gcl@koppor/github-contributors-list`

Expand Down
115 changes: 77 additions & 38 deletions gcl.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
///usr/bin/env jbang "$0" "$@" ; exit $?

//JAVA 21+
//JAVA 25+

//DEPS com.h2database:h2-mvstore:2.4.240
//DEPS org.eclipse.jgit:org.eclipse.jgit:7.4.0.202509020913-r
//DEPS org.kohsuke:github-api:1.330
//DEPS org.kohsuke:github-api:2.0-rc.5
//DEPS info.picocli:picocli:4.7.7
//DEPS one.util:streamex:0.8.4
//DEPS me.tongfei:progressbar:0.10.1
Expand All @@ -29,6 +29,7 @@
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
Expand Down Expand Up @@ -68,7 +69,7 @@
import picocli.CommandLine.Parameters;

@Command(name = "gcl",
version = "gcl 2024-04-26",
version = "gcl 2025-10-30",
mixinStandardHelpOptions = true,
sortSynopsis = false)
public class gcl implements Callable<Integer> {
Expand Down Expand Up @@ -139,9 +140,38 @@ public String getUserId() {
}

private record CoAuthor(String name, String email) {
/// Expected formats:
/// "Co-authored-by: Name <email>"
/// "Co-authored-by: Name <email" // missing '>'
/// "Co-authored-by: Name" // no email
/// Any case/spacing variations are tolerated.
public CoAuthor(String line) {
this(line.substring("Co-authored-by: ".length(), line.indexOf('<')).trim(),
line.substring(line.indexOf('<') + 1, line.indexOf('>')).trim());
Logger.trace("Parsing \"{}\"...", line);

final String prefix = "co-authored-by:";
final String raw = line == null ? "" : line.trim();
final String lower = raw.toLowerCase(Locale.ROOT);

int prefPos = lower.indexOf(prefix);
int start = (prefPos >= 0) ? prefPos + prefix.length() : 0;
String rest = raw.substring(Math.min(start, raw.length())).trim();

int lt = rest.indexOf('<');
if (lt < 0) {
Logger.warn("No < found in Co-authored-by line {}", line);
}
int gt = (lt >= 0) ? rest.indexOf('>', lt + 1) : -1;
if (gt < 0) {
Logger.warn("No > found in Co-authored-by line {}", line);
}

String name = (lt >= 0 ? rest.substring(0, lt) : rest).trim();
String email = (lt >= 0
? rest.substring(lt + 1, (gt >= 0 ? gt : rest.length()))
: "").trim();

this(name, email);

Logger.trace("Parsed \"{}\" into {}", line, this);
}
}
Expand Down Expand Up @@ -649,44 +679,48 @@ private Optional<Contributor> lookupContributorData(MVMap<String, Contributor> e
fallbackSources.put(coAuthor.name, new PRAppearance(prNumber, commitName));
return Optional.of(new Contributor(coAuthor.name, "", "", commitName));
}
PagedSearchIterable<GHUser> list = gitHub.searchUsers().q(email).list();
if (list.getTotalCount() == 1) {
GHUser user = list.iterator().next();
String login = user.getLogin();
if (ignoredUsers.contains(login)) {
Logger.trace("Ignored because of login {}: {}", login, coAuthor);

GHUser user = null;
String lookup = null;

if (!email.equals("")) {
PagedSearchIterable<GHUser> list = gitHub.searchUsers().q(email).list();
if (list.getTotalCount() == 1) {
user = list.iterator().next();
String login = user.getLogin();
if (ignoredUsers.contains(login)) {
Logger.trace("Ignored because of login {}: {}", login, coAuthor);
return Optional.empty();
}
Contributor newContributor = new Contributor(login, user.getHtmlUrl().toString(), user.getAvatarUrl(), commitName);
emailToContributor.put(email, newContributor);
return Optional.of(newContributor);
}
if (list.getTotalCount() > 1) {
Logger.error("Multiple users found for the email of {}. Ignoring", coAuthor);
return Optional.empty();
}
Contributor newContributor = new Contributor(login, user.getHtmlUrl().toString(), user.getAvatarUrl(), commitName);
emailToContributor.put(email, newContributor);
return Optional.of(newContributor);
}
if (list.getTotalCount() > 1) {
Logger.error("Multiple users found for the email of {}. Ignoring", coAuthor);
return Optional.empty();
}

String lookup = email;
lookup = email;

GHUser user = null;

if (email.contains("+")) {
Logger.trace("Found + in email. Removing the part before it.");
lookup = email.substring(email.indexOf('+') + 1);
}
if (email.contains("+")) {
Logger.trace("Found + in email. Removing the part before it.");
lookup = email.substring(email.indexOf('+') + 1);
}

if (user == null) {
Logger.trace("Trying to find username derived from email.");
int atPosition = lookup.indexOf('@');
if (atPosition < 0) {
Logger.debug("No @ found in email {}", email);
} else {
lookup = lookup.substring(0, atPosition);
Logger.trace("Looking up {}", lookup);
try {
user = gitHub.getUser(lookup);
} catch (IOException e) {
Logger.trace("User not found for {}", lookup);
if (user == null) {
Logger.trace("Trying to find username derived from email.");
int atPosition = lookup.indexOf('@');
if (atPosition < 0) {
Logger.debug("No @ found in email {}", email);
} else {
lookup = lookup.substring(0, atPosition);
Logger.trace("Looking up {}", lookup);
try {
user = gitHub.getUser(lookup);
} catch (IOException e) {
Logger.trace("User not found for {}", lookup);
}
}
}
}
Expand Down Expand Up @@ -739,6 +773,11 @@ private Optional<Contributor> lookupContributorData(MVMap<String, Contributor> e
name = usersLogin;
}

if (ignoredUsers.contains(name)) {
Logger.trace("Ignored because of name {}: {}", name, coAuthor);
return Optional.empty();
}

Contributor newContributor = new Contributor(name, user.getHtmlUrl().toString(), user.getAvatarUrl(), commitName);

Logger.trace("Found user {} for {}", newContributor, coAuthor);
Expand Down