diff --git a/contrib/README.md b/contrib/README.md new file mode 100644 index 000000000..1121a224a --- /dev/null +++ b/contrib/README.md @@ -0,0 +1,13 @@ +# Contrib Notes # + +The configuration script adapts according to the OS. +The `linux` directory contains scripts for Ubuntu and RedHat. +The Mac scripts have been folded in as well. +Common scripts are in this directory. + +This version of scripts has so far only been tested on Ubuntu and Mac. Someone else will have to test on RedHat. + +To run: +1. Edit `gitbucket.conf` to suit. +2. Type: `install` + diff --git a/contrib/gitbucket.conf b/contrib/gitbucket.conf new file mode 100644 index 000000000..ead333196 --- /dev/null +++ b/contrib/gitbucket.conf @@ -0,0 +1,62 @@ +# Configuration section is below. Ignore this part + +function isUbuntu { + if [ -f /etc/lsb-release ]; then + grep -i ubuntu /etc/lsb-release | head -n 1 | cut -d \ -f 1 | cut -d = -f 2 + fi +} + +function isRedHat { + if [ -d "/etc/rc.d/init.d" ]; then echo yes; fi +} + +function isMac { + if [[ "$(uname -a | cut -d \ -f 1 )" == "Darwin" ]]; then echo yes; fi +} + +# +# Configuration section start +# + +# Bind host +GITBUCKET_HOST=0.0.0.0 + +# Other Java option +GITBUCKET_JVM_OPTS=-Dmail.smtp.starttls.enable=true + +# Data directory, holds repositories +GITBUCKET_HOME=/var/lib/gitbucket + +GITBUCKET_LOG_DIR=/var/log/gitbucket + +# Server port +GITBUCKET_PORT=8080 + +# URL prefix for the GitBucket page (http://://) +GITBUCKET_PREFIX= + +# Directory where GitBucket is installed +# Configuration is stored here: +GITBUCKET_DIR=/usr/share/gitbucket +GITBUCKET_WAR_DIR=$GITBUCKET_DIR/lib + +# Path to the WAR file +GITBUCKET_WAR_FILE=$GITBUCKET_WAR_DIR/gitbucket.war + +# GitBucket version to fetch when installing +GITBUCKET_VERSION=2.1 + +# +# End of configuration section. Ignore this part +# +if [ `isUbuntu` ]; then + GITBUCKET_SERVICE=/etc/init.d/gitbucket +elif [ `isRedHat` ]; then + GITBUCKET_SERVICE=/etc/rc.d/init.d +elif [ `isMac` ]; then + GITBUCKET_SERVICE=/Library/StartupItems/GitBucket/GitBucket +else + echo "Don't know how to install onto this OS" + exit -2 +fi + diff --git a/contrib/redhat/gitbucket.init b/contrib/gitbucket.init similarity index 51% rename from contrib/redhat/gitbucket.init rename to contrib/gitbucket.init index 43e29e3e9..067564617 100644 --- a/contrib/redhat/gitbucket.init +++ b/contrib/gitbucket.init @@ -1,6 +1,8 @@ #!/bin/bash # -# /etc/rc.d/init.d/gitbucket +# RedHat: /etc/rc.d/init.d/gitbucket +# Ubuntu: /etc/init.d/gitbucket +# Mac OS/X: /Library/StartupItems/GitBucket # # Starts the GitBucket server # @@ -8,28 +10,44 @@ # description: Run GitBucket server # processname: java -# Source function library -. /etc/rc.d/init.d/functions +set -e + +[ -f /etc/rc.d/init.d/functions ] && source /etc/rc.d/init.d/functions # RedHat +[ -f /etc/rc.common ] && source /etc/rc.common # Mac OS/X # Default values GITBUCKET_HOME=/var/lib/gitbucket GITBUCKET_WAR_FILE=/usr/share/gitbucket/lib/gitbucket.war # Pull in cq settings -[ -f /etc/sysconfig/gitbucket ] && . /etc/sysconfig/gitbucket +[ -f /etc/sysconfig/gitbucket ] && source /etc/sysconfig/gitbucket # RedHat +[ -f gitbucket.conf ] && source gitbucket.conf # For all systems # Location of the log and PID file -LOG_FILE=/var/log/gitbucket/run.log +LOG_FILE=$GITBUCKET_LOG_DIR/run.log PID_FILE=/var/run/gitbucket.pid -# Default return value -RETVAL=0 +RED='\033[1m\E[37;41m' +GREEN='\033[1m\E[37;42m' +OFF='\E[0m' +if [ -z "$(which success)" ]; then + function success { + printf "%b\n" "$GREEN $* $OFF" + } +fi +if [ -z "$(which failure)" ]; then + function failure { + printf "%b\n" "$RED $* $OFF" + } +fi + +RETVAL=0 start() { echo -n $"Starting GitBucket server: " - # Compile statup parameters + START_OPTS= if [ $GITBUCKET_PORT ]; then START_OPTS="${START_OPTS} --port=${GITBUCKET_PORT}" fi @@ -40,17 +58,15 @@ start() { START_OPTS="${START_OPTS} --host=${GITBUCKET_HOST}" fi - # Run the Java process GITBUCKET_HOME="${GITBUCKET_HOME}" java $GITBUCKET_JVM_OPTS -jar $GITBUCKET_WAR_FILE $START_OPTS >>$LOG_FILE 2>&1 & RETVAL=$? - # Store PID of the Java process into a file echo $! > $PID_FILE if [ $RETVAL -eq 0 ] ; then - success "GitBucket startup" + success "Success" else - failure "GitBucket startup" + failure "Exit code $RETVAL" fi echo @@ -82,25 +98,41 @@ restart() { start } - -case "$1" in -start) +## MacOS proxies for System V service hooks: +StartService() { start - ;; -stop) +} + +StopService() { stop - ;; -restart) +} + +RestartService() { restart - ;; -status) - status -p $PID_FILE java - RETVAL=$? - ;; -*) - echo $"Usage: $0 [start|stop|restart|status]" - RETVAL=2 -esac +} -exit $RETVAL +if [ `isMac` ]; then + RunService "$1" +else + case "$1" in + start) + start + ;; + stop) + stop + ;; + restart) + restart + ;; + status) + status -p $PID_FILE java + RETVAL=$? + ;; + *) + echo $"Usage: $0 [start|stop|restart|status]" + RETVAL=2 + esac + exit $RETVAL +fi + diff --git a/contrib/install b/contrib/install new file mode 100755 index 000000000..860b02fcb --- /dev/null +++ b/contrib/install @@ -0,0 +1,69 @@ +#!/bin/bash + +# Only tested on Ubuntu 14.04 + +# Uses information stored in GitBucket git repo on GitHub as defaults. +# Edit gitbucket.conf before running this + +set -e + +GITBUCKET_VERSION=2.1 + +if [ ! -f gitbucket.conf ]; then + echo "gitbucket.conf not found, aborting" + exit -3 +fi +source gitbucket.conf + +function createDir { + if [ ! -d "$1" ]; then + echo "Making $1 directory." + sudo mkdir -p "$1" + fi +} + +if [ "$(which iptables)" ]; then + echo "Opening port $GITBUCKET_PORT in firewall." + sudo iptables -A INPUT -p tcp --dport $GITBUCKET_PORT -j ACCEPT + echo "Please use iptables-persistent:" + echo " sudo apt-get install iptables-persistent" + echo "After installed, you can save/reload iptables rules anytime:" + echo " sudo /etc/init.d/iptables-persistent save" + echo " sudo /etc/init.d/iptables-persistent reload" +fi + +createDir "$GITBUCKET_HOME" +createDir "$GITBUCKET_WAR_DIR" +createDir "$GITBUCKET_DIR" +createDir "$GITBUCKET_LOG_DIR" + +echo "Fetching GitBucket v$GITBUCKET_VERSION and saving as $GITBUCKET_WAR_FILE" +sudo wget -qO "$GITBUCKET_WAR_FILE" https://github.com/takezoe/gitbucket/releases/download/$GITBUCKET_VERSION/gitbucket.war + +sudo rm -f "$GITBUCKET_LOG_DIR/run.log" + +echo "Copying gitbucket.conf to $GITBUCKET_DIR" +sudo cp gitbucket.conf $GITBUCKET_DIR +if [ `isUbuntu` ] || [ `isRedHat` ]; then + sudo cp gitbucket.init "$GITBUCKET_SERVICE" + # Install gitbucket as a service that starts when system boots + sudo chown root:root $GITBUCKET_SERVICE + sudo chmod 755 $GITBUCKET_SERVICE + sudo update-rc.d "$(basename $GITBUCKET_SERVICE)" defaults 98 02 + echo "Starting GitBucket service" + sudo $GITBUCKET_SERVICE start +elif [ `isMac` ]; then + sudo macosx/makePlist + echo "Starting GitBucket service" + sudo cp gitbucket.conf "$GITBUCKET_SERVICE" + sudo cp gitbucket.init "$GITBUCKET_SERVICE" + sudo chmod a+x "$GITBUCKET_SERVICE" + sudo "$GITBUCKET_SERVICE" start +else + echo "Don't know how to install this OS" + exit -2 +fi + +if [ $? != 0 ]; then + less "$GITBUCKET_LOG_DIR/run.log" +fi diff --git a/contrib/redhat/gitbucket.spec b/contrib/linux/redhat/gitbucket.spec similarity index 100% rename from contrib/redhat/gitbucket.spec rename to contrib/linux/redhat/gitbucket.spec diff --git a/contrib/macosx/gitbucket.plist b/contrib/macosx/makePlist old mode 100644 new mode 100755 similarity index 56% rename from contrib/macosx/gitbucket.plist rename to contrib/macosx/makePlist index 827c15a1d..3f874ef8e --- a/contrib/macosx/gitbucket.plist +++ b/contrib/macosx/makePlist @@ -1,3 +1,10 @@ +#!/bin/bash + +# From http://docstore.mik.ua/orelly/unix3/mac/ch02_02.htm +source gitbucket.conf +GITBUCKET_SERVICE_DIR=`dirname "$GITBUCKET_SERVICE"` +mkdir -p "$GITBUCKET_SERVICE_DIR" +cat << EOF > "$GITBUCKET_SERVICE_DIR/gitbucket.plist" @@ -7,14 +14,15 @@ ProgramArguments /usr/bin/java - -Dmail.smtp.starttls.enable=true + $GITBUCKET_JVM_OPTS -jar gitbucket.war - --host=127.0.0.1 - --port=8080 + --host=$GITBUCKET_HOST + --port=$GITBUCKET_PORT --https=true RunAtLoad +EOF \ No newline at end of file diff --git a/contrib/redhat/gitbucket.conf b/contrib/redhat/gitbucket.conf deleted file mode 100644 index 103778ec6..000000000 --- a/contrib/redhat/gitbucket.conf +++ /dev/null @@ -1,17 +0,0 @@ -# Bind host -#GITBUCKET_HOST=0.0.0.0 - -# Server port -#GITBUCKET_PORT=8080 - -# Data directory (GITBUCKET_HOME/gitbucket) -#GITBUCKET_HOME=/var/lib/gitbucket - -# Path to the WAR file -#GITBUCKET_WAR_FILE=/usr/share/gitbucket/lib/gitbucket.war - -# URL prefix for the GitBucket page (http://://) -#GITBUCKET_PREFIX= - -# Other Java option -#GITBUCKET_JVM_OPTS= diff --git a/src/main/java/JettyLauncher.java b/src/main/java/JettyLauncher.java index 76500f045..6300547c5 100644 --- a/src/main/java/JettyLauncher.java +++ b/src/main/java/JettyLauncher.java @@ -1,10 +1,8 @@ -import org.eclipse.jetty.io.EndPoint; -import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.nio.SelectChannelConnector; import org.eclipse.jetty.webapp.WebAppContext; -import java.io.IOException; +import java.io.File; import java.net.URL; import java.security.ProtectionDomain; @@ -44,6 +42,14 @@ public class JettyLauncher { server.addConnector(connector); WebAppContext context = new WebAppContext(); + + File tmpDir = new File(getGitBucketHome(), "tmp"); + if(tmpDir.exists()){ + deleteDirectory(tmpDir); + } + tmpDir.mkdirs(); + context.setTempDirectory(tmpDir); + ProtectionDomain domain = JettyLauncher.class.getProtectionDomain(); URL location = domain.getCodeSource().getLocation(); @@ -59,4 +65,27 @@ public class JettyLauncher { server.start(); server.join(); } + + private static File getGitBucketHome(){ + String home = System.getProperty("gitbucket.home"); + if(home != null && home.length() > 0){ + return new File(home); + } + home = System.getenv("GITBUCKET_HOME"); + if(home != null && home.length() > 0){ + return new File(home); + } + return new File(System.getProperty("user.home"), ".gitbucket"); + } + + private static void deleteDirectory(File dir){ + for(File file: dir.listFiles()){ + if(file.isFile()){ + file.delete(); + } else if(file.isDirectory()){ + deleteDirectory(file); + } + } + dir.delete(); + } } diff --git a/src/main/scala/app/AccountController.scala b/src/main/scala/app/AccountController.scala index a908d7644..6e7d1c37d 100644 --- a/src/main/scala/app/AccountController.scala +++ b/src/main/scala/app/AccountController.scala @@ -335,7 +335,7 @@ trait AccountControllerBase extends AccountManagementControllerBase { builder.finish() JGitUtil.createNewCommit(git, inserter, headId, builder.getDirCache.writeTree(inserter), - loginAccount.fullName, loginAccount.mailAddress, "Initial commit") + Constants.HEAD, loginAccount.fullName, loginAccount.mailAddress, "Initial commit") } } diff --git a/src/main/scala/app/RepositoryViewerController.scala b/src/main/scala/app/RepositoryViewerController.scala index 702590a2f..42917306d 100644 --- a/src/main/scala/app/RepositoryViewerController.scala +++ b/src/main/scala/app/RepositoryViewerController.scala @@ -191,6 +191,7 @@ trait RepositoryViewerControllerBase extends ControllerBase { using(Git.open(getRepositoryDir(repository.owner, repository.name))){ git => val revCommit = JGitUtil.getRevCommitFromId(git, git.getRepository.resolve(id)) + val lastModifiedCommit = JGitUtil.getLastModifiedCommit(git, revCommit, path) getPathObjectId(git, path, revCommit).map { objectId => if(raw){ // Download @@ -200,7 +201,7 @@ trait RepositoryViewerControllerBase extends ControllerBase { } } else { repo.html.blob(id, repository, path.split("/").toList, JGitUtil.getContentInfo(git, path, objectId), - new JGitUtil.CommitInfo(revCommit), hasWritePermission(repository.owner, repository.name, context.loginAccount)) + new JGitUtil.CommitInfo(lastModifiedCommit), hasWritePermission(repository.owner, repository.name, context.loginAccount)) } } getOrElse NotFound } @@ -311,10 +312,10 @@ trait RepositoryViewerControllerBase extends ControllerBase { repo.html.guide(repository, hasWritePermission(repository.owner, repository.name, context.loginAccount)) } else { using(Git.open(getRepositoryDir(repository.owner, repository.name))){ git => - //val revisions = Seq(if(revstr.isEmpty) repository.repository.defaultBranch else revstr, repository.branchList.head) // get specified commit JGitUtil.getDefaultBranch(git, repository, revstr).map { case (objectId, revision) => defining(JGitUtil.getRevCommitFromId(git, objectId)) { revCommit => + val lastModifiedCommit = if(path == ".") revCommit else JGitUtil.getLastModifiedCommit(git, revCommit, path) // get files val files = JGitUtil.getFileList(git, revision, path) val parentPath = if (path == ".") Nil else path.split("/").toList @@ -329,7 +330,7 @@ trait RepositoryViewerControllerBase extends ControllerBase { repo.html.files(revision, repository, if(path == ".") Nil else path.split("/").toList, // current path - new JGitUtil.CommitInfo(revCommit), // latest commit + new JGitUtil.CommitInfo(lastModifiedCommit), // last modified commit files, readme, hasWritePermission(repository.owner, repository.name, context.loginAccount)) } } getOrElse NotFound @@ -350,7 +351,7 @@ trait RepositoryViewerControllerBase extends ControllerBase { val builder = DirCache.newInCore.builder() val inserter = git.getRepository.newObjectInserter() val headName = s"refs/heads/${branch}" - val headTip = git.getRepository.resolve(s"refs/heads/${branch}") + val headTip = git.getRepository.resolve(headName) JGitUtil.processTree(git, headTip){ (path, tree) => if(!newPath.exists(_ == path) && !oldPath.exists(_ == path)){ @@ -365,7 +366,7 @@ trait RepositoryViewerControllerBase extends ControllerBase { builder.finish() val commitId = JGitUtil.createNewCommit(git, inserter, headTip, builder.getDirCache.writeTree(inserter), - loginAccount.fullName, loginAccount.mailAddress, message) + headName, loginAccount.fullName, loginAccount.mailAddress, message) inserter.flush() inserter.release() diff --git a/src/main/scala/service/WikiService.scala b/src/main/scala/service/WikiService.scala index ac9dbd69a..add502131 100644 --- a/src/main/scala/service/WikiService.scala +++ b/src/main/scala/service/WikiService.scala @@ -182,7 +182,8 @@ trait WikiService { } builder.finish() - JGitUtil.createNewCommit(git, inserter, headId, builder.getDirCache.writeTree(inserter), committer.fullName, committer.mailAddress, + JGitUtil.createNewCommit(git, inserter, headId, builder.getDirCache.writeTree(inserter), + Constants.HEAD, committer.fullName, committer.mailAddress, pageName match { case Some(x) => s"Revert ${from} ... ${to} on ${x}" case None => s"Revert ${from} ... ${to}" @@ -229,7 +230,8 @@ trait WikiService { if(created || updated || removed){ builder.add(JGitUtil.createDirCacheEntry(newPageName + ".md", FileMode.REGULAR_FILE, inserter.insert(Constants.OBJ_BLOB, content.getBytes("UTF-8")))) builder.finish() - val newHeadId = JGitUtil.createNewCommit(git, inserter, headId, builder.getDirCache.writeTree(inserter), committer.fullName, committer.mailAddress, + val newHeadId = JGitUtil.createNewCommit(git, inserter, headId, builder.getDirCache.writeTree(inserter), + Constants.HEAD, committer.fullName, committer.mailAddress, if(message.trim.length == 0) { if(removed){ s"Rename ${currentPageName} to ${newPageName}" @@ -269,7 +271,8 @@ trait WikiService { } if(removed){ builder.finish() - JGitUtil.createNewCommit(git, inserter, headId, builder.getDirCache.writeTree(inserter), committer, mailAddress, message) + JGitUtil.createNewCommit(git, inserter, headId, builder.getDirCache.writeTree(inserter), + Constants.HEAD, committer, mailAddress, message) } } } diff --git a/src/main/scala/util/JGitUtil.scala b/src/main/scala/util/JGitUtil.scala index b4083a4a9..811ee74d0 100644 --- a/src/main/scala/util/JGitUtil.scala +++ b/src/main/scala/util/JGitUtil.scala @@ -504,7 +504,7 @@ object JGitUtil { } def createNewCommit(git: Git, inserter: ObjectInserter, headId: AnyObjectId, treeId: AnyObjectId, - fullName: String, mailAddress: String, message: String): ObjectId = { + ref: String, fullName: String, mailAddress: String, message: String): ObjectId = { val newCommit = new CommitBuilder() newCommit.setCommitter(new PersonIdent(fullName, mailAddress)) newCommit.setAuthor(new PersonIdent(fullName, mailAddress)) @@ -518,7 +518,7 @@ object JGitUtil { inserter.flush() inserter.release() - val refUpdate = git.getRepository.updateRef(Constants.HEAD) + val refUpdate = git.getRepository.updateRef(ref) refUpdate.setNewObjectId(newHeadId) refUpdate.update() @@ -652,4 +652,15 @@ object JGitUtil { }.head.id } + /** + * Returns the last modified commit of specified path + * @param git the Git object + * @param startCommit the search base commit id + * @param path the path of target file or directory + * @return the last modified commit of specified path + */ + def getLastModifiedCommit(git: Git, startCommit: RevCommit, path: String): RevCommit = { + return git.log.add(startCommit).addPath(path).setMaxCount(1).call.iterator.next + } + } diff --git a/src/main/scala/view/helpers.scala b/src/main/scala/view/helpers.scala index 6ac6f87b6..78f658f62 100644 --- a/src/main/scala/view/helpers.scala +++ b/src/main/scala/view/helpers.scala @@ -1,5 +1,5 @@ package view -import java.util.Date +import java.util.{Date, TimeZone} import java.text.SimpleDateFormat import play.twirl.api.Html import util.StringUtil @@ -18,7 +18,11 @@ object helpers extends AvatarImageProvider with LinkConverter with RequestCache /** * Format java.util.Date to "yyyy-MM-dd'T'hh:mm:ss'Z'". */ - def datetimeRFC3339(date: Date): String = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss'Z'").format(date).replaceAll("(\\d\\d)(\\d\\d)$","$1:$2") + def datetimeRFC3339(date: Date): String = { + val sf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'") + sf.setTimeZone(TimeZone.getTimeZone("UTC")) + sf.format(date) + } /** * Format java.util.Date to "yyyy-MM-dd". diff --git a/src/main/twirl/helper/copy.scala.html b/src/main/twirl/helper/copy.scala.html index b8f99c073..7a09a52fd 100644 --- a/src/main/twirl/helper/copy.scala.html +++ b/src/main/twirl/helper/copy.scala.html @@ -6,6 +6,24 @@