PrePush: Add a pre-push warning before committing to locked branches
This commit is contained in:
@@ -0,0 +1,40 @@
|
|||||||
|
import org.gradle.api.Project
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
apply { plugin("java") }
|
||||||
|
apply { plugin("jps-compatible") }
|
||||||
|
|
||||||
|
jvmTarget = "1.6"
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
"main" { projectDefault() }
|
||||||
|
"test" {}
|
||||||
|
}
|
||||||
|
|
||||||
|
project.addPrePushHookIfMissing()
|
||||||
|
|
||||||
|
fun Project.addPrePushHookIfMissing() {
|
||||||
|
val dotGitDirectory = rootProject.getGitDirectory()
|
||||||
|
val hooksDirectory = File(dotGitDirectory, "hooks").also { it.mkdirs() }
|
||||||
|
|
||||||
|
val prePushHook = File(projectDir, "pre-push.sh").also { require(it.exists()) }
|
||||||
|
val prePushTarget = File(hooksDirectory, "pre-push")
|
||||||
|
prePushHook.copyTo(prePushTarget, overwrite = true)
|
||||||
|
prePushTarget.setExecutable(true, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Project.getGitDirectory(): File {
|
||||||
|
val dotGitFile = File(projectDir, ".git")
|
||||||
|
|
||||||
|
return if (dotGitFile.isFile) {
|
||||||
|
val workTreeLink = dotGitFile.readLines().single { it.startsWith("gitdir: ") }
|
||||||
|
val mainRepoPath = workTreeLink
|
||||||
|
.substringAfter("gitdir: ", "")
|
||||||
|
.substringBefore("/.git/worktrees/", "")
|
||||||
|
.also { require(it.isNotEmpty()) }
|
||||||
|
|
||||||
|
File(mainRepoPath, ".git").also { require(it.isDirectory) }
|
||||||
|
} else {
|
||||||
|
dotGitFile
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
targetRepo="$2"
|
||||||
|
|
||||||
|
remoteRefs=""
|
||||||
|
|
||||||
|
javacPath="$JAVA_HOME/bin/javac"
|
||||||
|
javaPath="$JAVA_HOME/bin/java"
|
||||||
|
|
||||||
|
if [ -f "~/.kotlin-deactivate-prepush-hook" ]; then
|
||||||
|
echo "Pre-commit hook is deactivated"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -f "./libraries/tools/kotlin-prepush-hook/src/KotlinPrePushHook.java" ]; then
|
||||||
|
echo "Pre-commit hook .java file was not found in current branch, pre-push hook is disabled"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -f "$javaPath" ]; then
|
||||||
|
echo "'java' ($javaPath) was not found, pre-push hook is disabled"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -f "$javacPath" ]; then
|
||||||
|
echo "'javac' ($javacPath) was not found, pre-push hook is disabled"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
while read localRef localSha remoteRef remoteSha
|
||||||
|
do
|
||||||
|
# Looks like there're no [[ in Ubuntu by default
|
||||||
|
if [ "$remoteRef" = "refs/heads/rr/"* ]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$remoteRef" = "refs/heads/rrr/"* ]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z $remoteRefs ]; then
|
||||||
|
remoteRefs="$remoteRef"
|
||||||
|
else
|
||||||
|
remoteRefs="$remoteRefs,$remoteRef"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -z "$remoteRefs" ]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p ./build/prePushHook
|
||||||
|
"$javacPath" -d ./build/prePushHook ./libraries/tools/kotlin-prepush-hook/src/KotlinPrePushHook.java
|
||||||
|
cd ./build/prePushHook
|
||||||
|
|
||||||
|
"$javaPath" KotlinPrePushHook "$remoteRefs" "$targetRepo"
|
||||||
|
returnCode=$?
|
||||||
|
|
||||||
|
cd ../..
|
||||||
|
|
||||||
|
exit $returnCode
|
||||||
@@ -0,0 +1,111 @@
|
|||||||
|
import javax.swing.*;
|
||||||
|
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<String> 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: <remote refs> <target repository>, got " + Arrays.toString(args));
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> 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<String> remoteRefs) throws Exception {
|
||||||
|
Map<String, String> branchProperties = getProperties(new URL(BRANCH_DATA_URL));
|
||||||
|
|
||||||
|
Map<String, String> messages = new HashMap<>();
|
||||||
|
Map<String, String> branches = new HashMap<>();
|
||||||
|
|
||||||
|
for (Map.Entry<String, String> 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<String, String> 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<String, String> getProperties(URL url) throws Exception {
|
||||||
|
URLConnection connection = url.openConnection();
|
||||||
|
|
||||||
|
try (InputStream is = connection.getInputStream()) {
|
||||||
|
Properties properties = new Properties();
|
||||||
|
properties.load(is);
|
||||||
|
|
||||||
|
Map<String, String> propertyMap = new HashMap<>();
|
||||||
|
for (Map.Entry<Object, Object> entry : properties.entrySet()) {
|
||||||
|
if (entry.getKey() instanceof String && entry.getValue() instanceof String) {
|
||||||
|
propertyMap.put((String) entry.getKey(), (String) entry.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return propertyMap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -167,6 +167,7 @@ include ":kotlin-build-common",
|
|||||||
":examples:scripting-jvm-maven-deps",
|
":examples:scripting-jvm-maven-deps",
|
||||||
":examples:scripting-jvm-maven-deps-host",
|
":examples:scripting-jvm-maven-deps-host",
|
||||||
":pill:generate-all-tests",
|
":pill:generate-all-tests",
|
||||||
|
":libraries:kotlin-prepush-hook",
|
||||||
":include:kotlin-compiler",
|
":include:kotlin-compiler",
|
||||||
|
|
||||||
// plugin markers:
|
// plugin markers:
|
||||||
@@ -276,6 +277,7 @@ project(':examples:scripting-jvm-maven-deps').projectDir = "$rootDir/libraries/e
|
|||||||
project(':examples:scripting-jvm-maven-deps-host').projectDir = "$rootDir/libraries/examples/scripting/jvm-maven-deps/host" as File
|
project(':examples:scripting-jvm-maven-deps-host').projectDir = "$rootDir/libraries/examples/scripting/jvm-maven-deps/host" as File
|
||||||
project(':pill:generate-all-tests').projectDir = "$rootDir/plugins/pill/generate-all-tests" as File
|
project(':pill:generate-all-tests').projectDir = "$rootDir/plugins/pill/generate-all-tests" as File
|
||||||
project(':kotlin-imports-dumper-compiler-plugin').projectDir = "$rootDir/plugins/imports-dumper" as File
|
project(':kotlin-imports-dumper-compiler-plugin').projectDir = "$rootDir/plugins/imports-dumper" as File
|
||||||
|
project(':libraries:kotlin-prepush-hook').projectDir = "$rootDir/libraries/tools/kotlin-prepush-hook" as File
|
||||||
|
|
||||||
// plugin markers:
|
// plugin markers:
|
||||||
project(':kotlin-gradle-plugin:plugin-marker').projectDir = file("$rootDir/libraries/tools/kotlin-gradle-plugin/plugin-marker")
|
project(':kotlin-gradle-plugin:plugin-marker').projectDir = file("$rootDir/libraries/tools/kotlin-gradle-plugin/plugin-marker")
|
||||||
|
|||||||
Reference in New Issue
Block a user