mirror of
https://github.com/gitbucket/gitbucket.git
synced 2026-05-09 06:56:45 +02:00
Compare commits
79 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a6e7761141 | ||
|
|
eb053a66d7 | ||
|
|
5257c83563 | ||
|
|
04bc92001f | ||
|
|
d939082e1f | ||
|
|
a943a5985d | ||
|
|
9e19821256 | ||
|
|
455183a13e | ||
|
|
c241c08904 | ||
|
|
08389cb1a0 | ||
|
|
94f9d42fc4 | ||
|
|
f35ecce3c7 | ||
|
|
2837bb40b0 | ||
|
|
54331f976d | ||
|
|
b975e74de3 | ||
|
|
c45ab34f43 | ||
|
|
4ee442f697 | ||
|
|
3c25d322f2 | ||
|
|
265c6b3e0f | ||
|
|
fad4503aec | ||
|
|
d8f70bfde3 | ||
|
|
d7dfb44efc | ||
|
|
409330a9fb | ||
|
|
0fd7e07831 | ||
|
|
91bd26d2a9 | ||
|
|
e5c4cf3298 | ||
|
|
c874d3fd84 | ||
|
|
c06f95256e | ||
|
|
2f1e05833e | ||
|
|
719cad00d6 | ||
|
|
b372c71fbf | ||
|
|
6b252a7018 | ||
|
|
6575258b6c | ||
|
|
d33280f9af | ||
|
|
863bb80ad1 | ||
|
|
e4266f31a6 | ||
|
|
0405fccb69 | ||
|
|
9a41adcec8 | ||
|
|
1d54920165 | ||
|
|
7ace37cd07 | ||
|
|
eb6398654d | ||
|
|
10a4c3e2a4 | ||
|
|
d494014011 | ||
|
|
91bf562b91 | ||
|
|
ec3961555f | ||
|
|
33b46869b6 | ||
|
|
88db21ef07 | ||
|
|
d53948f4a9 | ||
|
|
f97992a776 | ||
|
|
f9d99703cb | ||
|
|
b015cdde74 | ||
|
|
92b35bd458 | ||
|
|
3c8026f135 | ||
|
|
6a3f51a784 | ||
|
|
160c4a8a72 | ||
|
|
c4ff760bda | ||
|
|
aaed8f595a | ||
|
|
3c0a2e8385 | ||
|
|
0eef0f9aa5 | ||
|
|
169e2f16fb | ||
|
|
3800391a0e | ||
|
|
1f564808d5 | ||
|
|
433639dd04 | ||
|
|
f1e4116672 | ||
|
|
6cf00c5c66 | ||
|
|
71248cd9b7 | ||
|
|
841e6d110c | ||
|
|
f7defffeab | ||
|
|
13e6f5f6cf | ||
|
|
130cbf0b24 | ||
|
|
642d85b6bf | ||
|
|
8fe7f85e1a | ||
|
|
d1fb794783 | ||
|
|
e7aedb405a | ||
|
|
9dc148dace | ||
|
|
8ad0b25023 | ||
|
|
f648d60abb | ||
|
|
bf90d2842b | ||
|
|
7004c4f44c |
6
.github/ISSUE_TEMPLATE.md
vendored
6
.github/ISSUE_TEMPLATE.md
vendored
@@ -1,8 +1,8 @@
|
||||
### Before submitting an issue to GitBucket I have first:
|
||||
|
||||
- [] read the [contribution guidelines](https://github.com/gitbucket/gitbucket/blob/master/.github/CONTRIBUTING.md)
|
||||
- [] searched for similar already existing issue
|
||||
- [] read the documentation and [wiki](https://github.com/gitbucket/gitbucket/wiki)
|
||||
- [ ] read the [contribution guidelines](https://github.com/gitbucket/gitbucket/blob/master/.github/CONTRIBUTING.md)
|
||||
- [ ] searched for similar already existing issue
|
||||
- [ ] read the documentation and [wiki](https://github.com/gitbucket/gitbucket/wiki)
|
||||
|
||||
*(if you have performed all the above, remove the paragraph and continue describing the issue with template below)*
|
||||
|
||||
|
||||
12
.github/PULL_REQUEST_TEMPLATE.md
vendored
12
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -1,8 +1,8 @@
|
||||
### Before submitting a pull-request to GitBucket I have first:
|
||||
|
||||
- [] read the [contribution guidelines](https://github.com/gitbucket/gitbucket/blob/master/.github/CONTRIBUTING.md)
|
||||
- [] rebased my branch over master
|
||||
- [] verified that project is compiling
|
||||
- [] verified that tests are passing
|
||||
- [] squashed my commits as appropriate *(keep several commits if it is relevant to understand the PR)*
|
||||
- [] [marked as closed using commit message](https://help.github.com/articles/closing-issues-via-commit-messages/) all issue ID that this PR should correct
|
||||
- [ ] read the [contribution guidelines](https://github.com/gitbucket/gitbucket/blob/master/.github/CONTRIBUTING.md)
|
||||
- [ ] rebased my branch over master
|
||||
- [ ] verified that project is compiling
|
||||
- [ ] verified that tests are passing
|
||||
- [ ] squashed my commits as appropriate *(keep several commits if it is relevant to understand the PR)*
|
||||
- [ ] [marked as closed using commit message](https://help.github.com/articles/closing-issues-via-commit-messages/) all issue ID that this PR should correct
|
||||
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -24,3 +24,7 @@ project/plugins/project/
|
||||
.idea/
|
||||
.idea_modules/
|
||||
*.iml
|
||||
|
||||
# Metals specific
|
||||
.metals
|
||||
.bloop
|
||||
@@ -1,8 +1,6 @@
|
||||
language: scala
|
||||
sudo: true
|
||||
jdk:
|
||||
- oraclejdk8
|
||||
- oraclejdk11
|
||||
- openjdk8
|
||||
- openjdk11
|
||||
script:
|
||||
|
||||
22
CHANGELOG.md
22
CHANGELOG.md
@@ -1,6 +1,28 @@
|
||||
# Changelog
|
||||
All changes to the project will be documented in this file.
|
||||
|
||||
### 4.33.0 - 31 Dec 2019
|
||||
|
||||
- All CLI options are configurable by environmental variables
|
||||
- Folding pull request files
|
||||
- WebHook security options
|
||||
- Add asignee and asignees properties to some Web APIs' response
|
||||
|
||||
### 4.32.0 - 7 Aug 2019
|
||||
|
||||
- Bump to Scala 2.13.0 and Scalatra 2.7.0
|
||||
- Draft pull request
|
||||
- Drop network installation of plugins
|
||||
- Compare view works for commit id
|
||||
- Apply default priority to pull requests
|
||||
- Focus title after clicking issue / pull request edit button
|
||||
|
||||
### 4.31.2 - 7 Apr 2019
|
||||
- Bug and security fix
|
||||
|
||||
### 4.31.1 - 17 Mar 2019
|
||||
- Bug fix
|
||||
|
||||
### 4.31.0 - 17 Mar 2019
|
||||
- Docker support in CI plugin
|
||||
- Verify GPG key signed commit
|
||||
|
||||
21
README.md
21
README.md
@@ -1,4 +1,4 @@
|
||||
GitBucket [](https://gitter.im/gitbucket/gitbucket) [](https://travis-ci.org/gitbucket/gitbucket) [](https://maven-badges.herokuapp.com/maven-central/io.github.gitbucket/gitbucket_2.12) [](https://github.com/gitbucket/gitbucket/blob/master/LICENSE)
|
||||
GitBucket [](https://gitter.im/gitbucket/gitbucket) [](https://travis-ci.org/gitbucket/gitbucket) [](https://maven-badges.herokuapp.com/maven-central/io.github.gitbucket/gitbucket_2.13) [](https://github.com/gitbucket/gitbucket/blob/master/LICENSE)
|
||||
=========
|
||||
|
||||
GitBucket is a Git web platform powered by Scala offering:
|
||||
@@ -22,7 +22,7 @@ The current version of GitBucket provides many features such as:
|
||||
- Account and group management with LDAP integration
|
||||
- a Plug-in system
|
||||
|
||||
If you want to try the development version of GitBucket, see the [Developer's Guide](https://github.com/gitbucket/gitbucket/blob/master/doc/how_to_run.md).
|
||||
If you want to try the development version of GitBucket, see the [Developer's Guide](https://github.com/gitbucket/gitbucket/blob/master/doc/readme.md).
|
||||
|
||||
Installation
|
||||
--------
|
||||
@@ -39,10 +39,11 @@ You can specify following options:
|
||||
- `--gitbucket.home=[DATA_DIR]`
|
||||
- `--temp_dir=[TEMP_DIR]`
|
||||
- `--max_file_size=[MAX_FILE_SIZE]`
|
||||
- `--upload_timeout=[MAX_UPLOAD_TIMEOUT]`
|
||||
|
||||
`TEMP_DIR` is used as the [temporary directory for the jetty application context](https://www.eclipse.org/jetty/documentation/9.3.x/ref-temporary-directories.html). This is the directory into which the `gitbucket.war` file is unpacked, the source files are compiled, etc. If given this parameter **must** match the path of an existing directory or the application will quit reporting an error; if not given the path used will be a `tmp` directory inside the gitbucket home.
|
||||
|
||||
`MAX_FILE_SIZE` is the max file size for upload files.
|
||||
`MAX_FILE_SIZE` is the max file size in bytes for upload files *( default is 3 MB -> 3 x 1024 x 1024 )*.
|
||||
|
||||
You can also deploy `gitbucket.war` to a servlet container which supports Servlet 3.0 (like Jetty, Tomcat, JBoss, etc)
|
||||
|
||||
@@ -68,13 +69,13 @@ Support
|
||||
- If you can't find same question and report, send it to [gitter room](https://gitter.im/gitbucket/gitbucket) before raising an issue.
|
||||
- The highest priority of GitBucket is the ease of installation and API compatibility with GitHub, so your feature request might be rejected if they go against those principles.
|
||||
|
||||
What's New in 4.31.x
|
||||
What's New in 4.32.x
|
||||
-------------
|
||||
### 4.31.0 - 17 Mar 2019
|
||||
- Docker support in CI plugin
|
||||
- Verify GPG key signed commit
|
||||
- OAuth2 Token (sent as a parameter) authentication support and new APIs in Web API
|
||||
- OGP (Open Graph protocol) support
|
||||
- Username completion with avatars
|
||||
### 4.33.0 - 31 Dec 2019
|
||||
|
||||
- All CLI options are configurable by environmental variables
|
||||
- Folding pull request files
|
||||
- WebHook security options
|
||||
- Add asignee and asignees properties to some Web APIs' response
|
||||
|
||||
See the [change log](CHANGELOG.md) for all of the updates.
|
||||
|
||||
75
build.sbt
75
build.sbt
@@ -3,10 +3,10 @@ import com.typesafe.sbt.pgp.PgpKeys._
|
||||
|
||||
val Organization = "io.github.gitbucket"
|
||||
val Name = "gitbucket"
|
||||
val GitBucketVersion = "4.31.0"
|
||||
val ScalatraVersion = "2.6.3"
|
||||
val JettyVersion = "9.4.14.v20181114"
|
||||
val JgitVersion = "5.2.0.201812061821-r"
|
||||
val GitBucketVersion = "4.33.0"
|
||||
val ScalatraVersion = "2.7.0-RC1"
|
||||
val JettyVersion = "9.4.25.v20191220"
|
||||
val JgitVersion = "5.6.0.201912101111-r"
|
||||
|
||||
lazy val root = (project in file("."))
|
||||
.enablePlugins(SbtTwirl, ScalatraPlugin)
|
||||
@@ -17,7 +17,7 @@ sourcesInBase := false
|
||||
organization := Organization
|
||||
name := Name
|
||||
version := GitBucketVersion
|
||||
scalaVersion := "2.12.8"
|
||||
scalaVersion := "2.13.1"
|
||||
|
||||
scalafmtOnCompile := true
|
||||
|
||||
@@ -27,9 +27,7 @@ coverageExcludedPackages := ".*\\.html\\..*"
|
||||
resolvers ++= Seq(
|
||||
Classpaths.typesafeReleases,
|
||||
Resolver.jcenterRepo,
|
||||
"amateras" at "http://amateras.sourceforge.jp/mvn/",
|
||||
"sonatype-snapshot" at "https://oss.sonatype.org/content/repositories/snapshots/",
|
||||
"amateras-snapshot" at "http://amateras.sourceforge.jp/mvn-snapshot/"
|
||||
"sonatype-snapshot" at "https://oss.sonatype.org/content/repositories/snapshots/"
|
||||
)
|
||||
|
||||
libraryDependencies ++= Seq(
|
||||
@@ -38,28 +36,29 @@ libraryDependencies ++= Seq(
|
||||
"org.scalatra" %% "scalatra" % ScalatraVersion,
|
||||
"org.scalatra" %% "scalatra-json" % ScalatraVersion,
|
||||
"org.scalatra" %% "scalatra-forms" % ScalatraVersion,
|
||||
"org.json4s" %% "json4s-jackson" % "3.5.2",
|
||||
"org.json4s" %% "json4s-jackson" % "3.6.7",
|
||||
"commons-io" % "commons-io" % "2.6",
|
||||
"io.github.gitbucket" % "solidbase" % "1.0.3",
|
||||
"io.github.gitbucket" % "markedj" % "1.0.15",
|
||||
"org.apache.commons" % "commons-compress" % "1.18",
|
||||
"io.github.gitbucket" % "markedj" % "1.0.16",
|
||||
"org.apache.commons" % "commons-compress" % "1.19",
|
||||
"org.apache.commons" % "commons-email" % "1.5",
|
||||
"org.apache.httpcomponents" % "httpclient" % "4.5.6",
|
||||
"commons-net" % "commons-net" % "3.6",
|
||||
"org.apache.httpcomponents" % "httpclient" % "4.5.10",
|
||||
"org.apache.sshd" % "apache-sshd" % "2.1.0" exclude ("org.slf4j", "slf4j-jdk14") exclude ("org.apache.sshd", "sshd-mina") exclude ("org.apache.sshd", "sshd-netty"),
|
||||
"org.apache.tika" % "tika-core" % "1.19.1",
|
||||
"com.github.takezoe" %% "blocking-slick-32" % "0.0.11",
|
||||
"org.apache.tika" % "tika-core" % "1.23",
|
||||
"com.github.takezoe" %% "blocking-slick-32" % "0.0.12",
|
||||
"com.novell.ldap" % "jldap" % "2009-10-07",
|
||||
"com.h2database" % "h2" % "1.4.197",
|
||||
"org.mariadb.jdbc" % "mariadb-java-client" % "2.3.0",
|
||||
"org.postgresql" % "postgresql" % "42.2.5",
|
||||
"com.h2database" % "h2" % "1.4.199",
|
||||
"org.mariadb.jdbc" % "mariadb-java-client" % "2.5.2",
|
||||
"org.postgresql" % "postgresql" % "42.2.6",
|
||||
"ch.qos.logback" % "logback-classic" % "1.2.3",
|
||||
"com.zaxxer" % "HikariCP" % "3.2.0",
|
||||
"com.typesafe" % "config" % "1.3.3",
|
||||
"com.typesafe.akka" %% "akka-actor" % "2.5.18",
|
||||
"fr.brouillard.oss.security.xhub" % "xhub4j-core" % "1.0.0",
|
||||
"com.zaxxer" % "HikariCP" % "3.4.1",
|
||||
"com.typesafe" % "config" % "1.4.0",
|
||||
"com.typesafe.akka" %% "akka-actor" % "2.5.27",
|
||||
"fr.brouillard.oss.security.xhub" % "xhub4j-core" % "1.1.0",
|
||||
"com.github.bkromhout" % "java-diff-utils" % "2.1.1",
|
||||
"org.cache2k" % "cache2k-all" % "1.2.0.Final",
|
||||
"com.enragedginger" %% "akka-quartz-scheduler" % "1.7.0-akka-2.5.x" exclude ("c3p0", "c3p0") exclude ("com.zaxxer", "HikariCP-java6"),
|
||||
"org.cache2k" % "cache2k-all" % "1.2.4.Final",
|
||||
"com.enragedginger" %% "akka-quartz-scheduler" % "1.8.1-akka-2.5.x" exclude ("com.mchange", "c3p0") exclude ("com.zaxxer", "HikariCP-java6"),
|
||||
"net.coobird" % "thumbnailator" % "0.4.8",
|
||||
"com.github.zafarkhaja" % "java-semver" % "0.9.0",
|
||||
"com.nimbusds" % "oauth2-oidc-sdk" % "5.64.4",
|
||||
@@ -67,17 +66,17 @@ libraryDependencies ++= Seq(
|
||||
"javax.servlet" % "javax.servlet-api" % "3.1.0" % "provided",
|
||||
"junit" % "junit" % "4.12" % "test",
|
||||
"org.scalatra" %% "scalatra-scalatest" % ScalatraVersion % "test",
|
||||
"org.mockito" % "mockito-core" % "2.23.4" % "test",
|
||||
"com.dimafeng" %% "testcontainers-scala" % "0.22.0" % "test",
|
||||
"org.testcontainers" % "mysql" % "1.10.3" % "test",
|
||||
"org.testcontainers" % "postgresql" % "1.10.3" % "test",
|
||||
"org.mockito" % "mockito-core" % "3.2.4" % "test",
|
||||
"com.dimafeng" %% "testcontainers-scala" % "0.34.2" % "test",
|
||||
"org.testcontainers" % "mysql" % "1.12.4" % "test",
|
||||
"org.testcontainers" % "postgresql" % "1.12.4" % "test",
|
||||
"net.i2p.crypto" % "eddsa" % "0.3.0",
|
||||
"is.tagomor.woothee" % "woothee-java" % "1.8.0",
|
||||
"is.tagomor.woothee" % "woothee-java" % "1.10.1",
|
||||
"org.ec4j.core" % "ec4j-core" % "0.0.3"
|
||||
)
|
||||
|
||||
// Compiler settings
|
||||
scalacOptions := Seq("-deprecation", "-language:postfixOps", "-opt:l:method", "-Xfuture")
|
||||
scalacOptions := Seq("-deprecation", "-language:postfixOps", "-opt:l:method")
|
||||
javacOptions in compile ++= Seq("-target", "8", "-source", "8")
|
||||
javaOptions in Jetty += "-Dlogback.configurationFile=/logback-dev.xml"
|
||||
|
||||
@@ -165,8 +164,8 @@ executableKey := {
|
||||
plugins.foreach { plugin =>
|
||||
plugin.trim.split(":") match {
|
||||
case Array(pluginId, pluginVersion) =>
|
||||
val url = "https://plugins.gitbucket-community.org/releases/" +
|
||||
s"gitbucket-${pluginId}-plugin/gitbucket-${pluginId}-plugin-gitbucket_${version.value}-${pluginVersion}.jar"
|
||||
val url = "https://github.com/" +
|
||||
s"gitbucket/gitbucket-${pluginId}-plugin/releases/download/${pluginVersion}/gitbucket-${pluginId}-plugin-${pluginVersion}.jar"
|
||||
log info s"Download: ${url}"
|
||||
IO transfer (new java.net.URL(url).openStream, pluginsDir / url.substring(url.lastIndexOf("/") + 1))
|
||||
case _ => ()
|
||||
@@ -258,3 +257,17 @@ licenseOverrides := {
|
||||
case DepModuleInfo("com.github.bkromhout", "java-diff-utils", _) =>
|
||||
LicenseInfo(LicenseCategory.Apache, "Apache-2.0", "http://www.apache.org/licenses/LICENSE-2.0")
|
||||
}
|
||||
|
||||
testOptions in Test ++= {
|
||||
if (scala.util.Properties.isWin) {
|
||||
Seq(
|
||||
Tests.Exclude(
|
||||
Set(
|
||||
"gitbucket.core.GitBucketCoreModuleSpec"
|
||||
)
|
||||
)
|
||||
)
|
||||
} else {
|
||||
Nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ To build war file, run the following command:
|
||||
$ sbt package
|
||||
```
|
||||
|
||||
`gitbucket_2.12-x.x.x.war` is generated into `target/scala-2.12`.
|
||||
`gitbucket_2.13-x.x.x.war` is generated into `target/scala-2.13`.
|
||||
|
||||
To build an executable war file, run
|
||||
|
||||
@@ -58,4 +58,4 @@ If you don't have docker, you can skip docker tests which require docker as foll
|
||||
|
||||
```shell
|
||||
$ sbt "testOnly * -- -l ExternalDBTest"
|
||||
```
|
||||
```
|
||||
|
||||
@@ -1 +1 @@
|
||||
sbt.version=1.2.6
|
||||
sbt.version=1.3.5
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
scalacOptions ++= Seq("-unchecked", "-deprecation", "-feature")
|
||||
|
||||
addSbtPlugin("com.geirsson" % "sbt-scalafmt" % "1.5.0")
|
||||
addSbtPlugin("com.typesafe.sbt" % "sbt-twirl" % "1.3.15")
|
||||
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.9")
|
||||
addSbtPlugin("com.geirsson" % "sbt-scalafmt" % "1.5.1")
|
||||
addSbtPlugin("com.typesafe.sbt" % "sbt-twirl" % "1.5.0")
|
||||
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.10")
|
||||
addSbtPlugin("org.scalatra.sbt" % "sbt-scalatra" % "1.0.3")
|
||||
addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.1.2")
|
||||
addSbtPlugin("com.typesafe.sbt" % "sbt-license-report" % "1.2.0")
|
||||
addSbtPlugin("net.virtual-void" % "sbt-dependency-graph" % "0.9.2")
|
||||
addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.5.1")
|
||||
|
||||
addSbtCoursier
|
||||
addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.6.1")
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
addSbtPlugin("io.get-coursier" % "sbt-coursier" % "1.0.0")
|
||||
@@ -16,12 +16,17 @@ public class JettyLauncher {
|
||||
System.setProperty("java.awt.headless", "true");
|
||||
|
||||
String host = null;
|
||||
int port = 8080;
|
||||
String port = null;
|
||||
InetSocketAddress address = null;
|
||||
String contextPath = "/";
|
||||
String tmpDirPath="";
|
||||
boolean forceHttps = false;
|
||||
|
||||
host = getEnvironmentVariable("gitbucket.host");
|
||||
port = getEnvironmentVariable("gitbucket.port");
|
||||
contextPath = getEnvironmentVariable("gitbucket.prefix");
|
||||
tmpDirPath = getEnvironmentVariable("gitbucket.tempDir");
|
||||
|
||||
for(String arg: args) {
|
||||
if(arg.startsWith("--") && arg.contains("=")) {
|
||||
String[] dim = arg.split("=");
|
||||
@@ -31,17 +36,17 @@ public class JettyLauncher {
|
||||
host = dim[1];
|
||||
break;
|
||||
case "--port":
|
||||
port = Integer.parseInt(dim[1]);
|
||||
port = dim[1];
|
||||
break;
|
||||
case "--prefix":
|
||||
contextPath = dim[1];
|
||||
if (!contextPath.startsWith("/")) {
|
||||
contextPath = "/" + contextPath;
|
||||
}
|
||||
break;
|
||||
case "--max_file_size":
|
||||
System.setProperty("gitbucket.maxFileSize", dim[1]);
|
||||
break;
|
||||
case "--upload_timeout":
|
||||
System.setProperty("gitbucket.UploadTimeout", dim[1]);
|
||||
break;
|
||||
case "--gitbucket.home":
|
||||
System.setProperty("gitbucket.home", dim[1]);
|
||||
break;
|
||||
@@ -59,10 +64,14 @@ public class JettyLauncher {
|
||||
}
|
||||
}
|
||||
|
||||
if (contextPath != null && !contextPath.startsWith("/")) {
|
||||
contextPath = "/" + contextPath;
|
||||
}
|
||||
|
||||
if(host != null) {
|
||||
address = new InetSocketAddress(host, port);
|
||||
address = new InetSocketAddress(host, getPort(port));
|
||||
} else {
|
||||
address = new InetSocketAddress(port);
|
||||
address = new InetSocketAddress(getPort(port));
|
||||
}
|
||||
|
||||
Server server = new Server(address);
|
||||
@@ -88,7 +97,7 @@ public class JettyLauncher {
|
||||
WebAppContext context = new WebAppContext();
|
||||
|
||||
File tmpDir;
|
||||
if(tmpDirPath.equals("")){
|
||||
if(tmpDirPath == null || tmpDirPath.equals("")){
|
||||
tmpDir = new File(getGitBucketHome(), "tmp");
|
||||
if(!tmpDir.exists()){
|
||||
tmpDir.mkdirs();
|
||||
@@ -111,7 +120,7 @@ public class JettyLauncher {
|
||||
ProtectionDomain domain = JettyLauncher.class.getProtectionDomain();
|
||||
URL location = domain.getCodeSource().getLocation();
|
||||
|
||||
context.setContextPath(contextPath);
|
||||
context.setContextPath(contextPath == null ? "" : contextPath);
|
||||
context.setDescriptor(location.toExternalForm() + "/WEB-INF/web.xml");
|
||||
context.setServer(server);
|
||||
context.setWar(location.toExternalForm());
|
||||
@@ -140,6 +149,23 @@ public class JettyLauncher {
|
||||
return new File(System.getProperty("user.home"), ".gitbucket");
|
||||
}
|
||||
|
||||
private static String getEnvironmentVariable(String key){
|
||||
String value = System.getenv(key.toUpperCase().replace('.', '_'));
|
||||
if (value != null && value.length() == 0){
|
||||
return null;
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
private static int getPort(String port){
|
||||
if(port == null) {
|
||||
return 8080;
|
||||
} else {
|
||||
return Integer.parseInt(port);
|
||||
}
|
||||
}
|
||||
|
||||
private static Handler addStatisticsHandler(Handler handler) {
|
||||
// The graceful shutdown is implemented via the statistics handler.
|
||||
// See the following: https://bugs.eclipse.org/bugs/show_bug.cgi?id=420142
|
||||
|
||||
@@ -1 +1,4 @@
|
||||
notifications:1.7.0
|
||||
notifications:1.8.0
|
||||
gist:4.18.0
|
||||
emoji:4.6.0
|
||||
pages:1.8.0
|
||||
|
||||
6
src/main/resources/update/gitbucket-core_4.32.xml
Normal file
6
src/main/resources/update/gitbucket-core_4.32.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<changeSet>
|
||||
<addColumn tableName="PULL_REQUEST">
|
||||
<column name="IS_DRAFT" type="boolean" nullable="false" defaultValueBoolean="false" />
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
@@ -61,5 +61,9 @@ object GitBucketCoreModule
|
||||
new Version("4.29.0"),
|
||||
new Version("4.30.0"),
|
||||
new Version("4.30.1"),
|
||||
new Version("4.31.0", new LiquibaseMigration("update/gitbucket-core_4.31.xml"))
|
||||
new Version("4.31.0", new LiquibaseMigration("update/gitbucket-core_4.31.xml")),
|
||||
new Version("4.31.1"),
|
||||
new Version("4.31.2"),
|
||||
new Version("4.32.0", new LiquibaseMigration("update/gitbucket-core_4.32.xml")),
|
||||
new Version("4.33.0")
|
||||
)
|
||||
|
||||
@@ -12,6 +12,7 @@ case class ApiIssue(
|
||||
number: Int,
|
||||
title: String,
|
||||
user: ApiUser,
|
||||
assignee: Option[ApiUser],
|
||||
labels: List[ApiLabel],
|
||||
state: String,
|
||||
created_at: Date,
|
||||
@@ -19,6 +20,7 @@ case class ApiIssue(
|
||||
body: String
|
||||
)(repositoryName: RepositoryName, isPullRequest: Boolean) {
|
||||
val id = 0 // dummy id
|
||||
val assignees = List(assignee).flatten
|
||||
val comments_url = ApiPath(s"/api/v3/repos/${repositoryName.fullName}/issues/${number}/comments")
|
||||
val html_url = ApiPath(s"/${repositoryName.fullName}/${if (isPullRequest) { "pull" } else { "issues" }}/${number}")
|
||||
val pull_request = if (isPullRequest) {
|
||||
@@ -36,11 +38,18 @@ case class ApiIssue(
|
||||
}
|
||||
|
||||
object ApiIssue {
|
||||
def apply(issue: Issue, repositoryName: RepositoryName, user: ApiUser, labels: List[ApiLabel]): ApiIssue =
|
||||
def apply(
|
||||
issue: Issue,
|
||||
repositoryName: RepositoryName,
|
||||
user: ApiUser,
|
||||
assignee: Option[ApiUser],
|
||||
labels: List[ApiLabel]
|
||||
): ApiIssue =
|
||||
ApiIssue(
|
||||
number = issue.issueId,
|
||||
title = issue.title,
|
||||
user = user,
|
||||
assignee = assignee,
|
||||
labels = labels,
|
||||
state = if (issue.closed) { "closed" } else { "open" },
|
||||
body = issue.content.getOrElse(""),
|
||||
|
||||
@@ -5,7 +5,6 @@ import java.io.File
|
||||
import gitbucket.core.account.html
|
||||
import gitbucket.core.helper
|
||||
import gitbucket.core.model._
|
||||
import gitbucket.core.plugin.PluginRegistry
|
||||
import gitbucket.core.service._
|
||||
import gitbucket.core.service.WebHookService._
|
||||
import gitbucket.core.ssh.SshUtil
|
||||
@@ -256,12 +255,7 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
||||
account,
|
||||
members,
|
||||
extraMailAddresses,
|
||||
context.loginAccount.exists(
|
||||
x =>
|
||||
members.exists { member =>
|
||||
member.userName == x.userName && member.isManager
|
||||
}
|
||||
)
|
||||
isGroupManager(context.loginAccount, members)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -273,12 +267,7 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
||||
if (account.isGroupAccount) Nil else getGroupsByUserName(userName),
|
||||
getVisibleRepositories(context.loginAccount, Some(userName)),
|
||||
extraMailAddresses,
|
||||
context.loginAccount.exists(
|
||||
x =>
|
||||
members.exists { member =>
|
||||
member.userName == x.userName && member.isManager
|
||||
}
|
||||
)
|
||||
isGroupManager(context.loginAccount, members)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -347,7 +336,7 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
||||
|
||||
updateImage(userName, form.fileId, form.clearImage)
|
||||
updateAccountExtraMailAddresses(userName, form.extraMailAddresses.filter(_ != ""))
|
||||
flash += "info" -> "Account information has been updated."
|
||||
flash.update("info", "Account information has been updated.")
|
||||
redirect(s"/${userName}/_edit")
|
||||
|
||||
} getOrElse NotFound()
|
||||
@@ -359,7 +348,7 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
||||
getAccountByUserName(userName, true).map {
|
||||
account =>
|
||||
if (isLastAdministrator(account)) {
|
||||
flash += "error" -> "Account can't be removed because this is last one administrator."
|
||||
flash.update("error", "Account can't be removed because this is last one administrator.")
|
||||
redirect(s"/${userName}/_edit")
|
||||
} else {
|
||||
// // Remove repositories
|
||||
@@ -439,7 +428,7 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
||||
val userName = params("userName")
|
||||
getAccountByUserName(userName).map { x =>
|
||||
val (tokenId, token) = generateAccessToken(userName, form.note)
|
||||
flash += "generatedToken" -> (tokenId, token)
|
||||
flash.update("generatedToken", (tokenId, token))
|
||||
}
|
||||
redirect(s"/${userName}/_application")
|
||||
})
|
||||
@@ -475,7 +464,7 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
||||
post("/:userName/_hooks/new", accountWebHookForm(false))(managersOnly { form =>
|
||||
val userName = params("userName")
|
||||
addAccountWebHook(userName, form.url, form.events, form.ctype, form.token)
|
||||
flash += "info" -> s"Webhook ${form.url} created"
|
||||
flash.update("info", s"Webhook ${form.url} created")
|
||||
redirect(s"/${userName}/_hooks")
|
||||
})
|
||||
|
||||
@@ -485,7 +474,7 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
||||
get("/:userName/_hooks/delete")(managersOnly {
|
||||
val userName = params("userName")
|
||||
deleteAccountWebHook(userName, params("url"))
|
||||
flash += "info" -> s"Webhook ${params("url")} deleted"
|
||||
flash.update("info", s"Webhook ${params("url")} deleted")
|
||||
redirect(s"/${userName}/_hooks")
|
||||
})
|
||||
|
||||
@@ -508,7 +497,7 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
||||
post("/:userName/_hooks/edit", accountWebHookForm(true))(managersOnly { form =>
|
||||
val userName = params("userName")
|
||||
updateAccountWebHook(userName, form.url, form.events, form.ctype, form.token)
|
||||
flash += "info" -> s"webhook ${form.url} updated"
|
||||
flash.update("info", s"webhook ${form.url} updated")
|
||||
redirect(s"/${userName}/_hooks")
|
||||
})
|
||||
|
||||
@@ -537,13 +526,14 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
||||
WebHookPushPayload.createDummyPayload(ownerAccount)
|
||||
}
|
||||
|
||||
val (webHook, json, reqFuture, resFuture) = callWebHook(WebHook.Push, List(dummyWebHookInfo), dummyPayload).head
|
||||
val (webHook, json, reqFuture, resFuture) =
|
||||
callWebHook(WebHook.Push, List(dummyWebHookInfo), dummyPayload, context.settings).head
|
||||
|
||||
val toErrorMap: PartialFunction[Throwable, Map[String, String]] = {
|
||||
case e: java.net.UnknownHostException => Map("error" -> ("Unknown host " + e.getMessage))
|
||||
case e: java.lang.IllegalArgumentException => Map("error" -> ("invalid url"))
|
||||
case e: org.apache.http.client.ClientProtocolException => Map("error" -> ("invalid url"))
|
||||
case NonFatal(e) => Map("error" -> (e.getClass + " " + e.getMessage))
|
||||
case NonFatal(e) => Map("error" -> (s"${e.getClass} ${e.getMessage}"))
|
||||
}
|
||||
|
||||
contentType = formats("json")
|
||||
@@ -683,7 +673,7 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
||||
|
||||
updateImage(form.groupName, form.fileId, form.clearImage)
|
||||
|
||||
flash += "info" -> "Account information has been updated."
|
||||
flash.update("info", "Account information has been updated.")
|
||||
redirect(s"/${groupName}/_editgroup")
|
||||
|
||||
} getOrElse NotFound()
|
||||
@@ -823,4 +813,13 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def isGroupManager(account: Option[Account], members: Seq[GroupMember]): Boolean = {
|
||||
account.exists { account =>
|
||||
account.isAdmin || members.exists { member =>
|
||||
member.userName == account.userName && member.isManager
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import javax.servlet.{FilterChain, ServletRequest, ServletResponse}
|
||||
import is.tagomor.woothee.Classifier
|
||||
|
||||
import scala.util.Try
|
||||
import scala.util.Using
|
||||
import net.coobird.thumbnailator.Thumbnails
|
||||
import org.eclipse.jgit.api.Git
|
||||
import org.eclipse.jgit.lib.ObjectId
|
||||
@@ -240,7 +241,7 @@ abstract class ControllerBase
|
||||
case false => None
|
||||
}
|
||||
|
||||
using(new TreeWalk(git.getRepository)) { treeWalk =>
|
||||
Using.resource(new TreeWalk(git.getRepository)) { treeWalk =>
|
||||
treeWalk.addTree(revCommit.getTree)
|
||||
treeWalk.setRecursive(true)
|
||||
_getPathObjectId(path, treeWalk)
|
||||
@@ -268,7 +269,7 @@ abstract class ControllerBase
|
||||
response.setContentLength(attrs("size").toInt)
|
||||
val oid = attrs("oid").split(":")(1)
|
||||
|
||||
using(new FileInputStream(FileUtil.getLfsFilePath(repository.owner, repository.name, oid))) { in =>
|
||||
Using.resource(new FileInputStream(FileUtil.getLfsFilePath(repository.owner, repository.name, oid))) { in =>
|
||||
IOUtils.copy(in, response.getOutputStream)
|
||||
}
|
||||
} else {
|
||||
@@ -324,6 +325,8 @@ case class Context(
|
||||
trait AccountManagementControllerBase extends ControllerBase {
|
||||
self: AccountService =>
|
||||
|
||||
private val logger = LoggerFactory.getLogger(getClass)
|
||||
|
||||
protected def updateImage(userName: String, fileId: Option[String], clearImage: Boolean): Unit =
|
||||
if (clearImage) {
|
||||
getAccountByUserName(userName).flatMap(_.image).foreach { image =>
|
||||
@@ -331,17 +334,21 @@ trait AccountManagementControllerBase extends ControllerBase {
|
||||
updateAvatarImage(userName, None)
|
||||
}
|
||||
} else {
|
||||
fileId.foreach { fileId =>
|
||||
val filename = "avatar." + FileUtil.getExtension(session.getAndRemove(Keys.Session.Upload(fileId)).get)
|
||||
val uploadDir = getUserUploadDir(userName)
|
||||
if (!uploadDir.exists) {
|
||||
uploadDir.mkdirs()
|
||||
try {
|
||||
fileId.foreach { fileId =>
|
||||
val filename = "avatar." + FileUtil.getExtension(session.getAndRemove(Keys.Session.Upload(fileId)).get)
|
||||
val uploadDir = getUserUploadDir(userName)
|
||||
if (!uploadDir.exists) {
|
||||
uploadDir.mkdirs()
|
||||
}
|
||||
Thumbnails
|
||||
.of(new File(getTemporaryDir(session.getId), FileUtil.checkFilename(fileId)))
|
||||
.size(324, 324)
|
||||
.toFile(new File(uploadDir, FileUtil.checkFilename(filename)))
|
||||
updateAvatarImage(userName, Some(filename))
|
||||
}
|
||||
Thumbnails
|
||||
.of(new File(getTemporaryDir(session.getId), FileUtil.checkFilename(fileId)))
|
||||
.size(324, 324)
|
||||
.toFile(new File(uploadDir, FileUtil.checkFilename(filename)))
|
||||
updateAvatarImage(userName, Some(filename))
|
||||
} catch {
|
||||
case e: Exception => logger.info("Error while updateImage" + e.getMessage)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -359,7 +366,7 @@ trait AccountManagementControllerBase extends ControllerBase {
|
||||
params: Map[String, Seq[String]],
|
||||
messages: Messages
|
||||
): Option[String] = {
|
||||
val extraMailAddresses = params.filterKeys(k => k.startsWith("extraMailAddresses"))
|
||||
val extraMailAddresses = params.view.filterKeys(k => k.startsWith("extraMailAddresses"))
|
||||
if (extraMailAddresses.exists {
|
||||
case (k, v) =>
|
||||
v.contains(value)
|
||||
@@ -382,7 +389,7 @@ trait AccountManagementControllerBase extends ControllerBase {
|
||||
params: Map[String, Seq[String]],
|
||||
messages: Messages
|
||||
): Option[String] = {
|
||||
val extraMailAddresses = params.filterKeys(k => k.startsWith("extraMailAddresses"))
|
||||
val extraMailAddresses = params.view.filterKeys(k => k.startsWith("extraMailAddresses"))
|
||||
if (Some(value) == params.optionValue("mailAddress") || extraMailAddresses.count {
|
||||
case (k, v) =>
|
||||
v.contains(value)
|
||||
|
||||
@@ -16,6 +16,8 @@ import org.scalatra._
|
||||
import org.scalatra.servlet.{FileItem, FileUploadSupport, MultipartConfig}
|
||||
import org.apache.commons.io.{FileUtils, IOUtils}
|
||||
|
||||
import scala.util.Using
|
||||
|
||||
/**
|
||||
* Provides Ajax based file upload functionality.
|
||||
*
|
||||
@@ -80,7 +82,7 @@ class FileUploadController
|
||||
{ (file, fileId) =>
|
||||
val fileName = file.getName
|
||||
LockUtil.lock(s"${owner}/${repository}/wiki") {
|
||||
using(Git.open(Directory.getWikiRepositoryDir(owner, repository))) {
|
||||
Using.resource(Git.open(Directory.getWikiRepositoryDir(owner, repository))) {
|
||||
git =>
|
||||
val builder = DirCache.newInCore.builder()
|
||||
val inserter = git.getRepository.newObjectInserter()
|
||||
|
||||
@@ -83,7 +83,7 @@ trait IndexControllerBase extends ControllerBase {
|
||||
get("/signin") {
|
||||
val redirect = params.get("redirect")
|
||||
if (redirect.isDefined && redirect.get.startsWith("/")) {
|
||||
flash += Keys.Flash.Redirect -> redirect.get
|
||||
flash.update(Keys.Flash.Redirect, redirect.get)
|
||||
}
|
||||
gitbucket.core.html.signin(flash.get("userName"), flash.get("password"), flash.get("error"))
|
||||
}
|
||||
@@ -96,9 +96,9 @@ trait IndexControllerBase extends ControllerBase {
|
||||
case _ => signin(account)
|
||||
}
|
||||
case None =>
|
||||
flash += "userName" -> form.userName
|
||||
flash += "password" -> form.password
|
||||
flash += "error" -> "Sorry, your Username and/or Password is incorrect. Please try again."
|
||||
flash.update("userName", form.userName)
|
||||
flash.update("password", form.password)
|
||||
flash.update("error", "Sorry, your Username and/or Password is incorrect. Please try again.")
|
||||
redirect("/signin")
|
||||
}
|
||||
}
|
||||
@@ -132,15 +132,15 @@ trait IndexControllerBase extends ControllerBase {
|
||||
val redirectURI = new URI(s"$baseUrl/signin/oidc")
|
||||
session.get(Keys.Session.OidcContext) match {
|
||||
case Some(context: OidcContext) =>
|
||||
authenticate(params, redirectURI, context.state, context.nonce, oidc) map { account =>
|
||||
authenticate(params.toMap, redirectURI, context.state, context.nonce, oidc).map { account =>
|
||||
signin(account, context.redirectBackURI)
|
||||
} orElse {
|
||||
flash += "error" -> "Sorry, authentication failed. Please try again."
|
||||
flash.update("error", "Sorry, authentication failed. Please try again.")
|
||||
session.invalidate()
|
||||
redirect("/signin")
|
||||
}
|
||||
case _ =>
|
||||
flash += "error" -> "Sorry, something wrong. Please try again."
|
||||
flash.update("error", "Sorry, something wrong. Please try again.")
|
||||
session.invalidate()
|
||||
redirect("/signin")
|
||||
}
|
||||
@@ -227,7 +227,7 @@ trait IndexControllerBase extends ControllerBase {
|
||||
} getOrElse ""
|
||||
})
|
||||
|
||||
// TODO Move to RepositoryViwerController?
|
||||
// TODO Move to RepositoryViewrController?
|
||||
get("/:owner/:repository/search")(referrersOnly { repository =>
|
||||
defining(params.getOrElse("q", "").trim, params.getOrElse("type", "code")) {
|
||||
case (query, target) =>
|
||||
|
||||
@@ -145,7 +145,7 @@ trait IssuesControllerBase extends ControllerBase {
|
||||
form.assignedUserName,
|
||||
form.milestoneId,
|
||||
form.priorityId,
|
||||
form.labelNames.toArray.flatMap(_.split(",")),
|
||||
form.labelNames.toSeq.flatMap(_.split(",")),
|
||||
context.loginAccount.get
|
||||
)
|
||||
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
package gitbucket.core.controller
|
||||
|
||||
import gitbucket.core.issues.milestones.html
|
||||
import gitbucket.core.service.{RepositoryService, MilestonesService, AccountService}
|
||||
import gitbucket.core.util.{ReferrerAuthenticator, WritableUsersAuthenticator}
|
||||
import gitbucket.core.service.{AccountService, MilestonesService, RepositoryService}
|
||||
import gitbucket.core.util.Implicits._
|
||||
import gitbucket.core.util.{ReferrerAuthenticator, WritableUsersAuthenticator}
|
||||
import gitbucket.core.util.SyntaxSugars._
|
||||
import org.scalatra.forms._
|
||||
import org.scalatra.i18n.Messages
|
||||
|
||||
class MilestonesController
|
||||
extends MilestonesControllerBase
|
||||
@@ -20,7 +22,7 @@ trait MilestonesControllerBase extends ControllerBase {
|
||||
case class MilestoneForm(title: String, description: Option[String], dueDate: Option[java.util.Date])
|
||||
|
||||
val milestoneForm = mapping(
|
||||
"title" -> trim(label("Title", text(required, maxlength(100)))),
|
||||
"title" -> trim(label("Title", text(required, maxlength(100), uniqueMilestone))),
|
||||
"description" -> trim(label("Description", optional(text()))),
|
||||
"dueDate" -> trim(label("Due Date", optional(date())))
|
||||
)(MilestoneForm.apply)
|
||||
@@ -86,4 +88,29 @@ trait MilestonesControllerBase extends ControllerBase {
|
||||
} getOrElse NotFound()
|
||||
})
|
||||
|
||||
private def uniqueMilestone: Constraint = new Constraint() {
|
||||
override def validate(
|
||||
name: String,
|
||||
value: String,
|
||||
params: Map[String, Seq[String]],
|
||||
messages: Messages
|
||||
): Option[String] = {
|
||||
for {
|
||||
owner <- params.optionValue("owner")
|
||||
repository <- params.optionValue("repository")
|
||||
_ <- params.optionValue("milestoneId") match {
|
||||
// existing milestone
|
||||
case Some(id) =>
|
||||
getMilestones(owner, repository)
|
||||
.find(m => m.title.equalsIgnoreCase(value) && m.milestoneId.toString != id)
|
||||
// new milestone
|
||||
case None =>
|
||||
getMilestones(owner, repository)
|
||||
.find(m => m.title.equalsIgnoreCase(value))
|
||||
}
|
||||
} yield {
|
||||
"Milestone already exists."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package gitbucket.core.controller
|
||||
|
||||
import gitbucket.core.model.{CommitComment, CommitComments, IssueComment, WebHook}
|
||||
import gitbucket.core.plugin.PluginRegistry
|
||||
import gitbucket.core.pulls.html
|
||||
import gitbucket.core.service.CommitStatusService
|
||||
import gitbucket.core.service.MergeService
|
||||
@@ -15,11 +13,9 @@ import gitbucket.core.util.Implicits._
|
||||
import gitbucket.core.util._
|
||||
import org.scalatra.forms._
|
||||
import org.eclipse.jgit.api.Git
|
||||
import org.eclipse.jgit.lib.{ObjectId, PersonIdent}
|
||||
import org.eclipse.jgit.revwalk.RevWalk
|
||||
import org.scalatra.BadRequest
|
||||
|
||||
import scala.collection.JavaConverters._
|
||||
import scala.util.Using
|
||||
|
||||
class PullRequestsController
|
||||
extends PullRequestsControllerBase
|
||||
@@ -69,6 +65,7 @@ trait PullRequestsControllerBase extends ControllerBase {
|
||||
"requestBranch" -> trim(text(required, maxlength(100))),
|
||||
"commitIdFrom" -> trim(text(required, maxlength(40))),
|
||||
"commitIdTo" -> trim(text(required, maxlength(40))),
|
||||
"isDraft" -> trim(boolean(required)),
|
||||
"assignedUserName" -> trim(optional(text())),
|
||||
"milestoneId" -> trim(optional(number())),
|
||||
"priorityId" -> trim(optional(number())),
|
||||
@@ -77,7 +74,8 @@ trait PullRequestsControllerBase extends ControllerBase {
|
||||
|
||||
val mergeForm = mapping(
|
||||
"message" -> trim(label("Message", text(required))),
|
||||
"strategy" -> trim(label("Strategy", text(required)))
|
||||
"strategy" -> trim(label("Strategy", text(required))),
|
||||
"isDraft" -> trim(boolean(required))
|
||||
)(MergeForm.apply)
|
||||
|
||||
case class PullRequestForm(
|
||||
@@ -90,13 +88,14 @@ trait PullRequestsControllerBase extends ControllerBase {
|
||||
requestBranch: String,
|
||||
commitIdFrom: String,
|
||||
commitIdTo: String,
|
||||
isDraft: Boolean,
|
||||
assignedUserName: Option[String],
|
||||
milestoneId: Option[Int],
|
||||
priorityId: Option[Int],
|
||||
labelNames: Option[String]
|
||||
)
|
||||
|
||||
case class MergeForm(message: String, strategy: String)
|
||||
case class MergeForm(message: String, strategy: String, isDraft: Boolean)
|
||||
|
||||
get("/:owner/:repository/pulls")(referrersOnly { repository =>
|
||||
val q = request.getParameter("q")
|
||||
@@ -133,7 +132,7 @@ trait PullRequestsControllerBase extends ControllerBase {
|
||||
hasDeveloperRole(pullreq.requestUserName, pullreq.requestRepositoryName, context.loginAccount),
|
||||
repository,
|
||||
getRepository(pullreq.requestUserName, pullreq.requestRepositoryName),
|
||||
flash.toMap.map(f => f._1 -> f._2.toString)
|
||||
flash.iterator.map(f => f._1 -> f._2.toString).toMap
|
||||
)
|
||||
|
||||
// html.pullreq(
|
||||
@@ -266,11 +265,11 @@ trait PullRequestsControllerBase extends ControllerBase {
|
||||
val repository = getRepository(owner, name).get
|
||||
val branchProtection = getProtectedBranchInfo(owner, name, pullreq.requestBranch)
|
||||
if (branchProtection.enabled) {
|
||||
flash += "error" -> s"branch ${pullreq.requestBranch} is protected."
|
||||
flash.update("error", s"branch ${pullreq.requestBranch} is protected.")
|
||||
} else {
|
||||
if (repository.repository.defaultBranch != pullreq.requestBranch) {
|
||||
val userName = context.loginAccount.get.userName
|
||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||
git.branchDelete().setForce(true).setBranchNames(pullreq.requestBranch).call()
|
||||
recordDeleteBranchActivity(repository.owner, repository.name, userName, pullreq.requestBranch)
|
||||
}
|
||||
@@ -283,9 +282,10 @@ trait PullRequestsControllerBase extends ControllerBase {
|
||||
"delete_branch"
|
||||
)
|
||||
} else {
|
||||
flash += "error" -> s"""Can't delete the default branch "${pullreq.requestBranch}"."""
|
||||
flash.update("error", s"""Can't delete the default branch "${pullreq.requestBranch}".""")
|
||||
}
|
||||
}
|
||||
|
||||
redirect(s"/${baseRepository.owner}/${baseRepository.name}/pull/${issueId}")
|
||||
}) getOrElse NotFound()
|
||||
})
|
||||
@@ -303,7 +303,7 @@ trait PullRequestsControllerBase extends ControllerBase {
|
||||
} yield {
|
||||
val branchProtection = getProtectedBranchInfo(owner, name, pullreq.requestBranch)
|
||||
if (branchProtection.needStatusCheck(loginAccount.userName)) {
|
||||
flash += "error" -> s"branch ${pullreq.requestBranch} is protected need status check."
|
||||
flash.update("error", s"branch ${pullreq.requestBranch} is protected need status check.")
|
||||
} else {
|
||||
LockUtil.lock(s"${owner}/${name}") {
|
||||
val alias =
|
||||
@@ -312,9 +312,11 @@ trait PullRequestsControllerBase extends ControllerBase {
|
||||
} else {
|
||||
s"${pullreq.userName}:${pullreq.branch}"
|
||||
}
|
||||
val existIds = using(Git.open(Directory.getRepositoryDir(owner, name))) { git =>
|
||||
JGitUtil.getAllCommitIds(git)
|
||||
}.toSet
|
||||
val existIds = Using
|
||||
.resource(Git.open(Directory.getRepositoryDir(owner, name))) { git =>
|
||||
JGitUtil.getAllCommitIds(git)
|
||||
}
|
||||
.toSet
|
||||
pullRemote(
|
||||
repository,
|
||||
pullreq.requestBranch,
|
||||
@@ -322,14 +324,15 @@ trait PullRequestsControllerBase extends ControllerBase {
|
||||
pullreq.branch,
|
||||
loginAccount,
|
||||
s"Merge branch '${alias}' into ${pullreq.requestBranch}",
|
||||
Some(pullreq)
|
||||
Some(pullreq),
|
||||
context.settings
|
||||
) match {
|
||||
case None => // conflict
|
||||
flash += "error" -> s"Can't automatic merging branch '${alias}' into ${pullreq.requestBranch}."
|
||||
flash.update("error", s"Can't automatic merging branch '${alias}' into ${pullreq.requestBranch}.")
|
||||
case Some(oldId) =>
|
||||
// update pull request
|
||||
updatePullRequests(owner, name, pullreq.requestBranch, loginAccount, "synchronize")
|
||||
flash += "info" -> s"Merge branch '${alias}' into ${pullreq.requestBranch}"
|
||||
updatePullRequests(owner, name, pullreq.requestBranch, loginAccount, "synchronize", context.settings)
|
||||
flash.update("info", s"Merge branch '${alias}' into ${pullreq.requestBranch}")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -338,14 +341,34 @@ trait PullRequestsControllerBase extends ControllerBase {
|
||||
}) getOrElse NotFound()
|
||||
})
|
||||
|
||||
post("/:owner/:repository/pull/:id/update_draft")(readableUsersOnly { baseRepository =>
|
||||
(for {
|
||||
issueId <- params("id").toIntOpt
|
||||
(_, pullreq) <- getPullRequest(baseRepository.owner, baseRepository.name, issueId)
|
||||
owner = pullreq.requestUserName
|
||||
name = pullreq.requestRepositoryName
|
||||
if hasDeveloperRole(owner, name, context.loginAccount)
|
||||
} yield {
|
||||
updateDraftToPullRequest(baseRepository.owner, baseRepository.name, issueId)
|
||||
}) getOrElse NotFound()
|
||||
})
|
||||
|
||||
post("/:owner/:repository/pull/:id/merge", mergeForm)(writableUsersOnly { (form, repository) =>
|
||||
params("id").toIntOpt.flatMap { issueId =>
|
||||
val owner = repository.owner
|
||||
val name = repository.name
|
||||
|
||||
mergePullRequest(repository, issueId, context.loginAccount.get, form.message, form.strategy) match {
|
||||
mergePullRequest(
|
||||
repository,
|
||||
issueId,
|
||||
context.loginAccount.get,
|
||||
form.message,
|
||||
form.strategy,
|
||||
form.isDraft,
|
||||
context.settings
|
||||
) match {
|
||||
case Right(objectId) => redirect(s"/${owner}/${name}/pull/${issueId}")
|
||||
case Left(message) => Some(BadRequest())
|
||||
case Left(message) => Some(BadRequest(message))
|
||||
}
|
||||
} getOrElse NotFound()
|
||||
})
|
||||
@@ -356,7 +379,7 @@ trait PullRequestsControllerBase extends ControllerBase {
|
||||
case (Some(originUserName), Some(originRepositoryName)) => {
|
||||
getRepository(originUserName, originRepositoryName).map {
|
||||
originRepository =>
|
||||
using(
|
||||
Using.resources(
|
||||
Git.open(getRepositoryDir(originUserName, originRepositoryName)),
|
||||
Git.open(getRepositoryDir(forkedRepository.owner, forkedRepository.name))
|
||||
) { (oldGit, newGit) =>
|
||||
@@ -372,7 +395,7 @@ trait PullRequestsControllerBase extends ControllerBase {
|
||||
} getOrElse NotFound()
|
||||
}
|
||||
case _ => {
|
||||
using(Git.open(getRepositoryDir(forkedRepository.owner, forkedRepository.name))) { git =>
|
||||
Using.resource(Git.open(getRepositoryDir(forkedRepository.owner, forkedRepository.name))) { git =>
|
||||
JGitUtil.getDefaultBranch(git, forkedRepository).map {
|
||||
case (_, defaultBranch) =>
|
||||
redirect(
|
||||
@@ -464,6 +487,7 @@ trait PullRequestsControllerBase extends ControllerBase {
|
||||
getAssignableUserNames(originRepository.owner, originRepository.name),
|
||||
getMilestones(originRepository.owner, originRepository.name),
|
||||
getPriorities(originRepository.owner, originRepository.name),
|
||||
getDefaultPriority(originRepository.owner, originRepository.name),
|
||||
getLabels(originRepository.owner, originRepository.name)
|
||||
)
|
||||
}
|
||||
@@ -493,7 +517,7 @@ trait PullRequestsControllerBase extends ControllerBase {
|
||||
}
|
||||
};
|
||||
originRepository <- getRepository(originOwner, originRepositoryName)) yield {
|
||||
using(
|
||||
Using.resources(
|
||||
Git.open(getRepositoryDir(originRepository.owner, originRepository.name)),
|
||||
Git.open(getRepositoryDir(forkedRepository.owner, forkedRepository.name))
|
||||
) {
|
||||
@@ -542,7 +566,9 @@ trait PullRequestsControllerBase extends ControllerBase {
|
||||
requestBranch = form.requestBranch,
|
||||
commitIdFrom = form.commitIdFrom,
|
||||
commitIdTo = form.commitIdTo,
|
||||
loginAccount = context.loginAccount.get
|
||||
isDraft = form.isDraft,
|
||||
loginAccount = context.loginAccount.get,
|
||||
settings = context.settings
|
||||
)
|
||||
|
||||
// insert labels
|
||||
@@ -567,7 +593,7 @@ trait PullRequestsControllerBase extends ControllerBase {
|
||||
context.loginAccount.map(x => Seq(x.mailAddress) ++ getAccountExtraMailAddresses(x.userName)).getOrElse(Nil)
|
||||
|
||||
val branches =
|
||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))) {
|
||||
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) {
|
||||
git =>
|
||||
JGitUtil
|
||||
.getBranches(
|
||||
|
||||
@@ -8,9 +8,9 @@ import gitbucket.core.util.Directory._
|
||||
import gitbucket.core.util.Implicits._
|
||||
import org.scalatra.forms._
|
||||
import gitbucket.core.releases.html
|
||||
import gitbucket.core.util.SyntaxSugars.using
|
||||
import org.apache.commons.io.FileUtils
|
||||
import org.eclipse.jgit.api.Git
|
||||
import scala.util.Using
|
||||
|
||||
class ReleaseController
|
||||
extends ReleaseControllerBase
|
||||
@@ -106,7 +106,7 @@ trait ReleaseControllerBase extends ControllerBase {
|
||||
createRelease(repository.owner, repository.name, form.name, form.content, tagName, loginAccount)
|
||||
|
||||
// Insert into RELEASE_ASSET
|
||||
val files = params.collect {
|
||||
val files = params.toMap.collect {
|
||||
case (name, value) if name.startsWith("file:") =>
|
||||
val Array(_, fileId) = name.split(":")
|
||||
(fileId, value)
|
||||
@@ -130,7 +130,7 @@ trait ReleaseControllerBase extends ControllerBase {
|
||||
val Seq(previousTag, currentTag) = multiParams("splat")
|
||||
val previousTagId = repository.tags.collectFirst { case x if x.name == previousTag => x.id }.getOrElse("")
|
||||
|
||||
val commitLog = using(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||
val commitLog = Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||
val commits = JGitUtil.getCommitLog(git, previousTagId, currentTag).reverse
|
||||
commits
|
||||
.map { commit =>
|
||||
@@ -174,7 +174,7 @@ trait ReleaseControllerBase extends ControllerBase {
|
||||
val assets = getReleaseAssets(repository.owner, repository.name, tagName)
|
||||
deleteReleaseAssets(repository.owner, repository.name, tagName)
|
||||
|
||||
val files = params.collect {
|
||||
val files = params.toMap.collect {
|
||||
case (name, value) if name.startsWith("file:") =>
|
||||
val Array(_, fileId) = name.split(":")
|
||||
(fileId, value)
|
||||
|
||||
@@ -12,12 +12,14 @@ import gitbucket.core.util.JGitUtil._
|
||||
import gitbucket.core.util.SyntaxSugars._
|
||||
import gitbucket.core.util.Implicits._
|
||||
import gitbucket.core.util.Directory._
|
||||
import gitbucket.core.model.WebHookContentType
|
||||
import org.scalatra.forms._
|
||||
import org.scalatra.i18n.Messages
|
||||
import org.eclipse.jgit.api.Git
|
||||
import org.eclipse.jgit.lib.Constants
|
||||
import org.eclipse.jgit.lib.ObjectId
|
||||
import gitbucket.core.model.WebHookContentType
|
||||
|
||||
import scala.util.Using
|
||||
|
||||
class RepositorySettingsController
|
||||
extends RepositorySettingsControllerBase
|
||||
@@ -147,7 +149,7 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
||||
// Update database
|
||||
renameRepository(repository.owner, repository.name, repository.owner, form.repositoryName)
|
||||
}
|
||||
flash += "info" -> "Repository settings has been updated."
|
||||
flash.update("info", "Repository settings has been updated.")
|
||||
redirect(s"/${repository.owner}/${form.repositoryName}/settings/options")
|
||||
})
|
||||
|
||||
@@ -164,10 +166,10 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
||||
} else {
|
||||
saveRepositoryDefaultBranch(repository.owner, repository.name, form.defaultBranch)
|
||||
// Change repository HEAD
|
||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||
git.getRepository.updateRef(Constants.HEAD, true).link(Constants.R_HEADS + form.defaultBranch)
|
||||
}
|
||||
flash += "info" -> "Repository default branch has been updated."
|
||||
flash.update("info", "Repository default branch has been updated.")
|
||||
redirect(s"/${repository.owner}/${repository.name}/settings/branches")
|
||||
}
|
||||
})
|
||||
@@ -231,7 +233,7 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
||||
*/
|
||||
post("/:owner/:repository/settings/hooks/new", webHookForm(false))(ownerOnly { (form, repository) =>
|
||||
addWebHook(repository.owner, repository.name, form.url, form.events, form.ctype, form.token)
|
||||
flash += "info" -> s"Webhook ${form.url} created"
|
||||
flash.update("info", s"Webhook ${form.url} created")
|
||||
redirect(s"/${repository.owner}/${repository.name}/settings/hooks")
|
||||
})
|
||||
|
||||
@@ -240,7 +242,7 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
||||
*/
|
||||
get("/:owner/:repository/settings/hooks/delete")(ownerOnly { repository =>
|
||||
deleteWebHook(repository.owner, repository.name, params("url"))
|
||||
flash += "info" -> s"Webhook ${params("url")} deleted"
|
||||
flash.update("info", s"Webhook ${params("url")} deleted")
|
||||
redirect(s"/${repository.owner}/${repository.name}/settings/hooks")
|
||||
})
|
||||
|
||||
@@ -252,11 +254,11 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
||||
Array(h.getName, h.getValue)
|
||||
}
|
||||
|
||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))) {
|
||||
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) {
|
||||
git =>
|
||||
import scala.collection.JavaConverters._
|
||||
import scala.concurrent.duration._
|
||||
import scala.concurrent._
|
||||
import scala.jdk.CollectionConverters._
|
||||
import scala.util.control.NonFatal
|
||||
import org.apache.http.util.EntityUtils
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
@@ -292,13 +294,14 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
||||
)
|
||||
}
|
||||
|
||||
val (webHook, json, reqFuture, resFuture) = callWebHook(WebHook.Push, List(dummyWebHookInfo), dummyPayload).head
|
||||
val (webHook, json, reqFuture, resFuture) =
|
||||
callWebHook(WebHook.Push, List(dummyWebHookInfo), dummyPayload, context.settings).head
|
||||
|
||||
val toErrorMap: PartialFunction[Throwable, Map[String, String]] = {
|
||||
case e: java.net.UnknownHostException => Map("error" -> ("Unknown host " + e.getMessage))
|
||||
case e: java.lang.IllegalArgumentException => Map("error" -> ("invalid url"))
|
||||
case e: org.apache.http.client.ClientProtocolException => Map("error" -> ("invalid url"))
|
||||
case NonFatal(e) => Map("error" -> (e.getClass + " " + e.getMessage))
|
||||
case NonFatal(e) => Map("error" -> (s"${e.getClass} ${e.getMessage}"))
|
||||
}
|
||||
|
||||
contentType = formats("json")
|
||||
@@ -350,7 +353,7 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
||||
*/
|
||||
post("/:owner/:repository/settings/hooks/edit", webHookForm(true))(ownerOnly { (form, repository) =>
|
||||
updateWebHook(repository.owner, repository.name, form.url, form.events, form.ctype, form.token)
|
||||
flash += "info" -> s"webhook ${form.url} updated"
|
||||
flash.update("info", s"webhook ${form.url} updated")
|
||||
redirect(s"/${repository.owner}/${repository.name}/settings/hooks")
|
||||
})
|
||||
|
||||
@@ -386,11 +389,11 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
||||
*/
|
||||
post("/:owner/:repository/settings/gc")(ownerOnly { repository =>
|
||||
LockUtil.lock(s"${repository.owner}/${repository.name}") {
|
||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||
git.gc().call()
|
||||
}
|
||||
}
|
||||
flash += "info" -> "Garbage collection has been executed."
|
||||
flash.update("info", "Garbage collection has been executed.")
|
||||
redirect(s"/${repository.owner}/${repository.name}/settings/danger")
|
||||
})
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
package gitbucket.core.controller
|
||||
|
||||
import java.io.File
|
||||
import java.io.{File, FileInputStream, FileOutputStream}
|
||||
|
||||
import scala.util.Using
|
||||
import javax.servlet.http.{HttpServletRequest, HttpServletResponse}
|
||||
import gitbucket.core.plugin.PluginRegistry
|
||||
import gitbucket.core.repo.html
|
||||
@@ -258,11 +259,11 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
||||
def getSummary(statuses: List[CommitStatus]): (CommitState, String) = {
|
||||
val stateMap = statuses.groupBy(_.state)
|
||||
val state = CommitState.combine(stateMap.keySet)
|
||||
val summary = stateMap.map { case (keyState, states) => states.size + " " + keyState.name }.mkString(", ")
|
||||
val summary = stateMap.map { case (keyState, states) => s"${states.size} ${keyState.name}" }.mkString(", ")
|
||||
state -> summary
|
||||
}
|
||||
|
||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))) {
|
||||
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) {
|
||||
git =>
|
||||
def getTags(sha: String): List[String] = {
|
||||
JGitUtil.getTagsOnCommit(git, sha)
|
||||
@@ -315,7 +316,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
||||
val protectedBranch = getProtectedBranchInfo(repository.owner, repository.name, branch)
|
||||
.needStatusCheck(context.loginAccount.get.userName)
|
||||
|
||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||
val revCommit = JGitUtil.getRevCommitFromId(git, git.getRepository.resolve(branch))
|
||||
|
||||
html.editor(
|
||||
@@ -351,9 +352,10 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
||||
repository = repository,
|
||||
branch = form.branch,
|
||||
path = form.path,
|
||||
files = files,
|
||||
files = files.toIndexedSeq,
|
||||
message = form.message.getOrElse("Add files via upload"),
|
||||
loginAccount = context.loginAccount.get
|
||||
loginAccount = context.loginAccount.get,
|
||||
settings = context.settings
|
||||
) {
|
||||
case (git, headTip, builder, inserter) =>
|
||||
JGitUtil.processTree(git, headTip) { (path, tree) =>
|
||||
@@ -384,7 +386,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
||||
val protectedBranch = getProtectedBranchInfo(repository.owner, repository.name, branch)
|
||||
.needStatusCheck(context.loginAccount.get.userName)
|
||||
|
||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))) {
|
||||
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) {
|
||||
git =>
|
||||
val revCommit = JGitUtil.getRevCommitFromId(git, git.getRepository.resolve(branch))
|
||||
|
||||
@@ -411,7 +413,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
||||
|
||||
get("/:owner/:repository/remove/*")(writableUsersOnly { repository =>
|
||||
val (branch, path) = repository.splitPath(multiParams("splat").head)
|
||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))) {
|
||||
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) {
|
||||
git =>
|
||||
val revCommit = JGitUtil.getRevCommitFromId(git, git.getRepository.resolve(branch))
|
||||
|
||||
@@ -440,7 +442,8 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
||||
charset = form.charset,
|
||||
message = form.message.getOrElse(s"Create ${form.newFileName}"),
|
||||
commit = form.commit,
|
||||
loginAccount = context.loginAccount.get
|
||||
loginAccount = context.loginAccount.get,
|
||||
settings = context.settings
|
||||
)
|
||||
|
||||
redirect(
|
||||
@@ -464,7 +467,8 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
||||
form.message.getOrElse(s"Rename ${form.oldFileName.get} to ${form.newFileName}")
|
||||
},
|
||||
commit = form.commit,
|
||||
loginAccount = context.loginAccount.get
|
||||
loginAccount = context.loginAccount.get,
|
||||
settings = context.settings
|
||||
)
|
||||
|
||||
redirect(
|
||||
@@ -484,11 +488,10 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
||||
charset = "",
|
||||
message = form.message.getOrElse(s"Delete ${form.fileName}"),
|
||||
commit = form.commit,
|
||||
loginAccount = context.loginAccount.get
|
||||
loginAccount = context.loginAccount.get,
|
||||
settings = context.settings
|
||||
)
|
||||
|
||||
println(form.path)
|
||||
|
||||
redirect(
|
||||
s"/${repository.owner}/${repository.name}/tree/${form.branch}${if (form.path.length == 0) "" else "/" + form.path}"
|
||||
)
|
||||
@@ -496,7 +499,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
||||
|
||||
get("/:owner/:repository/raw/*")(referrersOnly { repository =>
|
||||
val (id, path) = repository.splitPath(multiParams("splat").head)
|
||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||
val revCommit = JGitUtil.getRevCommitFromId(git, git.getRepository.resolve(id))
|
||||
|
||||
getPathObjectId(git, path, revCommit).map { objectId =>
|
||||
@@ -511,7 +514,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
||||
val blobRoute = get("/:owner/:repository/blob/*")(referrersOnly { repository =>
|
||||
val (id, path) = repository.splitPath(multiParams("splat").head)
|
||||
val raw = params.get("raw").getOrElse("false").toBoolean
|
||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))) {
|
||||
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) {
|
||||
git =>
|
||||
val revCommit = JGitUtil.getRevCommitFromId(git, git.getRepository.resolve(id))
|
||||
getPathObjectId(git, path, revCommit).map {
|
||||
@@ -551,7 +554,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
||||
ajaxGet("/:owner/:repository/get-blame/*")(referrersOnly { repository =>
|
||||
val (id, path) = repository.splitPath(multiParams("splat").head)
|
||||
contentType = formats("json")
|
||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))) {
|
||||
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) {
|
||||
git =>
|
||||
val last = git.log.add(git.getRepository.resolve(id)).addPath(path).setMaxCount(1).call.iterator.next.name
|
||||
Serialization.write(
|
||||
@@ -586,7 +589,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
||||
val id = params("id")
|
||||
|
||||
try {
|
||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))) {
|
||||
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) {
|
||||
git =>
|
||||
defining(JGitUtil.getRevCommitFromId(git, git.getRepository.resolve(id))) {
|
||||
revCommit =>
|
||||
@@ -615,7 +618,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
||||
|
||||
get("/:owner/:repository/patch/:id")(referrersOnly { repository =>
|
||||
try {
|
||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||
val diff = JGitUtil.getPatch(git, None, params("id"))
|
||||
contentType = formats("txt")
|
||||
diff
|
||||
@@ -628,7 +631,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
||||
get("/:owner/:repository/patch/*...*")(referrersOnly { repository =>
|
||||
try {
|
||||
val Seq(fromId, toId) = multiParams("splat")
|
||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||
val diff = JGitUtil.getPatch(git, Some(fromId), toId)
|
||||
contentType = formats("txt")
|
||||
diff
|
||||
@@ -748,7 +751,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
||||
*/
|
||||
get("/:owner/:repository/branches")(referrersOnly { repository =>
|
||||
val protectedBranches = getProtectedBranchList(repository.owner, repository.name).toSet
|
||||
val branches = using(Git.open(getRepositoryDir(repository.owner, repository.name))) {
|
||||
val branches = Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) {
|
||||
git =>
|
||||
JGitUtil
|
||||
.getBranches(
|
||||
@@ -788,14 +791,14 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
||||
* Creates a tag.
|
||||
*/
|
||||
post("/:owner/:repository/tag", tagForm)(writableUsersOnly { (form, repository) =>
|
||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||
JGitUtil.createTag(git, form.tagName, form.message, form.commitId)
|
||||
} match {
|
||||
case Right(message) =>
|
||||
flash += "info" -> message
|
||||
flash.update("info", message)
|
||||
redirect(s"/${repository.owner}/${repository.name}/commit/${form.commitId}")
|
||||
case Left(message) =>
|
||||
flash += "error" -> message
|
||||
flash.update("error", message)
|
||||
redirect(s"/${repository.owner}/${repository.name}/commit/${form.commitId}")
|
||||
}
|
||||
})
|
||||
@@ -806,16 +809,16 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
||||
post("/:owner/:repository/branches")(writableUsersOnly { repository =>
|
||||
val newBranchName = params.getOrElse("new", halt(400))
|
||||
val fromBranchName = params.getOrElse("from", halt(400))
|
||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||
JGitUtil.createBranch(git, fromBranchName, newBranchName)
|
||||
} match {
|
||||
case Right(message) =>
|
||||
flash += "info" -> message
|
||||
flash.update("info", message)
|
||||
redirect(
|
||||
s"/${repository.owner}/${repository.name}/tree/${StringUtil.urlEncode(newBranchName).replace("%2F", "/")}"
|
||||
)
|
||||
case Left(message) =>
|
||||
flash += "error" -> message
|
||||
flash.update("error", message)
|
||||
redirect(s"/${repository.owner}/${repository.name}/tree/${fromBranchName}")
|
||||
}
|
||||
})
|
||||
@@ -827,7 +830,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
||||
val branchName = multiParams("splat").head
|
||||
val userName = context.loginAccount.get.userName
|
||||
if (repository.repository.defaultBranch != branchName) {
|
||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||
git.branchDelete().setForce(true).setBranchNames(branchName).call()
|
||||
recordDeleteBranchActivity(repository.owner, repository.name, userName, branchName)
|
||||
}
|
||||
@@ -879,7 +882,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
||||
* Displays the file find of branch.
|
||||
*/
|
||||
get("/:owner/:repository/find/*")(referrersOnly { repository =>
|
||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||
val ref = multiParams("splat").head
|
||||
JGitUtil.getTreeId(git, ref).map { treeId =>
|
||||
html.find(ref, treeId, repository)
|
||||
@@ -891,7 +894,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
||||
* Get all file list of branch.
|
||||
*/
|
||||
ajaxGet("/:owner/:repository/tree-list/:tree")(referrersOnly { repository =>
|
||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||
val treeId = params("tree")
|
||||
contentType = formats("json")
|
||||
Map("paths" -> JGitUtil.getAllFileListByTreeId(git, treeId))
|
||||
@@ -915,7 +918,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
||||
* @return HTML of the file list
|
||||
*/
|
||||
private def fileList(repository: RepositoryService.RepositoryInfo, revstr: String = "", path: String = ".") = {
|
||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||
if (JGitUtil.isEmpty(git)) {
|
||||
html.guide(repository, hasDeveloperRole(repository.owner, repository.name, context.loginAccount))
|
||||
} else {
|
||||
@@ -974,16 +977,16 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
||||
def archive(revision: String, archiveFormat: String, archive: ArchiveOutputStream)(
|
||||
entryCreator: (String, Long, java.util.Date, Int) => ArchiveEntry
|
||||
): Unit = {
|
||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||
val oid = git.getRepository.resolve(revision)
|
||||
val commit = JGitUtil.getRevCommitFromId(git, oid)
|
||||
val date = commit.getCommitterIdent.getWhen
|
||||
val sha1 = oid.getName()
|
||||
val repositorySuffix = (if (sha1.startsWith(revision)) sha1 else revision).replace('/', '-')
|
||||
val pathSuffix = if (path.isEmpty) "" else '-' + path.replace('/', '-')
|
||||
val pathSuffix = if (path.isEmpty) "" else s"-${path.replace('/', '-')}"
|
||||
val baseName = repository.name + "-" + repositorySuffix + pathSuffix
|
||||
|
||||
using(new TreeWalk(git.getRepository)) { treeWalk =>
|
||||
Using.resource(new TreeWalk(git.getRepository)) { treeWalk =>
|
||||
treeWalk.addTree(commit.getTree)
|
||||
treeWalk.setRecursive(true)
|
||||
if (!path.isEmpty) {
|
||||
@@ -994,24 +997,31 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
||||
val entryPath =
|
||||
if (path.isEmpty) baseName + "/" + treeWalk.getPathString
|
||||
else path.split("/").last + treeWalk.getPathString.substring(path.length)
|
||||
val size = JGitUtil.getContentSize(git.getRepository.open(treeWalk.getObjectId(0)))
|
||||
val mode = treeWalk.getFileMode.getBits
|
||||
val entry: ArchiveEntry = entryCreator(entryPath, size, date, mode)
|
||||
JGitUtil.openFile(git, repository, commit.getTree, treeWalk.getPathString) { in =>
|
||||
val tempFile = File.createTempFile("gitbucket", ".archive")
|
||||
val size = Using.resource(new FileOutputStream(tempFile)) { out =>
|
||||
IOUtils.copy(
|
||||
EolStreamTypeUtil.wrapInputStream(
|
||||
in,
|
||||
EolStreamTypeUtil
|
||||
.detectStreamType(
|
||||
OperationType.CHECKOUT_OP,
|
||||
git.getRepository.getConfig.get(WorkingTreeOptions.KEY),
|
||||
treeWalk.getAttributes
|
||||
)
|
||||
),
|
||||
out
|
||||
)
|
||||
}
|
||||
|
||||
val entry: ArchiveEntry = entryCreator(entryPath, size, date, mode)
|
||||
archive.putArchiveEntry(entry)
|
||||
IOUtils.copy(
|
||||
EolStreamTypeUtil.wrapInputStream(
|
||||
in,
|
||||
EolStreamTypeUtil
|
||||
.detectStreamType(
|
||||
OperationType.CHECKOUT_OP,
|
||||
git.getRepository.getConfig.get(WorkingTreeOptions.KEY),
|
||||
treeWalk.getAttributes
|
||||
)
|
||||
),
|
||||
archive
|
||||
)
|
||||
Using.resource(new FileInputStream(tempFile)) { in =>
|
||||
IOUtils.copy(in, archive)
|
||||
}
|
||||
archive.closeArchiveEntry()
|
||||
tempFile.delete()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1032,9 +1042,10 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
||||
)
|
||||
contentType = "application/octet-stream"
|
||||
response.setBufferSize(1024 * 1024)
|
||||
using(new ZipArchiveOutputStream(response.getOutputStream)) { zip =>
|
||||
Using.resource(new ZipArchiveOutputStream(response.getOutputStream)) { zip =>
|
||||
archive(revision, ".zip", zip) { (path, size, date, mode) =>
|
||||
val entry = new ZipArchiveEntry(path)
|
||||
entry.setSize(size)
|
||||
entry.setUnixMode(mode)
|
||||
entry.setTime(date.getTime)
|
||||
entry
|
||||
@@ -1048,17 +1059,18 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
||||
)
|
||||
contentType = "application/octet-stream"
|
||||
response.setBufferSize(1024 * 1024)
|
||||
using(compressor match {
|
||||
Using.resource(compressor match {
|
||||
case "gz" => new GzipCompressorOutputStream(response.getOutputStream)
|
||||
case "bz2" => new BZip2CompressorOutputStream(response.getOutputStream)
|
||||
case "xz" => new XZCompressorOutputStream(response.getOutputStream)
|
||||
}) { compressorOutputStream =>
|
||||
using(new TarArchiveOutputStream(compressorOutputStream)) { tar =>
|
||||
Using.resource(new TarArchiveOutputStream(compressorOutputStream)) { tar =>
|
||||
tar.setBigNumberMode(TarArchiveOutputStream.BIGNUMBER_STAR)
|
||||
tar.setLongFileMode(TarArchiveOutputStream.LONGFILE_GNU)
|
||||
tar.setAddPaxHeadersForNonAsciiNames(true)
|
||||
archive(revision, ".tar.gz", tar) { (path, size, date, mode) =>
|
||||
val entry = new TarArchiveEntry(path)
|
||||
entry.setSize(size)
|
||||
entry.setModTime(date)
|
||||
entry.setMode(mode)
|
||||
entry
|
||||
@@ -1081,7 +1093,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
||||
val branch = params("branch")
|
||||
|
||||
LockUtil.lock(s"${owner}/${repository}") {
|
||||
using(Git.open(getRepositoryDir(owner, repository))) { git =>
|
||||
Using.resource(Git.open(getRepositoryDir(owner, repository))) { git =>
|
||||
val headName = s"refs/heads/${branch}"
|
||||
val headTip = git.getRepository.resolve(headName)
|
||||
if (headTip.getName != value) {
|
||||
|
||||
@@ -2,14 +2,11 @@ package gitbucket.core.controller
|
||||
|
||||
import java.io.FileInputStream
|
||||
|
||||
import com.github.zafarkhaja.semver.{Version => Semver}
|
||||
import gitbucket.core.GitBucketCoreModule
|
||||
import gitbucket.core.admin.html
|
||||
import gitbucket.core.plugin.{PluginInfoBase, PluginRegistry, PluginRepository}
|
||||
import gitbucket.core.plugin.PluginRegistry
|
||||
import gitbucket.core.service.SystemSettingsService._
|
||||
import gitbucket.core.service.{AccountService, RepositoryService}
|
||||
import gitbucket.core.ssh.SshServer
|
||||
import gitbucket.core.util.Directory._
|
||||
import gitbucket.core.util.Implicits._
|
||||
import gitbucket.core.util.StringUtil._
|
||||
import gitbucket.core.util.SyntaxSugars._
|
||||
@@ -21,8 +18,8 @@ import org.scalatra._
|
||||
import org.scalatra.forms._
|
||||
import org.scalatra.i18n.Messages
|
||||
|
||||
import scala.collection.JavaConverters._
|
||||
import scala.collection.mutable.ListBuffer
|
||||
import scala.util.Using
|
||||
|
||||
class SystemSettingsController
|
||||
extends SystemSettingsControllerBase
|
||||
@@ -94,16 +91,10 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
|
||||
),
|
||||
"skinName" -> trim(label("AdminLTE skin name", text(required))),
|
||||
"showMailAddress" -> trim(label("Show mail address", boolean())),
|
||||
"pluginNetworkInstall" -> trim(label("Network plugin installation", boolean())),
|
||||
"proxy" -> optionalIfNotChecked(
|
||||
"useProxy",
|
||||
mapping(
|
||||
"host" -> trim(label("Proxy host", text(required))),
|
||||
"port" -> trim(label("Proxy port", number())),
|
||||
"user" -> trim(label("Keystore", optional(text()))),
|
||||
"password" -> trim(label("Keystore", optional(text())))
|
||||
)(Proxy.apply)
|
||||
)
|
||||
"webhook" -> mapping(
|
||||
"blockPrivateAddress" -> trim(label("Block private address", boolean())),
|
||||
"whitelist" -> trim(label("Whitelist", multiLineText()))
|
||||
)(WebHook.apply)
|
||||
)(SystemSettings.apply).verifying { settings =>
|
||||
Vector(
|
||||
if (settings.ssh.enabled && settings.baseUrl.isEmpty) {
|
||||
@@ -229,30 +220,30 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
|
||||
val conn = request2Session(request).conn
|
||||
val meta = conn.getMetaData
|
||||
val tables = ListBuffer[Table]()
|
||||
using(meta.getTables(null, "%", "%", Array("TABLE", "VIEW"))) {
|
||||
Using.resource(meta.getTables(null, "%", "%", Array("TABLE", "VIEW"))) {
|
||||
rs =>
|
||||
while (rs.next()) {
|
||||
val tableName = rs.getString("TABLE_NAME")
|
||||
|
||||
val pkColumns = ListBuffer[String]()
|
||||
using(meta.getPrimaryKeys(null, null, tableName)) { rs =>
|
||||
Using.resource(meta.getPrimaryKeys(null, null, tableName)) { rs =>
|
||||
while (rs.next()) {
|
||||
pkColumns += rs.getString("COLUMN_NAME").toUpperCase
|
||||
}
|
||||
}
|
||||
|
||||
val columns = ListBuffer[Column]()
|
||||
using(meta.getColumns(null, "%", tableName, "%")) { rs =>
|
||||
Using.resource(meta.getColumns(null, "%", tableName, "%")) { rs =>
|
||||
while (rs.next()) {
|
||||
val columnName = rs.getString("COLUMN_NAME").toUpperCase
|
||||
columns += Column(columnName, pkColumns.contains(columnName))
|
||||
}
|
||||
}
|
||||
|
||||
tables += Table(tableName.toUpperCase, columns)
|
||||
tables += Table(tableName.toUpperCase, columns.toSeq)
|
||||
}
|
||||
}
|
||||
html.dbviewer(tables)
|
||||
html.dbviewer(tables.toSeq)
|
||||
})
|
||||
|
||||
post("/admin/dbviewer/_query")(adminOnly {
|
||||
@@ -263,10 +254,10 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
|
||||
if (trimmedQuery.nonEmpty) {
|
||||
try {
|
||||
val conn = request2Session(request).conn
|
||||
using(conn.prepareStatement(query)) {
|
||||
Using.resource(conn.prepareStatement(query)) {
|
||||
stmt =>
|
||||
if (trimmedQuery.toUpperCase.startsWith("SELECT")) {
|
||||
using(stmt.executeQuery()) {
|
||||
Using.resource(stmt.executeQuery()) {
|
||||
rs =>
|
||||
val meta = rs.getMetaData
|
||||
val columns = for (i <- 1 to meta.getColumnCount) yield {
|
||||
@@ -309,7 +300,7 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
|
||||
} SshServer.start(sshAddress, baseUrl)
|
||||
}
|
||||
|
||||
flash += "info" -> "System settings has been updated."
|
||||
flash.update("info", "System settings has been updated.")
|
||||
redirect("/admin/system")
|
||||
})
|
||||
|
||||
@@ -332,63 +323,12 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
|
||||
})
|
||||
|
||||
get("/admin/plugins")(adminOnly {
|
||||
// Installed plugins
|
||||
val enabledPlugins = PluginRegistry().getPlugins()
|
||||
val gitbucketVersion = GitBucketCoreModule.getVersions.asScala.last.getVersion
|
||||
val gitbucketSemver = Semver.valueOf(gitbucketVersion)
|
||||
|
||||
// Plugins in the remote repository
|
||||
val repositoryPlugins = if (context.settings.pluginNetworkInstall) {
|
||||
PluginRepository
|
||||
.getPlugins()
|
||||
.map {
|
||||
meta =>
|
||||
(meta, meta.versions.reverse.find {
|
||||
version =>
|
||||
val semver = Semver.valueOf(version.version)
|
||||
gitbucketVersion == version.gitbucketVersion && !enabledPlugins.exists { plugin =>
|
||||
if (plugin.pluginId == meta.id) {
|
||||
Semver.valueOf(plugin.pluginVersion) match {
|
||||
case x if x.greaterThan(semver) => true
|
||||
case x if x.equals(semver) =>
|
||||
plugin.gitbucketVersion match {
|
||||
case None => true
|
||||
case Some(x) => Semver.valueOf(x).greaterThanOrEqualTo(gitbucketSemver)
|
||||
}
|
||||
case _ => false
|
||||
}
|
||||
} else false
|
||||
}
|
||||
})
|
||||
}
|
||||
.collect {
|
||||
case (meta, Some(version)) =>
|
||||
new PluginInfoBase(
|
||||
pluginId = meta.id,
|
||||
pluginName = meta.name,
|
||||
pluginVersion = version.version,
|
||||
gitbucketVersion = Some(version.gitbucketVersion),
|
||||
description = meta.description
|
||||
)
|
||||
}
|
||||
} else Nil
|
||||
|
||||
// Merge
|
||||
val plugins = (enabledPlugins.map((_, true)) ++ repositoryPlugins.map((_, false)))
|
||||
.groupBy(_._1.pluginId)
|
||||
.map {
|
||||
case (pluginId, plugins) =>
|
||||
val (plugin, enabled) = plugins.head
|
||||
(plugin, enabled, if (plugins.length > 1) plugins.last._1.pluginVersion else "")
|
||||
}
|
||||
.toList
|
||||
|
||||
html.plugins(plugins, flash.get("info"))
|
||||
html.plugins(PluginRegistry().getPlugins(), flash.get("info"))
|
||||
})
|
||||
|
||||
post("/admin/plugins/_reload")(adminOnly {
|
||||
PluginRegistry.reload(request.getServletContext(), loadSystemSettings(), request2Session(request).conn)
|
||||
flash += "info" -> "All plugins were reloaded."
|
||||
flash.update("info", "All plugins were reloaded.")
|
||||
redirect("/admin/plugins")
|
||||
})
|
||||
|
||||
@@ -398,37 +338,7 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
|
||||
if (PluginRegistry().getPlugins().exists(_.pluginId == pluginId)) {
|
||||
PluginRegistry
|
||||
.uninstall(pluginId, request.getServletContext, loadSystemSettings(), request2Session(request).conn)
|
||||
flash += "info" -> s"${pluginId} was uninstalled."
|
||||
}
|
||||
|
||||
redirect("/admin/plugins")
|
||||
})
|
||||
|
||||
post("/admin/plugins/:pluginId/:version/_install")(adminOnly {
|
||||
if (context.settings.pluginNetworkInstall) {
|
||||
val pluginId = params("pluginId")
|
||||
val version = params("version")
|
||||
val gitbucketVersion = GitBucketCoreModule.getVersions.asScala.last.getVersion
|
||||
|
||||
PluginRepository
|
||||
.getPlugins()
|
||||
.collectFirst {
|
||||
case meta if meta.id == pluginId =>
|
||||
(meta, meta.versions.find(x => x.gitbucketVersion == gitbucketVersion && x.version == version))
|
||||
}
|
||||
.foreach {
|
||||
case (meta, version) =>
|
||||
version.foreach { version =>
|
||||
PluginRegistry.install(
|
||||
pluginId,
|
||||
new java.net.URL(version.url),
|
||||
request.getServletContext,
|
||||
loadSystemSettings(),
|
||||
request2Session(request).conn
|
||||
)
|
||||
flash += "info" -> s"${pluginId}:${version.version} was installed."
|
||||
}
|
||||
}
|
||||
flash.update("info", s"${pluginId} was uninstalled.")
|
||||
}
|
||||
|
||||
redirect("/admin/plugins")
|
||||
@@ -476,7 +386,7 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
|
||||
getAccountByUserName(userName, true).map {
|
||||
account =>
|
||||
if (account.isAdmin && (form.isRemoved || !form.isAdmin) && isLastAdministrator(account)) {
|
||||
flash += "error" -> "Account can't be turned off because this is last one administrator."
|
||||
flash.update("error", "Account can't be turned off because this is last one administrator.")
|
||||
redirect(s"/admin/users/${userName}/_edituser")
|
||||
} else {
|
||||
if (form.isRemoved) {
|
||||
@@ -601,13 +511,24 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
|
||||
response.setHeader("Content-Disposition", "attachment; filename=" + file.getName)
|
||||
response.setContentLength(file.length.toInt)
|
||||
|
||||
using(new FileInputStream(file)) { in =>
|
||||
Using.resource(new FileInputStream(file)) { in =>
|
||||
IOUtils.copy(in, response.outputStream)
|
||||
}
|
||||
|
||||
()
|
||||
})
|
||||
|
||||
private def multiLineText(constraints: Constraint*): SingleValueType[Seq[String]] =
|
||||
new SingleValueType[Seq[String]](constraints: _*) {
|
||||
def convert(value: String, messages: Messages): Seq[String] = {
|
||||
if (value == null) {
|
||||
Nil
|
||||
} else {
|
||||
value.split("\n").toIndexedSeq.map(_.trim)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def members: Constraint = new Constraint() {
|
||||
override def validate(name: String, value: String, messages: Messages): Option[String] = {
|
||||
if (value.split(",").exists {
|
||||
|
||||
@@ -13,6 +13,7 @@ import gitbucket.core.util.Directory._
|
||||
import org.scalatra.forms._
|
||||
import org.eclipse.jgit.api.Git
|
||||
import org.scalatra.i18n.Messages
|
||||
import scala.util.Using
|
||||
|
||||
class WikiController
|
||||
extends WikiControllerBase
|
||||
@@ -90,7 +91,7 @@ trait WikiControllerBase extends ControllerBase {
|
||||
get("/:owner/:repository/wiki/:page/_history")(referrersOnly { repository =>
|
||||
val pageName = StringUtil.urlDecode(params("page"))
|
||||
|
||||
using(Git.open(getWikiRepositoryDir(repository.owner, repository.name))) { git =>
|
||||
Using.resource(Git.open(getWikiRepositoryDir(repository.owner, repository.name))) { git =>
|
||||
JGitUtil.getCommitLog(git, "master", path = pageName + ".md") match {
|
||||
case Right((logs, hasNext)) => html.history(Some(pageName), logs, repository, isEditable(repository))
|
||||
case Left(_) => NotFound()
|
||||
@@ -102,7 +103,7 @@ trait WikiControllerBase extends ControllerBase {
|
||||
val pageName = StringUtil.urlDecode(params("page"))
|
||||
val Array(from, to) = params("commitId").split("\\.\\.\\.")
|
||||
|
||||
using(Git.open(getWikiRepositoryDir(repository.owner, repository.name))) { git =>
|
||||
Using.resource(Git.open(getWikiRepositoryDir(repository.owner, repository.name))) { git =>
|
||||
html.compare(
|
||||
Some(pageName),
|
||||
from,
|
||||
@@ -118,7 +119,7 @@ trait WikiControllerBase extends ControllerBase {
|
||||
get("/:owner/:repository/wiki/_compare/:commitId")(referrersOnly { repository =>
|
||||
val Array(from, to) = params("commitId").split("\\.\\.\\.")
|
||||
|
||||
using(Git.open(getWikiRepositoryDir(repository.owner, repository.name))) { git =>
|
||||
Using.resource(Git.open(getWikiRepositoryDir(repository.owner, repository.name))) { git =>
|
||||
html.compare(
|
||||
None,
|
||||
from,
|
||||
@@ -139,7 +140,7 @@ trait WikiControllerBase extends ControllerBase {
|
||||
if (revertWikiPage(repository.owner, repository.name, from, to, context.loginAccount.get, Some(pageName))) {
|
||||
redirect(s"/${repository.owner}/${repository.name}/wiki/${StringUtil.urlEncode(pageName)}")
|
||||
} else {
|
||||
flash += "info" -> "This patch was not able to be reversed."
|
||||
flash.update("info", "This patch was not able to be reversed.")
|
||||
redirect(
|
||||
s"/${repository.owner}/${repository.name}/wiki/${StringUtil.urlEncode(pageName)}/_compare/${from}...${to}"
|
||||
)
|
||||
@@ -154,7 +155,7 @@ trait WikiControllerBase extends ControllerBase {
|
||||
if (revertWikiPage(repository.owner, repository.name, from, to, context.loginAccount.get, None)) {
|
||||
redirect(s"/${repository.owner}/${repository.name}/wiki")
|
||||
} else {
|
||||
flash += "info" -> "This patch was not able to be reversed."
|
||||
flash.update("info", "This patch was not able to be reversed.")
|
||||
redirect(s"/${repository.owner}/${repository.name}/wiki/_compare/${from}...${to}")
|
||||
}
|
||||
} else Unauthorized()
|
||||
@@ -190,7 +191,7 @@ trait WikiControllerBase extends ControllerBase {
|
||||
form.pageName,
|
||||
commitId
|
||||
)
|
||||
callWebHookOf(repository.owner, repository.name, WebHook.Gollum) {
|
||||
callWebHookOf(repository.owner, repository.name, WebHook.Gollum, context.settings) {
|
||||
getAccountByUserName(repository.owner).map { repositoryUser =>
|
||||
WebHookGollumPayload("edited", form.pageName, commitId, repository, repositoryUser, loginAccount)
|
||||
}
|
||||
@@ -228,7 +229,7 @@ trait WikiControllerBase extends ControllerBase {
|
||||
commitId =>
|
||||
updateLastActivityDate(repository.owner, repository.name)
|
||||
recordCreateWikiPageActivity(repository.owner, repository.name, loginAccount.userName, form.pageName)
|
||||
callWebHookOf(repository.owner, repository.name, WebHook.Gollum) {
|
||||
callWebHookOf(repository.owner, repository.name, WebHook.Gollum, context.settings) {
|
||||
getAccountByUserName(repository.owner).map { repositoryUser =>
|
||||
WebHookGollumPayload("created", form.pageName, commitId, repository, repositoryUser, loginAccount)
|
||||
}
|
||||
@@ -269,7 +270,7 @@ trait WikiControllerBase extends ControllerBase {
|
||||
})
|
||||
|
||||
get("/:owner/:repository/wiki/_history")(referrersOnly { repository =>
|
||||
using(Git.open(getWikiRepositoryDir(repository.owner, repository.name))) { git =>
|
||||
Using.resource(Git.open(getWikiRepositoryDir(repository.owner, repository.name))) { git =>
|
||||
JGitUtil.getCommitLog(git, "master") match {
|
||||
case Right((logs, hasNext)) => html.history(None, logs, repository, isEditable(repository))
|
||||
case Left(_) => NotFound()
|
||||
@@ -279,7 +280,7 @@ trait WikiControllerBase extends ControllerBase {
|
||||
|
||||
get("/:owner/:repository/wiki/_blob/*")(referrersOnly { repository =>
|
||||
val path = multiParams("splat").head
|
||||
using(Git.open(getWikiRepositoryDir(repository.owner, repository.name))) { git =>
|
||||
Using.resource(Git.open(getWikiRepositoryDir(repository.owner, repository.name))) { git =>
|
||||
val revCommit = JGitUtil.getRevCommitFromId(git, git.getRepository.resolve("master"))
|
||||
|
||||
getPathObjectId(git, path, revCommit).map { objectId =>
|
||||
|
||||
@@ -3,10 +3,11 @@ import gitbucket.core.api.{ApiObject, ApiRef, JsonFormat}
|
||||
import gitbucket.core.controller.ControllerBase
|
||||
import gitbucket.core.util.Directory.getRepositoryDir
|
||||
import gitbucket.core.util.ReferrerAuthenticator
|
||||
import gitbucket.core.util.SyntaxSugars.using
|
||||
import gitbucket.core.util.Implicits._
|
||||
import org.eclipse.jgit.api.Git
|
||||
import scala.collection.JavaConverters._
|
||||
|
||||
import scala.jdk.CollectionConverters._
|
||||
import scala.util.Using
|
||||
|
||||
trait ApiGitReferenceControllerBase extends ControllerBase {
|
||||
self: ReferrerAuthenticator =>
|
||||
@@ -17,7 +18,7 @@ trait ApiGitReferenceControllerBase extends ControllerBase {
|
||||
*/
|
||||
get("/api/v3/repos/:owner/:repository/git/refs/*")(referrersOnly { repository =>
|
||||
val revstr = multiParams("splat").head
|
||||
using(Git.open(getRepositoryDir(params("owner"), params("repository")))) { git =>
|
||||
Using.resource(Git.open(getRepositoryDir(params("owner"), params("repository")))) { git =>
|
||||
val ref = git.getRepository().findRef(revstr)
|
||||
|
||||
if (ref != null) {
|
||||
|
||||
@@ -31,7 +31,7 @@ trait ApiIssueControllerBase extends ControllerBase {
|
||||
val condition = IssueSearchCondition(request)
|
||||
val baseOwner = getAccountByUserName(repository.owner).get
|
||||
|
||||
val issues: List[(Issue, Account)] =
|
||||
val issues: List[(Issue, Account, Option[Account])] =
|
||||
searchIssueByApi(
|
||||
condition = condition,
|
||||
offset = (page - 1) * PullRequestLimit,
|
||||
@@ -40,11 +40,12 @@ trait ApiIssueControllerBase extends ControllerBase {
|
||||
)
|
||||
|
||||
JsonFormat(issues.map {
|
||||
case (issue, issueUser) =>
|
||||
case (issue, issueUser, assignedUser) =>
|
||||
ApiIssue(
|
||||
issue = issue,
|
||||
repositoryName = RepositoryName(repository),
|
||||
user = ApiUser(issueUser),
|
||||
assignee = assignedUser.map(ApiUser(_)),
|
||||
labels = getIssueLabels(repository.owner, repository.name, issue.issueId)
|
||||
.map(ApiLabel(_, RepositoryName(repository)))
|
||||
)
|
||||
@@ -59,13 +60,15 @@ trait ApiIssueControllerBase extends ControllerBase {
|
||||
(for {
|
||||
issueId <- params("id").toIntOpt
|
||||
issue <- getIssue(repository.owner, repository.name, issueId.toString)
|
||||
openedUser <- getAccountByUserName(issue.openedUserName)
|
||||
users = getAccountsByUserNames(Set(issue.openedUserName) ++ issue.assignedUserName, Set())
|
||||
openedUser <- users.get(issue.openedUserName)
|
||||
} yield {
|
||||
JsonFormat(
|
||||
ApiIssue(
|
||||
issue,
|
||||
RepositoryName(repository),
|
||||
ApiUser(openedUser),
|
||||
issue.assignedUserName.flatMap(users.get(_)).map(ApiUser(_)),
|
||||
getIssueLabels(repository.owner, repository.name, issue.issueId).map(ApiLabel(_, RepositoryName(repository)))
|
||||
)
|
||||
)
|
||||
@@ -98,6 +101,7 @@ trait ApiIssueControllerBase extends ControllerBase {
|
||||
issue,
|
||||
RepositoryName(repository),
|
||||
ApiUser(loginAccount),
|
||||
issue.assignedUserName.flatMap(getAccountByUserName(_)).map(ApiUser(_)),
|
||||
getIssueLabels(repository.owner, repository.name, issue.issueId)
|
||||
.map(ApiLabel(_, RepositoryName(repository)))
|
||||
)
|
||||
|
||||
@@ -8,12 +8,12 @@ import gitbucket.core.service.PullRequestService.PullRequestLimit
|
||||
import gitbucket.core.util.Directory.getRepositoryDir
|
||||
import gitbucket.core.util.Implicits._
|
||||
import gitbucket.core.util.JGitUtil.CommitInfo
|
||||
import gitbucket.core.util.SyntaxSugars.using
|
||||
import gitbucket.core.util._
|
||||
import org.eclipse.jgit.api.Git
|
||||
import org.scalatra.NoContent
|
||||
import scala.util.Using
|
||||
|
||||
import scala.collection.JavaConverters._
|
||||
import scala.jdk.CollectionConverters._
|
||||
|
||||
trait ApiPullRequestControllerBase extends ControllerBase {
|
||||
self: AccountService
|
||||
@@ -114,7 +114,9 @@ trait ApiPullRequestControllerBase extends ControllerBase {
|
||||
requestBranch = reqBranch,
|
||||
commitIdFrom = commitIdFrom.getName,
|
||||
commitIdTo = commitIdTo.getName,
|
||||
loginAccount = context.loginAccount.get
|
||||
isDraft = false,
|
||||
loginAccount = context.loginAccount.get,
|
||||
settings = context.settings
|
||||
)
|
||||
getApiPullRequest(repository, issueId).map(JsonFormat(_))
|
||||
case _ =>
|
||||
@@ -141,7 +143,9 @@ trait ApiPullRequestControllerBase extends ControllerBase {
|
||||
requestBranch = reqBranch,
|
||||
commitIdFrom = commitIdFrom.getName,
|
||||
commitIdTo = commitIdTo.getName,
|
||||
loginAccount = context.loginAccount.get
|
||||
isDraft = false,
|
||||
loginAccount = context.loginAccount.get,
|
||||
settings = context.settings
|
||||
)
|
||||
getApiPullRequest(repository, createPullReqAlt.issue).map(JsonFormat(_))
|
||||
case _ =>
|
||||
@@ -171,7 +175,7 @@ trait ApiPullRequestControllerBase extends ControllerBase {
|
||||
issueId =>
|
||||
getPullRequest(owner, name, issueId) map {
|
||||
case (issue, pullreq) =>
|
||||
using(Git.open(getRepositoryDir(owner, name))) { git =>
|
||||
Using.resource(Git.open(getRepositoryDir(owner, name))) { git =>
|
||||
val oldId = git.getRepository.resolve(pullreq.commitIdFrom)
|
||||
val newId = git.getRepository.resolve(pullreq.commitIdTo)
|
||||
val repoFullName = RepositoryName(repository)
|
||||
|
||||
@@ -3,11 +3,11 @@ import gitbucket.core.api._
|
||||
import gitbucket.core.controller.ControllerBase
|
||||
import gitbucket.core.service.{AccountService, ProtectedBranchService, RepositoryService}
|
||||
import gitbucket.core.util._
|
||||
import gitbucket.core.util.SyntaxSugars._
|
||||
import gitbucket.core.util.Directory._
|
||||
import gitbucket.core.util.Implicits._
|
||||
import gitbucket.core.util.JGitUtil.getBranches
|
||||
import org.eclipse.jgit.api.Git
|
||||
import scala.util.Using
|
||||
|
||||
trait ApiRepositoryBranchControllerBase extends ControllerBase {
|
||||
self: RepositoryService
|
||||
@@ -25,7 +25,7 @@ trait ApiRepositoryBranchControllerBase extends ControllerBase {
|
||||
* https://developer.github.com/v3/repos/branches/#list-branches
|
||||
*/
|
||||
get("/api/v3/repos/:owner/:repository/branches")(referrersOnly { repository =>
|
||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||
JsonFormat(
|
||||
JGitUtil
|
||||
.getBranches(
|
||||
@@ -45,7 +45,7 @@ trait ApiRepositoryBranchControllerBase extends ControllerBase {
|
||||
* https://developer.github.com/v3/repos/branches/#get-branch
|
||||
*/
|
||||
get("/api/v3/repos/:owner/:repository/branches/*")(referrersOnly { repository =>
|
||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))) {
|
||||
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) {
|
||||
git =>
|
||||
(for {
|
||||
branch <- params.get("splat") if repository.branchList.contains(branch)
|
||||
@@ -214,7 +214,7 @@ trait ApiRepositoryBranchControllerBase extends ControllerBase {
|
||||
*/
|
||||
patch("/api/v3/repos/:owner/:repository/branches/*")(ownerOnly { repository =>
|
||||
import gitbucket.core.api._
|
||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))) {
|
||||
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) {
|
||||
git =>
|
||||
(for {
|
||||
branch <- params.get("splat") if repository.branchList.contains(branch)
|
||||
|
||||
@@ -7,9 +7,9 @@ import gitbucket.core.util.Directory.getRepositoryDir
|
||||
import gitbucket.core.util.Implicits._
|
||||
import gitbucket.core.util.JGitUtil.CommitInfo
|
||||
import gitbucket.core.util.{JGitUtil, ReferrerAuthenticator, RepositoryName}
|
||||
import gitbucket.core.util.SyntaxSugars.using
|
||||
import org.eclipse.jgit.api.Git
|
||||
import org.eclipse.jgit.revwalk.RevWalk
|
||||
import scala.util.Using
|
||||
|
||||
trait ApiRepositoryCommitControllerBase extends ControllerBase {
|
||||
self: AccountService with CommitsService with ReferrerAuthenticator =>
|
||||
@@ -27,11 +27,11 @@ trait ApiRepositoryCommitControllerBase extends ControllerBase {
|
||||
val name = repository.name
|
||||
val sha = params("sha")
|
||||
|
||||
using(Git.open(getRepositoryDir(owner, name))) {
|
||||
Using.resource(Git.open(getRepositoryDir(owner, name))) {
|
||||
git =>
|
||||
val repo = git.getRepository
|
||||
val objectId = repo.resolve(sha)
|
||||
val commitInfo = using(new RevWalk(repo)) { revWalk =>
|
||||
val commitInfo = Using.resource(new RevWalk(repo)) { revWalk =>
|
||||
new CommitInfo(revWalk.parseCommit(objectId))
|
||||
}
|
||||
|
||||
|
||||
@@ -5,10 +5,10 @@ import gitbucket.core.service.{RepositoryCommitFileService, RepositoryService}
|
||||
import gitbucket.core.util.Directory.getRepositoryDir
|
||||
import gitbucket.core.util.JGitUtil.{FileInfo, getContentFromId, getFileList}
|
||||
import gitbucket.core.util._
|
||||
import gitbucket.core.util.SyntaxSugars.using
|
||||
import gitbucket.core.view.helpers.{isRenderable, renderMarkup}
|
||||
import gitbucket.core.util.Implicits._
|
||||
import org.eclipse.jgit.api.Git
|
||||
import scala.util.Using
|
||||
|
||||
trait ApiRepositoryContentsControllerBase extends ControllerBase {
|
||||
self: ReferrerAuthenticator with WritableUsersAuthenticator with RepositoryCommitFileService =>
|
||||
@@ -45,7 +45,7 @@ trait ApiRepositoryContentsControllerBase extends ControllerBase {
|
||||
getFileList(git, revision, dirName).find(f => f.name.equals(fileName))
|
||||
}
|
||||
|
||||
using(Git.open(getRepositoryDir(params("owner"), params("repository")))) { git =>
|
||||
Using.resource(Git.open(getRepositoryDir(params("owner"), params("repository")))) { git =>
|
||||
val fileList = getFileList(git, refStr, path)
|
||||
if (fileList.isEmpty) { // file or NotFound
|
||||
getFileInfo(git, refStr, path)
|
||||
@@ -113,7 +113,7 @@ trait ApiRepositoryContentsControllerBase extends ControllerBase {
|
||||
data <- extractFromJsonBody[CreateAFile]
|
||||
} yield {
|
||||
val branch = data.branch.getOrElse(repository.repository.defaultBranch)
|
||||
val commit = using(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||
val commit = Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||
val revCommit = JGitUtil.getRevCommitFromId(git, git.getRepository.resolve(branch))
|
||||
revCommit.name
|
||||
}
|
||||
@@ -127,17 +127,14 @@ trait ApiRepositoryContentsControllerBase extends ControllerBase {
|
||||
branch,
|
||||
path,
|
||||
Some(paths.last),
|
||||
if (data.sha.isDefined) {
|
||||
Some(paths.last)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
data.sha.map(_ => paths.last),
|
||||
StringUtil.base64Decode(data.content),
|
||||
data.message,
|
||||
commit,
|
||||
context.loginAccount.get,
|
||||
data.committer.map(_.name).getOrElse(context.loginAccount.get.fullName),
|
||||
data.committer.map(_.email).getOrElse(context.loginAccount.get.mailAddress)
|
||||
data.committer.map(_.email).getOrElse(context.loginAccount.get.mailAddress),
|
||||
context.settings
|
||||
)
|
||||
ApiContents("file", paths.last, path, objectId.name, None, None)(RepositoryName(repository))
|
||||
}
|
||||
|
||||
@@ -6,12 +6,12 @@ import gitbucket.core.servlet.Database
|
||||
import gitbucket.core.util.Directory.getRepositoryDir
|
||||
import gitbucket.core.util._
|
||||
import gitbucket.core.util.Implicits._
|
||||
import gitbucket.core.util.SyntaxSugars.using
|
||||
import gitbucket.core.model.Profile.profile.blockingApi._
|
||||
import org.eclipse.jgit.api.Git
|
||||
|
||||
import scala.concurrent.Await
|
||||
import scala.concurrent.duration.Duration
|
||||
import scala.util.Using
|
||||
|
||||
trait ApiRepositoryControllerBase extends ControllerBase {
|
||||
self: RepositoryService
|
||||
@@ -193,7 +193,7 @@ trait ApiRepositoryControllerBase extends ControllerBase {
|
||||
*/
|
||||
get("/api/v3/repos/:owner/:repository/raw/*")(referrersOnly { repository =>
|
||||
val (id, path) = repository.splitPath(multiParams("splat").head)
|
||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||
val revCommit = JGitUtil.getRevCommitFromId(git, git.getRepository.resolve(id))
|
||||
|
||||
getPathObjectId(git, path, revCommit).map { objectId =>
|
||||
|
||||
@@ -4,6 +4,7 @@ import gitbucket.core.controller.ControllerBase
|
||||
import gitbucket.core.service.{AccountService, RepositoryService}
|
||||
import gitbucket.core.util.{AdminAuthenticator, UsersAuthenticator}
|
||||
import gitbucket.core.util.Implicits._
|
||||
import gitbucket.core.util.StringUtil._
|
||||
import org.scalatra.NoContent
|
||||
|
||||
trait ApiUserControllerBase extends ControllerBase {
|
||||
@@ -70,7 +71,7 @@ trait ApiUserControllerBase extends ControllerBase {
|
||||
} yield {
|
||||
val user = createAccount(
|
||||
data.login,
|
||||
data.password,
|
||||
pbkdf2_sha256(data.password),
|
||||
data.fullName.getOrElse(data.login),
|
||||
data.email,
|
||||
data.isAdmin.getOrElse(false),
|
||||
|
||||
@@ -12,6 +12,7 @@ trait PullRequestComponent extends TemplateComponent { self: Profile =>
|
||||
val requestBranch = column[String]("REQUEST_BRANCH")
|
||||
val commitIdFrom = column[String]("COMMIT_ID_FROM")
|
||||
val commitIdTo = column[String]("COMMIT_ID_TO")
|
||||
val isDraft = column[Boolean]("IS_DRAFT")
|
||||
def * =
|
||||
(
|
||||
userName,
|
||||
@@ -22,7 +23,8 @@ trait PullRequestComponent extends TemplateComponent { self: Profile =>
|
||||
requestRepositoryName,
|
||||
requestBranch,
|
||||
commitIdFrom,
|
||||
commitIdTo
|
||||
commitIdTo,
|
||||
isDraft
|
||||
) <> (PullRequest.tupled, PullRequest.unapply)
|
||||
|
||||
def byPrimaryKey(userName: String, repositoryName: String, issueId: Int) =
|
||||
@@ -41,5 +43,6 @@ case class PullRequest(
|
||||
requestRepositoryName: String,
|
||||
requestBranch: String,
|
||||
commitIdFrom: String,
|
||||
commitIdTo: String
|
||||
commitIdTo: String,
|
||||
isDraft: Boolean
|
||||
)
|
||||
|
||||
@@ -6,10 +6,10 @@ import gitbucket.core.controller.{Context, ControllerBase}
|
||||
import gitbucket.core.model.{Account, Issue}
|
||||
import gitbucket.core.service.RepositoryService.RepositoryInfo
|
||||
import gitbucket.core.service.SystemSettingsService.SystemSettings
|
||||
import gitbucket.core.util.SyntaxSugars._
|
||||
import io.github.gitbucket.solidbase.model.Version
|
||||
import org.apache.sshd.server.command.Command
|
||||
import play.twirl.api.Html
|
||||
import scala.util.Using
|
||||
|
||||
/**
|
||||
* Trait for define plugin interface.
|
||||
@@ -434,7 +434,7 @@ abstract class Plugin {
|
||||
* Helper method to get a resource from classpath.
|
||||
*/
|
||||
protected def fromClassPath(path: String): Array[Byte] =
|
||||
using(getClass.getClassLoader.getResourceAsStream(path)) { in =>
|
||||
Using.resource(getClass.getClassLoader.getResourceAsStream(path)) { in =>
|
||||
val bytes = new Array[Byte](in.available)
|
||||
in.read(bytes)
|
||||
bytes
|
||||
|
||||
@@ -15,19 +15,17 @@ import gitbucket.core.service.ProtectedBranchService.ProtectedBranchReceiveHook
|
||||
import gitbucket.core.service.RepositoryService.RepositoryInfo
|
||||
import gitbucket.core.service.SystemSettingsService
|
||||
import gitbucket.core.service.SystemSettingsService.SystemSettings
|
||||
import gitbucket.core.util.DatabaseConfig
|
||||
import gitbucket.core.util.{ConfigUtil, DatabaseConfig}
|
||||
import gitbucket.core.util.Directory._
|
||||
import gitbucket.core.util.HttpClientUtil._
|
||||
import io.github.gitbucket.solidbase.Solidbase
|
||||
import io.github.gitbucket.solidbase.manager.JDBCVersionManager
|
||||
import io.github.gitbucket.solidbase.model.Module
|
||||
import org.apache.commons.io.FileUtils
|
||||
import org.apache.http.client.methods.HttpGet
|
||||
import org.apache.sshd.server.command.Command
|
||||
import org.slf4j.LoggerFactory
|
||||
import play.twirl.api.Html
|
||||
|
||||
import scala.collection.JavaConverters._
|
||||
import scala.jdk.CollectionConverters._
|
||||
|
||||
class PluginRegistry {
|
||||
|
||||
@@ -227,40 +225,6 @@ object PluginRegistry {
|
||||
initialize(context, settings, conn)
|
||||
}
|
||||
|
||||
/**
|
||||
* Install a plugin from a specified jar file.
|
||||
*/
|
||||
def install(
|
||||
pluginId: String,
|
||||
url: java.net.URL,
|
||||
context: ServletContext,
|
||||
settings: SystemSettings,
|
||||
conn: java.sql.Connection
|
||||
): Unit =
|
||||
synchronized {
|
||||
shutdown(context, settings)
|
||||
|
||||
new File(PluginHome)
|
||||
.listFiles((_: File, name: String) => {
|
||||
name.startsWith(s"gitbucket-${pluginId}-plugin") && name.endsWith(".jar")
|
||||
})
|
||||
.foreach(_.delete())
|
||||
|
||||
withHttpClient(settings.pluginProxy) { httpClient =>
|
||||
val httpGet = new HttpGet(url.toString)
|
||||
try {
|
||||
val response = httpClient.execute(httpGet)
|
||||
val in = response.getEntity.getContent
|
||||
FileUtils.copyToFile(in, new File(PluginHome, new File(url.getFile).getName))
|
||||
} finally {
|
||||
httpGet.releaseConnection()
|
||||
}
|
||||
}
|
||||
|
||||
instance = new PluginRegistry()
|
||||
initialize(context, settings, conn)
|
||||
}
|
||||
|
||||
private def listPluginJars(dir: File): Seq[File] = {
|
||||
dir
|
||||
.listFiles(new FilenameFilter {
|
||||
@@ -271,7 +235,7 @@ object PluginRegistry {
|
||||
.reverse
|
||||
}
|
||||
|
||||
lazy val extraPluginDir: Option[String] = Option(System.getProperty("gitbucket.pluginDir"))
|
||||
lazy val extraPluginDir: Option[String] = ConfigUtil.getConfigValue[String]("gitbucket.pluginDir")
|
||||
|
||||
def getGitBucketVersion(pluginJarFileName: String): Option[String] = {
|
||||
val regex = ".+-gitbucket\\_(\\d+\\.\\d+\\.\\d+(-SNAPSHOT)?)-.+".r
|
||||
@@ -449,7 +413,6 @@ case class PluginInfo(
|
||||
|
||||
class PluginWatchThread(context: ServletContext, dir: String) extends Thread with SystemSettingsService {
|
||||
import gitbucket.core.model.Profile.profile.blockingApi._
|
||||
import scala.collection.JavaConverters._
|
||||
|
||||
private val logger = LoggerFactory.getLogger(classOf[PluginWatchThread])
|
||||
|
||||
@@ -479,7 +442,7 @@ class PluginWatchThread(context: ServletContext, dir: String) extends Thread wit
|
||||
}
|
||||
if (events.nonEmpty) {
|
||||
events.foreach { event =>
|
||||
logger.info(event.kind + ": " + event.context)
|
||||
logger.info(s"${event.kind}: ${event.context}")
|
||||
}
|
||||
new Thread {
|
||||
override def run(): Unit = {
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
package gitbucket.core.plugin
|
||||
|
||||
import gitbucket.core.controller.Context
|
||||
import gitbucket.core.util.SyntaxSugars.using
|
||||
import gitbucket.core.util.HttpClientUtil._
|
||||
import org.json4s._
|
||||
import org.apache.commons.io.IOUtils
|
||||
|
||||
import org.apache.http.client.methods.HttpGet
|
||||
import org.slf4j.LoggerFactory
|
||||
|
||||
object PluginRepository {
|
||||
private val logger = LoggerFactory.getLogger(getClass)
|
||||
implicit val formats = DefaultFormats
|
||||
|
||||
def parsePluginJson(json: String): Seq[PluginMetadata] = {
|
||||
org.json4s.jackson.JsonMethods.parse(json).extract[Seq[PluginMetadata]]
|
||||
}
|
||||
|
||||
def getPlugins()(implicit context: Context): Seq[PluginMetadata] = {
|
||||
try {
|
||||
val url = new java.net.URL("https://plugins.gitbucket-community.org/releases/plugins.json")
|
||||
|
||||
withHttpClient(context.settings.pluginProxy) { httpClient =>
|
||||
val httpGet = new HttpGet(url.toString)
|
||||
try {
|
||||
val response = httpClient.execute(httpGet)
|
||||
using(response.getEntity.getContent) { in =>
|
||||
val str = IOUtils.toString(in, "UTF-8")
|
||||
parsePluginJson(str)
|
||||
}
|
||||
} finally {
|
||||
httpGet.releaseConnection()
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
case t: Throwable =>
|
||||
logger.warn("Failed to access to the plugin repository: " + t.toString)
|
||||
Nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Mapped from plugins.json
|
||||
case class PluginMetadata(
|
||||
id: String,
|
||||
name: String,
|
||||
description: String,
|
||||
versions: Seq[VersionDef],
|
||||
default: Boolean = false
|
||||
) {
|
||||
lazy val latestVersion: VersionDef = versions.last
|
||||
}
|
||||
|
||||
case class VersionDef(
|
||||
version: String,
|
||||
url: String,
|
||||
gitbucketVersion: String
|
||||
)
|
||||
@@ -94,7 +94,8 @@ trait CommitsService {
|
||||
repository,
|
||||
issue,
|
||||
pullRequest,
|
||||
loginAccount
|
||||
loginAccount,
|
||||
context.settings
|
||||
)
|
||||
}
|
||||
case None =>
|
||||
|
||||
@@ -3,14 +3,14 @@ package gitbucket.core.service
|
||||
import java.io.ByteArrayInputStream
|
||||
|
||||
import gitbucket.core.model.GpgKey
|
||||
|
||||
import collection.JavaConverters._
|
||||
import gitbucket.core.model.Profile._
|
||||
import gitbucket.core.model.Profile.profile.blockingApi._
|
||||
import org.bouncycastle.bcpg.ArmoredInputStream
|
||||
import org.bouncycastle.openpgp.PGPPublicKeyRing
|
||||
import org.bouncycastle.openpgp.bc.BcPGPObjectFactory
|
||||
|
||||
import scala.jdk.CollectionConverters._
|
||||
|
||||
trait GpgKeyService {
|
||||
def getGpgPublicKeys(userName: String)(implicit s: Session): List[GpgKey] =
|
||||
GpgKeys.filter(_.userName === userName.bind).sortBy(_.gpgKeyId).list
|
||||
|
||||
@@ -80,16 +80,17 @@ trait HandleCommentService {
|
||||
|
||||
// call web hooks
|
||||
action match {
|
||||
case None => commentId foreach (callIssueCommentWebHook(repository, issue, _, loginAccount))
|
||||
case None =>
|
||||
commentId foreach (callIssueCommentWebHook(repository, issue, _, loginAccount, context.settings))
|
||||
case Some(act) =>
|
||||
val webHookAction = act match {
|
||||
case "close" => "closed"
|
||||
case "reopen" => "reopened"
|
||||
}
|
||||
if (issue.isPullRequest)
|
||||
callPullRequestWebHook(webHookAction, repository, issue.issueId, loginAccount)
|
||||
callPullRequestWebHook(webHookAction, repository, issue.issueId, loginAccount, context.settings)
|
||||
else
|
||||
callIssuesWebHook(webHookAction, repository, issue, loginAccount)
|
||||
callIssuesWebHook(webHookAction, repository, issue, loginAccount, context.settings)
|
||||
}
|
||||
|
||||
// call hooks
|
||||
|
||||
@@ -57,7 +57,7 @@ trait IssueCreationService {
|
||||
createReferComment(owner, name, issue, title + " " + body.getOrElse(""), loginAccount)
|
||||
|
||||
// call web hooks
|
||||
callIssuesWebHook("opened", repository, issue, loginAccount)
|
||||
callIssuesWebHook("opened", repository, issue, loginAccount, context.settings)
|
||||
|
||||
// call hooks
|
||||
PluginRegistry().getIssueHooks.foreach(_.created(issue, repository))
|
||||
|
||||
@@ -265,17 +265,19 @@ trait IssuesService {
|
||||
}
|
||||
|
||||
/** for api
|
||||
* @return (issue, issueUser, commentCount)
|
||||
* @return (issue, issueUser, assignedUser)
|
||||
*/
|
||||
def searchIssueByApi(condition: IssueSearchCondition, offset: Int, limit: Int, repos: (String, String)*)(
|
||||
implicit s: Session
|
||||
): List[(Issue, Account)] = {
|
||||
): List[(Issue, Account, Option[Account])] = {
|
||||
// get issues and comment count and labels
|
||||
searchIssueQueryBase(condition, false, offset, limit, repos)
|
||||
.join(Accounts)
|
||||
.on { case t1 ~ t2 ~ i ~ t3 => t3.userName === t1.openedUserName }
|
||||
.sortBy { case t1 ~ t2 ~ i ~ t3 => i asc }
|
||||
.map { case t1 ~ t2 ~ i ~ t3 => (t1, t3) }
|
||||
.joinLeft(Accounts)
|
||||
.on { case t1 ~ t2 ~ i ~ t3 ~ t4 => t4.userName === t1.assignedUserName }
|
||||
.sortBy { case t1 ~ t2 ~ i ~ t3 ~ t4 => i asc }
|
||||
.map { case t1 ~ t2 ~ i ~ t3 ~ t4 => (t1, t3, t4) }
|
||||
.list
|
||||
}
|
||||
|
||||
@@ -752,7 +754,7 @@ trait IssuesService {
|
||||
implicit s: Session
|
||||
): Unit = {
|
||||
extractIssueId(message).foreach { issueId =>
|
||||
val content = fromIssue.issueId + ":" + fromIssue.title
|
||||
val content = s"${fromIssue.issueId}:${fromIssue.title}"
|
||||
if (getIssue(owner, repository, issueId).isDefined) {
|
||||
// Not add if refer comment already exist.
|
||||
if (!getComments(owner, repository, issueId.toInt).exists { x =>
|
||||
|
||||
@@ -7,10 +7,8 @@ import gitbucket.core.plugin.PluginRegistry
|
||||
import gitbucket.core.service.RepositoryService.RepositoryInfo
|
||||
import gitbucket.core.util.Directory._
|
||||
import gitbucket.core.util.{JGitUtil, LockUtil}
|
||||
import gitbucket.core.util.SyntaxSugars._
|
||||
import gitbucket.core.model.Profile._
|
||||
import gitbucket.core.model.Profile.profile._
|
||||
import gitbucket.core.model.Profile.profile.blockingApi._
|
||||
import gitbucket.core.service.SystemSettingsService.SystemSettings
|
||||
import org.eclipse.jgit.merge.{MergeStrategy, Merger, RecursiveMerger}
|
||||
import org.eclipse.jgit.api.Git
|
||||
import org.eclipse.jgit.transport.RefSpec
|
||||
@@ -18,7 +16,8 @@ import org.eclipse.jgit.errors.NoMergeBaseException
|
||||
import org.eclipse.jgit.lib.{CommitBuilder, ObjectId, PersonIdent, Repository}
|
||||
import org.eclipse.jgit.revwalk.{RevCommit, RevWalk}
|
||||
|
||||
import scala.collection.JavaConverters._
|
||||
import scala.jdk.CollectionConverters._
|
||||
import scala.util.Using
|
||||
|
||||
trait MergeService {
|
||||
self: AccountService
|
||||
@@ -35,7 +34,7 @@ trait MergeService {
|
||||
* Returns true if conflict will be caused.
|
||||
*/
|
||||
def checkConflict(userName: String, repositoryName: String, branch: String, issueId: Int): Option[String] = {
|
||||
using(Git.open(getRepositoryDir(userName, repositoryName))) { git =>
|
||||
Using.resource(Git.open(getRepositoryDir(userName, repositoryName))) { git =>
|
||||
new MergeCacheInfo(git, branch, issueId).checkConflict()
|
||||
}
|
||||
}
|
||||
@@ -52,7 +51,7 @@ trait MergeService {
|
||||
branch: String,
|
||||
issueId: Int
|
||||
): Option[Option[String]] = {
|
||||
using(Git.open(getRepositoryDir(userName, repositoryName))) { git =>
|
||||
Using.resource(Git.open(getRepositoryDir(userName, repositoryName))) { git =>
|
||||
new MergeCacheInfo(git, branch, issueId).checkConflictCache()
|
||||
}
|
||||
}
|
||||
@@ -99,7 +98,7 @@ trait MergeService {
|
||||
requestBranch: String,
|
||||
issueId: Int
|
||||
): Unit = {
|
||||
using(Git.open(getRepositoryDir(userName, repositoryName))) { git =>
|
||||
Using.resource(Git.open(getRepositoryDir(userName, repositoryName))) { git =>
|
||||
git.fetch
|
||||
.setRemote(getRepositoryDir(requestUserName, requestRepositoryName).toURI.toString)
|
||||
.setRefSpecs(new RefSpec(s"refs/heads/${requestBranch}:refs/pull/${issueId}/head"))
|
||||
@@ -118,7 +117,7 @@ trait MergeService {
|
||||
remoteRepositoryName: String,
|
||||
remoteBranch: String
|
||||
): Either[String, (ObjectId, ObjectId, ObjectId)] = {
|
||||
using(Git.open(getRepositoryDir(localUserName, localRepositoryName))) { git =>
|
||||
Using.resource(Git.open(getRepositoryDir(localUserName, localRepositoryName))) { git =>
|
||||
val remoteRefName = s"refs/heads/${remoteBranch}"
|
||||
val tmpRefName = s"refs/remote-temp/${remoteUserName}/${remoteRepositoryName}/${remoteBranch}"
|
||||
val refSpec = new RefSpec(s"${remoteRefName}:${tmpRefName}").setForceUpdate(true)
|
||||
@@ -169,7 +168,8 @@ trait MergeService {
|
||||
remoteBranch: String,
|
||||
loginAccount: Account,
|
||||
message: String,
|
||||
pullreq: Option[PullRequest]
|
||||
pullreq: Option[PullRequest],
|
||||
settings: SystemSettings
|
||||
)(implicit s: Session, c: JsonFormat.Context): Option[ObjectId] = {
|
||||
val localUserName = localRepository.owner
|
||||
val localRepositoryName = localRepository.name
|
||||
@@ -177,7 +177,7 @@ trait MergeService {
|
||||
val remoteRepositoryName = remoteRepository.name
|
||||
tryMergeRemote(localUserName, localRepositoryName, localBranch, remoteUserName, remoteRepositoryName, remoteBranch).map {
|
||||
case (newTreeId, oldBaseId, oldHeadId) =>
|
||||
using(Git.open(getRepositoryDir(localUserName, localRepositoryName))) { git =>
|
||||
Using.resource(Git.open(getRepositoryDir(localUserName, localRepositoryName))) { git =>
|
||||
val existIds = JGitUtil.getAllCommitIds(git).toSet
|
||||
|
||||
val committer = new PersonIdent(loginAccount.fullName, loginAccount.mailAddress)
|
||||
@@ -214,7 +214,7 @@ trait MergeService {
|
||||
closeIssuesFromMessage(commit.fullMessage, loginAccount.userName, localUserName, localRepositoryName)
|
||||
.foreach { issueId =>
|
||||
getIssue(localRepository.owner, localRepository.name, issueId.toString).foreach { issue =>
|
||||
callIssuesWebHook("closed", localRepository, issue, loginAccount)
|
||||
callIssuesWebHook("closed", localRepository, issue, loginAccount, settings)
|
||||
PluginRegistry().getIssueHooks
|
||||
.foreach(
|
||||
_.closedByCommitComment(issue, localRepository, commit.fullMessage, loginAccount)
|
||||
@@ -225,7 +225,7 @@ trait MergeService {
|
||||
}
|
||||
|
||||
pullreq.foreach { pullreq =>
|
||||
callWebHookOf(localRepository.owner, localRepository.name, WebHook.Push) {
|
||||
callWebHookOf(localRepository.owner, localRepository.name, WebHook.Push, settings) {
|
||||
for {
|
||||
ownerAccount <- getAccountByUserName(localRepository.owner)
|
||||
} yield {
|
||||
@@ -252,135 +252,147 @@ trait MergeService {
|
||||
issueId: Int,
|
||||
loginAccount: Account,
|
||||
message: String,
|
||||
strategy: String
|
||||
strategy: String,
|
||||
isDraft: Boolean,
|
||||
settings: SystemSettings
|
||||
)(implicit s: Session, c: JsonFormat.Context, context: Context): Either[String, ObjectId] = {
|
||||
if (repository.repository.options.mergeOptions.split(",").contains(strategy)) {
|
||||
LockUtil.lock(s"${repository.owner}/${repository.name}") {
|
||||
getPullRequest(repository.owner, repository.name, issueId)
|
||||
.map {
|
||||
case (issue, pullreq) =>
|
||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||
// mark issue as merged and close.
|
||||
val commentId =
|
||||
createComment(repository.owner, repository.name, loginAccount.userName, issueId, message, "merge")
|
||||
createComment(repository.owner, repository.name, loginAccount.userName, issueId, "Close", "close")
|
||||
updateClosed(repository.owner, repository.name, issueId, true)
|
||||
if (!isDraft) {
|
||||
if (repository.repository.options.mergeOptions.split(",").contains(strategy)) {
|
||||
LockUtil.lock(s"${repository.owner}/${repository.name}") {
|
||||
getPullRequest(repository.owner, repository.name, issueId)
|
||||
.map {
|
||||
case (issue, pullreq) =>
|
||||
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||
// mark issue as merged and close.
|
||||
val commentId =
|
||||
createComment(repository.owner, repository.name, loginAccount.userName, issueId, message, "merge")
|
||||
createComment(repository.owner, repository.name, loginAccount.userName, issueId, "Close", "close")
|
||||
updateClosed(repository.owner, repository.name, issueId, true)
|
||||
|
||||
// record activity
|
||||
recordMergeActivity(repository.owner, repository.name, loginAccount.userName, issueId, message)
|
||||
// record activity
|
||||
recordMergeActivity(repository.owner, repository.name, loginAccount.userName, issueId, message)
|
||||
|
||||
val (commits, _) = getRequestCompareInfo(
|
||||
repository.owner,
|
||||
repository.name,
|
||||
pullreq.commitIdFrom,
|
||||
pullreq.requestUserName,
|
||||
pullreq.requestRepositoryName,
|
||||
pullreq.commitIdTo
|
||||
)
|
||||
val (commits, _) = getRequestCompareInfo(
|
||||
repository.owner,
|
||||
repository.name,
|
||||
pullreq.commitIdFrom,
|
||||
pullreq.requestUserName,
|
||||
pullreq.requestRepositoryName,
|
||||
pullreq.commitIdTo
|
||||
)
|
||||
|
||||
val revCommits = using(new RevWalk(git.getRepository)) { revWalk =>
|
||||
commits.flatten.map { commit =>
|
||||
revWalk.parseCommit(git.getRepository.resolve(commit.id))
|
||||
}
|
||||
}.reverse
|
||||
val revCommits = Using
|
||||
.resource(new RevWalk(git.getRepository)) { revWalk =>
|
||||
commits.flatten.map { commit =>
|
||||
revWalk.parseCommit(git.getRepository.resolve(commit.id))
|
||||
}
|
||||
}
|
||||
.reverse
|
||||
|
||||
// merge git repository
|
||||
(strategy match {
|
||||
case "merge-commit" =>
|
||||
Some(
|
||||
mergePullRequest(
|
||||
git,
|
||||
pullreq.branch,
|
||||
issueId,
|
||||
s"Merge pull request #${issueId} from ${pullreq.requestUserName}/${pullreq.requestBranch}\n\n" + message,
|
||||
new PersonIdent(loginAccount.fullName, loginAccount.mailAddress)
|
||||
// merge git repository
|
||||
(strategy match {
|
||||
case "merge-commit" =>
|
||||
Some(
|
||||
mergePullRequest(
|
||||
git,
|
||||
pullreq.branch,
|
||||
issueId,
|
||||
s"Merge pull request #${issueId} from ${pullreq.requestUserName}/${pullreq.requestBranch}\n\n" + message,
|
||||
new PersonIdent(loginAccount.fullName, loginAccount.mailAddress)
|
||||
)
|
||||
)
|
||||
)
|
||||
case "rebase" =>
|
||||
Some(
|
||||
rebasePullRequest(
|
||||
git,
|
||||
pullreq.branch,
|
||||
issueId,
|
||||
revCommits,
|
||||
new PersonIdent(loginAccount.fullName, loginAccount.mailAddress)
|
||||
case "rebase" =>
|
||||
Some(
|
||||
rebasePullRequest(
|
||||
git,
|
||||
pullreq.branch,
|
||||
issueId,
|
||||
revCommits,
|
||||
new PersonIdent(loginAccount.fullName, loginAccount.mailAddress)
|
||||
)
|
||||
)
|
||||
)
|
||||
case "squash" =>
|
||||
Some(
|
||||
squashPullRequest(
|
||||
git,
|
||||
pullreq.branch,
|
||||
issueId,
|
||||
s"${issue.title} (#${issueId})\n\n" + message,
|
||||
new PersonIdent(loginAccount.fullName, loginAccount.mailAddress)
|
||||
case "squash" =>
|
||||
Some(
|
||||
squashPullRequest(
|
||||
git,
|
||||
pullreq.branch,
|
||||
issueId,
|
||||
s"${issue.title} (#${issueId})\n\n" + message,
|
||||
new PersonIdent(loginAccount.fullName, loginAccount.mailAddress)
|
||||
)
|
||||
)
|
||||
)
|
||||
case _ =>
|
||||
None
|
||||
}) match {
|
||||
case Some(newCommitId) =>
|
||||
// close issue by content of pull request
|
||||
val defaultBranch = getRepository(repository.owner, repository.name).get.repository.defaultBranch
|
||||
if (pullreq.branch == defaultBranch) {
|
||||
commits.flatten.foreach { commit =>
|
||||
case _ =>
|
||||
None
|
||||
}) match {
|
||||
case Some(newCommitId) =>
|
||||
// close issue by content of pull request
|
||||
val defaultBranch = getRepository(repository.owner, repository.name).get.repository.defaultBranch
|
||||
if (pullreq.branch == defaultBranch) {
|
||||
commits.flatten.foreach { commit =>
|
||||
closeIssuesFromMessage(
|
||||
commit.fullMessage,
|
||||
loginAccount.userName,
|
||||
repository.owner,
|
||||
repository.name
|
||||
).foreach { issueId =>
|
||||
getIssue(repository.owner, repository.name, issueId.toString).foreach { issue =>
|
||||
callIssuesWebHook("closed", repository, issue, loginAccount, context.settings)
|
||||
PluginRegistry().getIssueHooks
|
||||
.foreach(_.closedByCommitComment(issue, repository, commit.fullMessage, loginAccount))
|
||||
}
|
||||
}
|
||||
}
|
||||
val issueContent = issue.title + " " + issue.content.getOrElse("")
|
||||
closeIssuesFromMessage(
|
||||
commit.fullMessage,
|
||||
issueContent,
|
||||
loginAccount.userName,
|
||||
repository.owner,
|
||||
repository.name
|
||||
).foreach { issueId =>
|
||||
getIssue(repository.owner, repository.name, issueId.toString).foreach { issue =>
|
||||
callIssuesWebHook("closed", repository, issue, loginAccount)
|
||||
PluginRegistry().getIssueHooks
|
||||
.foreach(_.closedByCommitComment(issue, repository, commit.fullMessage, loginAccount))
|
||||
}
|
||||
}
|
||||
}
|
||||
val issueContent = issue.title + " " + issue.content.getOrElse("")
|
||||
closeIssuesFromMessage(
|
||||
issueContent,
|
||||
loginAccount.userName,
|
||||
repository.owner,
|
||||
repository.name
|
||||
).foreach { issueId =>
|
||||
getIssue(repository.owner, repository.name, issueId.toString).foreach { issue =>
|
||||
callIssuesWebHook("closed", repository, issue, loginAccount)
|
||||
PluginRegistry().getIssueHooks
|
||||
.foreach(_.closedByCommitComment(issue, repository, issueContent, loginAccount))
|
||||
}
|
||||
}
|
||||
closeIssuesFromMessage(message, loginAccount.userName, repository.owner, repository.name)
|
||||
.foreach { issueId =>
|
||||
getIssue(repository.owner, repository.name, issueId.toString).foreach { issue =>
|
||||
callIssuesWebHook("closed", repository, issue, loginAccount)
|
||||
callIssuesWebHook("closed", repository, issue, loginAccount, context.settings)
|
||||
PluginRegistry().getIssueHooks
|
||||
.foreach(_.closedByCommitComment(issue, repository, issueContent, loginAccount))
|
||||
}
|
||||
}
|
||||
}
|
||||
closeIssuesFromMessage(message, loginAccount.userName, repository.owner, repository.name)
|
||||
.foreach { issueId =>
|
||||
getIssue(repository.owner, repository.name, issueId.toString).foreach { issue =>
|
||||
callIssuesWebHook("closed", repository, issue, loginAccount, context.settings)
|
||||
PluginRegistry().getIssueHooks
|
||||
.foreach(_.closedByCommitComment(issue, repository, issueContent, loginAccount))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
callPullRequestWebHook("closed", repository, issueId, context.loginAccount.get)
|
||||
callPullRequestWebHook("closed", repository, issueId, context.loginAccount.get, context.settings)
|
||||
|
||||
updatePullRequests(repository.owner, repository.name, pullreq.branch, loginAccount, "closed")
|
||||
updatePullRequests(
|
||||
repository.owner,
|
||||
repository.name,
|
||||
pullreq.branch,
|
||||
loginAccount,
|
||||
"closed",
|
||||
settings
|
||||
)
|
||||
|
||||
// call hooks
|
||||
PluginRegistry().getPullRequestHooks.foreach { h =>
|
||||
h.addedComment(commentId, message, issue, repository)
|
||||
h.merged(issue, repository)
|
||||
}
|
||||
// call hooks
|
||||
PluginRegistry().getPullRequestHooks.foreach { h =>
|
||||
h.addedComment(commentId, message, issue, repository)
|
||||
h.merged(issue, repository)
|
||||
}
|
||||
|
||||
Right(newCommitId)
|
||||
case None =>
|
||||
Left("Unknown strategy")
|
||||
Right(newCommitId)
|
||||
case None =>
|
||||
Left("Unknown strategy")
|
||||
}
|
||||
}
|
||||
}
|
||||
case _ => Left("Unknown error")
|
||||
}
|
||||
.getOrElse(Left("Pull request not found"))
|
||||
}
|
||||
} else Left("Strategy not allowed")
|
||||
|
||||
case _ => Left("Unknown error")
|
||||
}
|
||||
.getOrElse(Left("Pull request not found"))
|
||||
}
|
||||
} else Left("Strategy not allowed")
|
||||
} else Left("Draft pull requests cannot be merged")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -402,7 +414,7 @@ object MergeService {
|
||||
mergeCommit.setCommitter(committer)
|
||||
mergeCommit.setMessage(message)
|
||||
// insertObject and got mergeCommit Object Id
|
||||
using(repository.newObjectInserter) { inserter =>
|
||||
Using.resource(repository.newObjectInserter) { inserter =>
|
||||
val mergeCommitId = inserter.insert(mergeCommit)
|
||||
inserter.flush()
|
||||
mergeCommitId
|
||||
@@ -470,7 +482,7 @@ object MergeService {
|
||||
} catch {
|
||||
case e: NoMergeBaseException => true
|
||||
}
|
||||
val mergeTipCommit = using(new RevWalk(repository))(_.parseCommit(mergeTip))
|
||||
val mergeTipCommit = Using.resource(new RevWalk(repository))(_.parseCommit(mergeTip))
|
||||
val committer = mergeTipCommit.getCommitterIdent
|
||||
|
||||
def _updateBranch(treeId: ObjectId, message: String, branchName: String): Unit = {
|
||||
@@ -523,10 +535,10 @@ object MergeService {
|
||||
newCommit
|
||||
}
|
||||
|
||||
val mergeBaseTipCommit = using(new RevWalk(repository))(_.parseCommit(mergeBaseTip))
|
||||
val mergeBaseTipCommit = Using.resource(new RevWalk(repository))(_.parseCommit(mergeBaseTip))
|
||||
var previousId = mergeBaseTipCommit.getId
|
||||
|
||||
using(repository.newObjectInserter) { inserter =>
|
||||
Using.resource(repository.newObjectInserter) { inserter =>
|
||||
commits.foreach { commit =>
|
||||
val nextCommit = _cloneCommit(commit, previousId, mergeBaseTipCommit.getId)
|
||||
previousId = inserter.insert(nextCommit)
|
||||
@@ -542,8 +554,9 @@ object MergeService {
|
||||
throw new RuntimeException("This pull request can't merge automatically.")
|
||||
}
|
||||
|
||||
val mergeBaseTipCommit = using(new RevWalk(repository))(_.parseCommit(mergeBaseTip))
|
||||
val mergeBranchHeadCommit = using(new RevWalk(repository))(_.parseCommit(repository.resolve(mergedBranchName)))
|
||||
val mergeBaseTipCommit = Using.resource(new RevWalk(repository))(_.parseCommit(mergeBaseTip))
|
||||
val mergeBranchHeadCommit =
|
||||
Using.resource(new RevWalk(repository))(_.parseCommit(repository.resolve(mergedBranchName)))
|
||||
|
||||
// Create squash commit
|
||||
val mergeCommit = new CommitBuilder()
|
||||
@@ -554,7 +567,7 @@ object MergeService {
|
||||
mergeCommit.setMessage(message)
|
||||
|
||||
// insertObject and got squash commit Object Id
|
||||
val newCommitId = using(repository.newObjectInserter) { inserter =>
|
||||
val newCommitId = Using.resource(repository.newObjectInserter) { inserter =>
|
||||
val newCommitId = inserter.insert(mergeCommit)
|
||||
inserter.flush()
|
||||
newCommitId
|
||||
@@ -577,7 +590,7 @@ object MergeService {
|
||||
private def createMergeCommit(treeId: ObjectId, committer: PersonIdent, message: String) =
|
||||
Util.createMergeCommit(repository, treeId, committer, message, Seq[ObjectId](mergeBaseTip, mergeTip))
|
||||
|
||||
private def parseCommit(id: ObjectId) = using(new RevWalk(repository))(_.parseCommit(id))
|
||||
private def parseCommit(id: ObjectId) = Using.resource(new RevWalk(repository))(_.parseCommit(id))
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ import gitbucket.core.model.Account
|
||||
import gitbucket.core.model.Profile.profile.blockingApi._
|
||||
import org.slf4j.LoggerFactory
|
||||
|
||||
import scala.collection.JavaConverters.{asScalaSet, mapAsJavaMap}
|
||||
import scala.jdk.CollectionConverters._
|
||||
|
||||
/**
|
||||
* Service class for the OpenID Connect authentication.
|
||||
@@ -101,7 +101,7 @@ trait OpenIDConnectService {
|
||||
redirectURI: URI
|
||||
): Option[AuthenticationSuccessResponse] =
|
||||
try {
|
||||
AuthenticationResponseParser.parse(redirectURI, mapAsJavaMap(params)) match {
|
||||
AuthenticationResponseParser.parse(redirectURI, params.asJava) match {
|
||||
case response: AuthenticationSuccessResponse =>
|
||||
if (response.getState == state) {
|
||||
Some(response)
|
||||
@@ -207,5 +207,5 @@ object OpenIDConnectService {
|
||||
"RSA" -> Family.RSA,
|
||||
"ECDSA" -> Family.EC,
|
||||
"EdDSA" -> Family.ED
|
||||
).toMap.map { case (name, family) => (name, asScalaSet(family).toSet) }
|
||||
).toMap.map { case (name, family) => (name, family.asScala.toSet) }
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import gitbucket.core.service.RepositoryService.RepositoryInfo
|
||||
import gitbucket.core.api.JsonFormat
|
||||
import gitbucket.core.controller.Context
|
||||
import gitbucket.core.plugin.PluginRegistry
|
||||
import gitbucket.core.util.SyntaxSugars._
|
||||
import gitbucket.core.service.SystemSettingsService.SystemSettings
|
||||
import gitbucket.core.util.Directory._
|
||||
import gitbucket.core.util.Implicits._
|
||||
import gitbucket.core.util.JGitUtil
|
||||
@@ -19,7 +19,8 @@ import gitbucket.core.view.helpers
|
||||
import org.eclipse.jgit.api.Git
|
||||
import org.eclipse.jgit.lib.ObjectId
|
||||
|
||||
import scala.collection.JavaConverters._
|
||||
import scala.jdk.CollectionConverters._
|
||||
import scala.util.Using
|
||||
|
||||
trait PullRequestService {
|
||||
self: IssuesService
|
||||
@@ -48,6 +49,14 @@ trait PullRequestService {
|
||||
.map(pr => pr.commitIdTo -> pr.commitIdFrom)
|
||||
.update((commitIdTo, commitIdFrom))
|
||||
|
||||
def updateDraftToPullRequest(owner: String, repository: String, issueId: Int)(
|
||||
implicit s: Session
|
||||
): Unit =
|
||||
PullRequests
|
||||
.filter(_.byPrimaryKey(owner, repository, issueId))
|
||||
.map(pr => pr.isDraft)
|
||||
.update(false)
|
||||
|
||||
def getPullRequestCountGroupByUser(closed: Boolean, owner: Option[String], repository: Option[String])(
|
||||
implicit s: Session
|
||||
): List[PullRequestCount] =
|
||||
@@ -97,7 +106,9 @@ trait PullRequestService {
|
||||
requestBranch: String,
|
||||
commitIdFrom: String,
|
||||
commitIdTo: String,
|
||||
loginAccount: Account
|
||||
isDraft: Boolean,
|
||||
loginAccount: Account,
|
||||
settings: SystemSettings
|
||||
)(implicit s: Session, context: Context): Unit = {
|
||||
getIssue(originRepository.owner, originRepository.name, issueId.toString).foreach { baseIssue =>
|
||||
PullRequests insert PullRequest(
|
||||
@@ -109,7 +120,8 @@ trait PullRequestService {
|
||||
requestRepositoryName,
|
||||
requestBranch,
|
||||
commitIdFrom,
|
||||
commitIdTo
|
||||
commitIdTo,
|
||||
isDraft
|
||||
)
|
||||
|
||||
// fetch requested branch
|
||||
@@ -132,7 +144,7 @@ trait PullRequestService {
|
||||
)
|
||||
|
||||
// call web hook
|
||||
callPullRequestWebHook("opened", originRepository, issueId, loginAccount)
|
||||
callPullRequestWebHook("opened", originRepository, issueId, loginAccount, settings)
|
||||
|
||||
getIssue(originRepository.owner, originRepository.name, issueId.toString) foreach { issue =>
|
||||
// extract references and create refer comment
|
||||
@@ -216,7 +228,14 @@ trait PullRequestService {
|
||||
/**
|
||||
* Fetch pull request contents into refs/pull/${issueId}/head and update pull request table.
|
||||
*/
|
||||
def updatePullRequests(owner: String, repository: String, branch: String, loginAccount: Account, action: String)(
|
||||
def updatePullRequests(
|
||||
owner: String,
|
||||
repository: String,
|
||||
branch: String,
|
||||
loginAccount: Account,
|
||||
action: String,
|
||||
settings: SystemSettings
|
||||
)(
|
||||
implicit s: Session,
|
||||
c: JsonFormat.Context
|
||||
): Unit = {
|
||||
@@ -265,7 +284,8 @@ trait PullRequestService {
|
||||
action,
|
||||
getRepository(owner, repository).get,
|
||||
pullreq.requestBranch,
|
||||
loginAccount
|
||||
loginAccount,
|
||||
settings
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -377,7 +397,7 @@ trait PullRequestService {
|
||||
requestRepositoryName: String,
|
||||
requestCommitId: String
|
||||
): (Seq[Seq[CommitInfo]], Seq[DiffInfo]) =
|
||||
using(
|
||||
Using.resources(
|
||||
Git.open(getRepositoryDir(userName, repositoryName)),
|
||||
Git.open(getRepositoryDir(requestUserName, requestRepositoryName))
|
||||
) { (oldGit, newGit) =>
|
||||
@@ -468,7 +488,7 @@ trait PullRequestService {
|
||||
originId: String,
|
||||
forkedId: String
|
||||
): (Option[ObjectId], Option[ObjectId]) = {
|
||||
using(
|
||||
Using.resources(
|
||||
Git.open(getRepositoryDir(originRepository.owner, originRepository.name)),
|
||||
Git.open(getRepositoryDir(forkedRepository.owner, forkedRepository.name))
|
||||
) {
|
||||
@@ -534,7 +554,7 @@ object PullRequestService {
|
||||
lazy val commitStateSummary: (CommitState, String) = {
|
||||
val stateMap = statuses.groupBy(_.state)
|
||||
val state = CommitState.combine(stateMap.keySet)
|
||||
val summary = stateMap.map { case (keyState, states) => states.size + " " + keyState.name }.mkString(", ")
|
||||
val summary = stateMap.map { case (keyState, states) => s"${states.size} ${keyState.name}" }.mkString(", ")
|
||||
state -> summary
|
||||
}
|
||||
lazy val statusesAndRequired: List[(CommitStatus, Boolean)] = statuses.map { s =>
|
||||
|
||||
@@ -4,16 +4,18 @@ import gitbucket.core.model.{Account, WebHook}
|
||||
import gitbucket.core.model.Profile._
|
||||
import gitbucket.core.model.Profile.profile.blockingApi._
|
||||
import gitbucket.core.plugin.PluginRegistry
|
||||
import gitbucket.core.service.SystemSettingsService.SystemSettings
|
||||
import gitbucket.core.service.WebHookService.WebHookPushPayload
|
||||
import gitbucket.core.util.Directory.getRepositoryDir
|
||||
import gitbucket.core.util.JGitUtil.CommitInfo
|
||||
import gitbucket.core.util.{JGitUtil, LockUtil}
|
||||
import gitbucket.core.util.SyntaxSugars.using
|
||||
import org.eclipse.jgit.api.Git
|
||||
import org.eclipse.jgit.dircache.{DirCache, DirCacheBuilder}
|
||||
import org.eclipse.jgit.lib._
|
||||
import org.eclipse.jgit.transport.{ReceiveCommand, ReceivePack}
|
||||
|
||||
import scala.util.Using
|
||||
|
||||
trait RepositoryCommitFileService {
|
||||
self: AccountService with ActivityService with IssuesService with PullRequestService with WebHookPullRequestService =>
|
||||
import RepositoryCommitFileService._
|
||||
@@ -24,12 +26,13 @@ trait RepositoryCommitFileService {
|
||||
branch: String,
|
||||
path: String,
|
||||
message: String,
|
||||
loginAccount: Account
|
||||
loginAccount: Account,
|
||||
settings: SystemSettings
|
||||
)(
|
||||
f: (Git, ObjectId, DirCacheBuilder, ObjectInserter) => Unit
|
||||
)(implicit s: Session, c: JsonFormat.Context) = {
|
||||
// prepend path to the filename
|
||||
_commitFile(repository, branch, message, loginAccount, loginAccount.fullName, loginAccount.mailAddress)(f)
|
||||
_commitFile(repository, branch, message, loginAccount, loginAccount.fullName, loginAccount.mailAddress, settings)(f)
|
||||
}
|
||||
|
||||
def commitFile(
|
||||
@@ -42,7 +45,8 @@ trait RepositoryCommitFileService {
|
||||
charset: String,
|
||||
message: String,
|
||||
commit: String,
|
||||
loginAccount: Account
|
||||
loginAccount: Account,
|
||||
settings: SystemSettings
|
||||
)(implicit s: Session, c: JsonFormat.Context): ObjectId = {
|
||||
commitFile(
|
||||
repository,
|
||||
@@ -55,7 +59,8 @@ trait RepositoryCommitFileService {
|
||||
commit,
|
||||
loginAccount,
|
||||
loginAccount.fullName,
|
||||
loginAccount.mailAddress
|
||||
loginAccount.mailAddress,
|
||||
settings
|
||||
)
|
||||
}
|
||||
|
||||
@@ -70,7 +75,8 @@ trait RepositoryCommitFileService {
|
||||
commit: String,
|
||||
loginAccount: Account,
|
||||
fullName: String,
|
||||
mailAddress: String
|
||||
mailAddress: String,
|
||||
settings: SystemSettings
|
||||
)(implicit s: Session, c: JsonFormat.Context): ObjectId = {
|
||||
|
||||
val newPath = newFileName.map { newFileName =>
|
||||
@@ -80,7 +86,7 @@ trait RepositoryCommitFileService {
|
||||
if (path.length == 0) oldFileName else s"${path}/${oldFileName}"
|
||||
}
|
||||
|
||||
_commitFile(repository, branch, message, loginAccount, fullName, mailAddress) {
|
||||
_commitFile(repository, branch, message, loginAccount, fullName, mailAddress, settings) {
|
||||
case (git, headTip, builder, inserter) =>
|
||||
if (headTip.getName == commit) {
|
||||
val permission = JGitUtil
|
||||
@@ -111,13 +117,14 @@ trait RepositoryCommitFileService {
|
||||
message: String,
|
||||
loginAccount: Account,
|
||||
committerName: String,
|
||||
committerMailAddress: String
|
||||
committerMailAddress: String,
|
||||
settings: SystemSettings
|
||||
)(
|
||||
f: (Git, ObjectId, DirCacheBuilder, ObjectInserter) => Unit
|
||||
)(implicit s: Session, c: JsonFormat.Context): ObjectId = {
|
||||
|
||||
LockUtil.lock(s"${repository.owner}/${repository.name}") {
|
||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||
val builder = DirCache.newInCore.builder()
|
||||
val inserter = git.getRepository.newObjectInserter()
|
||||
val headName = s"refs/heads/${branch}"
|
||||
@@ -165,11 +172,11 @@ trait RepositoryCommitFileService {
|
||||
refUpdate.update()
|
||||
|
||||
// update pull request
|
||||
updatePullRequests(repository.owner, repository.name, branch, loginAccount, "synchronize")
|
||||
updatePullRequests(repository.owner, repository.name, branch, loginAccount, "synchronize", settings)
|
||||
|
||||
// record activity
|
||||
val commitInfo = new CommitInfo(JGitUtil.getRevCommitFromId(git, commitId))
|
||||
recordPushActivity(repository.owner, repository.name, committerName, branch, List(commitInfo))
|
||||
recordPushActivity(repository.owner, repository.name, loginAccount.userName, branch, List(commitInfo))
|
||||
|
||||
// create issue comment by commit message
|
||||
createIssueComment(repository.owner, repository.name, commitInfo)
|
||||
@@ -178,7 +185,7 @@ trait RepositoryCommitFileService {
|
||||
if (branch == repository.repository.defaultBranch) {
|
||||
closeIssuesFromMessage(message, committerName, repository.owner, repository.name).foreach { issueId =>
|
||||
getIssue(repository.owner, repository.name, issueId.toString).foreach { issue =>
|
||||
callIssuesWebHook("closed", repository, issue, loginAccount)
|
||||
callIssuesWebHook("closed", repository, issue, loginAccount, settings)
|
||||
PluginRegistry().getIssueHooks
|
||||
.foreach(_.closedByCommitComment(issue, repository, message, loginAccount))
|
||||
}
|
||||
@@ -191,7 +198,7 @@ trait RepositoryCommitFileService {
|
||||
}
|
||||
|
||||
val commit = new JGitUtil.CommitInfo(JGitUtil.getRevCommitFromId(git, commitId))
|
||||
callWebHookOf(repository.owner, repository.name, WebHook.Push) {
|
||||
callWebHookOf(repository.owner, repository.name, WebHook.Push, settings) {
|
||||
getAccountByUserName(repository.owner).map { ownerAccount =>
|
||||
WebHookPushPayload(
|
||||
git,
|
||||
|
||||
@@ -4,7 +4,6 @@ import java.nio.file.Files
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
import gitbucket.core.model.Profile.profile.blockingApi._
|
||||
import gitbucket.core.util.SyntaxSugars._
|
||||
import gitbucket.core.util.Directory._
|
||||
import gitbucket.core.util.{FileUtil, JGitUtil, LockUtil}
|
||||
import gitbucket.core.model.{Account, Role}
|
||||
@@ -18,6 +17,7 @@ import org.eclipse.jgit.lib.{Constants, FileMode}
|
||||
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
import scala.concurrent.Future
|
||||
import scala.util.Using
|
||||
|
||||
object RepositoryCreationService {
|
||||
|
||||
@@ -107,7 +107,7 @@ trait RepositoryCreationService {
|
||||
JGitUtil.initRepository(gitdir)
|
||||
|
||||
if (initOption == "README" || initOption == "EMPTY_COMMIT") {
|
||||
using(Git.open(gitdir)) { git =>
|
||||
Using.resource(Git.open(gitdir)) { git =>
|
||||
val builder = DirCache.newInCore.builder()
|
||||
val inserter = git.getRepository.newObjectInserter()
|
||||
val headId = git.getRepository.resolve(Constants.HEAD + "^{commit}")
|
||||
@@ -148,7 +148,7 @@ trait RepositoryCreationService {
|
||||
|
||||
copyRepositoryDir.foreach { dir =>
|
||||
try {
|
||||
using(Git.open(dir)) { git =>
|
||||
Using.resource(Git.open(dir)) { git =>
|
||||
git.push().setRemote(gitdir.toURI.toString).setPushAll().setPushTags().call()
|
||||
}
|
||||
} finally {
|
||||
|
||||
@@ -4,12 +4,12 @@ import gitbucket.core.model.Issue
|
||||
import gitbucket.core.util._
|
||||
import gitbucket.core.util.StringUtil
|
||||
import Directory._
|
||||
import SyntaxSugars._
|
||||
import org.eclipse.jgit.revwalk.RevWalk
|
||||
import org.eclipse.jgit.treewalk.TreeWalk
|
||||
import org.eclipse.jgit.lib.FileMode
|
||||
import org.eclipse.jgit.api.Git
|
||||
import gitbucket.core.model.Profile.profile.blockingApi._
|
||||
import scala.util.Using
|
||||
|
||||
trait RepositorySearchService { self: IssuesService =>
|
||||
import RepositorySearchService._
|
||||
@@ -37,12 +37,12 @@ trait RepositorySearchService { self: IssuesService =>
|
||||
}
|
||||
|
||||
def countFiles(owner: String, repository: String, query: String): Int =
|
||||
using(Git.open(getRepositoryDir(owner, repository))) { git =>
|
||||
Using.resource(Git.open(getRepositoryDir(owner, repository))) { git =>
|
||||
if (JGitUtil.isEmpty(git)) 0 else searchRepositoryFiles(git, query).length
|
||||
}
|
||||
|
||||
def searchFiles(owner: String, repository: String, query: String): List[FileSearchResult] =
|
||||
using(Git.open(getRepositoryDir(owner, repository))) { git =>
|
||||
Using.resource(Git.open(getRepositoryDir(owner, repository))) { git =>
|
||||
if (JGitUtil.isEmpty(git)) {
|
||||
Nil
|
||||
} else {
|
||||
@@ -57,12 +57,12 @@ trait RepositorySearchService { self: IssuesService =>
|
||||
}
|
||||
|
||||
def countWikiPages(owner: String, repository: String, query: String): Int =
|
||||
using(Git.open(Directory.getWikiRepositoryDir(owner, repository))) { git =>
|
||||
Using.resource(Git.open(Directory.getWikiRepositoryDir(owner, repository))) { git =>
|
||||
if (JGitUtil.isEmpty(git)) 0 else searchRepositoryFiles(git, query).length
|
||||
}
|
||||
|
||||
def searchWikiPages(owner: String, repository: String, query: String): List[FileSearchResult] =
|
||||
using(Git.open(Directory.getWikiRepositoryDir(owner, repository))) { git =>
|
||||
Using.resource(Git.open(Directory.getWikiRepositoryDir(owner, repository))) { git =>
|
||||
if (JGitUtil.isEmpty(git)) {
|
||||
Nil
|
||||
} else {
|
||||
|
||||
@@ -17,6 +17,7 @@ import org.eclipse.jgit.api.Git
|
||||
import org.eclipse.jgit.dircache.{DirCache, DirCacheBuilder}
|
||||
import org.eclipse.jgit.lib.{Repository => _, _}
|
||||
import org.eclipse.jgit.transport.{ReceiveCommand, ReceivePack}
|
||||
import scala.util.Using
|
||||
|
||||
trait RepositoryService {
|
||||
self: AccountService =>
|
||||
@@ -763,7 +764,7 @@ trait RepositoryService {
|
||||
}
|
||||
|
||||
// Get template file from project root. When didn't find, will lookup default folder.
|
||||
using(Git.open(Directory.getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||
Using.resource(Git.open(Directory.getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||
choiceTemplate(JGitUtil.getFileList(git, repository.repository.defaultBranch, "."))
|
||||
.orElse {
|
||||
choiceTemplate(JGitUtil.getFileList(git, repository.repository.defaultBranch, ".gitbucket"))
|
||||
|
||||
@@ -8,6 +8,7 @@ import gitbucket.core.service.SystemSettingsService._
|
||||
import gitbucket.core.util.ConfigUtil._
|
||||
import gitbucket.core.util.Directory._
|
||||
import gitbucket.core.util.SyntaxSugars._
|
||||
import scala.util.Using
|
||||
|
||||
trait SystemSettingsService {
|
||||
|
||||
@@ -69,19 +70,10 @@ trait SystemSettingsService {
|
||||
}
|
||||
props.setProperty(SkinName, settings.skinName.toString)
|
||||
props.setProperty(ShowMailAddress, settings.showMailAddress.toString)
|
||||
props.setProperty(PluginNetworkInstall, settings.pluginNetworkInstall.toString)
|
||||
settings.pluginProxy.foreach { proxy =>
|
||||
props.setProperty(PluginProxyHost, proxy.host)
|
||||
props.setProperty(PluginProxyPort, proxy.port.toString)
|
||||
proxy.user.foreach { user =>
|
||||
props.setProperty(PluginProxyUser, user)
|
||||
}
|
||||
proxy.password.foreach { password =>
|
||||
props.setProperty(PluginProxyPassword, password)
|
||||
}
|
||||
}
|
||||
props.setProperty(WebHookBlockPrivateAddress, settings.webHook.blockPrivateAddress.toString)
|
||||
props.setProperty(WebHookWhitelist, settings.webHook.whitelist.mkString("\n"))
|
||||
|
||||
using(new java.io.FileOutputStream(GitBucketConf)) { out =>
|
||||
Using.resource(new java.io.FileOutputStream(GitBucketConf)) { out =>
|
||||
props.store(out, null)
|
||||
}
|
||||
}
|
||||
@@ -90,7 +82,7 @@ trait SystemSettingsService {
|
||||
def loadSystemSettings(): SystemSettings = {
|
||||
defining(new java.util.Properties()) { props =>
|
||||
if (GitBucketConf.exists) {
|
||||
using(new java.io.FileInputStream(GitBucketConf)) { in =>
|
||||
Using.resource(new java.io.FileInputStream(GitBucketConf)) { in =>
|
||||
props.load(in)
|
||||
}
|
||||
}
|
||||
@@ -157,17 +149,7 @@ trait SystemSettingsService {
|
||||
},
|
||||
getValue(props, SkinName, "skin-blue"),
|
||||
getValue(props, ShowMailAddress, false),
|
||||
getValue(props, PluginNetworkInstall, false),
|
||||
if (getValue(props, PluginProxyHost, "").nonEmpty) {
|
||||
Some(
|
||||
Proxy(
|
||||
getValue(props, PluginProxyHost, ""),
|
||||
getValue(props, PluginProxyPort, 8080),
|
||||
getOptionValue(props, PluginProxyUser, None),
|
||||
getOptionValue(props, PluginProxyPassword, None)
|
||||
)
|
||||
)
|
||||
} else None
|
||||
WebHook(getValue(props, WebHookBlockPrivateAddress, false), getSeqValue(props, WebHookWhitelist, ""))
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -197,8 +179,7 @@ object SystemSettingsService {
|
||||
oidc: Option[OIDC],
|
||||
skinName: String,
|
||||
showMailAddress: Boolean,
|
||||
pluginNetworkInstall: Boolean,
|
||||
pluginProxy: Option[Proxy]
|
||||
webHook: WebHook
|
||||
) {
|
||||
|
||||
def baseUrl(request: HttpServletRequest): String =
|
||||
@@ -275,7 +256,7 @@ object SystemSettingsService {
|
||||
|
||||
case class SshAddress(host: String, port: Int, genericUser: String)
|
||||
|
||||
case class Lfs(serverUrl: Option[String])
|
||||
case class WebHook(blockPrivateAddress: Boolean, whitelist: Seq[String])
|
||||
|
||||
val DefaultSshPort = 29418
|
||||
val DefaultSmtpPort = 25
|
||||
@@ -326,9 +307,11 @@ object SystemSettingsService {
|
||||
private val PluginProxyPort = "plugin.proxy.port"
|
||||
private val PluginProxyUser = "plugin.proxy.user"
|
||||
private val PluginProxyPassword = "plugin.proxy.password"
|
||||
private val WebHookBlockPrivateAddress = "webhook.block_private_address"
|
||||
private val WebHookWhitelist = "webhook.whitelist"
|
||||
|
||||
private def getValue[A: ClassTag](props: java.util.Properties, key: String, default: A): A = {
|
||||
getSystemProperty(key).getOrElse(getEnvironmentVariable(key).getOrElse {
|
||||
getConfigValue(key).getOrElse {
|
||||
defining(props.getProperty(key)) { value =>
|
||||
if (value == null || value.isEmpty) {
|
||||
default
|
||||
@@ -336,11 +319,21 @@ object SystemSettingsService {
|
||||
convertType(value).asInstanceOf[A]
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private def getSeqValue[A: ClassTag](props: java.util.Properties, key: String, default: A): Seq[A] = {
|
||||
getValue[String](props, key, "").split("\n").toIndexedSeq.map { value =>
|
||||
if (value == null || value.isEmpty) {
|
||||
default
|
||||
} else {
|
||||
convertType(value).asInstanceOf[A]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def getOptionValue[A: ClassTag](props: java.util.Properties, key: String, default: Option[A]): Option[A] = {
|
||||
getSystemProperty(key).orElse(getEnvironmentVariable(key).orElse {
|
||||
getConfigValue(key).orElse {
|
||||
defining(props.getProperty(key)) { value =>
|
||||
if (value == null || value.isEmpty) {
|
||||
default
|
||||
@@ -348,7 +341,7 @@ object SystemSettingsService {
|
||||
Some(convertType(value)).asInstanceOf[Option[A]]
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,24 +3,26 @@ package gitbucket.core.service
|
||||
import fr.brouillard.oss.security.xhub.XHub
|
||||
import fr.brouillard.oss.security.xhub.XHub.{XHubConverter, XHubDigest}
|
||||
import gitbucket.core.api._
|
||||
import gitbucket.core.controller.Context
|
||||
import gitbucket.core.model.{
|
||||
Account,
|
||||
AccountWebHook,
|
||||
AccountWebHookEvent,
|
||||
CommitComment,
|
||||
Issue,
|
||||
IssueComment,
|
||||
Label,
|
||||
PullRequest,
|
||||
WebHook,
|
||||
RepositoryWebHook,
|
||||
RepositoryWebHookEvent,
|
||||
AccountWebHook,
|
||||
AccountWebHookEvent
|
||||
WebHook
|
||||
}
|
||||
import gitbucket.core.model.Profile._
|
||||
import gitbucket.core.model.Profile.profile.blockingApi._
|
||||
import org.apache.http.client.utils.URLEncodedUtils
|
||||
import gitbucket.core.util.JGitUtil.CommitInfo
|
||||
import gitbucket.core.util.{RepositoryName, StringUtil}
|
||||
import gitbucket.core.util.Implicits._
|
||||
import gitbucket.core.util.{HttpClientUtil, RepositoryName, StringUtil}
|
||||
import gitbucket.core.service.RepositoryService.RepositoryInfo
|
||||
import org.apache.http.NameValuePair
|
||||
import org.apache.http.client.entity.UrlEncodedFormEntity
|
||||
@@ -34,6 +36,7 @@ import scala.util.{Failure, Success}
|
||||
import org.apache.http.HttpRequest
|
||||
import org.apache.http.HttpResponse
|
||||
import gitbucket.core.model.WebHookContentType
|
||||
import gitbucket.core.service.SystemSettingsService.SystemSettings
|
||||
import org.apache.http.client.entity.EntityBuilder
|
||||
import org.apache.http.entity.ContentType
|
||||
|
||||
@@ -55,6 +58,7 @@ trait WebHookService {
|
||||
.map { case (w, t) => w -> t.event }
|
||||
.list
|
||||
.groupBy(_._1)
|
||||
.view
|
||||
.mapValues(_.map(_._2).toSet)
|
||||
.toList
|
||||
.sortBy(_._1.url)
|
||||
@@ -87,6 +91,7 @@ trait WebHookService {
|
||||
.map { case (w, t) => w -> t.event }
|
||||
.list
|
||||
.groupBy(_._1)
|
||||
.view
|
||||
.mapValues(_.map(_._2).toSet)
|
||||
.headOption
|
||||
|
||||
@@ -136,6 +141,7 @@ trait WebHookService {
|
||||
.map { case (w, t) => w -> t.event }
|
||||
.list
|
||||
.groupBy(_._1)
|
||||
.view
|
||||
.mapValues(_.map(_._2).toSet)
|
||||
.toList
|
||||
.sortBy(_._1.url)
|
||||
@@ -164,6 +170,7 @@ trait WebHookService {
|
||||
.map { case (w, t) => w -> t.event }
|
||||
.list
|
||||
.groupBy(_._1)
|
||||
.view
|
||||
.mapValues(_.map(_._2).toSet)
|
||||
.headOption
|
||||
|
||||
@@ -197,20 +204,28 @@ trait WebHookService {
|
||||
def deleteAccountWebHook(owner: String, url: String)(implicit s: Session): Unit =
|
||||
AccountWebHooks.filter(_.byPrimaryKey(owner, url)).delete
|
||||
|
||||
def callWebHookOf(owner: String, repository: String, event: WebHook.Event)(
|
||||
def callWebHookOf(owner: String, repository: String, event: WebHook.Event, settings: SystemSettings)(
|
||||
makePayload: => Option[WebHookPayload]
|
||||
)(implicit s: Session, c: JsonFormat.Context): Unit = {
|
||||
val webHooks = getWebHooksByEvent(owner, repository, event)
|
||||
if (webHooks.nonEmpty) {
|
||||
makePayload.map(callWebHook(event, webHooks, _))
|
||||
makePayload.map(callWebHook(event, webHooks, _, settings))
|
||||
}
|
||||
val accountWebHooks = getAccountWebHooksByEvent(owner, event)
|
||||
if (accountWebHooks.nonEmpty) {
|
||||
makePayload.map(callWebHook(event, accountWebHooks, _))
|
||||
makePayload.map(callWebHook(event, accountWebHooks, _, settings))
|
||||
}
|
||||
}
|
||||
|
||||
def callWebHook(event: WebHook.Event, webHooks: List[WebHook], payload: WebHookPayload)(
|
||||
private def validateTargetAddress(settings: SystemSettings, url: String): Boolean = {
|
||||
val host = new java.net.URL(url).getHost
|
||||
|
||||
!settings.webHook.blockPrivateAddress ||
|
||||
!HttpClientUtil.isPrivateAddress(host) ||
|
||||
settings.webHook.whitelist.exists(range => HttpClientUtil.inIpRange(range, host))
|
||||
}
|
||||
|
||||
def callWebHook(event: WebHook.Event, webHooks: List[WebHook], payload: WebHookPayload, settings: SystemSettings)(
|
||||
implicit c: JsonFormat.Context
|
||||
): List[(WebHook, String, Future[HttpRequest], Future[HttpResponse])] = {
|
||||
import org.apache.http.impl.client.HttpClientBuilder
|
||||
@@ -230,6 +245,9 @@ trait WebHookService {
|
||||
}
|
||||
}
|
||||
try {
|
||||
if (!validateTargetAddress(settings, webHook.url)) {
|
||||
throw new IllegalArgumentException(s"Illegal address: ${webHook.url}")
|
||||
}
|
||||
val httpClient = HttpClientBuilder.create.useSystemProperties.addInterceptorLast(itcp).build
|
||||
logger.debug(s"start web hook invocation for ${webHook.url}")
|
||||
val httpPost = new HttpPost(webHook.url)
|
||||
@@ -298,7 +316,6 @@ trait WebHookService {
|
||||
} else {
|
||||
Nil
|
||||
}
|
||||
// logger.debug("end callWebHook")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -311,10 +328,12 @@ trait WebHookPullRequestService extends WebHookService {
|
||||
action: String,
|
||||
repository: RepositoryService.RepositoryInfo,
|
||||
issue: Issue,
|
||||
sender: Account
|
||||
sender: Account,
|
||||
settings: SystemSettings
|
||||
)(implicit s: Session, context: JsonFormat.Context): Unit = {
|
||||
callWebHookOf(repository.owner, repository.name, WebHook.Issues) {
|
||||
val users = getAccountsByUserNames(Set(repository.owner, issue.openedUserName), Set(sender))
|
||||
callWebHookOf(repository.owner, repository.name, WebHook.Issues, settings) {
|
||||
val users =
|
||||
getAccountsByUserNames(Set(repository.owner, issue.openedUserName) ++ issue.assignedUserName, Set(sender))
|
||||
for {
|
||||
repoOwner <- users.get(repository.owner)
|
||||
issueUser <- users.get(issue.openedUserName)
|
||||
@@ -327,6 +346,7 @@ trait WebHookPullRequestService extends WebHookService {
|
||||
issue,
|
||||
RepositoryName(repository),
|
||||
ApiUser(issueUser),
|
||||
issue.assignedUserName.flatMap(users.get(_)).map(ApiUser(_)),
|
||||
getIssueLabels(repository.owner, repository.name, issue.issueId)
|
||||
.map(ApiLabel(_, RepositoryName(repository)))
|
||||
),
|
||||
@@ -340,10 +360,11 @@ trait WebHookPullRequestService extends WebHookService {
|
||||
action: String,
|
||||
repository: RepositoryService.RepositoryInfo,
|
||||
issueId: Int,
|
||||
sender: Account
|
||||
sender: Account,
|
||||
settings: SystemSettings
|
||||
)(implicit s: Session, c: JsonFormat.Context): Unit = {
|
||||
import WebHookService._
|
||||
callWebHookOf(repository.owner, repository.name, WebHook.PullRequest) {
|
||||
callWebHookOf(repository.owner, repository.name, WebHook.PullRequest, settings) {
|
||||
for {
|
||||
(issue, pullRequest) <- getPullRequest(repository.owner, repository.name, issueId)
|
||||
users = getAccountsByUserNames(
|
||||
@@ -396,13 +417,14 @@ trait WebHookPullRequestService extends WebHookService {
|
||||
if wht.event === WebHook.PullRequest.asInstanceOf[WebHook.Event].bind && wht.byRepositoryWebHook(wh)
|
||||
} yield {
|
||||
((is, iu, pr, bu, ru), wh)
|
||||
}).list.groupBy(_._1).mapValues(_.map(_._2))
|
||||
}).list.groupBy(_._1).map { case (k, v) => (k, v.map(_._2)) }
|
||||
|
||||
def callPullRequestWebHookByRequestBranch(
|
||||
action: String,
|
||||
requestRepository: RepositoryService.RepositoryInfo,
|
||||
requestBranch: String,
|
||||
sender: Account
|
||||
sender: Account,
|
||||
settings: SystemSettings
|
||||
)(implicit s: Session, c: JsonFormat.Context): Unit = {
|
||||
import WebHookService._
|
||||
for {
|
||||
@@ -433,7 +455,7 @@ trait WebHookPullRequestService extends WebHookService {
|
||||
mergedComment = getMergedComment(baseRepo.owner, baseRepo.name, issue.issueId)
|
||||
)
|
||||
|
||||
callWebHook(WebHook.PullRequest, webHooks, payload)
|
||||
callWebHook(WebHook.PullRequest, webHooks, payload, settings)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -447,10 +469,11 @@ trait WebHookPullRequestReviewCommentService extends WebHookService {
|
||||
repository: RepositoryService.RepositoryInfo,
|
||||
issue: Issue,
|
||||
pullRequest: PullRequest,
|
||||
sender: Account
|
||||
sender: Account,
|
||||
settings: SystemSettings
|
||||
)(implicit s: Session, c: JsonFormat.Context): Unit = {
|
||||
import WebHookService._
|
||||
callWebHookOf(repository.owner, repository.name, WebHook.PullRequestReviewComment) {
|
||||
callWebHookOf(repository.owner, repository.name, WebHook.PullRequestReviewComment, settings) {
|
||||
val users =
|
||||
getAccountsByUserNames(Set(repository.owner, pullRequest.requestUserName, issue.openedUserName), Set(sender))
|
||||
for {
|
||||
@@ -492,18 +515,20 @@ trait WebHookIssueCommentService extends WebHookPullRequestService {
|
||||
repository: RepositoryService.RepositoryInfo,
|
||||
issue: Issue,
|
||||
issueCommentId: Int,
|
||||
sender: Account
|
||||
sender: Account,
|
||||
settings: SystemSettings
|
||||
)(implicit s: Session, c: JsonFormat.Context): Unit = {
|
||||
callWebHookOf(repository.owner, repository.name, WebHook.IssueComment) {
|
||||
callWebHookOf(repository.owner, repository.name, WebHook.IssueComment, settings) {
|
||||
for {
|
||||
issueComment <- getComment(repository.owner, repository.name, issueCommentId.toString())
|
||||
users = getAccountsByUserNames(
|
||||
Set(issue.openedUserName, repository.owner, issueComment.commentedUserName),
|
||||
Set(issue.openedUserName, repository.owner, issueComment.commentedUserName) ++ issue.assignedUserName,
|
||||
Set(sender)
|
||||
)
|
||||
issueUser <- users.get(issue.openedUserName)
|
||||
repoOwner <- users.get(repository.owner)
|
||||
commenter <- users.get(issueComment.commentedUserName)
|
||||
assignedUser = issue.assignedUserName.flatMap(users.get(_))
|
||||
labels = getIssueLabels(repository.owner, repository.name, issue.issueId)
|
||||
} yield {
|
||||
WebHookIssueCommentPayload(
|
||||
@@ -513,6 +538,7 @@ trait WebHookIssueCommentService extends WebHookPullRequestService {
|
||||
commentUser = commenter,
|
||||
repository = repository,
|
||||
repositoryUser = repoOwner,
|
||||
assignedUser = assignedUser,
|
||||
sender = sender,
|
||||
labels = labels
|
||||
)
|
||||
@@ -686,6 +712,7 @@ object WebHookService {
|
||||
commentUser: Account,
|
||||
repository: RepositoryInfo,
|
||||
repositoryUser: Account,
|
||||
assignedUser: Option[Account],
|
||||
sender: Account,
|
||||
labels: List[Label]
|
||||
): WebHookIssueCommentPayload =
|
||||
@@ -696,6 +723,7 @@ object WebHookService {
|
||||
issue,
|
||||
RepositoryName(repository),
|
||||
ApiUser(issueUser),
|
||||
assignedUser.map(ApiUser(_)),
|
||||
labels.map(ApiLabel(_, RepositoryName(repository)))
|
||||
),
|
||||
comment =
|
||||
|
||||
@@ -14,7 +14,9 @@ import org.eclipse.jgit.diff.{DiffEntry, DiffFormatter}
|
||||
import java.io.ByteArrayInputStream
|
||||
import org.eclipse.jgit.patch._
|
||||
import org.eclipse.jgit.api.errors.PatchFormatException
|
||||
import scala.collection.JavaConverters._
|
||||
|
||||
import scala.jdk.CollectionConverters._
|
||||
import scala.util.Using
|
||||
|
||||
object WikiService {
|
||||
|
||||
@@ -73,7 +75,7 @@ trait WikiService {
|
||||
* Returns the wiki page.
|
||||
*/
|
||||
def getWikiPage(owner: String, repository: String, pageName: String): Option[WikiPageInfo] = {
|
||||
using(Git.open(Directory.getWikiRepositoryDir(owner, repository))) { git =>
|
||||
Using.resource(Git.open(Directory.getWikiRepositoryDir(owner, repository))) { git =>
|
||||
if (!JGitUtil.isEmpty(git)) {
|
||||
JGitUtil.getFileList(git, "master", ".").find(_.name == pageName + ".md").map { file =>
|
||||
WikiPageInfo(
|
||||
@@ -92,7 +94,7 @@ trait WikiService {
|
||||
* Returns the list of wiki page names.
|
||||
*/
|
||||
def getWikiPageList(owner: String, repository: String): List[String] = {
|
||||
using(Git.open(Directory.getWikiRepositoryDir(owner, repository))) { git =>
|
||||
Using.resource(Git.open(Directory.getWikiRepositoryDir(owner, repository))) { git =>
|
||||
JGitUtil
|
||||
.getFileList(git, "master", ".")
|
||||
.filter(_.name.endsWith(".md"))
|
||||
@@ -118,7 +120,7 @@ trait WikiService {
|
||||
|
||||
try {
|
||||
LockUtil.lock(s"${owner}/${repository}/wiki") {
|
||||
using(Git.open(Directory.getWikiRepositoryDir(owner, repository))) { git =>
|
||||
Using.resource(Git.open(Directory.getWikiRepositoryDir(owner, repository))) { git =>
|
||||
val reader = git.getRepository.newObjectReader
|
||||
val oldTreeIter = new CanonicalTreeParser
|
||||
oldTreeIter.reset(reader, git.getRepository.resolve(from + "^{tree}"))
|
||||
@@ -133,7 +135,7 @@ trait WikiService {
|
||||
}
|
||||
}
|
||||
|
||||
val patch = using(new java.io.ByteArrayOutputStream()) { out =>
|
||||
val patch = Using.resource(new java.io.ByteArrayOutputStream()) { out =>
|
||||
val formatter = new DiffFormatter(out)
|
||||
formatter.setRepository(git.getRepository)
|
||||
formatter.format(diffs.asJava)
|
||||
@@ -237,7 +239,7 @@ trait WikiService {
|
||||
currentId: Option[String]
|
||||
): Option[String] = {
|
||||
LockUtil.lock(s"${owner}/${repository}/wiki") {
|
||||
using(Git.open(Directory.getWikiRepositoryDir(owner, repository))) { git =>
|
||||
Using.resource(Git.open(Directory.getWikiRepositoryDir(owner, repository))) { git =>
|
||||
val builder = DirCache.newInCore.builder()
|
||||
val inserter = git.getRepository.newObjectInserter()
|
||||
val headId = git.getRepository.resolve(Constants.HEAD + "^{commit}")
|
||||
@@ -309,7 +311,7 @@ trait WikiService {
|
||||
message: String
|
||||
): Unit = {
|
||||
LockUtil.lock(s"${owner}/${repository}/wiki") {
|
||||
using(Git.open(Directory.getWikiRepositoryDir(owner, repository))) { git =>
|
||||
Using.resource(Git.open(Directory.getWikiRepositoryDir(owner, repository))) { git =>
|
||||
val builder = DirCache.newInCore.builder()
|
||||
val inserter = git.getRepository.newObjectInserter()
|
||||
val headId = git.getRepository.resolve(Constants.HEAD + "^{commit}")
|
||||
|
||||
@@ -7,6 +7,10 @@ import gitbucket.core.model.Account
|
||||
import gitbucket.core.service.SystemSettingsService.SystemSettings
|
||||
import gitbucket.core.service.{AccessTokenService, AccountService, SystemSettingsService}
|
||||
import gitbucket.core.util.{AuthUtil, Keys}
|
||||
import gitbucket.core.model.Profile.profile.blockingApi._
|
||||
// Imported names have higher precedence than names, defined in other files.
|
||||
// If Database is not bound by explicit import, then "Database" refers to the Database introduced by the wildcard import above.
|
||||
import gitbucket.core.servlet.Database
|
||||
|
||||
class ApiAuthenticationFilter extends Filter with AccessTokenService with AccountService with SystemSettingsService {
|
||||
|
||||
@@ -33,7 +37,9 @@ class ApiAuthenticationFilter extends Filter with AccessTokenService with Accoun
|
||||
} match {
|
||||
case Some(Right(account)) =>
|
||||
request.setAttribute(Keys.Session.LoginAccount, account)
|
||||
updateLastLoginDate(account.userName)
|
||||
Database() withTransaction { implicit session =>
|
||||
updateLastLoginDate(account.userName)
|
||||
}
|
||||
chain.doFilter(req, res)
|
||||
case None => chain.doFilter(req, res)
|
||||
case Some(Left(_)) => {
|
||||
@@ -47,8 +53,9 @@ class ApiAuthenticationFilter extends Filter with AccessTokenService with Accoun
|
||||
}
|
||||
|
||||
def doBasicAuth(auth: String, settings: SystemSettings, request: HttpServletRequest): Option[Account] = {
|
||||
implicit val session = request.getAttribute(Keys.Request.DBSession).asInstanceOf[slick.jdbc.JdbcBackend#Session]
|
||||
val Array(username, password) = AuthUtil.decodeAuthHeader(auth).split(":", 2)
|
||||
authenticate(settings, username, password)
|
||||
Database() withTransaction { implicit session =>
|
||||
authenticate(settings, username, password)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,12 +4,16 @@ import javax.servlet._
|
||||
import javax.servlet.http._
|
||||
|
||||
import gitbucket.core.model.Account
|
||||
import gitbucket.core.model.Profile.profile.blockingApi._
|
||||
import gitbucket.core.plugin.{GitRepositoryFilter, GitRepositoryRouting, PluginRegistry}
|
||||
import gitbucket.core.service.SystemSettingsService.SystemSettings
|
||||
import gitbucket.core.service.{AccessTokenService, AccountService, RepositoryService, SystemSettingsService}
|
||||
import gitbucket.core.util.Implicits._
|
||||
import gitbucket.core.util.{AuthUtil, Implicits, Keys}
|
||||
import gitbucket.core.util.{AuthUtil, Keys}
|
||||
import gitbucket.core.model.Profile.profile.blockingApi._
|
||||
// Imported names have higher precedence than names, defined in other files.
|
||||
// If Database is not bound by explicit import, then "Database" refers to the Database introduced by the wildcard import above.
|
||||
import gitbucket.core.servlet.Database
|
||||
|
||||
import org.slf4j.LoggerFactory
|
||||
|
||||
/**
|
||||
|
||||
@@ -8,7 +8,8 @@ import gitbucket.core.util.{FileUtil, StringUtil}
|
||||
import org.apache.commons.io.{FileUtils, IOUtils}
|
||||
import org.json4s.jackson.Serialization._
|
||||
import org.apache.http.HttpStatus
|
||||
import gitbucket.core.util.SyntaxSugars._
|
||||
|
||||
import scala.util.Using
|
||||
|
||||
/**
|
||||
* Provides GitLFS Transfer API
|
||||
@@ -28,8 +29,8 @@ class GitLfsTransferServlet extends HttpServlet {
|
||||
if (file.exists()) {
|
||||
res.setStatus(HttpStatus.SC_OK)
|
||||
res.setContentType("application/octet-stream")
|
||||
res.setContentLength(file.length.toInt)
|
||||
using(new FileInputStream(file), res.getOutputStream) { (in, out) =>
|
||||
res.setHeader("Content-Length", file.length.toString)
|
||||
Using.resources(new FileInputStream(file), res.getOutputStream) { (in, out) =>
|
||||
IOUtils.copy(in, out)
|
||||
out.flush()
|
||||
}
|
||||
@@ -45,7 +46,7 @@ class GitLfsTransferServlet extends HttpServlet {
|
||||
} yield {
|
||||
val file = new File(FileUtil.getLfsFilePath(owner, repository, oid))
|
||||
FileUtils.forceMkdir(file.getParentFile)
|
||||
using(req.getInputStream, new FileOutputStream(file)) { (in, out) =>
|
||||
Using.resources(req.getInputStream, new FileOutputStream(file)) { (in, out) =>
|
||||
IOUtils.copy(in, out)
|
||||
}
|
||||
res.setStatus(HttpStatus.SC_OK)
|
||||
@@ -71,7 +72,7 @@ class GitLfsTransferServlet extends HttpServlet {
|
||||
|
||||
private def sendError(res: HttpServletResponse, status: Int, message: String): Unit = {
|
||||
res.setStatus(status)
|
||||
using(res.getWriter()) { out =>
|
||||
Using.resource(res.getWriter()) { out =>
|
||||
out.write(write(GitLfs.Error(message)))
|
||||
out.flush()
|
||||
}
|
||||
|
||||
@@ -4,9 +4,10 @@ import java.io.File
|
||||
import java.util
|
||||
import java.util.Date
|
||||
|
||||
import scala.util.Using
|
||||
|
||||
import gitbucket.core.api
|
||||
import gitbucket.core.model.WebHook
|
||||
import gitbucket.core.model.Profile.profile.blockingApi._
|
||||
import gitbucket.core.plugin.{GitRepositoryRouting, PluginRegistry}
|
||||
import gitbucket.core.service.IssuesService.IssueSearchCondition
|
||||
import gitbucket.core.service.WebHookService._
|
||||
@@ -14,6 +15,11 @@ import gitbucket.core.service._
|
||||
import gitbucket.core.util.SyntaxSugars._
|
||||
import gitbucket.core.util.Implicits._
|
||||
import gitbucket.core.util._
|
||||
import gitbucket.core.model.Profile.profile.blockingApi._
|
||||
// Imported names have higher precedence than names, defined in other files.
|
||||
// If Database is not bound by explicit import, then "Database" refers to the Database introduced by the wildcard import above.
|
||||
import gitbucket.core.servlet.Database
|
||||
|
||||
import org.eclipse.jgit.api.Git
|
||||
import org.eclipse.jgit.http.server.GitServlet
|
||||
import org.eclipse.jgit.lib._
|
||||
@@ -108,7 +114,7 @@ class GitRepositoryServlet extends GitServlet with SystemSettingsService {
|
||||
GitLfs.Action(
|
||||
href = baseUrl + "/git-lfs/" + owner + "/" + repository + "/" + requestObject.oid,
|
||||
header =
|
||||
Map("Authorization" -> StringUtil.encodeBlowfish(timeout + " " + requestObject.oid)),
|
||||
Map("Authorization" -> StringUtil.encodeBlowfish(s"$timeout ${requestObject.oid}")),
|
||||
expires_at = new Date(timeout)
|
||||
)
|
||||
)
|
||||
@@ -129,7 +135,7 @@ class GitRepositoryServlet extends GitServlet with SystemSettingsService {
|
||||
GitLfs.Action(
|
||||
href = baseUrl + "/git-lfs/" + owner + "/" + repository + "/" + requestObject.oid,
|
||||
header =
|
||||
Map("Authorization" -> StringUtil.encodeBlowfish(timeout + " " + requestObject.oid)),
|
||||
Map("Authorization" -> StringUtil.encodeBlowfish(s"$timeout ${requestObject.oid}")),
|
||||
expires_at = new Date(timeout)
|
||||
)
|
||||
)
|
||||
@@ -140,7 +146,7 @@ class GitRepositoryServlet extends GitServlet with SystemSettingsService {
|
||||
}
|
||||
|
||||
res.setContentType("application/vnd.git-lfs+json")
|
||||
using(res.getWriter) { out =>
|
||||
Using.resource(res.getWriter) { out =>
|
||||
out.print(write(batchResponse))
|
||||
out.flush()
|
||||
}
|
||||
@@ -216,7 +222,7 @@ class GitBucketReceivePackFactory extends ReceivePackFactory[HttpServletRequest]
|
||||
}
|
||||
}
|
||||
|
||||
import scala.collection.JavaConverters._
|
||||
import scala.jdk.CollectionConverters._
|
||||
|
||||
class CommitLogHook(owner: String, repository: String, pusher: String, baseUrl: String, sshUrl: Option[String])
|
||||
extends PostReceiveHook
|
||||
@@ -233,7 +239,8 @@ class CommitLogHook(owner: String, repository: String, pusher: String, baseUrl:
|
||||
with MilestonesService
|
||||
with WebHookPullRequestService
|
||||
with WebHookPullRequestReviewCommentService
|
||||
with CommitsService {
|
||||
with CommitsService
|
||||
with SystemSettingsService {
|
||||
|
||||
private val logger = LoggerFactory.getLogger(classOf[CommitLogHook])
|
||||
private var existIds: Seq[String] = Nil
|
||||
@@ -250,7 +257,7 @@ class CommitLogHook(owner: String, repository: String, pusher: String, baseUrl:
|
||||
command.setResult(ReceiveCommand.Result.REJECTED_OTHER_REASON, error)
|
||||
}
|
||||
}
|
||||
using(Git.open(Directory.getRepositoryDir(owner, repository))) { git =>
|
||||
Using.resource(Git.open(Directory.getRepositoryDir(owner, repository))) { git =>
|
||||
existIds = JGitUtil.getAllCommitIds(git)
|
||||
}
|
||||
} catch {
|
||||
@@ -263,9 +270,11 @@ class CommitLogHook(owner: String, repository: String, pusher: String, baseUrl:
|
||||
}
|
||||
|
||||
def onPostReceive(receivePack: ReceivePack, commands: java.util.Collection[ReceiveCommand]): Unit = {
|
||||
val settings = loadSystemSettings()
|
||||
|
||||
Database() withTransaction { implicit session =>
|
||||
try {
|
||||
using(Git.open(Directory.getRepositoryDir(owner, repository))) { git =>
|
||||
Using.resource(Git.open(Directory.getRepositoryDir(owner, repository))) { git =>
|
||||
JGitUtil.removeCache(git)
|
||||
|
||||
val pushedIds = scala.collection.mutable.Set[String]()
|
||||
@@ -289,7 +298,7 @@ class CommitLogHook(owner: String, repository: String, pusher: String, baseUrl:
|
||||
if (JGitUtil.isEmpty(git) && commits.nonEmpty && branchName != repositoryInfo.repository.defaultBranch) {
|
||||
saveRepositoryDefaultBranch(owner, repository, branchName)
|
||||
// Change repository HEAD
|
||||
using(Git.open(Directory.getRepositoryDir(owner, repository))) { git =>
|
||||
Using.resource(Git.open(Directory.getRepositoryDir(owner, repository))) { git =>
|
||||
git.getRepository.updateRef(Constants.HEAD, true).link(Constants.R_HEADS + branchName)
|
||||
}
|
||||
}
|
||||
@@ -311,7 +320,7 @@ class CommitLogHook(owner: String, repository: String, pusher: String, baseUrl:
|
||||
getAccountByUserName(pusher).foreach { pusherAccount =>
|
||||
closeIssuesFromMessage(commit.fullMessage, pusher, owner, repository).foreach { issueId =>
|
||||
getIssue(owner, repository, issueId.toString).foreach { issue =>
|
||||
callIssuesWebHook("closed", repositoryInfo, issue, pusherAccount)
|
||||
callIssuesWebHook("closed", repositoryInfo, issue, pusherAccount, settings)
|
||||
PluginRegistry().getIssueHooks
|
||||
.foreach(_.closedByCommitComment(issue, repositoryInfo, commit.fullMessage, pusherAccount))
|
||||
}
|
||||
@@ -331,7 +340,7 @@ class CommitLogHook(owner: String, repository: String, pusher: String, baseUrl:
|
||||
}.isDefined) {
|
||||
markMergeAndClosePullRequest(pusher, owner, repository, pull)
|
||||
getAccountByUserName(pusher).foreach { pusherAccount =>
|
||||
callPullRequestWebHook("closed", repositoryInfo, pull.issueId, pusherAccount)
|
||||
callPullRequestWebHook("closed", repositoryInfo, pull.issueId, pusherAccount, settings)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -359,14 +368,14 @@ class CommitLogHook(owner: String, repository: String, pusher: String, baseUrl:
|
||||
case ReceiveCommand.Type.CREATE | ReceiveCommand.Type.UPDATE |
|
||||
ReceiveCommand.Type.UPDATE_NONFASTFORWARD =>
|
||||
getAccountByUserName(pusher).foreach { pusherAccount =>
|
||||
updatePullRequests(owner, repository, branchName, pusherAccount, "synchronize")
|
||||
updatePullRequests(owner, repository, branchName, pusherAccount, "synchronize", settings)
|
||||
}
|
||||
case _ =>
|
||||
}
|
||||
}
|
||||
|
||||
// call web hook
|
||||
callWebHookOf(owner, repository, WebHook.Push) {
|
||||
callWebHookOf(owner, repository, WebHook.Push, settings) {
|
||||
for {
|
||||
pusherAccount <- getAccountByUserName(pusher)
|
||||
ownerAccount <- getAccountByUserName(owner)
|
||||
@@ -384,7 +393,7 @@ class CommitLogHook(owner: String, repository: String, pusher: String, baseUrl:
|
||||
}
|
||||
}
|
||||
if (command.getType == ReceiveCommand.Type.CREATE) {
|
||||
callWebHookOf(owner, repository, WebHook.Create) {
|
||||
callWebHookOf(owner, repository, WebHook.Create, settings) {
|
||||
for {
|
||||
pusherAccount <- getAccountByUserName(pusher)
|
||||
ownerAccount <- getAccountByUserName(owner)
|
||||
@@ -422,11 +431,14 @@ class WikiCommitHook(owner: String, repository: String, pusher: String, baseUrl:
|
||||
extends PostReceiveHook
|
||||
with WebHookService
|
||||
with AccountService
|
||||
with RepositoryService {
|
||||
with RepositoryService
|
||||
with SystemSettingsService {
|
||||
|
||||
private val logger = LoggerFactory.getLogger(classOf[WikiCommitHook])
|
||||
|
||||
override def onPostReceive(receivePack: ReceivePack, commands: util.Collection[ReceiveCommand]): Unit = {
|
||||
val settings = loadSystemSettings()
|
||||
|
||||
Database() withTransaction { implicit session =>
|
||||
try {
|
||||
commands.asScala.headOption.foreach { command =>
|
||||
@@ -443,7 +455,7 @@ class WikiCommitHook(owner: String, repository: String, pusher: String, baseUrl:
|
||||
|
||||
commitIds.foreach {
|
||||
case (oldCommitId, newCommitId) =>
|
||||
val commits = using(Git.open(Directory.getWikiRepositoryDir(owner, repository))) { git =>
|
||||
val commits = Using.resource(Git.open(Directory.getWikiRepositoryDir(owner, repository))) { git =>
|
||||
JGitUtil.getCommitLog(git, oldCommitId, newCommitId).flatMap { commit =>
|
||||
val diffs = JGitUtil.getDiffs(git, None, commit.id, false, false)
|
||||
diffs.collect {
|
||||
@@ -462,7 +474,7 @@ class WikiCommitHook(owner: String, repository: String, pusher: String, baseUrl:
|
||||
(commits.head._1, fileName, commits.last._3)
|
||||
}
|
||||
|
||||
callWebHookOf(owner, repository, WebHook.Gollum) {
|
||||
callWebHookOf(owner, repository, WebHook.Gollum, settings) {
|
||||
for {
|
||||
pusherAccount <- getAccountByUserName(pusher)
|
||||
repositoryUser <- getAccountByUserName(owner)
|
||||
|
||||
@@ -9,9 +9,12 @@ import gitbucket.core.plugin.PluginRegistry
|
||||
import gitbucket.core.service.{ActivityService, SystemSettingsService}
|
||||
import gitbucket.core.util.DatabaseConfig
|
||||
import gitbucket.core.util.Directory._
|
||||
import gitbucket.core.util.SyntaxSugars._
|
||||
import gitbucket.core.util.JDBCUtil._
|
||||
import gitbucket.core.model.Profile.profile.blockingApi._
|
||||
// Imported names have higher precedence than names, defined in other files.
|
||||
// If Database is not bound by explicit import, then "Database" refers to the Database introduced by the wildcard import above.
|
||||
import gitbucket.core.servlet.Database
|
||||
|
||||
import io.github.gitbucket.solidbase.Solidbase
|
||||
import io.github.gitbucket.solidbase.manager.JDBCVersionManager
|
||||
import javax.servlet.{ServletContextEvent, ServletContextListener}
|
||||
@@ -21,7 +24,8 @@ import org.slf4j.LoggerFactory
|
||||
import akka.actor.{Actor, ActorSystem, Props}
|
||||
import com.typesafe.akka.extension.quartz.QuartzSchedulerExtension
|
||||
|
||||
import scala.collection.JavaConverters._
|
||||
import scala.jdk.CollectionConverters._
|
||||
import scala.util.Using
|
||||
|
||||
/**
|
||||
* Initialize GitBucket system.
|
||||
@@ -84,7 +88,7 @@ class InitializeListener extends ServletContextListener with SystemSettingsServi
|
||||
}
|
||||
|
||||
// Install bundled plugins
|
||||
extractBundledPlugins(gitbucketVersion)
|
||||
extractBundledPlugins()
|
||||
|
||||
// Load plugins
|
||||
logger.info("Initialize plugins")
|
||||
@@ -134,11 +138,11 @@ class InitializeListener extends ServletContextListener with SystemSettingsServi
|
||||
}
|
||||
}
|
||||
|
||||
private def extractBundledPlugins(gitbucketVersion: String): Unit = {
|
||||
private def extractBundledPlugins(): Unit = {
|
||||
logger.info("Extract bundled plugins...")
|
||||
val cl = Thread.currentThread.getContextClassLoader
|
||||
try {
|
||||
using(cl.getResourceAsStream("bundle-plugins.txt")) { pluginsFile =>
|
||||
Using.resource(cl.getResourceAsStream("bundle-plugins.txt")) { pluginsFile =>
|
||||
if (pluginsFile != null) {
|
||||
val plugins = IOUtils.readLines(pluginsFile, "UTF-8")
|
||||
val gitbucketVersion = GitBucketCoreModule.getVersions.asScala.last.getVersion
|
||||
@@ -146,14 +150,14 @@ class InitializeListener extends ServletContextListener with SystemSettingsServi
|
||||
plugins.asScala.foreach { plugin =>
|
||||
plugin.trim.split(":") match {
|
||||
case Array(pluginId, pluginVersion) =>
|
||||
val fileName = s"gitbucket-${pluginId}-plugin-gitbucket_${gitbucketVersion}-${pluginVersion}.jar"
|
||||
val fileName = s"gitbucket-${pluginId}-plugin-${pluginVersion}.jar"
|
||||
val in = cl.getResourceAsStream("plugins/" + fileName)
|
||||
if (in != null) {
|
||||
val file = new File(PluginHome, fileName)
|
||||
logger.info(s"Extract to ${file.getAbsolutePath}")
|
||||
|
||||
FileUtils.forceMkdirParent(file)
|
||||
using(in, new FileOutputStream(file)) {
|
||||
Using.resources(in, new FileOutputStream(file)) {
|
||||
case (in, out) => IOUtils.copy(in, out)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,13 +11,13 @@ import org.apache.sshd.server.session.ServerSession
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.io.{File, InputStream, OutputStream}
|
||||
|
||||
import SyntaxSugars._
|
||||
import org.eclipse.jgit.api.Git
|
||||
import Directory._
|
||||
import gitbucket.core.ssh.PublicKeyAuthenticator.AuthType
|
||||
import org.eclipse.jgit.transport.{ReceivePack, UploadPack}
|
||||
import org.apache.sshd.server.shell.UnknownCommand
|
||||
import org.eclipse.jgit.errors.RepositoryNotFoundException
|
||||
import scala.util.Using
|
||||
|
||||
object GitCommand {
|
||||
val DefaultCommandRegex = """\Agit-(upload|receive)-pack '/([a-zA-Z0-9\-_.]+)/([a-zA-Z0-9\-\+_.]+).git'\Z""".r
|
||||
@@ -152,7 +152,7 @@ class DefaultGitUploadPack(owner: String, repoName: String)
|
||||
}
|
||||
|
||||
if (execute) {
|
||||
using(Git.open(getRepositoryDir(owner, repoName))) { git =>
|
||||
Using.resource(Git.open(getRepositoryDir(owner, repoName))) { git =>
|
||||
val repository = git.getRepository
|
||||
val upload = new UploadPack(repository)
|
||||
upload.upload(in, out, err)
|
||||
@@ -177,7 +177,7 @@ class DefaultGitReceivePack(owner: String, repoName: String, baseUrl: String, ss
|
||||
}
|
||||
|
||||
if (execute) {
|
||||
using(Git.open(getRepositoryDir(owner, repoName))) { git =>
|
||||
Using.resource(Git.open(getRepositoryDir(owner, repoName))) { git =>
|
||||
val repository = git.getRepository
|
||||
val receive = new ReceivePack(repository)
|
||||
if (!repoName.endsWith(".wiki")) {
|
||||
@@ -202,7 +202,7 @@ class PluginGitUploadPack(repoName: String, routing: GitRepositoryRouting)
|
||||
|
||||
if (execute) {
|
||||
val path = routing.urlPattern.r.replaceFirstIn(repoName, routing.localPath)
|
||||
using(Git.open(new File(Directory.GitBucketHome, path))) { git =>
|
||||
Using.resource(Git.open(new File(Directory.GitBucketHome, path))) { git =>
|
||||
val repository = git.getRepository
|
||||
val upload = new UploadPack(repository)
|
||||
upload.upload(in, out, err)
|
||||
@@ -222,7 +222,7 @@ class PluginGitReceivePack(repoName: String, routing: GitRepositoryRouting)
|
||||
|
||||
if (execute) {
|
||||
val path = routing.urlPattern.r.replaceFirstIn(repoName, routing.localPath)
|
||||
using(Git.open(new File(Directory.GitBucketHome, path))) { git =>
|
||||
Using.resource(Git.open(new File(Directory.GitBucketHome, path))) { git =>
|
||||
val repository = git.getRepository
|
||||
val receive = new ReceivePack(repository)
|
||||
receive.receive(in, out, err)
|
||||
|
||||
41
src/main/scala/gitbucket/core/util/ConfigUtil.scala
Normal file
41
src/main/scala/gitbucket/core/util/ConfigUtil.scala
Normal file
@@ -0,0 +1,41 @@
|
||||
package gitbucket.core.util
|
||||
|
||||
import gitbucket.core.util.SyntaxSugars.defining
|
||||
|
||||
import scala.reflect.ClassTag
|
||||
|
||||
object ConfigUtil {
|
||||
|
||||
def getConfigValue[A: ClassTag](key: String): Option[A] = {
|
||||
getSystemProperty(key).orElse(getEnvironmentVariable(key))
|
||||
}
|
||||
|
||||
def getEnvironmentVariable[A: ClassTag](key: String): Option[A] = {
|
||||
val name = (if (key.startsWith("gitbucket.")) "" else "GITBUCKET_") + key.toUpperCase.replace('.', '_')
|
||||
val value = System.getenv(name)
|
||||
if (value != null && value.nonEmpty) {
|
||||
Some(convertType(value))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
def getSystemProperty[A: ClassTag](key: String): Option[A] = {
|
||||
val name = if (key.startsWith("gitbucket.")) key else "gitbucket." + key
|
||||
val value = System.getProperty(name)
|
||||
if (value != null && value.nonEmpty) {
|
||||
Some(convertType(value))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
def convertType[A: ClassTag](value: String): A =
|
||||
defining(implicitly[ClassTag[A]].runtimeClass) { c =>
|
||||
if (c == classOf[Boolean]) value.toBoolean
|
||||
else if (c == classOf[Long]) value.toLong
|
||||
else if (c == classOf[Int]) value.toInt
|
||||
else value
|
||||
}.asInstanceOf[A]
|
||||
|
||||
}
|
||||
@@ -6,7 +6,6 @@ import java.io.File
|
||||
import Directory._
|
||||
import ConfigUtil._
|
||||
import com.github.takezoe.slick.blocking.{BlockingH2Driver, BlockingJdbcProfile, BlockingMySQLDriver}
|
||||
import gitbucket.core.util.SyntaxSugars.defining
|
||||
import liquibase.database.AbstractJdbcDatabase
|
||||
import liquibase.database.core.{H2Database, MySQLDatabase, PostgresDatabase}
|
||||
import org.apache.commons.io.FileUtils
|
||||
@@ -54,16 +53,14 @@ object DatabaseConfig {
|
||||
lazy val minimumIdle: Option[Int] = getOptionValue("db.minimumIdle", config.getInt)
|
||||
lazy val maximumPoolSize: Option[Int] = getOptionValue("db.maximumPoolSize", config.getInt)
|
||||
|
||||
private def getValue[T](path: String, f: String => T): T = {
|
||||
getSystemProperty(path).getOrElse(getEnvironmentVariable(path).getOrElse {
|
||||
f(path)
|
||||
})
|
||||
private def getValue[T: ClassTag](path: String, f: String => T): T = {
|
||||
getConfigValue(path).getOrElse(f(path))
|
||||
}
|
||||
|
||||
private def getOptionValue[T](path: String, f: String => T): Option[T] = {
|
||||
getSystemProperty(path).orElse(getEnvironmentVariable(path).orElse {
|
||||
private def getOptionValue[T: ClassTag](path: String, f: String => T): Option[T] = {
|
||||
getConfigValue(path).orElse {
|
||||
if (config.hasPath(path)) Some(f(path)) else None
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -114,33 +111,3 @@ object DatabaseType {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object ConfigUtil {
|
||||
|
||||
def getEnvironmentVariable[A](key: String): Option[A] = {
|
||||
val value = System.getenv("GITBUCKET_" + key.toUpperCase.replace('.', '_'))
|
||||
if (value != null && value.nonEmpty) {
|
||||
Some(convertType(value)).asInstanceOf[Option[A]]
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
def getSystemProperty[A](key: String): Option[A] = {
|
||||
val value = System.getProperty("gitbucket." + key)
|
||||
if (value != null && value.nonEmpty) {
|
||||
Some(convertType(value)).asInstanceOf[Option[A]]
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
def convertType[A: ClassTag](value: String) =
|
||||
defining(implicitly[ClassTag[A]].runtimeClass) { c =>
|
||||
if (c == classOf[Boolean]) value.toBoolean
|
||||
else if (c == classOf[Long]) value.toLong
|
||||
else if (c == classOf[Int]) value.toInt
|
||||
else value
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -12,7 +12,8 @@ import org.eclipse.jgit.api.Git
|
||||
import org.eclipse.jgit.lib.{ObjectReader, Repository}
|
||||
import org.eclipse.jgit.revwalk.{RevTree, RevWalk}
|
||||
import org.eclipse.jgit.treewalk.TreeWalk
|
||||
import gitbucket.core.util.SyntaxSugars._
|
||||
|
||||
import scala.util.Using
|
||||
|
||||
object EditorConfigUtil {
|
||||
private class JGitResource(repo: Repository, revStr: String, path: Ec4jPath) extends Resource {
|
||||
@@ -27,7 +28,7 @@ object EditorConfigUtil {
|
||||
}
|
||||
|
||||
private def getRevTree: RevTree = {
|
||||
using(repo.newObjectReader()) { reader: ObjectReader =>
|
||||
Using.resource(repo.newObjectReader()) { reader: ObjectReader =>
|
||||
val revWalk = new RevWalk(reader)
|
||||
val id = repo.resolve(revStr)
|
||||
val commit = revWalk.parseCommit(id)
|
||||
@@ -36,7 +37,7 @@ object EditorConfigUtil {
|
||||
}
|
||||
|
||||
override def exists(): Boolean = {
|
||||
using(repo.newObjectReader()) { reader: ObjectReader =>
|
||||
Using.resource(repo.newObjectReader()) { reader: ObjectReader =>
|
||||
try {
|
||||
val treeWalk = Option(TreeWalk.forPath(reader, removeInitialSlash(path), getRevTree))
|
||||
treeWalk.isDefined
|
||||
@@ -59,7 +60,7 @@ object EditorConfigUtil {
|
||||
}
|
||||
|
||||
override def openReader(): Reader = {
|
||||
using(repo.newObjectReader) { reader: ObjectReader =>
|
||||
Using.resource(repo.newObjectReader) { reader: ObjectReader =>
|
||||
val treeWalk = TreeWalk.forPath(reader, removeInitialSlash(path), getRevTree)
|
||||
new InputStreamReader(reader.open(treeWalk.getObjectId(0)).openStream, StandardCharsets.UTF_8)
|
||||
}
|
||||
|
||||
@@ -27,16 +27,18 @@ object FileUtil {
|
||||
}
|
||||
|
||||
def getSafeMimeType(name: String): String = {
|
||||
getMimeType(name).replace("text/html", "text/plain")
|
||||
getMimeType(name)
|
||||
.replace("text/html", "text/plain")
|
||||
.replace("image/svg+xml", "text/plain; charset=UTF-8")
|
||||
}
|
||||
|
||||
def isImage(name: String): Boolean = getMimeType(name).startsWith("image/")
|
||||
def isImage(name: String): Boolean = getSafeMimeType(name).startsWith("image/")
|
||||
|
||||
def isLarge(size: Long): Boolean = (size > 1024 * 1000)
|
||||
|
||||
def isText(content: Array[Byte]): Boolean = !content.contains(0)
|
||||
|
||||
def generateFileId: String = System.currentTimeMillis + Random.alphanumeric.take(10).mkString
|
||||
def generateFileId: String = s"${System.currentTimeMillis}${Random.alphanumeric.take(10).mkString}"
|
||||
|
||||
def getExtension(name: String): String =
|
||||
name.lastIndexOf('.') match {
|
||||
@@ -56,7 +58,7 @@ object FileUtil {
|
||||
}
|
||||
|
||||
def getLfsFilePath(owner: String, repository: String, oid: String): String =
|
||||
Directory.getLfsDir(owner, repository) + "/" + checkFilename(oid)
|
||||
s"${Directory.getLfsDir(owner, repository)}/${checkFilename(oid)}"
|
||||
|
||||
def readableSize(size: Long): String = FileUtils.byteCountToDisplaySize(size)
|
||||
|
||||
@@ -90,10 +92,12 @@ object FileUtil {
|
||||
name
|
||||
}
|
||||
|
||||
lazy val MaxFileSize =
|
||||
if (System.getProperty("gitbucket.maxFileSize") != null)
|
||||
System.getProperty("gitbucket.maxFileSize").toLong
|
||||
else
|
||||
3 * 1024 * 1024
|
||||
lazy val MaxFileSize: Long = {
|
||||
ConfigUtil.getConfigValue[Long]("gitbucket.maxFileSize").getOrElse(3 * 1024 * 1024)
|
||||
}
|
||||
|
||||
lazy val UploadTimeout: Long = {
|
||||
ConfigUtil.getConfigValue[Long]("gitbucket.UploadTimeout").getOrElse(3 * 10000)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
package gitbucket.core.util
|
||||
import java.io.ByteArrayInputStream
|
||||
|
||||
import collection.JavaConverters._
|
||||
import scala.jdk.CollectionConverters._
|
||||
|
||||
import gitbucket.core.model.Profile._
|
||||
import gitbucket.core.model.Profile.profile.blockingApi._
|
||||
import org.bouncycastle.bcpg.ArmoredInputStream
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package gitbucket.core.util
|
||||
|
||||
import java.net.{InetAddress, URL}
|
||||
|
||||
import gitbucket.core.service.SystemSettingsService
|
||||
import org.apache.commons.net.util.SubnetUtils
|
||||
import org.apache.http.HttpHost
|
||||
import org.apache.http.auth.{AuthScope, UsernamePasswordCredentials}
|
||||
import org.apache.http.impl.client.{BasicCredentialsProvider, CloseableHttpClient, HttpClientBuilder}
|
||||
@@ -32,4 +35,19 @@ object HttpClientUtil {
|
||||
}
|
||||
}
|
||||
|
||||
def isPrivateAddress(address: String): Boolean = {
|
||||
val ipAddress = InetAddress.getByName(address)
|
||||
ipAddress.isSiteLocalAddress || ipAddress.isLinkLocalAddress || ipAddress.isLoopbackAddress
|
||||
}
|
||||
|
||||
def inIpRange(ipRange: String, ipAddress: String): Boolean = {
|
||||
if (ipRange.contains('/')) {
|
||||
val utils = new SubnetUtils(ipRange)
|
||||
utils.setInclusiveHostCount(true)
|
||||
utils.getInfo.isInRange(ipAddress)
|
||||
} else {
|
||||
ipRange == ipAddress
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,9 +3,9 @@ package gitbucket.core.util
|
||||
import java.io._
|
||||
import java.sql._
|
||||
import java.text.SimpleDateFormat
|
||||
import SyntaxSugars._
|
||||
import scala.annotation.tailrec
|
||||
import scala.collection.mutable.ListBuffer
|
||||
import scala.util.Using
|
||||
|
||||
/**
|
||||
* Provides implicit class which extends java.sql.Connection.
|
||||
@@ -26,7 +26,7 @@ object JDBCUtil {
|
||||
|
||||
def find[T](sql: String, params: Any*)(f: ResultSet => T): Option[T] = {
|
||||
execute(sql, params: _*) { stmt =>
|
||||
using(stmt.executeQuery()) { rs =>
|
||||
Using.resource(stmt.executeQuery()) { rs =>
|
||||
if (rs.next) Some(f(rs)) else None
|
||||
}
|
||||
}
|
||||
@@ -34,7 +34,7 @@ object JDBCUtil {
|
||||
|
||||
def select[T](sql: String, params: Any*)(f: ResultSet => T): Seq[T] = {
|
||||
execute(sql, params: _*) { stmt =>
|
||||
using(stmt.executeQuery()) { rs =>
|
||||
Using.resource(stmt.executeQuery()) { rs =>
|
||||
val list = new ListBuffer[T]
|
||||
while (rs.next) {
|
||||
list += f(rs)
|
||||
@@ -46,14 +46,14 @@ object JDBCUtil {
|
||||
|
||||
def selectInt(sql: String, params: Any*): Int = {
|
||||
execute(sql, params: _*) { stmt =>
|
||||
using(stmt.executeQuery()) { rs =>
|
||||
Using.resource(stmt.executeQuery()) { rs =>
|
||||
if (rs.next) rs.getInt(1) else 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def execute[T](sql: String, params: Any*)(f: (PreparedStatement) => T): T = {
|
||||
using(conn.prepareStatement(sql)) { stmt =>
|
||||
Using.resource(conn.prepareStatement(sql)) { stmt =>
|
||||
params.zipWithIndex.foreach {
|
||||
case (p, i) =>
|
||||
p match {
|
||||
@@ -68,7 +68,7 @@ object JDBCUtil {
|
||||
def importAsSQL(in: InputStream): Unit = {
|
||||
conn.setAutoCommit(false)
|
||||
try {
|
||||
using(in) { in =>
|
||||
Using.resource(in) { in =>
|
||||
var out = new ByteArrayOutputStream()
|
||||
|
||||
var length = 0
|
||||
@@ -111,7 +111,7 @@ object JDBCUtil {
|
||||
val dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss")
|
||||
val file = File.createTempFile("gitbucket-export-", ".sql")
|
||||
|
||||
using(new FileOutputStream(file)) { out =>
|
||||
Using.resource(new FileOutputStream(file)) { out =>
|
||||
val dbMeta = conn.getMetaData
|
||||
val allTablesInDatabase = allTablesOrderByDependencies(dbMeta)
|
||||
|
||||
@@ -168,7 +168,7 @@ object JDBCUtil {
|
||||
}
|
||||
|
||||
def allTableNames(): Seq[String] = {
|
||||
using(conn.getMetaData.getTables(null, null, "%", Seq("TABLE").toArray)) { rs =>
|
||||
Using.resource(conn.getMetaData.getTables(null, null, "%", Seq("TABLE").toArray)) { rs =>
|
||||
val tableNames = new ListBuffer[String]
|
||||
while (rs.next) {
|
||||
val name = rs.getString("TABLE_NAME").toUpperCase
|
||||
@@ -188,7 +188,7 @@ object JDBCUtil {
|
||||
tableName
|
||||
}
|
||||
|
||||
using(meta.getExportedKeys(null, null, normalizedTableName)) { rs =>
|
||||
Using.resource(meta.getExportedKeys(null, null, normalizedTableName)) { rs =>
|
||||
val children = new ListBuffer[String]
|
||||
while (rs.next) {
|
||||
val childTableName = rs.getString("FKTABLE_NAME").toUpperCase
|
||||
@@ -218,7 +218,7 @@ object JDBCUtil {
|
||||
ordered ++ orphans
|
||||
}
|
||||
|
||||
def tsort[A](edges: Traversable[(A, A)]): Iterable[A] = {
|
||||
def tsort[A](edges: Iterable[(A, A)]): Iterable[A] = {
|
||||
@tailrec
|
||||
def tsort(toPreds: Map[A, Set[A]], done: Iterable[A]): Iterable[A] = {
|
||||
val (noPreds, hasPreds) = toPreds.partition { _._2.isEmpty }
|
||||
@@ -226,7 +226,7 @@ object JDBCUtil {
|
||||
if (hasPreds.isEmpty) done else sys.error(hasPreds.toString)
|
||||
} else {
|
||||
val found = noPreds.map { _._1 }
|
||||
tsort(hasPreds.mapValues { _ -- found }, done ++ found)
|
||||
tsort(hasPreds.map { case (k, v) => (k, v -- found) }, done ++ found)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,8 @@ import StringUtil._
|
||||
import SyntaxSugars._
|
||||
|
||||
import scala.annotation.tailrec
|
||||
import scala.collection.JavaConverters._
|
||||
import scala.jdk.CollectionConverters._
|
||||
import scala.util.Using
|
||||
import org.eclipse.jgit.lib._
|
||||
import org.eclipse.jgit.revwalk._
|
||||
import org.eclipse.jgit.revwalk.filter._
|
||||
@@ -20,7 +21,6 @@ import org.eclipse.jgit.errors.{ConfigInvalidException, IncorrectObjectTypeExcep
|
||||
import org.eclipse.jgit.transport.RefSpec
|
||||
import java.util.Date
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.function.Consumer
|
||||
|
||||
import org.cache2k.Cache2kBuilder
|
||||
import org.eclipse.jgit.api.errors._
|
||||
@@ -29,6 +29,8 @@ import org.eclipse.jgit.dircache.DirCacheEntry
|
||||
import org.eclipse.jgit.util.io.DisabledOutputStream
|
||||
import org.slf4j.LoggerFactory
|
||||
|
||||
import scala.util.Using.Releasable
|
||||
|
||||
/**
|
||||
* Provides complex JGit operations.
|
||||
*/
|
||||
@@ -36,6 +38,10 @@ object JGitUtil {
|
||||
|
||||
private val logger = LoggerFactory.getLogger(JGitUtil.getClass)
|
||||
|
||||
implicit val objectDatabaseReleasable = new Releasable[ObjectDatabase] {
|
||||
override def release(resource: ObjectDatabase): Unit = resource.close()
|
||||
}
|
||||
|
||||
/**
|
||||
* The repository data.
|
||||
*
|
||||
@@ -318,7 +324,7 @@ object JGitUtil {
|
||||
* Returns the repository information. It contains branch names and tag names.
|
||||
*/
|
||||
def getRepositoryInfo(owner: String, repository: String): RepositoryInfo = {
|
||||
using(Git.open(getRepositoryDir(owner, repository))) { git =>
|
||||
Using.resource(Git.open(getRepositoryDir(owner, repository))) { git =>
|
||||
try {
|
||||
RepositoryInfo(
|
||||
owner,
|
||||
@@ -365,7 +371,7 @@ object JGitUtil {
|
||||
* @return HTML of the file list
|
||||
*/
|
||||
def getFileList(git: Git, revision: String, path: String = ".", baseUrl: Option[String] = None): List[FileInfo] = {
|
||||
using(new RevWalk(git.getRepository)) { revWalk =>
|
||||
Using.resource(new RevWalk(git.getRepository)) { revWalk =>
|
||||
val objectId = git.getRepository.resolve(revision)
|
||||
if (objectId == null) return Nil
|
||||
val revCommit = revWalk.parseCommit(objectId)
|
||||
@@ -374,12 +380,12 @@ object JGitUtil {
|
||||
if (path == ".") {
|
||||
val treeWalk = new TreeWalk(git.getRepository)
|
||||
treeWalk.addTree(rev.getTree)
|
||||
using(treeWalk)(f)
|
||||
Using.resource(treeWalk)(f)
|
||||
} else {
|
||||
val treeWalk = TreeWalk.forPath(git.getRepository, path, rev.getTree)
|
||||
if (treeWalk != null) {
|
||||
treeWalk.enterSubtree
|
||||
using(treeWalk)(f)
|
||||
Using.resource(treeWalk)(f)
|
||||
}
|
||||
}
|
||||
@tailrec
|
||||
@@ -387,7 +393,7 @@ object JGitUtil {
|
||||
tuple: (ObjectId, FileMode, String, String, Option[String], RevCommit)
|
||||
): (ObjectId, FileMode, String, String, Option[String], RevCommit) = tuple match {
|
||||
case (oid, FileMode.TREE, name, path, _, commit) =>
|
||||
(using(new TreeWalk(git.getRepository)) { walk =>
|
||||
(Using.resource(new TreeWalk(git.getRepository)) { walk =>
|
||||
walk.addTree(oid)
|
||||
// single tree child, or None
|
||||
if (walk.next() && walk.getFileMode(0) == FileMode.TREE) {
|
||||
@@ -521,7 +527,7 @@ object JGitUtil {
|
||||
* get all file list by revision. only file.
|
||||
*/
|
||||
def getTreeId(git: Git, revision: String): Option[String] = {
|
||||
using(new RevWalk(git.getRepository)) { revWalk =>
|
||||
Using.resource(new RevWalk(git.getRepository)) { revWalk =>
|
||||
val objectId = git.getRepository.resolve(revision)
|
||||
if (objectId == null) return None
|
||||
val revCommit = revWalk.parseCommit(objectId)
|
||||
@@ -533,10 +539,10 @@ object JGitUtil {
|
||||
* get all file list by tree object id.
|
||||
*/
|
||||
def getAllFileListByTreeId(git: Git, treeId: String): List[String] = {
|
||||
using(new RevWalk(git.getRepository)) { revWalk =>
|
||||
Using.resource(new RevWalk(git.getRepository)) { revWalk =>
|
||||
val objectId = git.getRepository.resolve(treeId + "^{tree}")
|
||||
if (objectId == null) return Nil
|
||||
using(new TreeWalk(git.getRepository)) { treeWalk =>
|
||||
Using.resource(new TreeWalk(git.getRepository)) { treeWalk =>
|
||||
treeWalk.addTree(objectId)
|
||||
treeWalk.setRecursive(true)
|
||||
var ret: List[String] = Nil
|
||||
@@ -587,7 +593,7 @@ object JGitUtil {
|
||||
case _ => (logs, i.hasNext)
|
||||
}
|
||||
|
||||
using(new RevWalk(git.getRepository)) { revWalk =>
|
||||
Using.resource(new RevWalk(git.getRepository)) { revWalk =>
|
||||
defining(git.getRepository.resolve(revision)) { objectId =>
|
||||
if (objectId == null) {
|
||||
Left(s"${revision} can't be resolved.")
|
||||
@@ -619,7 +625,7 @@ object JGitUtil {
|
||||
case false => logs
|
||||
}
|
||||
|
||||
using(new RevWalk(git.getRepository)) { revWalk =>
|
||||
Using.resource(new RevWalk(git.getRepository)) { revWalk =>
|
||||
revWalk.markStart(revWalk.parseCommit(git.getRepository.resolve(begin)))
|
||||
getCommitLog(revWalk.iterator, Nil).reverse
|
||||
}
|
||||
@@ -679,12 +685,12 @@ object JGitUtil {
|
||||
}
|
||||
|
||||
private def getDiffEntries(git: Git, from: Option[String], to: String): Seq[DiffEntry] = {
|
||||
using(new RevWalk(git.getRepository)) { revWalk =>
|
||||
Using.resource(new RevWalk(git.getRepository)) { revWalk =>
|
||||
val df = new DiffFormatter(DisabledOutputStream.INSTANCE)
|
||||
df.setRepository(git.getRepository)
|
||||
|
||||
val toCommit = revWalk.parseCommit(git.getRepository.resolve(to))
|
||||
from match {
|
||||
(from match {
|
||||
case None => {
|
||||
toCommit.getParentCount match {
|
||||
case 0 =>
|
||||
@@ -700,12 +706,12 @@ object JGitUtil {
|
||||
val fromCommit = revWalk.parseCommit(git.getRepository.resolve(from))
|
||||
df.scan(fromCommit.getTree, toCommit.getTree).asScala
|
||||
}
|
||||
}
|
||||
}).toSeq
|
||||
}
|
||||
}
|
||||
|
||||
def getParentCommitId(git: Git, id: String): Option[String] = {
|
||||
using(new RevWalk(git.getRepository)) { revWalk =>
|
||||
Using.resource(new RevWalk(git.getRepository)) { revWalk =>
|
||||
val commit = revWalk.parseCommit(git.getRepository.resolve(id))
|
||||
commit.getParentCount match {
|
||||
case 0 => None
|
||||
@@ -787,7 +793,7 @@ object JGitUtil {
|
||||
|
||||
private def makePatchFromDiffEntry(git: Git, diff: DiffEntry): String = {
|
||||
val out = new ByteArrayOutputStream()
|
||||
using(new DiffFormatter(out)) { formatter =>
|
||||
Using.resource(new DiffFormatter(out)) { formatter =>
|
||||
formatter.setRepository(git.getRepository)
|
||||
formatter.format(diff)
|
||||
val patch = new String(out.toByteArray) // TODO charset???
|
||||
@@ -799,7 +805,7 @@ object JGitUtil {
|
||||
* Returns the list of branch names of the specified commit.
|
||||
*/
|
||||
def getBranchesOfCommit(git: Git, commitId: String): List[String] =
|
||||
using(new RevWalk(git.getRepository)) { revWalk =>
|
||||
Using.resource(new RevWalk(git.getRepository)) { revWalk =>
|
||||
defining(revWalk.parseCommit(git.getRepository.resolve(commitId + "^0"))) { commit =>
|
||||
git.getRepository.getRefDatabase
|
||||
.getRefsByPrefix(Constants.R_HEADS)
|
||||
@@ -842,7 +848,7 @@ object JGitUtil {
|
||||
* Returns the list of tags which contains the specified commit.
|
||||
*/
|
||||
def getTagsOfCommit(git: Git, commitId: String): List[String] =
|
||||
using(new RevWalk(git.getRepository)) { revWalk =>
|
||||
Using.resource(new RevWalk(git.getRepository)) { revWalk =>
|
||||
defining(revWalk.parseCommit(git.getRepository.resolve(commitId + "^0"))) { commit =>
|
||||
git.getRepository.getRefDatabase
|
||||
.getRefsByPrefix(Constants.R_TAGS)
|
||||
@@ -863,13 +869,13 @@ object JGitUtil {
|
||||
}
|
||||
|
||||
def initRepository(dir: java.io.File): Unit =
|
||||
using(new RepositoryBuilder().setGitDir(dir).setBare.build) { repository =>
|
||||
Using.resource(new RepositoryBuilder().setGitDir(dir).setBare.build) { repository =>
|
||||
repository.create(true)
|
||||
setReceivePack(repository)
|
||||
}
|
||||
|
||||
def cloneRepository(from: java.io.File, to: java.io.File): Unit =
|
||||
using(Git.cloneRepository.setURI(from.toURI.toString).setDirectory(to).setBare(true).call) { git =>
|
||||
Using.resource(Git.cloneRepository.setURI(from.toURI.toString).setDirectory(to).setBare(true).call) { git =>
|
||||
setReceivePack(git.getRepository)
|
||||
}
|
||||
|
||||
@@ -899,7 +905,7 @@ object JGitUtil {
|
||||
def createTag(git: Git, name: String, message: Option[String], commitId: String) = {
|
||||
try {
|
||||
val objectId: ObjectId = git.getRepository.resolve(commitId)
|
||||
using(new RevWalk(git.getRepository)) { walk =>
|
||||
Using.resource(new RevWalk(git.getRepository)) { walk =>
|
||||
val tagCommand = git.tag().setName(name).setObjectId(walk.parseCommit(objectId))
|
||||
message.foreach { message =>
|
||||
tagCommand.setMessage(message)
|
||||
@@ -908,10 +914,10 @@ object JGitUtil {
|
||||
}
|
||||
Right("Tag added.")
|
||||
} catch {
|
||||
case e: GitAPIException => Left("Sorry, some Git operation error occurs.")
|
||||
case e: ConcurrentRefUpdateException => Left("Sorry some error occurs.")
|
||||
case e: ConcurrentRefUpdateException => Left("Sorry, some error occurs.")
|
||||
case e: InvalidTagNameException => Left("Sorry, that name is invalid.")
|
||||
case e: NoHeadException => Left("Sorry, this repo doesn't have HEAD reference")
|
||||
case e: GitAPIException => Left("Sorry, some Git operation error occurs.")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1004,7 +1010,7 @@ object JGitUtil {
|
||||
case false => None
|
||||
}
|
||||
|
||||
using(new TreeWalk(git.getRepository)) { treeWalk =>
|
||||
Using.resource(new TreeWalk(git.getRepository)) { treeWalk =>
|
||||
treeWalk.addTree(revTree)
|
||||
treeWalk.setRecursive(true)
|
||||
getPathObjectId(path, treeWalk)
|
||||
@@ -1049,7 +1055,7 @@ object JGitUtil {
|
||||
|
||||
def getContentInfo(git: Git, path: String, objectId: ObjectId): ContentInfo = {
|
||||
// Viewer
|
||||
using(git.getRepository.getObjectDatabase) { db =>
|
||||
Using.resource(git.getRepository.getObjectDatabase) { db =>
|
||||
val loader = db.open(objectId)
|
||||
val isLfs = isLfsPointer(loader)
|
||||
val large = FileUtil.isLarge(loader.getSize)
|
||||
@@ -1087,7 +1093,7 @@ object JGitUtil {
|
||||
*/
|
||||
def getContentFromId(git: Git, id: ObjectId, fetchLargeFile: Boolean): Option[Array[Byte]] =
|
||||
try {
|
||||
using(git.getRepository.getObjectDatabase) { db =>
|
||||
Using.resource(git.getRepository.getObjectDatabase) { db =>
|
||||
val loader = db.open(id)
|
||||
if (loader.isLarge || (fetchLargeFile == false && FileUtil.isLarge(loader.getSize))) {
|
||||
None
|
||||
@@ -1109,7 +1115,7 @@ object JGitUtil {
|
||||
*/
|
||||
def getObjectLoaderFromId[A](git: Git, id: ObjectId)(f: ObjectLoader => A): Option[A] =
|
||||
try {
|
||||
using(git.getRepository.getObjectDatabase) { db =>
|
||||
Using.resource(git.getRepository.getObjectDatabase) { db =>
|
||||
Some(f(db.open(id)))
|
||||
}
|
||||
} catch {
|
||||
@@ -1132,8 +1138,8 @@ object JGitUtil {
|
||||
}
|
||||
|
||||
def processTree[T](git: Git, id: ObjectId)(f: (String, CanonicalTreeParser) => T): Seq[T] = {
|
||||
using(new RevWalk(git.getRepository)) { revWalk =>
|
||||
using(new TreeWalk(git.getRepository)) { treeWalk =>
|
||||
Using.resource(new RevWalk(git.getRepository)) { revWalk =>
|
||||
Using.resource(new TreeWalk(git.getRepository)) { treeWalk =>
|
||||
val index = treeWalk.addTree(revWalk.parseTree(id))
|
||||
treeWalk.setRecursive(true)
|
||||
val result = new collection.mutable.ListBuffer[T]()
|
||||
@@ -1177,7 +1183,7 @@ object JGitUtil {
|
||||
requestRepositoryName: String,
|
||||
requestBranch: String
|
||||
): (String, String) =
|
||||
using(
|
||||
Using.resources(
|
||||
Git.open(Directory.getRepositoryDir(userName, repositoryName)),
|
||||
Git.open(Directory.getRepositoryDir(requestUserName, requestRepositoryName))
|
||||
) { (oldGit, newGit) =>
|
||||
@@ -1247,7 +1253,7 @@ object JGitUtil {
|
||||
} finally {
|
||||
walk.dispose()
|
||||
}
|
||||
}
|
||||
}.toSeq
|
||||
}
|
||||
|
||||
def getBlame(git: Git, id: String, path: String): Iterable[BlameInfo] = {
|
||||
@@ -1277,7 +1283,7 @@ object JGitUtil {
|
||||
}
|
||||
idLine :+= (c.name, i)
|
||||
}
|
||||
val limeMap = idLine.groupBy(_._1).mapValues(_.map(_._2).toSet)
|
||||
val limeMap = idLine.groupBy(_._1).view.mapValues(_.map(_._2).toSet)
|
||||
blameMap.values.map { b =>
|
||||
b.copy(lines = limeMap(b.id))
|
||||
}
|
||||
@@ -1294,7 +1300,7 @@ object JGitUtil {
|
||||
* @return sha1
|
||||
*/
|
||||
def getShaByRef(owner: String, name: String, revstr: String): Option[String] = {
|
||||
using(Git.open(getRepositoryDir(owner, name))) { git =>
|
||||
Using.resource(Git.open(getRepositoryDir(owner, name))) { git =>
|
||||
Option(git.getRepository.resolve(revstr)).map(ObjectId.toString(_))
|
||||
}
|
||||
}
|
||||
@@ -1309,14 +1315,14 @@ object JGitUtil {
|
||||
if (lfsAttrs.nonEmpty) {
|
||||
val oid = lfsAttrs("oid").split(":")(1)
|
||||
|
||||
using(new FileInputStream(FileUtil.getLfsFilePath(repository.owner, repository.name, oid))) { in =>
|
||||
Using.resource(new FileInputStream(FileUtil.getLfsFilePath(repository.owner, repository.name, oid))) { in =>
|
||||
f(in)
|
||||
}
|
||||
} else {
|
||||
throw new NoSuchElementException("LFS attribute is empty.")
|
||||
}
|
||||
} else {
|
||||
using(loader.openStream()) { in =>
|
||||
Using.resource(loader.openStream()) { in =>
|
||||
f(in)
|
||||
}
|
||||
}
|
||||
@@ -1325,7 +1331,7 @@ object JGitUtil {
|
||||
def openFile[T](git: Git, repository: RepositoryService.RepositoryInfo, tree: RevTree, path: String)(
|
||||
f: InputStream => T
|
||||
): T = {
|
||||
using(TreeWalk.forPath(git.getRepository, path, tree)) { treeWalk =>
|
||||
Using.resource(TreeWalk.forPath(git.getRepository, path, tree)) { treeWalk =>
|
||||
openFile(git, repository, treeWalk)(f)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ object SyntaxSugars {
|
||||
|
||||
def defining[A, B](value: A)(f: A => B): B = f(value)
|
||||
|
||||
@deprecated("Use scala.util.Using.resource instead", "4.32.0")
|
||||
def using[A <: { def close(): Unit }, B](resource: A)(f: A => B): B =
|
||||
try f(resource)
|
||||
finally {
|
||||
@@ -21,6 +22,7 @@ object SyntaxSugars {
|
||||
}
|
||||
}
|
||||
|
||||
@deprecated("Use scala.util.Using.resources instead", "4.32.0")
|
||||
def using[A <: { def close(): Unit }, B <: { def close(): Unit }, C](resource1: A, resource2: B)(f: (A, B) => C): C =
|
||||
try f(resource1, resource2)
|
||||
finally {
|
||||
@@ -36,10 +38,12 @@ object SyntaxSugars {
|
||||
}
|
||||
}
|
||||
|
||||
@deprecated("Use scala.util.Using.resource instead", "4.32.0")
|
||||
def using[T](git: Git)(f: Git => T): T =
|
||||
try f(git)
|
||||
finally git.getRepository.close()
|
||||
|
||||
@deprecated("Use scala.util.Using.resources instead", "4.32.0")
|
||||
def using[T](git1: Git, git2: Git)(f: (Git, Git) => T): T =
|
||||
try f(git1, git2)
|
||||
finally {
|
||||
|
||||
@@ -8,7 +8,7 @@ trait Validations {
|
||||
/**
|
||||
* Constraint for the identifier such as user name or page name.
|
||||
*/
|
||||
def identifier: Constraint = new Constraint() {
|
||||
val identifier: Constraint = new Constraint() {
|
||||
override def validate(name: String, value: String, messages: Messages): Option[String] =
|
||||
if (!value.matches("[a-zA-Z0-9\\-_.]+")) {
|
||||
Some(s"${name} contains invalid character.")
|
||||
@@ -22,19 +22,22 @@ trait Validations {
|
||||
/**
|
||||
* Constraint for the password.
|
||||
*/
|
||||
def password: Constraint = new Constraint() {
|
||||
override def validate(name: String, value: String, messages: Messages): Option[String] =
|
||||
if (System.getProperty("gitbucket.validate.password") != "false" && !value.matches("[a-zA-Z0-9\\-_.]+")) {
|
||||
val password: Constraint = new Constraint() {
|
||||
lazy val validatePassword = ConfigUtil.getConfigValue[Boolean]("gitbucket.validate.password").getOrElse(true)
|
||||
|
||||
override def validate(name: String, value: String, messages: Messages): Option[String] = {
|
||||
if (validatePassword == true && !value.matches("[a-zA-Z0-9\\-_.]+")) {
|
||||
Some(s"${name} contains invalid character.")
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constraint for the repository identifier.
|
||||
*/
|
||||
def repository: Constraint = new Constraint() {
|
||||
val repository: Constraint = new Constraint() {
|
||||
override def validate(name: String, value: String, messages: Messages): Option[String] =
|
||||
if (!value.matches("[a-zA-Z0-9\\-\\+_.]+")) {
|
||||
Some(s"${name} contains invalid character.")
|
||||
@@ -48,7 +51,7 @@ trait Validations {
|
||||
/**
|
||||
* Constraint for the color pattern.
|
||||
*/
|
||||
def color = pattern("#[0-9a-fA-F]{6}")
|
||||
val color = pattern("#[0-9a-fA-F]{6}")
|
||||
|
||||
/**
|
||||
* ValueType for the java.util.Date property.
|
||||
|
||||
@@ -169,22 +169,31 @@ object Markdown {
|
||||
private def fixUrl(url: String, branch: String, isImage: Boolean = false): String = {
|
||||
lazy val urlWithRawParam: String = url + (if (isImage && !url.endsWith("?raw=true")) "?raw=true" else "")
|
||||
|
||||
if (url.startsWith("http://") || url.startsWith("https://") || url.startsWith("mailto:") || url.startsWith("/")) {
|
||||
if (url.startsWith("http://") || url.startsWith("https://") || url.startsWith("mailto:")) {
|
||||
url
|
||||
} else if (url.startsWith("/")) {
|
||||
context.baseUrl + url
|
||||
} else if (url.startsWith("#")) {
|
||||
("#" + generateAnchorName(url.substring(1)))
|
||||
} else if (!enableWikiLink) {
|
||||
if (context.currentPath.contains("/blob/")) {
|
||||
urlWithRawParam
|
||||
} else if (context.currentPath.contains("/tree/")) {
|
||||
val paths = context.currentPath.split("/")
|
||||
val path = if (paths.length > 3) paths.drop(4).mkString("/") else branch
|
||||
repository.httpUrl.replaceFirst("/git/", "/").stripSuffix(".git") + "/blob/" + path + "/" + urlWithRawParam
|
||||
} else {
|
||||
repository.httpUrl.replaceFirst("/git/", "/").stripSuffix(".git") + "/blob/" + branch + "/" + urlWithRawParam
|
||||
}
|
||||
} else {
|
||||
repository.httpUrl.replaceFirst("/git/", "/").stripSuffix(".git") + "/wiki/_blob/" + url
|
||||
// Relative path
|
||||
if (!enableWikiLink) {
|
||||
if (context.currentPath.contains("/blob/")) {
|
||||
val paths = context.currentPath.split("/").dropRight(1)
|
||||
val path = if (paths.length > 3) paths.drop(4).mkString("/") else branch
|
||||
repository.httpUrl.replaceFirst("/git/", "/").stripSuffix(".git") + "/blob/" + path + "/" + urlWithRawParam
|
||||
} else if (context.currentPath.contains("/tree/")) {
|
||||
val paths = context.currentPath.split("/")
|
||||
val path = if (paths.length > 3) paths.drop(4).mkString("/") else branch
|
||||
repository.httpUrl.replaceFirst("/git/", "/").stripSuffix(".git") + "/blob/" + path + "/" + urlWithRawParam
|
||||
} else {
|
||||
repository.httpUrl
|
||||
.replaceFirst("/git/", "/")
|
||||
.stripSuffix(".git") + "/blob/" + branch + "/" + urlWithRawParam
|
||||
}
|
||||
} else {
|
||||
repository.httpUrl.replaceFirst("/git/", "/").stripSuffix(".git") + "/wiki/_blob/" + url
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import java.util.{Date, Locale, TimeZone}
|
||||
import com.nimbusds.jose.util.JSONObjectUtils
|
||||
import gitbucket.core.controller.Context
|
||||
import gitbucket.core.model.CommitState
|
||||
import gitbucket.core.model.PullRequest
|
||||
import gitbucket.core.plugin.{PluginRegistry, RenderRequest}
|
||||
import gitbucket.core.service.RepositoryService.RepositoryInfo
|
||||
import gitbucket.core.service.{RepositoryService, RequestCache}
|
||||
@@ -218,14 +219,18 @@ object helpers extends AvatarImageProvider with LinkConverter with RequestCache
|
||||
.replaceAll(
|
||||
"\\[branch:([^\\s]+?)/([^\\s]+?)#([^\\s]+?)\\]",
|
||||
(m: Match) =>
|
||||
s"""<a href="${context.path}/${m.group(1)}/${m.group(2)}/tree/${encodeRefName(m.group(3))}">${m
|
||||
.group(3)}</a>"""
|
||||
s"""<a href="${context.path}/${m.group(1)}/${m.group(2)}/tree/${encodeRefName(m.group(3))}">${StringUtil
|
||||
.escapeHtml(
|
||||
m.group(3)
|
||||
)}</a>"""
|
||||
)
|
||||
.replaceAll(
|
||||
"\\[tag:([^\\s]+?)/([^\\s]+?)#([^\\s]+?)\\]",
|
||||
(m: Match) =>
|
||||
s"""<a href="${context.path}/${m.group(1)}/${m.group(2)}/tree/${encodeRefName(m.group(3))}">${m
|
||||
.group(3)}</a>"""
|
||||
s"""<a href="${context.path}/${m.group(1)}/${m.group(2)}/tree/${encodeRefName(m.group(3))}">${StringUtil
|
||||
.escapeHtml(
|
||||
m.group(3)
|
||||
)}</a>"""
|
||||
)
|
||||
.replaceAll("\\[user:([^\\s]+?)\\]", (m: Match) => user(m.group(1)).body)
|
||||
.replaceAll(
|
||||
@@ -237,8 +242,10 @@ object helpers extends AvatarImageProvider with LinkConverter with RequestCache
|
||||
.replaceAll(
|
||||
"\\[release:([^\\s]+?)/([^\\s]+?)/([^\\s]+?):(.+)\\]",
|
||||
(m: Match) =>
|
||||
s"""<a href="${context.path}/${m.group(1)}/${m.group(2)}/releases/${encodeRefName(m.group(3))}">${m
|
||||
.group(4)}</a>"""
|
||||
s"""<a href="${context.path}/${m.group(1)}/${m.group(2)}/releases/${encodeRefName(m.group(3))}">${StringUtil
|
||||
.escapeHtml(
|
||||
m.group(4)
|
||||
)}</a>"""
|
||||
)
|
||||
)
|
||||
|
||||
@@ -267,6 +274,18 @@ object helpers extends AvatarImageProvider with LinkConverter with RequestCache
|
||||
*/
|
||||
def url(userName: String)(implicit context: Context): String = s"${context.path}/${encodeRefName(userName)}"
|
||||
|
||||
/**
|
||||
* Generates the url to the pull request base branch.
|
||||
*/
|
||||
def basePRBranchUrl(pullreq: PullRequest)(implicit context: Context): String =
|
||||
s"${context.path}/${encodeRefName(pullreq.userName)}/${encodeRefName(pullreq.repositoryName)}/tree/${encodeRefName(pullreq.branch)}"
|
||||
|
||||
/**
|
||||
* Generates the url to the pull request branch.
|
||||
*/
|
||||
def requestPRBranchUrl(pullreq: PullRequest)(implicit context: Context): String =
|
||||
s"${context.path}/${encodeRefName(pullreq.requestUserName)}/${encodeRefName(pullreq.repositoryName)}/tree/${encodeRefName(pullreq.requestBranch)}"
|
||||
|
||||
/**
|
||||
* Returns the url to the root of assets.
|
||||
*/
|
||||
|
||||
@@ -12,21 +12,26 @@
|
||||
</div>
|
||||
</div>
|
||||
<div style="padding-left: 10px; padding-right: 10px;">
|
||||
@account.description.map{ description =>
|
||||
@account.description.map { description =>
|
||||
<p style="color: #999">@description</p>
|
||||
}
|
||||
@if(account.url.isDefined){
|
||||
@account.url.map { url =>
|
||||
<p style="white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">
|
||||
<i class="octicon octicon-home"></i> <a href="@account.url">@account.url</a>
|
||||
<i class="octicon octicon-home"></i>
|
||||
@if(url.startsWith("http://") || url.startsWith("https://")){
|
||||
<a href="@url">@url</a>
|
||||
} else {
|
||||
<span style="color: #999">@url</span>
|
||||
}
|
||||
</p>
|
||||
}
|
||||
@if(context.settings.showMailAddress){
|
||||
<p style="white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">
|
||||
<i class="octicon octicon-mail"></i> <a href="mailto: @account.mailAddress">@account.mailAddress</a>
|
||||
<i class="octicon octicon-mail"></i> <a href="mailto:@account.mailAddress">@account.mailAddress</a>
|
||||
</p>
|
||||
@extraMailAddresses.map{ mail =>
|
||||
@extraMailAddresses.map { mail =>
|
||||
<p style="white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">
|
||||
<i class="octicon octicon-mail"></i> <a href="mailto: @mail">@mail</a>
|
||||
<i class="octicon octicon-mail"></i> <a href="mailto:@mail">@mail</a>
|
||||
</p>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,10 +84,10 @@ isCreateRepoOptionPublic: Boolean)(implicit context: gitbucket.core.controller.C
|
||||
<span class="strong">Copy existing git repository</span>
|
||||
<div class="normal muted">
|
||||
Create new repository from existing git repository.
|
||||
<input type="text" class="form-control" name="sourceUrl" id="sourceUrl" disabled placeholder="Source git repository URL..."/>
|
||||
<span id="error-sourceUrl" class="error"></span>
|
||||
</div>
|
||||
</label>
|
||||
<input type="text" class="form-control" name="sourceUrl" id="sourceUrl" disabled placeholder="Source git repository URL..."/>
|
||||
<span id="error-sourceUrl" class="error"></span>
|
||||
</fieldset>
|
||||
<fieldset class="border-top form-actions">
|
||||
<input type="submit" class="btn btn-success" value="Create repository"/>
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
</div>
|
||||
<form method="POST" action="@context.path/@account.userName/_ssh" validate="true" autocomplete="off">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading strong">Add a SSH Key</div>
|
||||
<div class="panel-heading strong">Add a public SSH Key</div>
|
||||
<div class="panel-body">
|
||||
<fieldset class="form-group">
|
||||
<label for="title" class="strong">Title</label>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@(plugins: List[(gitbucket.core.plugin.PluginInfoBase, Boolean, String)], info: Option[Any])(implicit context: gitbucket.core.controller.Context)
|
||||
@(plugins: List[gitbucket.core.plugin.PluginInfoBase], info: Option[Any])(implicit context: gitbucket.core.controller.Context)
|
||||
@gitbucket.core.html.main("Plugins"){
|
||||
@gitbucket.core.admin.html.menu("plugins") {
|
||||
@gitbucket.core.helper.html.information(info)
|
||||
@@ -8,26 +8,17 @@
|
||||
<h1 class="system-settings-title">Plugins</h1>
|
||||
@if(plugins.size > 0) {
|
||||
<ul>
|
||||
@plugins.map { case (plugin, enabled, updatableVersion) =>
|
||||
@plugins.map { plugin =>
|
||||
<li><a href="#@plugin.pluginId">@plugin.pluginId:@plugin.pluginVersion</a></li>
|
||||
}
|
||||
</ul>
|
||||
|
||||
@plugins.map { case (plugin, enabled, updatableVersion) =>
|
||||
@plugins.map { plugin =>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading strong" id="@plugin.pluginId">
|
||||
<form method="POST" class="pull-right">
|
||||
@if(enabled){
|
||||
@if(updatableVersion.nonEmpty){
|
||||
<input type="submit" value="Update" class="btn btn-success btn-sm update-plugin" style="position: relative; top: -5px; left: 10px;"
|
||||
data-name="@plugin.pluginName" formaction="@{context.path}/admin/plugins/@{plugin.pluginId}/@{updatableVersion}/_install">
|
||||
}
|
||||
<input type="submit" value="Uninstall" class="btn btn-danger btn-sm uninstall-plugin" style="position: relative; top: -5px; left: 10px;"
|
||||
data-name="@plugin.pluginName" formaction="@{context.path}/admin/plugins/@{plugin.pluginId}/_uninstall">
|
||||
} else {
|
||||
<input type="submit" value="Install" class="btn btn-success btn-sm install-plugin" style="position: relative; top: -5px; left: 10px;"
|
||||
data-name="@plugin.pluginName" formaction="@{context.path}/admin/plugins/@{plugin.pluginId}/@{plugin.pluginVersion}/_install">
|
||||
}
|
||||
<input type="submit" value="Uninstall" class="btn btn-danger btn-sm uninstall-plugin" style="position: relative; top: -5px; left: 10px;"
|
||||
data-name="@plugin.pluginName" formaction="@{context.path}/admin/plugins/@{plugin.pluginId}/_uninstall">
|
||||
</form>
|
||||
@plugin.pluginName
|
||||
</div>
|
||||
@@ -58,15 +49,5 @@
|
||||
var name = $(e.target).data('name');
|
||||
return confirm('Uninstall ' + name + '. Are you sure?');
|
||||
});
|
||||
|
||||
$('.install-plugin').click(function(e){
|
||||
var name = $(e.target).data('name');
|
||||
return confirm('Install ' + name + '. Are you sure?');
|
||||
});
|
||||
|
||||
$('.update-plugin').click(function(e){
|
||||
var name = $(e.target).data('name');
|
||||
return confirm('Update ' + name + '. Are you sure?');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
<ul class="nav nav-tabs fill-width" id="pullreq-tab">
|
||||
<li><a href="#system">System settings</a></li>
|
||||
<li><a href="#authentication">Authentication</a></li>
|
||||
<li><a href="#plugins">Plugins</a></li>
|
||||
</ul>
|
||||
<div class="tab-content fill-width" style="padding-top: 20px;">
|
||||
<div class="tab-pane" id="system">
|
||||
@@ -17,9 +16,6 @@
|
||||
<div class="tab-pane" id="authentication">
|
||||
@settings_authentication(info)
|
||||
</div>
|
||||
<div class="tab-pane" id="plugins">
|
||||
@settings_plugins(info)
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="align-right" style="margin-top: 20px;">
|
||||
@@ -34,9 +30,6 @@ $(function(){
|
||||
if(location.hash == '#authentication'){
|
||||
$('li:has(a[href="#authentication"])').addClass('active');
|
||||
$('div#authentication').addClass('active');
|
||||
} else if(location.hash == '#plugins'){
|
||||
$('li:has(a[href="#plugins"])').addClass('active');
|
||||
$('div#plugins').addClass('active');
|
||||
} else {
|
||||
$('li:has(a[href="#system"])').addClass('active');
|
||||
$('div#system').addClass('active');
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
@(info: Option[Any])(implicit context: gitbucket.core.controller.Context)
|
||||
<label class="strong">Plugin repositories</label>
|
||||
<fieldset>
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" id="pluginNetworkInstall" name="pluginNetworkInstall"@if(context.settings.pluginNetworkInstall){ checked} />
|
||||
<a href="https://plugins.gitbucket-community.org" target="_blank">plugins.gitbucket-community.org</a>
|
||||
</label>
|
||||
</fieldset>
|
||||
<hr>
|
||||
<fieldset>
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" id="useProxy" name="useProxy"@if(context.settings.pluginProxy.isDefined){ checked} />
|
||||
Use proxy
|
||||
</label>
|
||||
</fieldset>
|
||||
<div class="proxy">
|
||||
<div class="form-group">
|
||||
<label class="control-label col-md-2" for="proxyHost">Proxy host</label>
|
||||
<div class="col-md-10">
|
||||
<input type="text" id="proxyHost" name="proxy.host" class="form-control" value="@context.settings.pluginProxy.map(_.host)"/>
|
||||
<span id="error-proxy_host" class="error"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-md-2" for="proxyPort">Proxy port</label>
|
||||
<div class="col-md-10">
|
||||
<input type="text" id="proxyPort" name="proxy.port" class="form-control input-mini" value="@context.settings.pluginProxy.map(_.port)"/>
|
||||
<span id="error-proxy_port" class="error"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-md-2" for="proxyUser">Proxy user</label>
|
||||
<div class="col-md-10">
|
||||
<input type="text" id="proxyUser" name="proxy.user" class="form-control" value="@context.settings.pluginProxy.map(_.user)"/>
|
||||
<span id="error-proxy_user" class="error"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-md-2" for="proxyPassword">Proxy password</label>
|
||||
<div class="col-md-10">
|
||||
<input type="password" id="proxyPassword" name="proxy.password" class="form-control" value="@context.settings.pluginProxy.map(_.password)"/>
|
||||
<span id="error-proxy_password" class="error"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
$(function(){
|
||||
$('#useProxy').change(function(){
|
||||
$('.proxy input').prop('disabled', !$(this).prop('checked'));
|
||||
}).change();
|
||||
});
|
||||
</script>
|
||||
@@ -283,6 +283,23 @@
|
||||
Send notifications
|
||||
</label>
|
||||
</fieldset>
|
||||
<!--====================================================================-->
|
||||
<!-- Web hook -->
|
||||
<!--====================================================================-->
|
||||
<hr>
|
||||
<label class="strong">Web hook</label>
|
||||
<fieldset>
|
||||
<label class="checkbox" for="blockPrivateAddress">
|
||||
<input type="checkbox" id="blockPrivateAddress" name="webhook.blockPrivateAddress"@if(context.settings.webHook.blockPrivateAddress){ checked}/>
|
||||
Block sending to private addresses
|
||||
</label>
|
||||
</fieldset>
|
||||
<div class="webhook">
|
||||
<label><span class="strong">IP whitelist</span></label>
|
||||
<fieldset>
|
||||
<textarea name="webhook.whitelist" class="form-control" style="height: 100px;">@context.settings.webHook.whitelist.mkString("\n")</textarea>
|
||||
</fieldset>
|
||||
</div>
|
||||
<script>
|
||||
$(function(){
|
||||
$('#skinName').change(function(evt) {
|
||||
@@ -344,5 +361,9 @@ $(function(){
|
||||
$('#notification').prop('checked', false);
|
||||
}
|
||||
}).change();
|
||||
|
||||
$('#blockPrivateAddress').change(function(){
|
||||
$('.webhook textarea').prop('disabled', !$(this).prop('checked'));
|
||||
}).change();
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@issues.map { case IssueInfo(issue, labels, milestone, priority, commentCount, commitStatus) =>
|
||||
@issues.map { case IssueInfo(issue, labels, milestone, priority, commentCount, commitStatus) => {
|
||||
<tr>
|
||||
<td style="padding-top: 12px; padding-bottom: 12px;">
|
||||
<a href="@context.path/@issue.userName/@issue.repositoryName">@issue.userName/@issue.repositoryName</a> ・
|
||||
@@ -52,7 +52,7 @@
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
}}
|
||||
@if(issues.isEmpty){
|
||||
<tr>
|
||||
<td style="padding: 20px; background-color: #eee; text-align: center;">
|
||||
|
||||
@@ -66,6 +66,8 @@ $(function(){
|
||||
@dropzone(clickable: Boolean, textareaId: Option[String]) = {
|
||||
url: '@context.path/upload/file/@repository.owner/@repository.name',
|
||||
maxFilesize: @{FileUtil.MaxFileSize / 1024 / 1024},
|
||||
//timeout defaults to 30 secs
|
||||
timeout: @{FileUtil.UploadTimeout},
|
||||
clickable: @clickable,
|
||||
previewTemplate: "<div class=\"dz-preview\">\n <div class=\"dz-progress\"><span class=\"dz-upload\" data-dz-uploadprogress>Uploading your files...</span></div>\n <div class=\"dz-error-message\"><span data-dz-errormessage></span></div>\n</div>",
|
||||
success: function(file, id) {
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
<table class="table table-bordered diff-outside" commitId="@newCommitId" fileName="@diff.newPath" data-diff-id="@i">
|
||||
<tr>
|
||||
<th style="font-weight: normal;" class="box-header">
|
||||
<i class="fa fa-chevron-down rotate" data-toggle="collapse" data-target=".diff-collapse-@i" aria-hidden="true" style="cursor: pointer; font-size: 12px"></i>
|
||||
@if(diff.changeType == ChangeType.COPY || diff.changeType == ChangeType.RENAME){
|
||||
@if(newCommitId.isDefined){
|
||||
<div class="pull-right align-right">
|
||||
@@ -92,7 +93,7 @@
|
||||
}
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<tr class="diff-collapse-@i collapse in">
|
||||
<td style="padding: 0;">
|
||||
@if(diff.oldObjectId == diff.newObjectId){
|
||||
@if(diff.oldPath != diff.newPath){
|
||||
@@ -139,6 +140,17 @@
|
||||
<script type="text/javascript" src="@helpers.assets("/vendors/jsdifflib/difflib.js")"></script>
|
||||
<link href="@helpers.assets("/vendors/jsdifflib/diffview.css")" type="text/css" rel="stylesheet" />
|
||||
<script>
|
||||
|
||||
$(".rotate").click(function(){
|
||||
if($(this).hasClass('fa-chevron-right')) {
|
||||
$(this).removeClass('fa-chevron-right');
|
||||
$(this).addClass('fa-chevron-down');
|
||||
} else {
|
||||
$(this).removeClass('fa-chevron-down');
|
||||
$(this).addClass('fa-chevron-right');
|
||||
}
|
||||
});
|
||||
|
||||
$(function(){
|
||||
@if(showIndex){
|
||||
$('#toggle-file-list').click(function(){
|
||||
|
||||
@@ -114,9 +114,9 @@
|
||||
@gitbucket.core.helper.html.datetimeago(comment.registeredDate)
|
||||
</div>
|
||||
<div style="discussion-item-content">
|
||||
@defining(comment.content.split(":")){ case Array(issueId, rest @ _*) =>
|
||||
@defining(comment.content.split(":")){ case Array(issueId, rest @ _*) => {
|
||||
@helpers.issueLink(repository, issueId.toInt, rest.mkString(":"))
|
||||
}
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -65,6 +65,7 @@ $(function(){
|
||||
$('#edit').click(function(){
|
||||
$('.edit-title').show();
|
||||
$('.show-title').hide();
|
||||
$('#edit-title').focus();
|
||||
return false;
|
||||
});
|
||||
|
||||
@@ -79,9 +80,9 @@ $(function(){
|
||||
}).done(function(data){
|
||||
$('#show-title').empty().text(data.title);
|
||||
$('#cancel').click();
|
||||
$(this).removeAttr('disabled');
|
||||
$('#update').removeAttr('disabled');
|
||||
}).fail(function(req){
|
||||
$(this).removeAttr('disabled');
|
||||
$('#update').removeAttr('disabled');
|
||||
$('#error-edit-title').text($.parseJSON(req.responseText).title);
|
||||
});
|
||||
return false;
|
||||
|
||||
@@ -56,9 +56,9 @@
|
||||
</div>
|
||||
<span id="label-priority">
|
||||
@issue.flatMap(_.priorityId).orElse(defaultPriority.map(_.priorityId)).map { priorityId =>
|
||||
@priorities.collect { case priority if(priority.priorityId == priorityId) =>
|
||||
@priorities.collect { case priority if(priority.priorityId == priorityId) => {
|
||||
<a class="issue-priority" style="background-color: #@priority.color; color: #@priority.fontColor;" href="@helpers.url(repository)/issues?priority=@helpers.urlEncode(priority.priorityName)&state=open"@if(!priority.description.isEmpty) { title="@priority.description.get" }>@priority.priorityName</a>
|
||||
}
|
||||
}}
|
||||
}.getOrElse {
|
||||
<span class="muted small">No priority</span>
|
||||
}
|
||||
@@ -102,16 +102,16 @@
|
||||
</div>
|
||||
<div id="milestone-progress-area">
|
||||
@issue.flatMap(_.milestoneId).map { milestoneId =>
|
||||
@milestones.collect { case (milestone, openCount, closeCount) if(milestone.milestoneId == milestoneId) =>
|
||||
@milestones.collect { case (milestone, openCount, closeCount) if(milestone.milestoneId == milestoneId) => {
|
||||
@gitbucket.core.issues.milestones.html.progress(openCount + closeCount, closeCount)
|
||||
}
|
||||
}}
|
||||
}
|
||||
</div>
|
||||
<span id="label-milestone">
|
||||
@issue.flatMap(_.milestoneId).map { milestoneId =>
|
||||
@milestones.collect { case (milestone, _, _) if(milestone.milestoneId == milestoneId) =>
|
||||
@milestones.collect { case (milestone, _, _) if(milestone.milestoneId == milestoneId) => {
|
||||
<a class="strong small username" href="@helpers.url(repository)/issues?milestone=@helpers.urlEncode(milestone.title)&state=open">@milestone.title</a>
|
||||
}
|
||||
}}
|
||||
}.getOrElse {
|
||||
<span class="muted small">No milestone</span>
|
||||
}
|
||||
@@ -307,7 +307,7 @@ $(function(){
|
||||
$('#label-assigned').empty()
|
||||
.append($this.find('img.avatar-mini').clone(false)).append(' ')
|
||||
.append($('<a class="username strong small">').attr('href', '@context.path/' + userName).text(userName));
|
||||
$('a.assign[data-name=' + jqSelectorEscape(userName) + '] i').addClass('octicon-check');
|
||||
$('a.assign[data-name=' + jqSelectorEscape(userName.toString()) + '] i').addClass('octicon-check');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
<script>
|
||||
$(function(){
|
||||
$('#submit-@labelId').click(function(e){
|
||||
$.post('@helpers.url(repository)/issues/labels/@{if(labelId == "new") "new" else labelId + "/edit"}', {
|
||||
$.post('@helpers.url(repository)/issues/labels/@{if(labelId == "new") "new" else s"$labelId/edit"}', {
|
||||
'labelName' : $('#labelName-@labelId').val(),
|
||||
'labelColor': $('#labelColor-@labelId').val()
|
||||
}, function(data, status){
|
||||
|
||||
@@ -206,7 +206,7 @@
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
@issues.map { case IssueInfo(issue, labels, milestone, priority, commentCount, commitStatus) =>
|
||||
@issues.map { case IssueInfo(issue, labels, milestone, priority, commentCount, commitStatus) => {
|
||||
<tr>
|
||||
<td style="padding-top: 12px; padding-bottom: 12px;">
|
||||
@if(isManageable){
|
||||
@@ -253,7 +253,7 @@
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
}}
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="pull-right">
|
||||
|
||||
@@ -29,9 +29,9 @@
|
||||
<input type="submit" class="btn btn-success" value="Create milestone"/>
|
||||
} else {
|
||||
@if(milestone.get.closedDate.isDefined){
|
||||
<input type="button" class="btn btn-default" value="Open" id="open" onclick="location.href='@helpers.url(repository)/issues/milestones/@milestone.get.milestoneId/close';"/>
|
||||
<input type="button" class="btn btn-default" value="Open" id="open" onclick="location.href='@helpers.url(repository)/issues/milestones/@milestone.get.milestoneId/open';"/>
|
||||
} else {
|
||||
<input type="button" class="btn btn-default" value="Close" id="close" onclick="location.href='@helpers.url(repository)/issues/milestones/@milestone.get.milestoneId/open';"/>
|
||||
<input type="button" class="btn btn-default" value="Close" id="close" onclick="location.href='@helpers.url(repository)/issues/milestones/@milestone.get.milestoneId/close';"/>
|
||||
}
|
||||
<input type="submit" class="btn btn-success" value="Update milestone"/>
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
<td style="padding-top: 15px; padding-bottom: 15px;">
|
||||
<div class="milestone row">
|
||||
<div class="col-md-4">
|
||||
<a href="@helpers.url(repository)/issues?milestone=@milestone.title&state=open" class="milestone-title">@milestone.title</a>
|
||||
<a href="@helpers.url(repository)/issues?milestone=@helpers.urlEncode(milestone.title)&state=open" class="milestone-title">@milestone.title</a>
|
||||
<div>
|
||||
@if(milestone.closedDate.isDefined){
|
||||
<span class="muted">Closed @gitbucket.core.helper.html.datetimeago(milestone.closedDate.get)</span>
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
<script>
|
||||
$(function(){
|
||||
$('#submit-@priorityId').click(function(e){
|
||||
$.post('@helpers.url(repository)/issues/priorities/@{if(priorityId == "new") "new" else priorityId + "/edit"}', {
|
||||
$.post('@helpers.url(repository)/issues/priorities/@{if(priorityId == "new") "new" else s"$priorityId/edit"}', {
|
||||
'priorityName' : $('#priorityName-@priorityId').val(),
|
||||
'description' : $('#description-@priorityId').val(),
|
||||
'priorityColor': $('#priorityColor-@priorityId').val()
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
<link rel="icon" href="@helpers.assets("/common/images/gitbucket.png")" type="image/vnd.microsoft.icon" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link href="@helpers.assets("/vendors/google-fonts/css/source-sans-pro.css")" rel="stylesheet">
|
||||
<link href="@helpers.assets("/vendors/bootstrap-3.3.7/css/bootstrap.min.css")" rel="stylesheet">
|
||||
<link href="@helpers.assets("/vendors/bootstrap-3.4.1/css/bootstrap.min.css")" rel="stylesheet">
|
||||
<link href="@helpers.assets("/vendors/octicons-4.4.0/octicons.min.css")" rel="stylesheet">
|
||||
<link href="@helpers.assets("/vendors/bootstrap-datetimepicker-4.17.44/css/bootstrap-datetimepicker.min.css")" rel="stylesheet">
|
||||
<link href="@helpers.assets("/vendors/colorpicker/css/bootstrap-colorpicker.min.css")" rel="stylesheet">
|
||||
@@ -36,12 +36,12 @@
|
||||
<link href="@helpers.assets("/vendors/jquery-ui/jquery-ui.structure.min.css")" rel="stylesheet">
|
||||
<link href="@helpers.assets("/vendors/jquery-ui/jquery-ui.theme.min.css")" rel="stylesheet">
|
||||
<link href="@helpers.assets("/common/css/gitbucket.css")" rel="stylesheet">
|
||||
<script src="@helpers.assets("/vendors/jquery/jquery-3.2.1.min.js")"></script>
|
||||
<script src="@helpers.assets("/vendors/jquery/jquery-3.4.1.min.js")"></script>
|
||||
<script src="@helpers.assets("/vendors/jquery-ui/jquery-ui.min.js")"></script>
|
||||
<script src="@helpers.assets("/vendors/dropzone/dropzone.min.js")"></script>
|
||||
<script src="@helpers.assets("/common/js/validation.js")"></script>
|
||||
<script src="@helpers.assets("/common/js/gitbucket.js")"></script>
|
||||
<script src="@helpers.assets("/vendors/bootstrap-3.3.7/js/bootstrap.min.js")"></script>
|
||||
<script src="@helpers.assets("/vendors/bootstrap-3.4.1/js/bootstrap.min.js")"></script>
|
||||
<script src="@helpers.assets("/vendors/bootstrap3-typeahead/bootstrap3-typeahead.min.js")"></script>
|
||||
<script src="@helpers.assets("/vendors/bootstrap-datetimepicker-4.17.44/js/moment.min.js")"></script>
|
||||
<script src="@helpers.assets("/vendors/bootstrap-datetimepicker-4.17.44/js/bootstrap-datetimepicker.min.js")"></script>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
repository: gitbucket.core.service.RepositoryService.RepositoryInfo)(implicit context: gitbucket.core.controller.Context)
|
||||
@import gitbucket.core.view.helpers
|
||||
@gitbucket.core.pulls.html.menu("commits", issue, pullreq, commits.flatten, comments, changedFileSize, isManageable, repository){
|
||||
<table class="table table-bordered">
|
||||
<table class="table table-bordered table-hover">
|
||||
@commits.map { day =>
|
||||
<tr>
|
||||
<th rowspan="@day.size" width="100">@helpers.date(day.head.commitTime)</th>
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
collaborators: List[String],
|
||||
milestones: List[gitbucket.core.model.Milestone],
|
||||
priorities: List[gitbucket.core.model.Priority],
|
||||
defaultPriority: Option[gitbucket.core.model.Priority],
|
||||
labels: List[gitbucket.core.model.Label])(implicit context: gitbucket.core.controller.Context)
|
||||
@import gitbucket.core.view.helpers
|
||||
@gitbucket.core.html.main(s"Pull requests - ${repository.owner}/${repository.name}", Some(repository)){
|
||||
@@ -27,6 +28,9 @@
|
||||
}
|
||||
}
|
||||
@gitbucket.core.helper.html.dropdown(originId, "base", filter=("origin_branch", "Find Branch...")) {
|
||||
@if(!originRepository.branchList.contains(originId)){
|
||||
<li><a href="#" class="origin-branch" data-branch="@helpers.encodeRefName(originId)">@gitbucket.core.helper.html.checkicon(true) @originId</a></li>
|
||||
}
|
||||
@originRepository.branchList.map { branch =>
|
||||
<li><a href="#" class="origin-branch" data-branch="@helpers.encodeRefName(branch)">@gitbucket.core.helper.html.checkicon(branch == originId) @branch</a></li>
|
||||
}
|
||||
@@ -38,16 +42,21 @@
|
||||
}
|
||||
}
|
||||
@gitbucket.core.helper.html.dropdown(forkedId, "compare", filter=("forked_branch", "Find Branch...")) {
|
||||
@if(!forkedRepository.branchList.contains(forkedId)){
|
||||
<li><a href="#" class="origin-branch" data-branch="@helpers.encodeRefName(forkedId)">@gitbucket.core.helper.html.checkicon(true) @forkedId</a></li>
|
||||
}
|
||||
@forkedRepository.branchList.map { branch =>
|
||||
<li><a href="#" class="forked-branch" data-branch="@helpers.encodeRefName(branch)">@gitbucket.core.helper.html.checkicon(branch == forkedId) @branch</a></li>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
<div class="check-conflict" style="display: none;">
|
||||
<img src="@helpers.assets("/common/images/indicator.gif")"/> Checking...
|
||||
</div>
|
||||
@if(originRepository.branchList.contains(originId) && forkedRepository.branchList.contains(forkedId)){
|
||||
<div class="check-conflict" style="display: none;">
|
||||
<img src="@helpers.assets("/common/images/indicator.gif")"/> Checking...
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
@if(commits.nonEmpty && context.loginAccount.isDefined){
|
||||
@if(commits.nonEmpty && context.loginAccount.isDefined && originRepository.branchList.contains(originId) && forkedRepository.branchList.contains(forkedId)){
|
||||
<div style="margin-bottom: 10px; padding: 8px; background-color: #fff9ea" id="create-pull-request" class="box-content">
|
||||
<a href="#" class="btn btn-success" id="show-form">Create pull request</a>
|
||||
|
||||
@@ -70,19 +79,29 @@
|
||||
completionContext = "issues",
|
||||
style = "height: 200px;"
|
||||
)
|
||||
<input type="hidden" name="targetUserName" value="@originRepository.owner"/>
|
||||
<input type="hidden" name="targetBranch" value="@originId"/>
|
||||
<input type="hidden" name="requestUserName" value="@forkedRepository.owner"/>
|
||||
<input type="hidden" name="requestRepositoryName" value="@forkedRepository.name"/>
|
||||
<input type="hidden" name="requestBranch" value="@forkedId"/>
|
||||
<input type="hidden" name="commitIdFrom" value="@sourceId"/>
|
||||
<input type="hidden" name="commitIdTo" value="@commitId"/>
|
||||
<div class="align-right">
|
||||
<input type="submit" class="btn btn-success" value="Create pull request"/>
|
||||
<div class="text-right">
|
||||
<input type="hidden" name="targetUserName" value="@originRepository.owner"/>
|
||||
<input type="hidden" name="targetBranch" value="@originId"/>
|
||||
<input type="hidden" name="requestUserName" value="@forkedRepository.owner"/>
|
||||
<input type="hidden" name="requestRepositoryName" value="@forkedRepository.name"/>
|
||||
<input type="hidden" name="requestBranch" value="@forkedId"/>
|
||||
<input type="hidden" name="commitIdFrom" value="@sourceId"/>
|
||||
<input type="hidden" name="commitIdTo" value="@commitId"/>
|
||||
<input type="hidden" id="is-draft" name="isDraft" value=false />
|
||||
<div class="btn-group dropdown">
|
||||
<input type="submit" class="btn btn-success" tabindex="2" value="Create pull request" id="submit-button" validate="true" formaction="@context.path/@originRepository.owner/@originRepository.name/pulls/new"/>
|
||||
<button type="button" class="btn btn-success dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-right">
|
||||
<li><a id="pull-request">Create pull request</a></li>
|
||||
<li><a id="draft-request">Create draft request</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
@gitbucket.core.issues.html.issueinfo(None, Nil, Nil, collaborators, milestones.map((_, 0, 0)), priorities, None, labels, hasOriginWritePermission, repository)
|
||||
@gitbucket.core.issues.html.issueinfo(None, Nil, Nil, collaborators, milestones.map((_, 0, 0)), priorities, defaultPriority, labels, hasOriginWritePermission, repository)
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
@@ -93,16 +112,6 @@
|
||||
<h4>There isn't anything to compare.</h4>
|
||||
<span class="strong">@originRepository.owner:@originId</span> and <span class="strong">@forkedRepository.owner:@forkedId</span> are identical.
|
||||
</div>
|
||||
@*
|
||||
<table class="table table-bordered table-hover table-issues">
|
||||
<tr>
|
||||
<td style="padding: 20px; background-color: #eee; text-align: center;">
|
||||
<h4>There isn't anything to compare.</h4>
|
||||
<span class="strong">@originRepository.owner:@originId</span> and <span class="strong">@forkedRepository.owner:@forkedId</span> are identical.
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
*@
|
||||
} else {
|
||||
<div style="margin-bottom: 10px; padding: 4px;" class="panel panel-default">
|
||||
<table class="fill-width">
|
||||
@@ -146,14 +155,6 @@
|
||||
@helpers.user(commit.authorName, commit.authorEmailAddress, "username strong")
|
||||
</td>
|
||||
<td><span class="monospace">@commit.shortMessage</span></td>
|
||||
@*
|
||||
<span class="badge" style="display: inline">@if(comments.isDefined){
|
||||
@comments.get.flatMap @{
|
||||
case comment: CommitComment => Some(comment)
|
||||
case other => None
|
||||
}.count(t => t.commitId == commit.id && !t.pullRequest)
|
||||
}</span>
|
||||
*@
|
||||
<td style="width: 10%; text-align: right;">
|
||||
<a href="@helpers.url(repository)/commit/@commit.id" class="monospace">@commit.id.substring(0, 7)</a>
|
||||
</td>
|
||||
@@ -163,12 +164,24 @@
|
||||
}
|
||||
</div>
|
||||
@gitbucket.core.helper.html.diff(diffs, repository, Some(commitId), Some(sourceId), true, None, false, false)
|
||||
<p>Showing you all comments on commits in this comparison.</p>
|
||||
@gitbucket.core.issues.html.commentlist(None, comments, false, repository, None)
|
||||
}
|
||||
}
|
||||
}
|
||||
<script>
|
||||
|
||||
$(function(){
|
||||
$('#draft-request').click(function(){
|
||||
$("#is-draft").val(true);
|
||||
$('#submit-button').attr('value', 'Create draft request')
|
||||
});
|
||||
$('#pull-request').click(function(){
|
||||
$("#is-draft").val(false);
|
||||
$('#submit-button').attr('value', 'Create pull request')
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
$(function(){
|
||||
function updateSelector(e){
|
||||
e.parents('ul').find('i').attr('class', 'octicon');
|
||||
@@ -220,7 +233,7 @@ $(function(){
|
||||
$('#show-form').click();
|
||||
}
|
||||
|
||||
@if(context.loginAccount.isDefined){
|
||||
@if(context.loginAccount.isDefined && originRepository.branchList.contains(originId) && forkedRepository.branchList.contains(forkedId)){
|
||||
function checkConflict(from, to){
|
||||
$('.check-conflict').show();
|
||||
$.get('@helpers.url(forkedRepository)/compare/' + from + '...' + to + '/mergecheck',
|
||||
|
||||
@@ -47,8 +47,8 @@
|
||||
<span class="muted">
|
||||
@helpers.user(comment.commentedUserName, styleClass = "username strong")
|
||||
merged @commits.size @helpers.plural(commits.size, "commit")
|
||||
into <code>@pullreq.userName:@pullreq.branch</code>
|
||||
from <code>@pullreq.requestUserName:@pullreq.requestBranch</code>
|
||||
into <code><a href="@helpers.basePRBranchUrl(pullreq)">@pullreq.userName:@pullreq.branch</a></code>
|
||||
from <code><a href="@helpers.requestPRBranchUrl(pullreq)">@pullreq.requestUserName:@pullreq.requestBranch</a></code>
|
||||
@gitbucket.core.helper.html.datetimeago(comment.registeredDate)
|
||||
</span>
|
||||
}.getOrElse {
|
||||
@@ -56,8 +56,8 @@
|
||||
<span class="muted">
|
||||
@helpers.user(issue.openedUserName, styleClass = "username strong")
|
||||
wants to merge @commits.size @helpers.plural(commits.size, "commit")
|
||||
into <code>@pullreq.userName:@pullreq.branch</code>
|
||||
from <code>@pullreq.requestUserName:@pullreq.requestBranch</code>
|
||||
into <code><a href="@helpers.basePRBranchUrl(pullreq)">@pullreq.userName:@pullreq.branch</a></code>
|
||||
from <code><a href="@helpers.requestPRBranchUrl(pullreq)">@pullreq.requestUserName:@pullreq.requestBranch</a></code>
|
||||
</span>
|
||||
}
|
||||
} else {
|
||||
@@ -65,8 +65,8 @@
|
||||
<span class="muted">
|
||||
@helpers.user(issue.openedUserName, styleClass = "username strong")
|
||||
wants to merge @commits.size @helpers.plural(commits.size, "commit")
|
||||
into <code>@pullreq.userName:@pullreq.branch</code>
|
||||
from <code>@pullreq.requestUserName:@pullreq.requestBranch</code>
|
||||
into <code><a href="@helpers.basePRBranchUrl(pullreq)">@pullreq.userName:@pullreq.branch</a></code>
|
||||
from <code><a href="@helpers.requestPRBranchUrl(pullreq)">@pullreq.requestUserName:@pullreq.requestBranch</a></code>
|
||||
</span>
|
||||
}
|
||||
</div>
|
||||
@@ -91,6 +91,7 @@
|
||||
$('#edit').click(function(){
|
||||
$('.edit-title').show();
|
||||
$('.show-title').hide();
|
||||
$('#edit-title').focus();
|
||||
return false;
|
||||
});
|
||||
|
||||
|
||||
@@ -76,6 +76,16 @@
|
||||
</div>
|
||||
} else {
|
||||
<div class="merge-indicator merge-indicator-success"><span class="octicon octicon-check"></span></div>
|
||||
|
||||
@if(pullreq.isDraft){
|
||||
<span class="strong">This pull request is still a work in progress.</span>
|
||||
<div class="pull-right">
|
||||
<input type="button" class="btn btn-default" value="Ready for review" id="ready-for-review" />
|
||||
</div>
|
||||
<div class="small">
|
||||
Draft pull requests cannot be merged.
|
||||
</div>
|
||||
} else {
|
||||
@if(status.hasMergePermission){
|
||||
<span class="strong">Merging can be performed automatically.</span>
|
||||
<div class="small">
|
||||
@@ -87,13 +97,14 @@
|
||||
Only those with write access to this repository can merge pull requests.
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</div>
|
||||
@if(status.hasMergePermission){
|
||||
<div style="padding:15px; border-top:solid 1px #e5e5e5; background:#fafafa">
|
||||
<input type="button" class="btn @if(!status.hasProblem){btn-success} else {btn-default}" id="merge-pull-request-button" value="Merge pull request"@if(!status.canMerge){ disabled="true"}/>
|
||||
<input type="button" class="btn @if(!status.hasProblem){btn-success} else {btn-default}" id="merge-pull-request-button" value="Merge pull request"@if(!status.canMerge || pullreq.isDraft){ disabled="true"}/>
|
||||
You can also merge branches on the <a href="#" class="show-command-line">command line</a>.
|
||||
<div id="command-line" style="display: none;margin-top: 15px;">
|
||||
<hr />
|
||||
@@ -191,6 +202,7 @@
|
||||
<input type="button" class="btn btn-default" value="Cancel" id="cancel-merge-pull-request"/>
|
||||
<input type="submit" class="btn btn-success" value="Confirm merge"/>
|
||||
<input type="hidden" name="strategy" value="@originRepository.repository.options.defaultMergeOption"/>
|
||||
<input type="hidden" name="isDraft" value="@pullreq.isDraft" />
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
@@ -199,6 +211,15 @@
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
$(function(){
|
||||
$('#ready-for-review').click(function(){
|
||||
$.post('@helpers.url(originRepository)/pull/@issue.issueId/update_draft', function(data, status){
|
||||
location.reload();
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
$(function(){
|
||||
$('.show-command-line').click(function(){
|
||||
$('#command-line').toggle();
|
||||
|
||||
@@ -73,6 +73,8 @@ $(function(){
|
||||
$("#drop").dropzone({
|
||||
maxFilesize: @{gitbucket.core.util.FileUtil.MaxFileSize / 1024 / 1024},
|
||||
url: '@context.path/upload/release/@repository.owner/@repository.name/@helpers.encodeRefName(tag.name)',
|
||||
//timeout defaults to 30 secs
|
||||
timeout: @{gitbucket.core.util.FileUtil.UploadTimeout},
|
||||
previewTemplate: "<div class=\"dz-preview\">\n <div class=\"dz-progress\"><span class=\"dz-upload\" data-dz-uploadprogress>Uploading your files...</span></div>\n <div class=\"dz-error-message\"><span data-dz-errormessage></span></div>\n</div>",
|
||||
success: function(file, id) {
|
||||
var attach =
|
||||
|
||||
@@ -981,6 +981,12 @@ div.pullreq-info {
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.collapsing {
|
||||
-webkit-transition: none;
|
||||
transition: none;
|
||||
display: none;
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
/* Wiki */
|
||||
/****************************************************************************/
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user