import javax.swing.*; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.net.URLConnection; import java.util.*; import java.util.stream.Collectors; public class KotlinPrePushHook { private static final String BRANCH_DATA_URL = "https://raw.githubusercontent.com/Kotlin/kotlin-branch-status/master/status.txt"; private static final String MESSAGE_PREFIX = "MESSAGE."; private static final List KOTLIN_GIT_REPOS = Arrays.asList( "git@github.com:JetBrains/kotlin.git", "https://github.com/JetBrains/kotlin.git"); private static final String REF_PREFIX = "refs/heads/"; public static void main(String[] args) throws Exception { if (args.length != 2) { error("Usage: , got " + Arrays.toString(args)); } List remoteRefs = Arrays .stream(args[0].split(",")) .filter(ref -> ref.startsWith(REF_PREFIX)) .map(ref -> ref.substring(REF_PREFIX.length())).collect(Collectors.toList()); String targetRepo = args[1]; if (!KOTLIN_GIT_REPOS.contains(targetRepo)) { return; } check(remoteRefs); } private static void check(List remoteRefs) throws Exception { Map branchProperties = getProperties(new URL(BRANCH_DATA_URL)); if (branchProperties.isEmpty()) { return; } Map messages = new HashMap<>(); Map branches = new HashMap<>(); for (Map.Entry entry : branchProperties.entrySet()) { String key = entry.getKey(); String value = entry.getValue(); if (key.startsWith(MESSAGE_PREFIX)) { messages.put(key.substring(MESSAGE_PREFIX.length()), value); } else { branches.put(key, value); } } for (Map.Entry entry : branches.entrySet()) { String branchName = entry.getKey(); if (remoteRefs.contains(branchName)) { String message = messages.get(entry.getValue()); if (message == null) { error("Invalid message key '" + entry.getValue() + "', expected one of these: " + messages.keySet()); } //noinspection ConstantConditions if (!showWarning(message, branchName)) { error("Push aborted."); } } } } private static void error(String message) { //noinspection UseOfSystemOutOrSystemErr System.err.println(message); System.exit(1); } private static boolean showWarning(String reason, String targetBranch) { String baseMessage = "You are about to commit to '" + targetBranch + "',\n" + "and it is probably not the best idea.\n" + "Please think twice before pressing 'Yes'."; String confirmationMessage = "Do you still want to commit to '" + targetBranch + "'?"; String completeMessage = baseMessage + "\n\nReason: " + reason.replace("\\n", "\n") + "\n\n" + confirmationMessage; int result = JOptionPane.showOptionDialog( null, completeMessage, "Friendly warning", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE, null, new String[] { "Yes", "No" }, "No"); return result == JOptionPane.YES_OPTION; } private static Map getProperties(URL url) throws Exception { URLConnection connection = url.openConnection(); try (InputStream is = connection.getInputStream()) { Properties properties = new Properties(); properties.load(is); Map propertyMap = new HashMap<>(); for (Map.Entry entry : properties.entrySet()) { if (entry.getKey() instanceof String && entry.getValue() instanceof String) { propertyMap.put((String) entry.getKey(), (String) entry.getValue()); } } return propertyMap; } catch (IOException e) { System.err.println("Can't fetch " + BRANCH_DATA_URL + " (" + e.getMessage() + ")."); System.err.println("Pre-push hook won't work."); return Collections.emptyMap(); } } }