--- /dev/null
+/*\r
+ * Copyright 2013 gitblit.com.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package com.gitblit.git;\r
+\r
+import static org.eclipse.jgit.transport.BasePackPushConnection.CAPABILITY_SIDE_BAND_64K;\r
+import groovy.lang.Binding;\r
+import groovy.util.GroovyScriptEngine;\r
+\r
+import java.io.File;\r
+import java.io.IOException;\r
+import java.text.MessageFormat;\r
+import java.util.Collection;\r
+import java.util.LinkedHashSet;\r
+import java.util.List;\r
+import java.util.Set;\r
+import java.util.concurrent.TimeUnit;\r
+\r
+import org.eclipse.jgit.lib.BatchRefUpdate;\r
+import org.eclipse.jgit.lib.NullProgressMonitor;\r
+import org.eclipse.jgit.lib.PersonIdent;\r
+import org.eclipse.jgit.lib.ProgressMonitor;\r
+import org.eclipse.jgit.lib.Repository;\r
+import org.eclipse.jgit.revwalk.RevCommit;\r
+import org.eclipse.jgit.transport.PostReceiveHook;\r
+import org.eclipse.jgit.transport.PreReceiveHook;\r
+import org.eclipse.jgit.transport.ReceiveCommand;\r
+import org.eclipse.jgit.transport.ReceiveCommand.Result;\r
+import org.eclipse.jgit.transport.ReceivePack;\r
+import org.slf4j.Logger;\r
+import org.slf4j.LoggerFactory;\r
+\r
+import com.gitblit.Constants;\r
+import com.gitblit.Constants.AccessRestrictionType;\r
+import com.gitblit.GitBlit;\r
+import com.gitblit.Keys;\r
+import com.gitblit.client.Translation;\r
+import com.gitblit.models.RepositoryModel;\r
+import com.gitblit.models.UserModel;\r
+import com.gitblit.utils.ArrayUtils;\r
+import com.gitblit.utils.ClientLogger;\r
+import com.gitblit.utils.CommitCache;\r
+import com.gitblit.utils.JGitUtils;\r
+import com.gitblit.utils.RefLogUtils;\r
+import com.gitblit.utils.StringUtils;\r
+\r
+\r
+/**\r
+ * GitblitReceivePack processes receive commands. It also executes Groovy pre-\r
+ * and post- receive hooks.\r
+ *\r
+ * The general execution flow is:\r
+ * <ol>\r
+ * <li>onPreReceive()</li>\r
+ * <li>executeCommands()</li>\r
+ * <li>onPostReceive()</li>\r
+ * </ol>\r
+ *\r
+ * @author Android Open Source Project\r
+ * @author James Moger\r
+ *\r
+ */\r
+public class GitblitReceivePack extends ReceivePack implements PreReceiveHook, PostReceiveHook {\r
+\r
+ private static final Logger LOGGER = LoggerFactory.getLogger(GitblitReceivePack.class);\r
+\r
+ protected final RepositoryModel repository;\r
+\r
+ protected final UserModel user;\r
+\r
+ protected final File groovyDir;\r
+\r
+ protected String gitblitUrl;\r
+\r
+ protected String repositoryUrl;\r
+\r
+ protected GroovyScriptEngine gse;\r
+\r
+ public GitblitReceivePack(Repository db, RepositoryModel repository, UserModel user) {\r
+ super(db);\r
+ this.repository = repository;\r
+ this.user = user == null ? UserModel.ANONYMOUS : user;\r
+ this.groovyDir = GitBlit.getGroovyScriptsFolder();\r
+ try {\r
+ // set Grape root\r
+ File grapeRoot = GitBlit.getFileOrFolder(Keys.groovy.grapeFolder, "${baseFolder}/groovy/grape").getAbsoluteFile();\r
+ grapeRoot.mkdirs();\r
+ System.setProperty("grape.root", grapeRoot.getAbsolutePath());\r
+ this.gse = new GroovyScriptEngine(groovyDir.getAbsolutePath());\r
+ } catch (IOException e) {\r
+ }\r
+\r
+ // set advanced ref permissions\r
+ setAllowCreates(user.canCreateRef(repository));\r
+ setAllowDeletes(user.canDeleteRef(repository));\r
+ setAllowNonFastForwards(user.canRewindRef(repository));\r
+ \r
+ // setup pre and post receive hook\r
+ setPreReceiveHook(this);\r
+ setPostReceiveHook(this);\r
+ }\r
+\r
+ /**\r
+ * Instrumentation point where the incoming push event has been parsed,\r
+ * validated, objects created BUT refs have not been updated. You might\r
+ * use this to enforce a branch-write permissions model.\r
+ */\r
+ @Override\r
+ public void onPreReceive(ReceivePack rp, Collection<ReceiveCommand> commands) {\r
+\r
+ if (repository.isFrozen) {\r
+ // repository is frozen/readonly\r
+ for (ReceiveCommand cmd : commands) {\r
+ sendRejection(cmd, "Gitblit does not allow pushes to \"{0}\" because it is frozen!", repository.name);\r
+ }\r
+ return;\r
+ }\r
+\r
+ if (!repository.isBare) {\r
+ // repository has a working copy\r
+ for (ReceiveCommand cmd : commands) {\r
+ sendRejection(cmd, "Gitblit does not allow pushes to \"{0}\" because it has a working copy!", repository.name);\r
+ }\r
+ return;\r
+ }\r
+\r
+ if (!user.canPush(repository)) {\r
+ // user does not have push permissions\r
+ for (ReceiveCommand cmd : commands) {\r
+ sendRejection(cmd, "User \"{0}\" does not have push permissions for \"{1}\"!", user.username, repository.name);\r
+ }\r
+ return;\r
+ }\r
+\r
+ if (repository.accessRestriction.atLeast(AccessRestrictionType.PUSH) && repository.verifyCommitter) {\r
+ // enforce committer verification\r
+ if (StringUtils.isEmpty(user.emailAddress)) {\r
+ // emit warning if user does not have an email address\r
+ LOGGER.warn(MessageFormat.format("Consider setting an email address for {0} ({1}) to improve committer verification.", user.getDisplayName(), user.username));\r
+ }\r
+\r
+ // Optionally enforce that the committer of first parent chain\r
+ // match the account being used to push the commits.\r
+ //\r
+ // This requires all merge commits are executed with the "--no-ff"\r
+ // option to force a merge commit even if fast-forward is possible.\r
+ // This ensures that the chain first parents has the commit\r
+ // identity of the merging user.\r
+ boolean allRejected = false;\r
+ for (ReceiveCommand cmd : commands) {\r
+ String firstParent = null;\r
+ try {\r
+ List<RevCommit> commits = JGitUtils.getRevLog(rp.getRepository(), cmd.getOldId().name(), cmd.getNewId().name());\r
+ for (RevCommit commit : commits) {\r
+\r
+ if (firstParent != null) {\r
+ if (!commit.getName().equals(firstParent)) {\r
+ // ignore: commit is right-descendant of a merge\r
+ continue;\r
+ }\r
+ }\r
+\r
+ // update expected next commit id\r
+ if (commit.getParentCount() == 0) {\r
+ firstParent = null;\r
+ } else {\r
+ firstParent = commit.getParents()[0].getId().getName();\r
+ }\r
+\r
+ PersonIdent committer = commit.getCommitterIdent();\r
+ if (!user.is(committer.getName(), committer.getEmailAddress())) {\r
+ String reason;\r
+ if (StringUtils.isEmpty(user.emailAddress)) {\r
+ // account does not have an email address\r
+ reason = MessageFormat.format("{0} by {1} <{2}> was not committed by {3} ({4})",\r
+ commit.getId().name(), committer.getName(), StringUtils.isEmpty(committer.getEmailAddress()) ? "?":committer.getEmailAddress(), user.getDisplayName(), user.username);\r
+ } else {\r
+ // account has an email address\r
+ reason = MessageFormat.format("{0} by {1} <{2}> was not committed by {3} ({4}) <{5}>",\r
+ commit.getId().name(), committer.getName(), StringUtils.isEmpty(committer.getEmailAddress()) ? "?":committer.getEmailAddress(), user.getDisplayName(), user.username, user.emailAddress);\r
+ }\r
+ LOGGER.warn(reason);\r
+ cmd.setResult(Result.REJECTED_OTHER_REASON, reason);\r
+ allRejected &= true;\r
+ break;\r
+ } else {\r
+ allRejected = false;\r
+ }\r
+ }\r
+ } catch (Exception e) {\r
+ LOGGER.error("Failed to verify commits were made by pushing user", e);\r
+ }\r
+ }\r
+\r
+ if (allRejected) {\r
+ // all ref updates rejected, abort\r
+ return;\r
+ }\r
+ }\r
+\r
+ // reset branch commit cache on REWIND and DELETE\r
+ for (ReceiveCommand cmd : commands) {\r
+ String ref = cmd.getRefName();\r
+ if (ref.startsWith(Constants.R_HEADS)) {\r
+ switch (cmd.getType()) {\r
+ case UPDATE_NONFASTFORWARD:\r
+ case DELETE:\r
+ CommitCache.instance().clear(repository.name, ref);\r
+ break;\r
+ default:\r
+ break;\r
+ }\r
+ }\r
+ }\r
+\r
+ Set<String> scripts = new LinkedHashSet<String>();\r
+ scripts.addAll(GitBlit.self().getPreReceiveScriptsInherited(repository));\r
+ if (!ArrayUtils.isEmpty(repository.preReceiveScripts)) {\r
+ scripts.addAll(repository.preReceiveScripts);\r
+ }\r
+ runGroovy(commands, scripts);\r
+ for (ReceiveCommand cmd : commands) {\r
+ if (!Result.NOT_ATTEMPTED.equals(cmd.getResult())) {\r
+ LOGGER.warn(MessageFormat.format("{0} {1} because \"{2}\"", cmd.getNewId()\r
+ .getName(), cmd.getResult(), cmd.getMessage()));\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Instrumentation point where the incoming push has been applied to the\r
+ * repository. This is the point where we would trigger a Jenkins build\r
+ * or send an email.\r
+ */\r
+ @Override\r
+ public void onPostReceive(ReceivePack rp, Collection<ReceiveCommand> commands) {\r
+ if (commands.size() == 0) {\r
+ LOGGER.debug("skipping post-receive hooks, no refs created, updated, or removed");\r
+ return;\r
+ }\r
+\r
+ // log ref changes\r
+ for (ReceiveCommand cmd : commands) {\r
+\r
+ if (Result.OK.equals(cmd.getResult())) {\r
+ // add some logging for important ref changes\r
+ switch (cmd.getType()) {\r
+ case DELETE:\r
+ LOGGER.info(MessageFormat.format("{0} DELETED {1} in {2} ({3})", user.username, cmd.getRefName(), repository.name, cmd.getOldId().name()));\r
+ break;\r
+ case CREATE:\r
+ LOGGER.info(MessageFormat.format("{0} CREATED {1} in {2}", user.username, cmd.getRefName(), repository.name));\r
+ break;\r
+ case UPDATE:\r
+ LOGGER.info(MessageFormat.format("{0} UPDATED {1} in {2} (from {3} to {4})", user.username, cmd.getRefName(), repository.name, cmd.getOldId().name(), cmd.getNewId().name()));\r
+ break;\r
+ case UPDATE_NONFASTFORWARD:\r
+ LOGGER.info(MessageFormat.format("{0} UPDATED NON-FAST-FORWARD {1} in {2} (from {3} to {4})", user.username, cmd.getRefName(), repository.name, cmd.getOldId().name(), cmd.getNewId().name()));\r
+ break;\r
+ default:\r
+ break;\r
+ }\r
+ }\r
+ }\r
+\r
+ if (repository.useIncrementalPushTags) {\r
+ // tag each pushed branch tip\r
+ String emailAddress = user.emailAddress == null ? rp.getRefLogIdent().getEmailAddress() : user.emailAddress;\r
+ PersonIdent userIdent = new PersonIdent(user.getDisplayName(), emailAddress);\r
+\r
+ for (ReceiveCommand cmd : commands) {\r
+ if (!cmd.getRefName().startsWith(Constants.R_HEADS)) {\r
+ // only tag branch ref changes\r
+ continue;\r
+ }\r
+\r
+ if (!ReceiveCommand.Type.DELETE.equals(cmd.getType())\r
+ && ReceiveCommand.Result.OK.equals(cmd.getResult())) {\r
+ String objectId = cmd.getNewId().getName();\r
+ String branch = cmd.getRefName().substring(Constants.R_HEADS.length());\r
+ // get translation based on the server's locale setting\r
+ String template = Translation.get("gb.incrementalPushTagMessage");\r
+ String msg = MessageFormat.format(template, branch);\r
+ String prefix;\r
+ if (StringUtils.isEmpty(repository.incrementalPushTagPrefix)) {\r
+ prefix = GitBlit.getString(Keys.git.defaultIncrementalPushTagPrefix, "r");\r
+ } else {\r
+ prefix = repository.incrementalPushTagPrefix;\r
+ }\r
+\r
+ JGitUtils.createIncrementalRevisionTag(\r
+ rp.getRepository(),\r
+ objectId,\r
+ userIdent,\r
+ prefix,\r
+ "0",\r
+ msg);\r
+ }\r
+ }\r
+ }\r
+\r
+ // update push log\r
+ try {\r
+ RefLogUtils.updateRefLog(user, rp.getRepository(), commands);\r
+ LOGGER.debug(MessageFormat.format("{0} push log updated", repository.name));\r
+ } catch (Exception e) {\r
+ LOGGER.error(MessageFormat.format("Failed to update {0} pushlog", repository.name), e);\r
+ }\r
+\r
+ // run Groovy hook scripts\r
+ Set<String> scripts = new LinkedHashSet<String>();\r
+ scripts.addAll(GitBlit.self().getPostReceiveScriptsInherited(repository));\r
+ if (!ArrayUtils.isEmpty(repository.postReceiveScripts)) {\r
+ scripts.addAll(repository.postReceiveScripts);\r
+ }\r
+ runGroovy(commands, scripts);\r
+ }\r
+\r
+ /** Execute commands to update references. */\r
+ @Override\r
+ protected void executeCommands() {\r
+ List<ReceiveCommand> toApply = filterCommands(Result.NOT_ATTEMPTED);\r
+ if (toApply.isEmpty()) {\r
+ return;\r
+ }\r
+\r
+ ProgressMonitor updating = NullProgressMonitor.INSTANCE;\r
+ boolean sideBand = isCapabilityEnabled(CAPABILITY_SIDE_BAND_64K);\r
+ if (sideBand) {\r
+ SideBandProgressMonitor pm = new SideBandProgressMonitor(msgOut);\r
+ pm.setDelayStart(250, TimeUnit.MILLISECONDS);\r
+ updating = pm;\r
+ }\r
+\r
+ BatchRefUpdate batch = getRepository().getRefDatabase().newBatchUpdate();\r
+ batch.setAllowNonFastForwards(isAllowNonFastForwards());\r
+ batch.setRefLogIdent(getRefLogIdent());\r
+ batch.setRefLogMessage("push", true);\r
+\r
+ for (ReceiveCommand cmd : toApply) {\r
+ if (Result.NOT_ATTEMPTED != cmd.getResult()) {\r
+ // Already rejected by the core receive process.\r
+ continue;\r
+ }\r
+ batch.addCommand(cmd);\r
+ }\r
+\r
+ if (!batch.getCommands().isEmpty()) {\r
+ try {\r
+ batch.execute(getRevWalk(), updating);\r
+ } catch (IOException err) {\r
+ for (ReceiveCommand cmd : toApply) {\r
+ if (cmd.getResult() == Result.NOT_ATTEMPTED) {\r
+ sendRejection(cmd, "lock error: {0}", err.getMessage());\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ protected void setGitblitUrl(String url) {\r
+ this.gitblitUrl = url;\r
+ }\r
+\r
+ protected void setRepositoryUrl(String url) {\r
+ this.repositoryUrl = url;\r
+ }\r
+\r
+ protected void sendRejection(final ReceiveCommand cmd, final String why, Object... objects) {\r
+ String text;\r
+ if (ArrayUtils.isEmpty(objects)) {\r
+ text = why;\r
+ } else {\r
+ text = MessageFormat.format(why, objects);\r
+ }\r
+ cmd.setResult(Result.REJECTED_OTHER_REASON, text);\r
+ LOGGER.error(text + " (" + user.username + ")");\r
+ }\r
+\r
+ protected void sendMessage(String msg, Object... objects) {\r
+ String text;\r
+ if (ArrayUtils.isEmpty(objects)) {\r
+ text = msg;\r
+ super.sendMessage(msg);\r
+ } else {\r
+ text = MessageFormat.format(msg, objects);\r
+ super.sendMessage(text);\r
+ }\r
+ LOGGER.info(text + " (" + user.username + ")");\r
+ }\r
+\r
+ protected void sendError(String msg, Object... objects) {\r
+ String text;\r
+ if (ArrayUtils.isEmpty(objects)) {\r
+ text = msg;\r
+ super.sendError(msg);\r
+ } else {\r
+ text = MessageFormat.format(msg, objects);\r
+ super.sendError(text);\r
+ }\r
+ LOGGER.error(text + " (" + user.username + ")");\r
+ }\r
+\r
+ /**\r
+ * Runs the specified Groovy hook scripts.\r
+ *\r
+ * @param repository\r
+ * @param user\r
+ * @param commands\r
+ * @param scripts\r
+ */\r
+ protected void runGroovy(Collection<ReceiveCommand> commands, Set<String> scripts) {\r
+ if (scripts == null || scripts.size() == 0) {\r
+ // no Groovy scripts to execute\r
+ return;\r
+ }\r
+\r
+ Binding binding = new Binding();\r
+ binding.setVariable("gitblit", GitBlit.self());\r
+ binding.setVariable("repository", repository);\r
+ binding.setVariable("receivePack", this);\r
+ binding.setVariable("user", user);\r
+ binding.setVariable("commands", commands);\r
+ binding.setVariable("url", gitblitUrl);\r
+ binding.setVariable("logger", LOGGER);\r
+ binding.setVariable("clientLogger", new ClientLogger(this));\r
+ for (String script : scripts) {\r
+ if (StringUtils.isEmpty(script)) {\r
+ continue;\r
+ }\r
+ // allow script to be specified without .groovy extension\r
+ // this is easier to read in the settings\r
+ File file = new File(groovyDir, script);\r
+ if (!file.exists() && !script.toLowerCase().endsWith(".groovy")) {\r
+ file = new File(groovyDir, script + ".groovy");\r
+ if (file.exists()) {\r
+ script = file.getName();\r
+ }\r
+ }\r
+ try {\r
+ Object result = gse.run(script, binding);\r
+ if (result instanceof Boolean) {\r
+ if (!((Boolean) result)) {\r
+ LOGGER.error(MessageFormat.format(\r
+ "Groovy script {0} has failed! Hook scripts aborted.", script));\r
+ break;\r
+ }\r
+ }\r
+ } catch (Exception e) {\r
+ LOGGER.error(\r
+ MessageFormat.format("Failed to execute Groovy script {0}", script), e);\r
+ }\r
+ }\r
+ }\r
+}\r
import com.gitblit.models.RepositoryModel;
import com.gitblit.models.UserModel;
import com.gitblit.utils.HttpUtils;
+import com.gitblit.utils.StringUtils;
/**
- * The receive pack factory creates a receive pack which accepts pushes from
- * clients.
- *
+ * The receive pack factory creates the receive pack which processes pushes.
+ *
* @author James Moger
*
* @param <X> the connection type
public class GitblitReceivePackFactory<X> implements ReceivePackFactory<X> {
protected final Logger logger = LoggerFactory.getLogger(GitblitReceivePackFactory.class);
-
+
@Override
public ReceivePack create(X req, Repository db)
throws ServiceNotEnabledException, ServiceNotAuthorizedException {
- final ReceivePack rp = new ReceivePack(db);
UserModel user = UserModel.ANONYMOUS;
String repositoryName = "";
String origin = "";
String gitblitUrl = "";
+ String repositoryUrl = "";
int timeout = 0;
-
+
if (req instanceof HttpServletRequest) {
- // http/https request may or may not be authenticated
+ // http/https request may or may not be authenticated
HttpServletRequest request = (HttpServletRequest) req;
repositoryName = request.getAttribute("gitblitRepositoryName").toString();
origin = request.getRemoteHost();
gitblitUrl = HttpUtils.getGitblitURL(request);
+ repositoryUrl = request.getRequestURI();
// determine pushing user
String username = request.getRemoteUser();
- if (username != null && !"".equals(username)) {
- user = GitBlit.self().getUserModel(username);
- if (user == null) {
- // anonymous push, create a temporary usermodel
- user = new UserModel(username);
+ if (!StringUtils.isEmpty(username)) {
+ UserModel u = GitBlit.self().getUserModel(username);
+ if (u != null) {
+ user = u;
}
}
} else if (req instanceof GitDaemonClient) {
- // git daemon request is alway anonymous
+ // git daemon request is always anonymous
GitDaemonClient client = (GitDaemonClient) req;
repositoryName = client.getRepositoryName();
origin = client.getRemoteAddress().getHostAddress();
+
// set timeout from Git daemon
timeout = client.getDaemon().getTimeout();
}
- // set pushing user identity for reflog
- rp.setRefLogIdent(new PersonIdent(user.username, user.username + "@" + origin));
- rp.setTimeout(timeout);
-
- // set advanced ref permissions
- RepositoryModel repository = GitBlit.self().getRepositoryModel(repositoryName);
- rp.setAllowCreates(user.canCreateRef(repository));
- rp.setAllowDeletes(user.canDeleteRef(repository));
- rp.setAllowNonFastForwards(user.canRewindRef(repository));
+ // TODO make this a setting
+ boolean allowAnonymousPushes = true;
+ if (!allowAnonymousPushes && UserModel.ANONYMOUS.equals(user)) {
+ // prohibit anonymous pushes
+ throw new ServiceNotEnabledException();
+ }
- // setup the receive hook
- ReceiveHook hook = new ReceiveHook();
- hook.user = user;
- hook.repository = repository;
- hook.gitblitUrl = gitblitUrl;
+ final RepositoryModel repository = GitBlit.self().getRepositoryModel(repositoryName);
- rp.setPreReceiveHook(hook);
- rp.setPostReceiveHook(hook);
+ final GitblitReceivePack rp = new GitblitReceivePack(db, repository, user);
+ rp.setGitblitUrl(gitblitUrl);
+ rp.setRepositoryUrl(repositoryUrl);
+ rp.setRefLogIdent(new PersonIdent(user.username, user.username + "@" + origin));
+ rp.setTimeout(timeout);
return rp;
}
-}
+}
\ No newline at end of file
package com.gitblit.git;
import java.util.ArrayList;
-import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;
import org.eclipse.jgit.transport.resolver.UploadPackFactory;
+import com.gitblit.Constants;
import com.gitblit.GitBlit;
import com.gitblit.models.UserModel;
// JGit's RefMap is custom and does not support iterator removal :(
List<String> toRemove = new ArrayList<String>();
for (String ref : refs.keySet()) {
- if (ref.startsWith("refs/gitblit/")) {
+ if (ref.startsWith(Constants.R_GITBLIT)) {
toRemove.add(ref);
}
}
return refs;
}
}
-}
+}
\ No newline at end of file
+++ /dev/null
-/*\r
- * Copyright 2011 gitblit.com.\r
- *\r
- * Licensed under the Apache License, Version 2.0 (the "License");\r
- * you may not use this file except in compliance with the License.\r
- * You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package com.gitblit.git;\r
-\r
-import groovy.lang.Binding;\r
-import groovy.util.GroovyScriptEngine;\r
-\r
-import java.io.File;\r
-import java.io.IOException;\r
-import java.text.MessageFormat;\r
-import java.util.Collection;\r
-import java.util.LinkedHashSet;\r
-import java.util.List;\r
-import java.util.Set;\r
-\r
-import org.eclipse.jgit.lib.PersonIdent;\r
-import org.eclipse.jgit.revwalk.RevCommit;\r
-import org.eclipse.jgit.transport.PostReceiveHook;\r
-import org.eclipse.jgit.transport.PreReceiveHook;\r
-import org.eclipse.jgit.transport.ReceiveCommand;\r
-import org.eclipse.jgit.transport.ReceiveCommand.Result;\r
-import org.eclipse.jgit.transport.ReceivePack;\r
-import org.slf4j.Logger;\r
-import org.slf4j.LoggerFactory;\r
-\r
-import com.gitblit.Constants;\r
-import com.gitblit.Constants.AccessRestrictionType;\r
-import com.gitblit.GitBlit;\r
-import com.gitblit.Keys;\r
-import com.gitblit.client.Translation;\r
-import com.gitblit.models.RepositoryModel;\r
-import com.gitblit.models.UserModel;\r
-import com.gitblit.utils.ArrayUtils;\r
-import com.gitblit.utils.ClientLogger;\r
-import com.gitblit.utils.CommitCache;\r
-import com.gitblit.utils.JGitUtils;\r
-import com.gitblit.utils.RefLogUtils;\r
-import com.gitblit.utils.StringUtils;\r
-\r
-/**\r
- * The Gitblit receive hook allows for special processing on push events.\r
- * That might include rejecting writes to specific branches or executing a\r
- * script.\r
- * \r
- * @author James Moger\r
- * \r
- */\r
-public class ReceiveHook implements PreReceiveHook, PostReceiveHook {\r
-\r
- protected final Logger logger = LoggerFactory.getLogger(ReceiveHook.class);\r
-\r
- protected UserModel user;\r
- \r
- protected RepositoryModel repository;\r
-\r
- protected String gitblitUrl;\r
-\r
- private GroovyScriptEngine gse;\r
-\r
- private File groovyDir;\r
-\r
- public ReceiveHook() {\r
- groovyDir = GitBlit.getGroovyScriptsFolder();\r
- try {\r
- // set Grape root\r
- File grapeRoot = GitBlit.getFileOrFolder(Keys.groovy.grapeFolder, "${baseFolder}/groovy/grape").getAbsoluteFile();\r
- grapeRoot.mkdirs();\r
- System.setProperty("grape.root", grapeRoot.getAbsolutePath());\r
-\r
- gse = new GroovyScriptEngine(groovyDir.getAbsolutePath()); \r
- } catch (IOException e) {\r
- //throw new ServletException("Failed to instantiate Groovy Script Engine!", e);\r
- }\r
- }\r
-\r
- /**\r
- * Instrumentation point where the incoming push event has been parsed,\r
- * validated, objects created BUT refs have not been updated. You might\r
- * use this to enforce a branch-write permissions model.\r
- */\r
- @Override\r
- public void onPreReceive(ReceivePack rp, Collection<ReceiveCommand> commands) {\r
- if (repository.isFrozen) {\r
- // repository is frozen/readonly\r
- String reason = MessageFormat.format("Gitblit does not allow pushes to \"{0}\" because it is frozen!", repository.name);\r
- logger.warn(reason);\r
- for (ReceiveCommand cmd : commands) {\r
- cmd.setResult(Result.REJECTED_OTHER_REASON, reason);\r
- }\r
- return;\r
- }\r
- \r
- if (!repository.isBare) {\r
- // repository has a working copy\r
- String reason = MessageFormat.format("Gitblit does not allow pushes to \"{0}\" because it has a working copy!", repository.name);\r
- logger.warn(reason);\r
- for (ReceiveCommand cmd : commands) {\r
- cmd.setResult(Result.REJECTED_OTHER_REASON, reason);\r
- }\r
- return;\r
- }\r
-\r
- if (!user.canPush(repository)) {\r
- // user does not have push permissions\r
- String reason = MessageFormat.format("User \"{0}\" does not have push permissions for \"{1}\"!", user.username, repository.name);\r
- logger.warn(reason);\r
- for (ReceiveCommand cmd : commands) {\r
- cmd.setResult(Result.REJECTED_OTHER_REASON, reason);\r
- }\r
- return;\r
- }\r
-\r
- if (repository.accessRestriction.atLeast(AccessRestrictionType.PUSH) && repository.verifyCommitter) {\r
- // enforce committer verification\r
- if (StringUtils.isEmpty(user.emailAddress)) {\r
- // emit warning if user does not have an email address \r
- logger.warn(MessageFormat.format("Consider setting an email address for {0} ({1}) to improve committer verification.", user.getDisplayName(), user.username));\r
- }\r
-\r
- // Optionally enforce that the committer of the left parent chain\r
- // match the account being used to push the commits.\r
- // \r
- // This requires all merge commits are executed with the "--no-ff"\r
- // option to force a merge commit even if fast-forward is possible.\r
- // This ensures that the chain of left parents has the commit\r
- // identity of the merging user.\r
- boolean allRejected = false;\r
- for (ReceiveCommand cmd : commands) {\r
- String linearParent = null;\r
- try {\r
- List<RevCommit> commits = JGitUtils.getRevLog(rp.getRepository(), cmd.getOldId().name(), cmd.getNewId().name());\r
- for (RevCommit commit : commits) {\r
- \r
- if (linearParent != null) {\r
- if (!commit.getName().equals(linearParent)) {\r
- // ignore: commit is right-descendant of a merge\r
- continue;\r
- }\r
- }\r
- \r
- // update expected next commit id\r
- if (commit.getParentCount() == 0) {\r
- linearParent = null;\r
- } else {\r
- linearParent = commit.getParents()[0].getId().getName();\r
- }\r
- \r
- PersonIdent committer = commit.getCommitterIdent();\r
- if (!user.is(committer.getName(), committer.getEmailAddress())) {\r
- String reason;\r
- if (StringUtils.isEmpty(user.emailAddress)) {\r
- // account does not have an email address\r
- reason = MessageFormat.format("{0} by {1} <{2}> was not committed by {3} ({4})", \r
- commit.getId().name(), committer.getName(), StringUtils.isEmpty(committer.getEmailAddress()) ? "?":committer.getEmailAddress(), user.getDisplayName(), user.username);\r
- } else {\r
- // account has an email address\r
- reason = MessageFormat.format("{0} by {1} <{2}> was not committed by {3} ({4}) <{5}>", \r
- commit.getId().name(), committer.getName(), StringUtils.isEmpty(committer.getEmailAddress()) ? "?":committer.getEmailAddress(), user.getDisplayName(), user.username, user.emailAddress);\r
- }\r
- logger.warn(reason);\r
- cmd.setResult(Result.REJECTED_OTHER_REASON, reason);\r
- allRejected &= true;\r
- break;\r
- } else {\r
- allRejected = false;\r
- }\r
- }\r
- } catch (Exception e) {\r
- logger.error("Failed to verify commits were made by pushing user", e);\r
- }\r
- }\r
-\r
- if (allRejected) {\r
- // all ref updates rejected, abort\r
- return;\r
- }\r
- }\r
- \r
- // reset branch commit cache on REWIND and DELETE\r
- for (ReceiveCommand cmd : commands) {\r
- String ref = cmd.getRefName();\r
- if (ref.startsWith(Constants.R_HEADS)) {\r
- switch (cmd.getType()) {\r
- case UPDATE_NONFASTFORWARD:\r
- case DELETE:\r
- CommitCache.instance().clear(repository.name, ref);\r
- break;\r
- default:\r
- break;\r
- }\r
- }\r
- }\r
-\r
- Set<String> scripts = new LinkedHashSet<String>();\r
- scripts.addAll(GitBlit.self().getPreReceiveScriptsInherited(repository));\r
- if (!ArrayUtils.isEmpty(repository.preReceiveScripts)) {\r
- scripts.addAll(repository.preReceiveScripts);\r
- }\r
- runGroovy(repository, user, commands, rp, scripts);\r
- for (ReceiveCommand cmd : commands) {\r
- if (!Result.NOT_ATTEMPTED.equals(cmd.getResult())) {\r
- logger.warn(MessageFormat.format("{0} {1} because \"{2}\"", cmd.getNewId()\r
- .getName(), cmd.getResult(), cmd.getMessage()));\r
- }\r
- }\r
- }\r
-\r
- /**\r
- * Instrumentation point where the incoming push has been applied to the\r
- * repository. This is the point where we would trigger a Jenkins build\r
- * or send an email.\r
- */\r
- @Override\r
- public void onPostReceive(ReceivePack rp, Collection<ReceiveCommand> commands) {\r
- if (commands.size() == 0) {\r
- logger.debug("skipping post-receive hooks, no refs created, updated, or removed");\r
- return;\r
- }\r
-\r
- // log ref changes\r
- for (ReceiveCommand cmd : commands) {\r
- if (Result.OK.equals(cmd.getResult())) {\r
- // add some logging for important ref changes\r
- switch (cmd.getType()) {\r
- case DELETE:\r
- logger.info(MessageFormat.format("{0} DELETED {1} in {2} ({3})", user.username, cmd.getRefName(), repository.name, cmd.getOldId().name()));\r
- break;\r
- case CREATE:\r
- logger.info(MessageFormat.format("{0} CREATED {1} in {2}", user.username, cmd.getRefName(), repository.name));\r
- break;\r
- case UPDATE:\r
- logger.info(MessageFormat.format("{0} UPDATED {1} in {2} (from {3} to {4})", user.username, cmd.getRefName(), repository.name, cmd.getOldId().name(), cmd.getNewId().name()));\r
- break;\r
- case UPDATE_NONFASTFORWARD:\r
- logger.info(MessageFormat.format("{0} UPDATED NON-FAST-FORWARD {1} in {2} (from {3} to {4})", user.username, cmd.getRefName(), repository.name, cmd.getOldId().name(), cmd.getNewId().name()));\r
- break;\r
- default:\r
- break;\r
- }\r
- }\r
- }\r
-\r
- if (repository.useIncrementalPushTags) {\r
- // tag each pushed branch tip\r
- String emailAddress = user.emailAddress == null ? rp.getRefLogIdent().getEmailAddress() : user.emailAddress;\r
- PersonIdent userIdent = new PersonIdent(user.getDisplayName(), emailAddress);\r
-\r
- for (ReceiveCommand cmd : commands) {\r
- if (!cmd.getRefName().startsWith("refs/heads/")) {\r
- // only tag branch ref changes\r
- continue;\r
- }\r
-\r
- if (!ReceiveCommand.Type.DELETE.equals(cmd.getType())\r
- && ReceiveCommand.Result.OK.equals(cmd.getResult())) {\r
- String objectId = cmd.getNewId().getName();\r
- String branch = cmd.getRefName().substring("refs/heads/".length());\r
- // get translation based on the server's locale setting\r
- String template = Translation.get("gb.incrementalPushTagMessage");\r
- String msg = MessageFormat.format(template, branch);\r
- String prefix;\r
- if (StringUtils.isEmpty(repository.incrementalPushTagPrefix)) {\r
- prefix = GitBlit.getString(Keys.git.defaultIncrementalPushTagPrefix, "r");\r
- } else {\r
- prefix = repository.incrementalPushTagPrefix;\r
- }\r
-\r
- JGitUtils.createIncrementalRevisionTag(\r
- rp.getRepository(),\r
- objectId,\r
- userIdent,\r
- prefix,\r
- "0",\r
- msg);\r
- }\r
- } \r
- }\r
-\r
- // update push log\r
- try {\r
- RefLogUtils.updateRefLog(user, rp.getRepository(), commands);\r
- logger.debug(MessageFormat.format("{0} push log updated", repository.name));\r
- } catch (Exception e) {\r
- logger.error(MessageFormat.format("Failed to update {0} pushlog", repository.name), e);\r
- }\r
-\r
- // run Groovy hook scripts \r
- Set<String> scripts = new LinkedHashSet<String>();\r
- scripts.addAll(GitBlit.self().getPostReceiveScriptsInherited(repository));\r
- if (!ArrayUtils.isEmpty(repository.postReceiveScripts)) {\r
- scripts.addAll(repository.postReceiveScripts);\r
- }\r
- runGroovy(repository, user, commands, rp, scripts);\r
- }\r
-\r
- /**\r
- * Runs the specified Groovy hook scripts.\r
- * \r
- * @param repository\r
- * @param user\r
- * @param commands\r
- * @param scripts\r
- */\r
- protected void runGroovy(RepositoryModel repository, UserModel user,\r
- Collection<ReceiveCommand> commands, ReceivePack rp, Set<String> scripts) {\r
- if (scripts == null || scripts.size() == 0) {\r
- // no Groovy scripts to execute\r
- return;\r
- }\r
-\r
- Binding binding = new Binding();\r
- binding.setVariable("gitblit", GitBlit.self());\r
- binding.setVariable("repository", repository);\r
- binding.setVariable("receivePack", rp);\r
- binding.setVariable("user", user);\r
- binding.setVariable("commands", commands);\r
- binding.setVariable("url", gitblitUrl);\r
- binding.setVariable("logger", logger);\r
- binding.setVariable("clientLogger", new ClientLogger(rp));\r
- for (String script : scripts) {\r
- if (StringUtils.isEmpty(script)) {\r
- continue;\r
- }\r
- // allow script to be specified without .groovy extension\r
- // this is easier to read in the settings\r
- File file = new File(groovyDir, script);\r
- if (!file.exists() && !script.toLowerCase().endsWith(".groovy")) {\r
- file = new File(groovyDir, script + ".groovy");\r
- if (file.exists()) {\r
- script = file.getName();\r
- }\r
- }\r
- try {\r
- Object result = gse.run(script, binding);\r
- if (result instanceof Boolean) {\r
- if (!((Boolean) result)) {\r
- logger.error(MessageFormat.format(\r
- "Groovy script {0} has failed! Hook scripts aborted.", script));\r
- break;\r
- }\r
- }\r
- } catch (Exception e) {\r
- logger.error(\r
- MessageFormat.format("Failed to execute Groovy script {0}", script), e);\r
- }\r
- }\r
- }\r
-}
\ No newline at end of file
--- /dev/null
+/*\r
+ * Copyright (C) 2008-2010, Google Inc.\r
+ * and other copyright owners as documented in the project's IP log.\r
+ *\r
+ * This program and the accompanying materials are made available\r
+ * under the terms of the Eclipse Distribution License v1.0 which\r
+ * accompanies this distribution, is reproduced below, and is\r
+ * available at http://www.eclipse.org/org/documents/edl-v10.php\r
+ *\r
+ * All rights reserved.\r
+ *\r
+ * Redistribution and use in source and binary forms, with or\r
+ * without modification, are permitted provided that the following\r
+ * conditions are met:\r
+ *\r
+ * - Redistributions of source code must retain the above copyright\r
+ * notice, this list of conditions and the following disclaimer.\r
+ *\r
+ * - Redistributions in binary form must reproduce the above\r
+ * copyright notice, this list of conditions and the following\r
+ * disclaimer in the documentation and/or other materials provided\r
+ * with the distribution.\r
+ *\r
+ * - Neither the name of the Eclipse Foundation, Inc. nor the\r
+ * names of its contributors may be used to endorse or promote\r
+ * products derived from this software without specific prior\r
+ * written permission.\r
+ *\r
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND\r
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,\r
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\r
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR\r
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\r
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\r
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\r
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\r
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,\r
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\r
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\r
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
+ */\r
+\r
+package com.gitblit.git;\r
+\r
+import java.io.IOException;\r
+import java.io.OutputStream;\r
+\r
+import org.eclipse.jgit.lib.BatchingProgressMonitor;\r
+import org.eclipse.jgit.lib.Constants;\r
+\r
+/** Write progress messages out to the sideband channel. */\r
+class SideBandProgressMonitor extends BatchingProgressMonitor {\r
+ private final OutputStream out;\r
+\r
+ private boolean write;\r
+\r
+ SideBandProgressMonitor(final OutputStream os) {\r
+ out = os;\r
+ write = true;\r
+ }\r
+\r
+ @Override\r
+ protected void onUpdate(String taskName, int workCurr) {\r
+ StringBuilder s = new StringBuilder();\r
+ format(s, taskName, workCurr);\r
+ s.append(" \r"); //$NON-NLS-1$\r
+ send(s);\r
+ }\r
+\r
+ @Override\r
+ protected void onEndTask(String taskName, int workCurr) {\r
+ StringBuilder s = new StringBuilder();\r
+ format(s, taskName, workCurr);\r
+ s.append(", done\n"); //$NON-NLS-1$\r
+ send(s);\r
+ }\r
+\r
+ private void format(StringBuilder s, String taskName, int workCurr) {\r
+ s.append(taskName);\r
+ s.append(": "); //$NON-NLS-1$\r
+ s.append(workCurr);\r
+ }\r
+\r
+ @Override\r
+ protected void onUpdate(String taskName, int cmp, int totalWork, int pcnt) {\r
+ StringBuilder s = new StringBuilder();\r
+ format(s, taskName, cmp, totalWork, pcnt);\r
+ s.append(" \r"); //$NON-NLS-1$\r
+ send(s);\r
+ }\r
+\r
+ @Override\r
+ protected void onEndTask(String taskName, int cmp, int totalWork, int pcnt) {\r
+ StringBuilder s = new StringBuilder();\r
+ format(s, taskName, cmp, totalWork, pcnt);\r
+ s.append("\n"); //$NON-NLS-1$\r
+ send(s);\r
+ }\r
+\r
+ private void format(StringBuilder s, String taskName, int cmp,\r
+ int totalWork, int pcnt) {\r
+ s.append(taskName);\r
+ s.append(": "); //$NON-NLS-1$\r
+ if (pcnt < 100)\r
+ s.append(' ');\r
+ if (pcnt < 10)\r
+ s.append(' ');\r
+ s.append(pcnt);\r
+ s.append("% ("); //$NON-NLS-1$\r
+ s.append(cmp);\r
+ s.append("/"); //$NON-NLS-1$\r
+ s.append(totalWork);\r
+ s.append(")"); //$NON-NLS-1$\r
+ }\r
+\r
+ private void send(StringBuilder s) {\r
+ if (write) {\r
+ try {\r
+ out.write(Constants.encode(s.toString()));\r
+ out.flush();\r
+ } catch (IOException err) {\r
+ write = false;\r
+ }\r
+ }\r
+ }\r
+}
\ No newline at end of file
#
# Gitblit Unit Testing properties
#
-
+git.allowAnonymousPushes = true
+git.defaultAccessRestriction = NONE
git.repositoriesFolder = ${baseFolder}/git
git.searchRepositoriesSubfolders = true
git.enableGitServlet = true