diff --git a/README.md b/README.md index 985130b..741b3a8 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ See 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` diff --git a/gcl.java b/gcl.java index f4692e1..33f9cca 100755 --- a/gcl.java +++ b/gcl.java @@ -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 @@ -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; @@ -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 { @@ -139,9 +140,38 @@ public String getUserId() { } private record CoAuthor(String name, String email) { + /// Expected formats: + /// "Co-authored-by: Name " + /// "Co-authored-by: Name ' + /// "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); } } @@ -649,44 +679,48 @@ private Optional lookupContributorData(MVMap e fallbackSources.put(coAuthor.name, new PRAppearance(prNumber, commitName)); return Optional.of(new Contributor(coAuthor.name, "", "", commitName)); } - PagedSearchIterable 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 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); + } } } } @@ -739,6 +773,11 @@ private Optional lookupContributorData(MVMap 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);