mirror of
https://github.com/gitbucket/gitbucket.git
synced 2025-11-03 20:15:59 +01:00
Compare commits
257 Commits
4.30.0
...
disable-gi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f16f395539 | ||
|
|
3534b7172d | ||
|
|
42a7f974e9 | ||
|
|
b8e02d995a | ||
|
|
e0d038aa92 | ||
|
|
3d01df2bdc | ||
|
|
6f08f1fd23 | ||
|
|
3dd366b394 | ||
|
|
8cf4528959 | ||
|
|
5d77bc5d98 | ||
|
|
572c9ef558 | ||
|
|
7c736c526e | ||
|
|
696d354f3c | ||
|
|
501cbf54ab | ||
|
|
c15d69d566 | ||
|
|
83eb933230 | ||
|
|
43bec9b0df | ||
|
|
279305c202 | ||
|
|
ecbb86c006 | ||
|
|
921fb17ef0 | ||
|
|
0362de7d35 | ||
|
|
cf0d8ea2d0 | ||
|
|
0e9026447d | ||
|
|
cf4d9cb03c | ||
|
|
2a1edeaca3 | ||
|
|
eebabf9b08 | ||
|
|
d882fcad12 | ||
|
|
f976290282 | ||
|
|
f3f9d5dae2 | ||
|
|
71dffd1089 | ||
|
|
f411e98c9a | ||
|
|
1baa489bc7 | ||
|
|
f996b0fc4a | ||
|
|
eb6ba1c800 | ||
|
|
8fac1baa3c | ||
|
|
2d327543b9 | ||
|
|
8a080efe9d | ||
|
|
aaaf61e29e | ||
|
|
1294323df5 | ||
|
|
9ef4e75746 | ||
|
|
ded4ab702d | ||
|
|
f893d045c7 | ||
|
|
ef2e3adcfb | ||
|
|
a1908c5398 | ||
|
|
ec4f0d6531 | ||
|
|
9ef366237c | ||
|
|
9197ad2600 | ||
|
|
2cb7ecd851 | ||
|
|
7d1ad4ce66 | ||
|
|
c274acc8f4 | ||
|
|
7a0d48dd7a | ||
|
|
9c6f9048e1 | ||
|
|
4ff4acfd7e | ||
|
|
d8e9f07721 | ||
|
|
662c5638dd | ||
|
|
02b830d034 | ||
|
|
3734529e5c | ||
|
|
715ec24389 | ||
|
|
5615b23548 | ||
|
|
ca2eeb48cf | ||
|
|
b063c0a80c | ||
|
|
ee8b5692bd | ||
|
|
7a749dca67 | ||
|
|
0378f26ee6 | ||
|
|
4d281273c1 | ||
|
|
e23e11e1a8 | ||
|
|
772835dd22 | ||
|
|
bbf2e57548 | ||
|
|
aecda130b6 | ||
|
|
1d48030e7c | ||
|
|
929ba2fa19 | ||
|
|
6fefa947ca | ||
|
|
072cd2e846 | ||
|
|
5d20cd0365 | ||
|
|
03ec055f66 | ||
|
|
4b6a5e5d49 | ||
|
|
6d21e38159 | ||
|
|
a47c8249bf | ||
|
|
59d7a672b3 | ||
|
|
564e95d36e | ||
|
|
9aee99be74 | ||
|
|
aecd8b503d | ||
|
|
fa65eeea35 | ||
|
|
fe43584c3d | ||
|
|
c255b13dfc | ||
|
|
917b204e5b | ||
|
|
6225fd79fc | ||
|
|
cbec567ef4 | ||
|
|
84b62242d3 | ||
|
|
f17f74c30b | ||
|
|
63b04d5a27 | ||
|
|
f02073a24c | ||
|
|
cb87d126de | ||
|
|
941f8002e5 | ||
|
|
90f4f5cd89 | ||
|
|
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 | ||
|
|
76eeb3d0f7 | ||
|
|
279caca502 | ||
|
|
1c6bdc7369 | ||
|
|
8705d3450a | ||
|
|
33277bf25f | ||
|
|
cbd2342208 | ||
|
|
831f87f62e | ||
|
|
06c9609587 | ||
|
|
9b43d31b75 | ||
|
|
23a3c7f960 | ||
|
|
81acfaa424 | ||
|
|
9de40292c4 | ||
|
|
c489a7ed75 | ||
|
|
6c26eb8333 | ||
|
|
6a57c5ed74 | ||
|
|
dce33aaabc | ||
|
|
b1db0ff498 | ||
|
|
a348b483c3 | ||
|
|
d7ce99526c | ||
|
|
90f0cb862a | ||
|
|
ff0c7f6a50 | ||
|
|
d608b171de | ||
|
|
5e76488276 | ||
|
|
bc265c09ff | ||
|
|
c7fe828252 | ||
|
|
9fbf67d451 | ||
|
|
e5572d5833 | ||
|
|
6b647e4cf3 | ||
|
|
4661dc3124 | ||
|
|
e428346d3b | ||
|
|
59af264463 | ||
|
|
01c60a2faa | ||
|
|
0f880143e3 | ||
|
|
bbba8b4b30 | ||
|
|
3a3a864bcb | ||
|
|
2bc0b3716a | ||
|
|
1e89e70e57 | ||
|
|
c160e7a945 | ||
|
|
d6f6938465 | ||
|
|
0d2a154622 | ||
|
|
142ea20eaf | ||
|
|
c9b910937c | ||
|
|
70c863c80c | ||
|
|
b90d206514 | ||
|
|
29ea484a26 | ||
|
|
8aa6e83673 | ||
|
|
aef8e32da3 | ||
|
|
be0f64a6ad | ||
|
|
3c88fabab3 | ||
|
|
ee85ee0660 | ||
|
|
f639cf1134 | ||
|
|
65549d4456 | ||
|
|
d194681981 | ||
|
|
5b5ddb251b | ||
|
|
5ce72e2056 | ||
|
|
ef2218a3d8 | ||
|
|
2745a3bfaa | ||
|
|
dd3fc3b0be | ||
|
|
957dfaef52 | ||
|
|
9e8015f475 | ||
|
|
38c8977dab | ||
|
|
fdf2bc6adf | ||
|
|
89344f92b3 | ||
|
|
8e8eeaf6c8 | ||
|
|
dcf2f1dfdf | ||
|
|
a7f183d40d | ||
|
|
114de52434 | ||
|
|
468cab6982 | ||
|
|
02f12d40f0 | ||
|
|
1da452aa92 | ||
|
|
2cf2adafd3 | ||
|
|
261e72cae4 | ||
|
|
1cc3ab30cf | ||
|
|
987f4f1afd | ||
|
|
9a1536484a | ||
|
|
5dd03cff69 | ||
|
|
a6d682fdee | ||
|
|
f8013c0ec0 | ||
|
|
d6fff29a72 | ||
|
|
4cf37af114 | ||
|
|
916edf9415 | ||
|
|
dba5a44c60 | ||
|
|
c20a43433d |
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:
|
### Before submitting an issue to GitBucket I have first:
|
||||||
|
|
||||||
- [] read the [contribution guidelines](https://github.com/gitbucket/gitbucket/blob/master/.github/CONTRIBUTING.md)
|
- [ ] read the [contribution guidelines](https://github.com/gitbucket/gitbucket/blob/master/.github/CONTRIBUTING.md)
|
||||||
- [] searched for similar already existing issue
|
- [ ] searched for similar already existing issue
|
||||||
- [] read the documentation and [wiki](https://github.com/gitbucket/gitbucket/wiki)
|
- [ ] 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)*
|
*(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:
|
### Before submitting a pull-request to GitBucket I have first:
|
||||||
|
|
||||||
- [] read the [contribution guidelines](https://github.com/gitbucket/gitbucket/blob/master/.github/CONTRIBUTING.md)
|
- [ ] read the [contribution guidelines](https://github.com/gitbucket/gitbucket/blob/master/.github/CONTRIBUTING.md)
|
||||||
- [] rebased my branch over master
|
- [ ] rebased my branch over master
|
||||||
- [] verified that project is compiling
|
- [ ] verified that project is compiling
|
||||||
- [] verified that tests are passing
|
- [ ] verified that tests are passing
|
||||||
- [] squashed my commits as appropriate *(keep several commits if it is relevant to understand the PR)*
|
- [ ] 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
|
- [ ] [marked as closed using commit message](https://help.github.com/articles/closing-issues-via-commit-messages/) all issue ID that this PR should correct
|
||||||
|
|||||||
29
.github/workflows/build.yml
vendored
Normal file
29
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
name: build
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ master ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ master ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
java: [8, 11]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Set up JDK
|
||||||
|
uses: actions/setup-java@v1
|
||||||
|
with:
|
||||||
|
java-version: ${{ matrix.java }}
|
||||||
|
- name: Run tests
|
||||||
|
run: sbt scalafmtSbtCheck scalafmtCheck test:scalafmtCheck test
|
||||||
|
- name: Build executable
|
||||||
|
run: sbt executable
|
||||||
|
- name: Upload artifacts
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: gitbucket-java${{ matrix.java }}-${{ github.sha }}
|
||||||
|
path: ./target/executable/gitbucket.*
|
||||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -24,3 +24,8 @@ project/plugins/project/
|
|||||||
.idea/
|
.idea/
|
||||||
.idea_modules/
|
.idea_modules/
|
||||||
*.iml
|
*.iml
|
||||||
|
|
||||||
|
# Metals specific
|
||||||
|
.metals
|
||||||
|
.bloop
|
||||||
|
project/metals.sbt
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
version = "1.5.1"
|
||||||
project.git = true
|
project.git = true
|
||||||
|
|
||||||
maxColumn = 120
|
maxColumn = 120
|
||||||
|
|||||||
21
.travis.yml
21
.travis.yml
@@ -1,21 +0,0 @@
|
|||||||
language: scala
|
|
||||||
sudo: true
|
|
||||||
jdk:
|
|
||||||
- oraclejdk8
|
|
||||||
- oraclejdk11
|
|
||||||
- openjdk8
|
|
||||||
- openjdk11
|
|
||||||
script:
|
|
||||||
- sbt scalafmtSbtCheck scalafmtCheck test:scalafmtCheck test
|
|
||||||
before_script:
|
|
||||||
- sudo /etc/init.d/mysql stop
|
|
||||||
- sudo /etc/init.d/postgresql stop
|
|
||||||
- sudo chmod +x /usr/local/bin/sbt
|
|
||||||
cache:
|
|
||||||
directories:
|
|
||||||
- $HOME/.ivy2/cache
|
|
||||||
- $HOME/.sbt/boot
|
|
||||||
- $HOME/.sbt/launchers
|
|
||||||
- $HOME/.coursier
|
|
||||||
- $HOME/.embedmysql
|
|
||||||
- $HOME/.embedpostgresql
|
|
||||||
34
CHANGELOG.md
34
CHANGELOG.md
@@ -1,7 +1,39 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
All changes to the project will be documented in this file.
|
All changes to the project will be documented in this file.
|
||||||
|
|
||||||
## 4.30.0 - xx Dec 2018
|
### 4.33.0 - 31 Dec 2019
|
||||||
|
|
||||||
|
- All CLI options are configurable by environment variables
|
||||||
|
- Folding pull request files
|
||||||
|
- WebHook security options
|
||||||
|
- Add assignee and assignees 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
|
||||||
|
- OAuth2 Token (sent as a parameter) authentication support and new APIs in Web API
|
||||||
|
- OGP (Open Graph protocol) support
|
||||||
|
- Username completion with avatars
|
||||||
|
|
||||||
|
### 4.30.1 - 22 Dec 2018
|
||||||
|
- Bug fix for several WebHooks and Web API
|
||||||
|
|
||||||
|
## 4.30.0 - 15 Dec 2018
|
||||||
- Automatic ChangeLog Summary generation for new Releases
|
- Automatic ChangeLog Summary generation for new Releases
|
||||||
- A lot of GitBucket Web API updates to increase compatibility with the GitHub API.
|
- A lot of GitBucket Web API updates to increase compatibility with the GitHub API.
|
||||||
- Display of checkboxes in Markdown files in Git repositories
|
- Display of checkboxes in Markdown files in Git repositories
|
||||||
|
|||||||
32
README.md
32
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://github.com/gitbucket/gitbucket/actions?query=workflow%3Abuild+branch%3Amaster) [](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:
|
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
|
- Account and group management with LDAP integration
|
||||||
- a Plug-in system
|
- 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
|
Installation
|
||||||
--------
|
--------
|
||||||
@@ -31,19 +31,6 @@ GitBucket requires **Java8**. You have to install it, if it is not already insta
|
|||||||
1. Download the latest **gitbucket.war** from [the releases page](https://github.com/gitbucket/gitbucket/releases) and run it by `java -jar gitbucket.war`.
|
1. Download the latest **gitbucket.war** from [the releases page](https://github.com/gitbucket/gitbucket/releases) and run it by `java -jar gitbucket.war`.
|
||||||
2. Go to `http://[hostname]:8080/` and log in with ID: **root** / Pass: **root**.
|
2. Go to `http://[hostname]:8080/` and log in with ID: **root** / Pass: **root**.
|
||||||
|
|
||||||
You can specify following options:
|
|
||||||
|
|
||||||
- `--port=[NUMBER]`
|
|
||||||
- `--prefix=[CONTEXTPATH]`
|
|
||||||
- `--host=[HOSTNAME]`
|
|
||||||
- `--gitbucket.home=[DATA_DIR]`
|
|
||||||
- `--temp_dir=[TEMP_DIR]`
|
|
||||||
- `--max_file_size=[MAX_FILE_SIZE]`
|
|
||||||
|
|
||||||
`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.
|
|
||||||
|
|
||||||
You can also deploy `gitbucket.war` to a servlet container which supports Servlet 3.0 (like Jetty, Tomcat, JBoss, etc)
|
You can also deploy `gitbucket.war` to a servlet container which supports Servlet 3.0 (like Jetty, Tomcat, JBoss, etc)
|
||||||
|
|
||||||
For more information about installation on Mac or Windows Server (with IIS), or configuration of Apache or Nginx and also integration with other tools or services such as Jenkins or Slack, see [Wiki](https://github.com/gitbucket/gitbucket/wiki).
|
For more information about installation on Mac or Windows Server (with IIS), or configuration of Apache or Nginx and also integration with other tools or services such as Jenkins or Slack, see [Wiki](https://github.com/gitbucket/gitbucket/wiki).
|
||||||
@@ -68,14 +55,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.
|
- 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.
|
- 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.30.x
|
What's New in 4.33.x
|
||||||
-------------
|
-------------
|
||||||
### 4.30.0 - xx Dec 2018
|
### 4.33.0 - 31 Dec 2019
|
||||||
- Automatic ChangeLog Summary generation for new Releases
|
|
||||||
- A lot of GitBucket Web API updates to increase compatibility with the GitHub API.
|
- All CLI options are configurable by environment variables
|
||||||
- Display of checkboxes in Markdown files in Git repositories
|
- Folding pull request files
|
||||||
- A new extension point for plugins: anonymousAccessiblePaths
|
- WebHook security options
|
||||||
- Group support in the Gist Plugin
|
- Add assignee and assignees properties to some Web APIs' response
|
||||||
- Allow redirection to the Release Page from the Activity Timeline Page
|
|
||||||
|
|
||||||
See the [change log](CHANGELOG.md) for all of the updates.
|
See the [change log](CHANGELOG.md) for all of the updates.
|
||||||
|
|||||||
82
build.sbt
82
build.sbt
@@ -3,10 +3,10 @@ import com.typesafe.sbt.pgp.PgpKeys._
|
|||||||
|
|
||||||
val Organization = "io.github.gitbucket"
|
val Organization = "io.github.gitbucket"
|
||||||
val Name = "gitbucket"
|
val Name = "gitbucket"
|
||||||
val GitBucketVersion = "4.30.0"
|
val GitBucketVersion = "4.33.0"
|
||||||
val ScalatraVersion = "2.6.3"
|
val ScalatraVersion = "2.7.0-RC1"
|
||||||
val JettyVersion = "9.4.14.v20181114"
|
val JettyVersion = "9.4.30.v20200611"
|
||||||
val JgitVersion = "5.1.3.201810200350-r"
|
val JgitVersion = "5.8.0.202006091008-r"
|
||||||
|
|
||||||
lazy val root = (project in file("."))
|
lazy val root = (project in file("."))
|
||||||
.enablePlugins(SbtTwirl, ScalatraPlugin)
|
.enablePlugins(SbtTwirl, ScalatraPlugin)
|
||||||
@@ -17,17 +17,17 @@ sourcesInBase := false
|
|||||||
organization := Organization
|
organization := Organization
|
||||||
name := Name
|
name := Name
|
||||||
version := GitBucketVersion
|
version := GitBucketVersion
|
||||||
scalaVersion := "2.12.8"
|
scalaVersion := "2.13.1"
|
||||||
|
|
||||||
scalafmtOnCompile := true
|
scalafmtOnCompile := true
|
||||||
|
|
||||||
|
coverageExcludedPackages := ".*\\.html\\..*"
|
||||||
|
|
||||||
// dependency settings
|
// dependency settings
|
||||||
resolvers ++= Seq(
|
resolvers ++= Seq(
|
||||||
Classpaths.typesafeReleases,
|
Classpaths.typesafeReleases,
|
||||||
Resolver.jcenterRepo,
|
Resolver.jcenterRepo,
|
||||||
"amateras" at "http://amateras.sourceforge.jp/mvn/",
|
"sonatype-snapshot" at "https://oss.sonatype.org/content/repositories/snapshots/"
|
||||||
"sonatype-snapshot" at "https://oss.sonatype.org/content/repositories/snapshots/",
|
|
||||||
"amateras-snapshot" at "http://amateras.sourceforge.jp/mvn-snapshot/"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
libraryDependencies ++= Seq(
|
libraryDependencies ++= Seq(
|
||||||
@@ -36,45 +36,47 @@ libraryDependencies ++= Seq(
|
|||||||
"org.scalatra" %% "scalatra" % ScalatraVersion,
|
"org.scalatra" %% "scalatra" % ScalatraVersion,
|
||||||
"org.scalatra" %% "scalatra-json" % ScalatraVersion,
|
"org.scalatra" %% "scalatra-json" % ScalatraVersion,
|
||||||
"org.scalatra" %% "scalatra-forms" % ScalatraVersion,
|
"org.scalatra" %% "scalatra-forms" % ScalatraVersion,
|
||||||
"org.json4s" %% "json4s-jackson" % "3.6.2",
|
"org.json4s" %% "json4s-jackson" % "3.6.9",
|
||||||
"commons-io" % "commons-io" % "2.6",
|
"commons-io" % "commons-io" % "2.7",
|
||||||
"io.github.gitbucket" % "solidbase" % "1.0.3",
|
"io.github.gitbucket" % "solidbase" % "1.0.3",
|
||||||
"io.github.gitbucket" % "markedj" % "1.0.15",
|
"io.github.gitbucket" % "markedj" % "1.0.16",
|
||||||
"org.apache.commons" % "commons-compress" % "1.18",
|
"org.apache.commons" % "commons-compress" % "1.20",
|
||||||
"org.apache.commons" % "commons-email" % "1.5",
|
"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.12",
|
||||||
"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.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",
|
"org.apache.tika" % "tika-core" % "1.24.1",
|
||||||
"com.github.takezoe" %% "blocking-slick-32" % "0.0.11",
|
"com.github.takezoe" %% "blocking-slick-32" % "0.0.12",
|
||||||
"com.novell.ldap" % "jldap" % "2009-10-07",
|
"com.novell.ldap" % "jldap" % "2009-10-07",
|
||||||
"com.h2database" % "h2" % "1.4.197",
|
"com.h2database" % "h2" % "1.4.199",
|
||||||
"org.mariadb.jdbc" % "mariadb-java-client" % "2.3.0",
|
"org.mariadb.jdbc" % "mariadb-java-client" % "2.6.0",
|
||||||
"org.postgresql" % "postgresql" % "42.2.5",
|
"org.postgresql" % "postgresql" % "42.2.6",
|
||||||
"ch.qos.logback" % "logback-classic" % "1.2.3",
|
"ch.qos.logback" % "logback-classic" % "1.2.3",
|
||||||
"com.zaxxer" % "HikariCP" % "3.2.0",
|
"com.zaxxer" % "HikariCP" % "3.4.5",
|
||||||
"com.typesafe" % "config" % "1.3.3",
|
"com.typesafe" % "config" % "1.4.0",
|
||||||
"com.typesafe.akka" %% "akka-actor" % "2.5.18",
|
"com.typesafe.akka" %% "akka-actor" % "2.5.27",
|
||||||
"fr.brouillard.oss.security.xhub" % "xhub4j-core" % "1.0.0",
|
"fr.brouillard.oss.security.xhub" % "xhub4j-core" % "1.1.0",
|
||||||
"com.github.bkromhout" % "java-diff-utils" % "2.1.1",
|
"com.github.bkromhout" % "java-diff-utils" % "2.1.1",
|
||||||
"org.cache2k" % "cache2k-all" % "1.2.0.Final",
|
"org.cache2k" % "cache2k-all" % "1.2.4.Final",
|
||||||
"com.enragedginger" %% "akka-quartz-scheduler" % "1.7.0-akka-2.5.x" exclude ("c3p0", "c3p0") exclude ("com.zaxxer", "HikariCP-java6"),
|
"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",
|
"net.coobird" % "thumbnailator" % "0.4.11",
|
||||||
"com.github.zafarkhaja" % "java-semver" % "0.9.0",
|
"com.github.zafarkhaja" % "java-semver" % "0.9.0",
|
||||||
"com.nimbusds" % "oauth2-oidc-sdk" % "5.64.4",
|
"com.nimbusds" % "oauth2-oidc-sdk" % "5.64.4",
|
||||||
"org.eclipse.jetty" % "jetty-webapp" % JettyVersion % "provided",
|
"org.eclipse.jetty" % "jetty-webapp" % JettyVersion % "provided",
|
||||||
"javax.servlet" % "javax.servlet-api" % "3.1.0" % "provided",
|
"javax.servlet" % "javax.servlet-api" % "3.1.0" % "provided",
|
||||||
"junit" % "junit" % "4.12" % "test",
|
"junit" % "junit" % "4.13" % "test",
|
||||||
"org.scalatra" %% "scalatra-scalatest" % ScalatraVersion % "test",
|
"org.scalatra" %% "scalatra-scalatest" % ScalatraVersion % "test",
|
||||||
"org.mockito" % "mockito-core" % "2.23.4" % "test",
|
"org.mockito" % "mockito-core" % "3.3.3" % "test",
|
||||||
"com.wix" % "wix-embedded-mysql" % "4.2.0" % "test",
|
"com.dimafeng" %% "testcontainers-scala" % "0.37.0" % "test",
|
||||||
"ru.yandex.qatools.embed" % "postgresql-embedded" % "2.10" % "test",
|
"org.testcontainers" % "mysql" % "1.14.3" % "test",
|
||||||
|
"org.testcontainers" % "postgresql" % "1.14.3" % "test",
|
||||||
"net.i2p.crypto" % "eddsa" % "0.3.0",
|
"net.i2p.crypto" % "eddsa" % "0.3.0",
|
||||||
"is.tagomor.woothee" % "woothee-java" % "1.8.0",
|
"is.tagomor.woothee" % "woothee-java" % "1.11.0",
|
||||||
"org.ec4j.core" % "ec4j-core" % "0.0.3"
|
"org.ec4j.core" % "ec4j-core" % "0.0.3"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Compiler settings
|
// 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")
|
javacOptions in compile ++= Seq("-target", "8", "-source", "8")
|
||||||
javaOptions in Jetty += "-Dlogback.configurationFile=/logback-dev.xml"
|
javaOptions in Jetty += "-Dlogback.configurationFile=/logback-dev.xml"
|
||||||
|
|
||||||
@@ -162,8 +164,8 @@ executableKey := {
|
|||||||
plugins.foreach { plugin =>
|
plugins.foreach { plugin =>
|
||||||
plugin.trim.split(":") match {
|
plugin.trim.split(":") match {
|
||||||
case Array(pluginId, pluginVersion) =>
|
case Array(pluginId, pluginVersion) =>
|
||||||
val url = "https://plugins.gitbucket-community.org/releases/" +
|
val url = "https://github.com/" +
|
||||||
s"gitbucket-${pluginId}-plugin/gitbucket-${pluginId}-plugin-gitbucket_${version.value}-${pluginVersion}.jar"
|
s"gitbucket/gitbucket-${pluginId}-plugin/releases/download/${pluginVersion}/gitbucket-${pluginId}-plugin-${pluginVersion}.jar"
|
||||||
log info s"Download: ${url}"
|
log info s"Download: ${url}"
|
||||||
IO transfer (new java.net.URL(url).openStream, pluginsDir / url.substring(url.lastIndexOf("/") + 1))
|
IO transfer (new java.net.URL(url).openStream, pluginsDir / url.substring(url.lastIndexOf("/") + 1))
|
||||||
case _ => ()
|
case _ => ()
|
||||||
@@ -255,3 +257,17 @@ licenseOverrides := {
|
|||||||
case DepModuleInfo("com.github.bkromhout", "java-diff-utils", _) =>
|
case DepModuleInfo("com.github.bkromhout", "java-diff-utils", _) =>
|
||||||
LicenseInfo(LicenseCategory.Apache, "Apache-2.0", "http://www.apache.org/licenses/LICENSE-2.0")
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
How to run from the source tree
|
How to build and run from the source tree
|
||||||
========
|
========
|
||||||
|
|
||||||
Install [sbt](http://www.scala-sbt.org/index.html) at first.
|
First of all, Install [sbt](http://www.scala-sbt.org/index.html).
|
||||||
|
|
||||||
```
|
```shell
|
||||||
$ brew install sbt
|
$ brew install sbt
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -12,7 +12,7 @@ Run for Development
|
|||||||
|
|
||||||
If you want to test GitBucket, type the following command in the root directory of the source tree.
|
If you want to test GitBucket, type the following command in the root directory of the source tree.
|
||||||
|
|
||||||
```
|
```shell
|
||||||
$ sbt ~jetty:start
|
$ sbt ~jetty:start
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -25,15 +25,15 @@ Build war file
|
|||||||
|
|
||||||
To build war file, run the following command:
|
To build war file, run the following command:
|
||||||
|
|
||||||
```
|
```shell
|
||||||
$ sbt package
|
$ 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
|
To build an executable war file, run
|
||||||
|
|
||||||
```
|
```shell
|
||||||
$ sbt executable
|
$ sbt executable
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -41,8 +41,21 @@ at the top of the source tree. It generates executable `gitbucket.war` into `tar
|
|||||||
|
|
||||||
Run tests spec
|
Run tests spec
|
||||||
---------
|
---------
|
||||||
|
Before running tests, you need to install docker.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ brew cask install docker # Install Docker
|
||||||
|
$ open /Applications/Docker.app # Start Docker
|
||||||
|
```
|
||||||
|
|
||||||
To run the full series of tests, run the following command:
|
To run the full series of tests, run the following command:
|
||||||
|
|
||||||
```
|
```shell
|
||||||
$ sbt test
|
$ sbt test
|
||||||
```
|
```
|
||||||
|
|
||||||
|
If you don't have docker, you can skip docker tests which require docker as follows:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ sbt "testOnly * -- -l ExternalDBTest"
|
||||||
|
```
|
||||||
22
doc/debug.md
Normal file
22
doc/debug.md
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
Debug GitBucket on IntelliJ
|
||||||
|
========
|
||||||
|
Add following configuration for allowing remote debugging to `buils.sbt`:
|
||||||
|
|
||||||
|
```scala
|
||||||
|
javaOptions in Jetty ++= Seq(
|
||||||
|
"-Xdebug",
|
||||||
|
"-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8000"
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
Run GitBucket:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ sbt ~jetty:start
|
||||||
|
```
|
||||||
|
|
||||||
|
In IntelliJ, create remote debug configuration as follows. Make sure port number is same as above configuration.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Then you can start debugging on IntelliJ!
|
||||||
@@ -9,16 +9,17 @@ This directory has following structure:
|
|||||||
* /repositories
|
* /repositories
|
||||||
* /USER_NAME
|
* /USER_NAME
|
||||||
* /REPO_NAME.git (substance of repository. GitServlet sees this directory)
|
* /REPO_NAME.git (substance of repository. GitServlet sees this directory)
|
||||||
|
* /REPO_NAME.wiki.git (wiki repository)
|
||||||
* /REPO_NAME
|
* /REPO_NAME
|
||||||
* /issues (files which are attached to issue)
|
* /issues (files which are attached to issue)
|
||||||
* /REPO_NAME.wiki.git (wiki repository)
|
* /lfs (LFS managed files)
|
||||||
* /data
|
* /data
|
||||||
* /USER_NAME
|
* /USER_NAME
|
||||||
* /files
|
* /files
|
||||||
* avatar.xxx (image file of user avatar)
|
* avatar.xxx (image file of user avatar)
|
||||||
* /plugins
|
* /plugins
|
||||||
* /PLUGIN_NAME
|
* plugin.jar
|
||||||
* plugin.js
|
* /.installed (copied available plugins from the parent directory automatically)
|
||||||
* /tmp
|
* /tmp
|
||||||
* /_upload
|
* /_upload
|
||||||
* /SESSION_ID (removed at session timeout)
|
* /SESSION_ID (removed at session timeout)
|
||||||
|
|||||||
172
doc/licenses.md
172
doc/licenses.md
@@ -4,98 +4,128 @@ Category | License | Dependency | Notes
|
|||||||
--- | --- | --- | ---
|
--- | --- | --- | ---
|
||||||
Apache | [ Apache License, Version 2.0 ]( http://opensource.org/licenses/apache2.0.php ) | org.osgi # org.osgi.core # 4.3.1 | <notextile></notextile>
|
Apache | [ Apache License, Version 2.0 ]( http://opensource.org/licenses/apache2.0.php ) | org.osgi # org.osgi.core # 4.3.1 | <notextile></notextile>
|
||||||
Apache | [Apache 2](http://www.apache.org/licenses/LICENSE-2.0.txt) | com.googlecode.javaewah # JavaEWAH # 1.1.6 | <notextile></notextile>
|
Apache | [Apache 2](http://www.apache.org/licenses/LICENSE-2.0.txt) | com.googlecode.javaewah # JavaEWAH # 1.1.6 | <notextile></notextile>
|
||||||
Apache | [Apache 2](http://www.apache.org/licenses/LICENSE-2.0) | org.cache2k # cache2k-all # 1.0.0.CR1 | <notextile></notextile>
|
Apache | [Apache 2](http://www.apache.org/licenses/LICENSE-2.0) | org.cache2k # cache2k-all # 1.2.0.Final | <notextile></notextile>
|
||||||
Apache | [Apache 2](http://www.apache.org/licenses/LICENSE-2.0.txt) | org.objenesis # objenesis # 2.5 | <notextile></notextile>
|
Apache | [Apache 2](http://www.apache.org/licenses/LICENSE-2.0) | org.cache2k # cache2k-api # 1.2.0.Final | <notextile></notextile>
|
||||||
Apache | [Apache 2.0 License](http://www.apache.org/licenses/LICENSE-2.0) | org.apache.sshd # apache-sshd # 1.4.0 | <notextile></notextile>
|
Apache | [Apache 2](http://www.apache.org/licenses/LICENSE-2.0) | org.cache2k # cache2k-core # 1.2.0.Final | <notextile></notextile>
|
||||||
Apache | [Apache 2.0 License](http://www.apache.org/licenses/LICENSE-2.0) | org.apache.sshd # sshd-core # 1.4.0 | <notextile></notextile>
|
Apache | [Apache 2](http://www.apache.org/licenses/LICENSE-2.0.txt) | org.objenesis # objenesis # 2.6 | <notextile></notextile>
|
||||||
Apache | [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) | com.typesafe # config # 1.3.1 | <notextile></notextile>
|
Apache | [Apache 2.0 License](http://www.apache.org/licenses/LICENSE-2.0) | org.apache.sshd # apache-sshd # 2.1.0 | <notextile></notextile>
|
||||||
Apache | [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) | com.typesafe.akka # akka-actor_2.12 # 2.5.0 | <notextile></notextile>
|
Apache | [Apache 2.0 License](http://www.apache.org/licenses/LICENSE-2.0) | org.apache.sshd # sshd-cli # 2.1.0 | <notextile></notextile>
|
||||||
Apache | [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) | commons-io # commons-io # 2.5 | <notextile></notextile>
|
Apache | [Apache 2.0 License](http://www.apache.org/licenses/LICENSE-2.0) | org.apache.sshd # sshd-common # 2.1.0 | <notextile></notextile>
|
||||||
Apache | [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) | fr.brouillard.oss.security.xhub # xhub4j-core # 1.0.0 | <notextile></notextile>
|
Apache | [Apache 2.0 License](http://www.apache.org/licenses/LICENSE-2.0) | org.apache.sshd # sshd-core # 2.1.0 | <notextile></notextile>
|
||||||
Apache | [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) | org.apache.commons # commons-compress # 1.13 | <notextile></notextile>
|
Apache | [Apache 2.0 License](http://www.apache.org/licenses/LICENSE-2.0) | org.apache.sshd # sshd-putty # 2.1.0 | <notextile></notextile>
|
||||||
Apache | [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) | org.apache.commons # commons-email # 1.4 | <notextile></notextile>
|
Apache | [Apache 2.0 License](http://www.apache.org/licenses/LICENSE-2.0) | org.apache.sshd # sshd-scp # 2.1.0 | <notextile></notextile>
|
||||||
Apache | [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) | org.apache.commons # commons-lang3 # 3.5 | <notextile></notextile>
|
Apache | [Apache 2.0 License](http://www.apache.org/licenses/LICENSE-2.0) | org.apache.sshd # sshd-sftp # 2.1.0 | <notextile></notextile>
|
||||||
Apache | [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) | org.apache.httpcomponents # httpclient # 4.5.3 | <notextile></notextile>
|
Apache | [Apache License v2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) | net.java.dev.jna # jna # 4.5.1 | <notextile></notextile>
|
||||||
Apache | [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) | org.apache.httpcomponents # httpcore # 4.4.6 | <notextile></notextile>
|
Apache | [Apache License v2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) | net.java.dev.jna # jna-platform # 4.5.1 | <notextile></notextile>
|
||||||
Apache | [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) | org.apache.httpcomponents # httpmime # 4.5.2 | <notextile></notextile>
|
Apache | [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) | com.github.stephenc.jcip # jcip-annotations # 1.0-1 | <notextile></notextile>
|
||||||
Apache | [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) | org.apache.tika # tika-core # 1.14 | <notextile></notextile>
|
Apache | [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) | com.kohlschutter.junixsocket # junixsocket-common # 2.0.4 | <notextile></notextile>
|
||||||
Apache | [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) | org.liquibase # liquibase-core # 3.4.1 | <notextile></notextile>
|
Apache | [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) | com.kohlschutter.junixsocket # junixsocket-native-common # 2.0.4 | <notextile></notextile>
|
||||||
Apache | [Apache Software License - Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) | org.eclipse.jetty # jetty-http # 9.2.19.v20160908 | <notextile></notextile>
|
Apache | [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0) | com.typesafe # config # 1.3.3 | <notextile></notextile>
|
||||||
Apache | [Apache Software License - Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) | org.eclipse.jetty # jetty-io # 9.2.19.v20160908 | <notextile></notextile>
|
Apache | [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) | com.typesafe.akka # akka-actor_2.12 # 2.5.18 | <notextile></notextile>
|
||||||
Apache | [Apache Software License - Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) | org.eclipse.jetty # jetty-security # 9.2.19.v20160908 | <notextile></notextile>
|
Apache | [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) | commons-io # commons-io # 2.6 | <notextile></notextile>
|
||||||
Apache | [Apache Software License - Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) | org.eclipse.jetty # jetty-server # 9.2.19.v20160908 | <notextile></notextile>
|
Apache | [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) | is.tagomor.woothee # woothee-java # 1.8.0 | <notextile></notextile>
|
||||||
Apache | [Apache Software License - Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) | org.eclipse.jetty # jetty-servlet # 9.2.19.v20160908 | <notextile></notextile>
|
Apache | [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) | org.apache.commons # commons-compress # 1.18 | <notextile></notextile>
|
||||||
Apache | [Apache Software License - Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) | org.eclipse.jetty # jetty-util # 9.2.19.v20160908 | <notextile></notextile>
|
Apache | [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) | org.apache.commons # commons-email # 1.5 | <notextile></notextile>
|
||||||
Apache | [Apache Software License - Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) | org.eclipse.jetty # jetty-webapp # 9.2.19.v20160908 | <notextile></notextile>
|
Apache | [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) | org.apache.commons # commons-lang3 # 3.6 | <notextile></notextile>
|
||||||
Apache | [Apache Software License - Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) | org.eclipse.jetty # jetty-xml # 9.2.19.v20160908 | <notextile></notextile>
|
Apache | [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) | org.apache.httpcomponents # httpclient # 4.5.6 | <notextile></notextile>
|
||||||
Apache | [Apache Software License, Version 1.1](http://www.apache.org/licenses/LICENSE-1.1) | org.bouncycastle # bcpg-jdk15on # 1.56 | <notextile></notextile>
|
Apache | [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) | org.apache.httpcomponents # httpcore # 4.4.10 | <notextile></notextile>
|
||||||
|
Apache | [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) | org.apache.httpcomponents # httpmime # 4.5.3 | <notextile></notextile>
|
||||||
|
Apache | [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) | org.apache.tika # tika-core # 1.19.1 | <notextile></notextile>
|
||||||
|
Apache | [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) | org.ec4j.core # ec4j-core # 0.0.3 | <notextile></notextile>
|
||||||
|
Apache | [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) | org.liquibase # liquibase-core # 3.6.2 | <notextile></notextile>
|
||||||
|
Apache | [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) | org.yaml # snakeyaml # 1.18 | <notextile></notextile>
|
||||||
|
Apache | [Apache License, version 2.0](http://www.apache.org/licenses/LICENSE-2.0.html) | com.nimbusds # oauth2-oidc-sdk # 5.64.4 | <notextile></notextile>
|
||||||
|
Apache | [Apache Software License - Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) | org.eclipse.jetty # jetty-http # 9.4.6.v20170531 | <notextile></notextile>
|
||||||
|
Apache | [Apache Software License - Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) | org.eclipse.jetty # jetty-io # 9.4.6.v20170531 | <notextile></notextile>
|
||||||
|
Apache | [Apache Software License - Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) | org.eclipse.jetty # jetty-security # 9.4.6.v20170531 | <notextile></notextile>
|
||||||
|
Apache | [Apache Software License - Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) | org.eclipse.jetty # jetty-server # 9.4.6.v20170531 | <notextile></notextile>
|
||||||
|
Apache | [Apache Software License - Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) | org.eclipse.jetty # jetty-servlet # 9.4.6.v20170531 | <notextile></notextile>
|
||||||
|
Apache | [Apache Software License - Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) | org.eclipse.jetty # jetty-util # 9.4.6.v20170531 | <notextile></notextile>
|
||||||
|
Apache | [Apache Software License - Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) | org.eclipse.jetty # jetty-webapp # 9.4.6.v20170531 | <notextile></notextile>
|
||||||
|
Apache | [Apache Software License - Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) | org.eclipse.jetty # jetty-xml # 9.4.6.v20170531 | <notextile></notextile>
|
||||||
|
Apache | [Apache Software License, Version 1.1](http://www.apache.org/licenses/LICENSE-1.1) | org.bouncycastle # bcpg-jdk15on # 1.60 | <notextile></notextile>
|
||||||
Apache | [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0) | com.github.bkromhout # java-diff-utils # 2.1.1 | <notextile></notextile>
|
Apache | [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0) | com.github.bkromhout # java-diff-utils # 2.1.1 | <notextile></notextile>
|
||||||
Apache | [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.html) | com.typesafe.play # twirl-api_2.12 # 1.3.7 | <notextile></notextile>
|
Apache | [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.html) | com.typesafe.play # twirl-api_2.12 # 1.3.15 | <notextile></notextile>
|
||||||
Apache | [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0) | org.json4s # json4s-ast_2.12 # 3.5.1 | <notextile></notextile>
|
Apache | [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0) | org.json4s # json4s-ast_2.12 # 3.5.2 | <notextile></notextile>
|
||||||
Apache | [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0) | org.json4s # json4s-core_2.12 # 3.5.1 | <notextile></notextile>
|
Apache | [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0) | org.json4s # json4s-core_2.12 # 3.5.2 | <notextile></notextile>
|
||||||
Apache | [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0) | org.json4s # json4s-jackson_2.12 # 3.5.1 | <notextile></notextile>
|
Apache | [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0) | org.json4s # json4s-jackson_2.12 # 3.5.2 | <notextile></notextile>
|
||||||
Apache | [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0) | org.json4s # json4s-scalap_2.12 # 3.5.1 | <notextile></notextile>
|
Apache | [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0) | org.json4s # json4s-scalap_2.12 # 3.5.2 | <notextile></notextile>
|
||||||
Apache | [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) | com.enragedginger # akka-quartz-scheduler_2.12 # 1.6.0-akka-2.4.x | <notextile></notextile>
|
Apache | [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0) | org.scala-lang # scala-library # 2.12.8 | <notextile></notextile>
|
||||||
|
Apache | [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0) | org.scala-lang # scala-reflect # 2.12.8 | <notextile></notextile>
|
||||||
|
Apache | [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) | com.enragedginger # akka-quartz-scheduler_2.12 # 1.7.0-akka-2.5.x | <notextile></notextile>
|
||||||
Apache | [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) | com.fasterxml.jackson.core # jackson-annotations # 2.8.0 | <notextile></notextile>
|
Apache | [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) | com.fasterxml.jackson.core # jackson-annotations # 2.8.0 | <notextile></notextile>
|
||||||
Apache | [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) | com.fasterxml.jackson.core # jackson-core # 2.8.4 | <notextile></notextile>
|
Apache | [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) | com.fasterxml.jackson.core # jackson-core # 2.8.4 | <notextile></notextile>
|
||||||
Apache | [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) | com.fasterxml.jackson.core # jackson-databind # 2.8.4 | <notextile></notextile>
|
Apache | [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) | com.fasterxml.jackson.core # jackson-databind # 2.8.4 | <notextile></notextile>
|
||||||
Apache | [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) | com.github.takezoe # blocking-slick-32_2.12 # 0.0.10 | <notextile></notextile>
|
Apache | [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) | com.github.takezoe # blocking-slick-32_2.12 # 0.0.11 | <notextile></notextile>
|
||||||
Apache | [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) | com.google.code.findbugs # jsr305 # 3.0.0 | <notextile></notextile>
|
Apache | [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) | com.google.code.findbugs # jsr305 # 3.0.0 | <notextile></notextile>
|
||||||
Apache | [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) | com.zaxxer # HikariCP # 2.6.1 | <notextile></notextile>
|
Apache | [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) | com.nimbusds # lang-tag # 1.4.3 | <notextile></notextile>
|
||||||
Apache | [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) | commons-codec # commons-codec # 1.9 | <notextile></notextile>
|
Apache | [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) | com.nimbusds # nimbus-jose-jwt # 5.5 | <notextile></notextile>
|
||||||
Apache | [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) | commons-logging # commons-logging # 1.2 | <notextile></notextile>
|
Apache | [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) | com.zaxxer # HikariCP # 3.2.0 | <notextile></notextile>
|
||||||
Apache | [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) | de.flapdoodle.embed # de.flapdoodle.embed.process # 2.0.1 | <notextile></notextile>
|
|
||||||
Apache | [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) | eu.medsea.mimeutil # mime-util # 2.1.3 | <notextile></notextile>
|
Apache | [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) | eu.medsea.mimeutil # mime-util # 2.1.3 | <notextile></notextile>
|
||||||
Apache | [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) | io.github.gitbucket # markedj # 1.0.15 | <notextile></notextile>
|
Apache | [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) | io.github.gitbucket # markedj # 1.0.15 | <notextile></notextile>
|
||||||
Apache | [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) | io.github.gitbucket # scalatra-forms_2.12 # 1.1.0 | <notextile></notextile>
|
Apache | [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) | io.github.gitbucket # solidbase # 1.0.3 | <notextile></notextile>
|
||||||
Apache | [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) | io.github.gitbucket # solidbase # 1.0.2 | <notextile></notextile>
|
Apache | [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) | net.bytebuddy # byte-buddy # 1.9.3 | <notextile></notextile>
|
||||||
Apache | [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) | net.bytebuddy # byte-buddy # 1.6.11 | <notextile></notextile>
|
Apache | [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) | net.bytebuddy # byte-buddy-agent # 1.9.3 | <notextile></notextile>
|
||||||
Apache | [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) | net.bytebuddy # byte-buddy-agent # 1.6.11 | <notextile></notextile>
|
Apache | [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) | net.minidev # accessors-smart # 1.2 | <notextile></notextile>
|
||||||
Apache | [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) | org.quartz-scheduler # quartz # 2.2.3 | <notextile></notextile>
|
Apache | [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) | net.minidev # json-smart # 2.3 | <notextile></notextile>
|
||||||
Apache | [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) | ru.yandex.qatools.embed # postgresql-embedded # 2.0 | <notextile></notextile>
|
Apache | [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) | org.jetbrains # annotations # 15.0 | <notextile></notextile>
|
||||||
Apache | [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) | tomcat # tomcat-apr # 5.5.23 | <notextile></notextile>
|
Apache | [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) | org.quartz-scheduler # quartz # 2.3.0 | <notextile></notextile>
|
||||||
Apache | [the Apache License, ASL Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) | org.scalactic # scalactic_2.12 # 3.0.0 | <notextile></notextile>
|
Apache | [the Apache License, ASL Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) | org.scalactic # scalactic_2.12 # 3.0.3 | <notextile></notextile>
|
||||||
Apache | [the Apache License, ASL Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) | org.scalatest # scalatest_2.12 # 3.0.0 | <notextile></notextile>
|
Apache | [the Apache License, ASL Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) | org.scalatest # scalatest_2.12 # 3.0.3 | <notextile></notextile>
|
||||||
BSD | [BSD](LICENSE.txt) | com.thoughtworks.paranamer # paranamer # 2.8 | <notextile></notextile>
|
BSD | [BSD](http://github.com/scalatra/scalatra/raw/HEAD/LICENSE) | org.scalatra # scalatra-common_2.12 # 2.6.3 | <notextile></notextile>
|
||||||
BSD | [BSD](http://software.clapper.org/grizzled-slf4j/license.html) | org.clapper # grizzled-slf4j_2.12 # 1.3.0 | <notextile></notextile>
|
BSD | [BSD](http://github.com/scalatra/scalatra/raw/HEAD/LICENSE) | org.scalatra # scalatra-forms_2.12 # 2.6.3 | <notextile></notextile>
|
||||||
BSD | [BSD](http://github.com/scalatra/scalatra/raw/HEAD/LICENSE) | org.scalatra # scalatra-common_2.12 # 2.5.0 | <notextile></notextile>
|
BSD | [BSD](http://github.com/scalatra/scalatra/raw/HEAD/LICENSE) | org.scalatra # scalatra-json_2.12 # 2.6.3 | <notextile></notextile>
|
||||||
BSD | [BSD](http://github.com/scalatra/scalatra/raw/HEAD/LICENSE) | org.scalatra # scalatra-json_2.12 # 2.5.0 | <notextile></notextile>
|
BSD | [BSD](http://github.com/scalatra/scalatra/raw/HEAD/LICENSE) | org.scalatra # scalatra-scalatest_2.12 # 2.6.3 | <notextile></notextile>
|
||||||
BSD | [BSD](http://github.com/scalatra/scalatra/raw/HEAD/LICENSE) | org.scalatra # scalatra-scalatest_2.12 # 2.5.0 | <notextile></notextile>
|
BSD | [BSD](http://github.com/scalatra/scalatra/raw/HEAD/LICENSE) | org.scalatra # scalatra-test_2.12 # 2.6.3 | <notextile></notextile>
|
||||||
BSD | [BSD](http://github.com/scalatra/scalatra/raw/HEAD/LICENSE) | org.scalatra # scalatra-test_2.12 # 2.5.0 | <notextile></notextile>
|
BSD | [BSD](http://github.com/scalatra/scalatra/raw/HEAD/LICENSE) | org.scalatra # scalatra_2.12 # 2.6.3 | <notextile></notextile>
|
||||||
BSD | [BSD](http://github.com/scalatra/scalatra/raw/HEAD/LICENSE) | org.scalatra # scalatra_2.12 # 2.5.0 | <notextile></notextile>
|
|
||||||
BSD | [BSD 3-Clause](http://www.scala-lang.org/license.html) | org.scala-lang # scala-library # 2.12.3 | <notextile></notextile>
|
|
||||||
BSD | [BSD 3-Clause](http://www.scala-lang.org/license.html) | org.scala-lang # scala-reflect # 2.12.3 | <notextile></notextile>
|
|
||||||
BSD | [BSD 3-clause](http://opensource.org/licenses/BSD-3-Clause) | org.scala-lang.modules # scala-java8-compat_2.12 # 0.8.0 | <notextile></notextile>
|
BSD | [BSD 3-clause](http://opensource.org/licenses/BSD-3-Clause) | org.scala-lang.modules # scala-java8-compat_2.12 # 0.8.0 | <notextile></notextile>
|
||||||
BSD | [BSD 3-clause](http://opensource.org/licenses/BSD-3-Clause) | org.scala-lang.modules # scala-parser-combinators_2.12 # 1.0.4 | <notextile></notextile>
|
BSD | [BSD 3-clause](http://opensource.org/licenses/BSD-3-Clause) | org.scala-lang.modules # scala-parser-combinators_2.12 # 1.0.6 | <notextile></notextile>
|
||||||
BSD | [BSD 3-clause](http://opensource.org/licenses/BSD-3-Clause) | org.scala-lang.modules # scala-xml_2.12 # 1.0.6 | <notextile></notextile>
|
BSD | [BSD 3-clause](http://opensource.org/licenses/BSD-3-Clause) | org.scala-lang.modules # scala-xml_2.12 # 1.0.6 | <notextile></notextile>
|
||||||
BSD | [BSD License](http://www.opensource.org/licenses/bsd-license.php) | com.wix # wix-embedded-mysql # 2.1.4 | <notextile></notextile>
|
BSD | [BSD-2-Clause](https://jdbc.postgresql.org/about/license.html) | org.postgresql # postgresql # 42.2.5 | <notextile></notextile>
|
||||||
BSD | [BSD-2-Clause](https://jdbc.postgresql.org/about/license.html) | org.postgresql # postgresql # 42.0.0 | <notextile></notextile>
|
BSD | [Eclipse Distribution License (New BSD License)](null) | org.eclipse.jgit # org.eclipse.jgit # 5.2.0.201812061821-r | <notextile></notextile>
|
||||||
BSD | [Eclipse Distribution License (New BSD License)](null) | org.eclipse.jgit # org.eclipse.jgit # 4.8.0.201706111038-r | <notextile></notextile>
|
BSD | [Eclipse Distribution License (New BSD License)](null) | org.eclipse.jgit # org.eclipse.jgit.archive # 5.2.0.201812061821-r | <notextile></notextile>
|
||||||
BSD | [Eclipse Distribution License (New BSD License)](null) | org.eclipse.jgit # org.eclipse.jgit.archive # 4.8.0.201706111038-r | <notextile></notextile>
|
BSD | [Eclipse Distribution License (New BSD License)](null) | org.eclipse.jgit # org.eclipse.jgit.http.server # 5.2.0.201812061821-r | <notextile></notextile>
|
||||||
BSD | [Eclipse Distribution License (New BSD License)](null) | org.eclipse.jgit # org.eclipse.jgit.http.server # 4.8.0.201706111038-r | <notextile></notextile>
|
|
||||||
BSD | [New BSD License](http://www.opensource.org/licenses/bsd-license.php) | org.hamcrest # hamcrest-core # 1.3 | <notextile></notextile>
|
BSD | [New BSD License](http://www.opensource.org/licenses/bsd-license.php) | org.hamcrest # hamcrest-core # 1.3 | <notextile></notextile>
|
||||||
BSD | [Revised BSD](http://www.jcraft.com/jsch/LICENSE.txt) | com.jcraft # jsch # 0.1.54 | <notextile></notextile>
|
BSD | [Revised BSD](http://www.jcraft.com/jsch/LICENSE.txt) | com.jcraft # jsch # 0.1.54 | <notextile></notextile>
|
||||||
|
BSD | [Revised BSD](http://www.jcraft.com/jzlib/LICENSE.txt) | com.jcraft # jzlib # 1.1.1 | <notextile></notextile>
|
||||||
BSD | [Two-clause BSD-style license](http://github.com/slick/slick/blob/master/LICENSE.txt) | com.typesafe.slick # slick_2.12 # 3.2.1 | <notextile></notextile>
|
BSD | [Two-clause BSD-style license](http://github.com/slick/slick/blob/master/LICENSE.txt) | com.typesafe.slick # slick_2.12 # 3.2.1 | <notextile></notextile>
|
||||||
CC0 | [CC0](http://creativecommons.org/publicdomain/zero/1.0/) | org.reactivestreams # reactive-streams # 1.0.0 | <notextile></notextile>
|
CC0 | [CC0](http://creativecommons.org/publicdomain/zero/1.0/) | org.reactivestreams # reactive-streams # 1.0.0 | <notextile></notextile>
|
||||||
CDDL | [COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0](https://glassfish.dev.java.net/public/CDDLv1.0.html) | javax.activation # activation # 1.1.1 | <notextile></notextile>
|
CC0 | [CC0 1.0 Universal](https://creativecommons.org/publicdomain/zero/1.0/) | net.i2p.crypto # eddsa # 0.3.0 | <notextile></notextile>
|
||||||
GPL | [CDDL/GPLv2+CE](https://glassfish.java.net/public/CDDL+GPL_1_1.html) | com.sun.mail # javax.mail # 1.5.2 | <notextile></notextile>
|
CC0 | [CC0 1.0 Universal License](http://creativecommons.org/publicdomain/zero/1.0/) | org.scijava # native-lib-loader # 2.0.2 | <notextile></notextile>
|
||||||
|
CDDL | [Common Development and Distribution License (CDDL) v1.0](https://glassfish.dev.java.net/public/CDDLv1.0.html) | javax.activation # activation # 1.1 | <notextile></notextile>
|
||||||
|
GPL | [CDDL/GPLv2+CE](https://javaee.github.io/javamail/LICENSE) | com.sun.mail # javax.mail # 1.6.1 | <notextile></notextile>
|
||||||
|
GPL | [GPL2 w/ CPE](https://oss.oracle.com/licenses/CDDL+GPL-1.1) | javax.xml.bind # jaxb-api # 2.3.0 | <notextile></notextile>
|
||||||
|
GPL with Classpath Extension | [CDDL + GPLv2 with classpath exception](https://github.com/javaee/javax.annotation/blob/master/LICENSE) | javax.annotation # javax.annotation-api # 1.3.1 | <notextile></notextile>
|
||||||
GPL with Classpath Extension | [CDDL + GPLv2 with classpath exception](https://glassfish.dev.java.net/nonav/public/CDDL+GPL.html) | javax.servlet # javax.servlet-api # 3.1.0 | <notextile></notextile>
|
GPL with Classpath Extension | [CDDL + GPLv2 with classpath exception](https://glassfish.dev.java.net/nonav/public/CDDL+GPL.html) | javax.servlet # javax.servlet-api # 3.1.0 | <notextile></notextile>
|
||||||
LGPL | [GNU Lesser General Public License](http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html) | ch.qos.logback # logback-classic # 1.2.3 | <notextile></notextile>
|
LGPL | [GNU Lesser General Public License](http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html) | ch.qos.logback # logback-classic # 1.2.3 | <notextile></notextile>
|
||||||
LGPL | [GNU Lesser General Public License](http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html) | ch.qos.logback # logback-core # 1.2.3 | <notextile></notextile>
|
LGPL | [GNU Lesser General Public License](http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html) | ch.qos.logback # logback-core # 1.2.3 | <notextile></notextile>
|
||||||
LGPL | [LGPL, version 2.1](http://www.gnu.org/licenses/licenses.html) | net.java.dev.jna # jna # 4.0.0 | <notextile></notextile>
|
LGPL | [GNU Lesser General Public License, Version 2.1](http://www.gnu.org/licenses/lgpl-2.1.html) | com.mchange # c3p0 # 0.9.5.2 | <notextile></notextile>
|
||||||
LGPL | [LGPL, version 2.1](http://www.gnu.org/licenses/licenses.html) | net.java.dev.jna # jna-platform # 4.0.0 | <notextile></notextile>
|
LGPL | [GNU Lesser General Public License, Version 2.1](http://www.gnu.org/licenses/lgpl-2.1.html) | com.mchange # mchange-commons-java # 0.2.11 | <notextile></notextile>
|
||||||
LGPL | [LGPL-2.1](null) | org.mariadb.jdbc # mariadb-java-client # 2.0.3 | <notextile></notextile>
|
LGPL | [LGPL-2.1](null) | org.mariadb.jdbc # mariadb-java-client # 2.3.0 | <notextile></notextile>
|
||||||
|
MIT | [MIT](http://opensource.org/licenses/MIT) | org.rnorth # tcp-unix-socket-proxy # 1.0.2 | <notextile></notextile>
|
||||||
|
MIT | [MIT](http://opensource.org/licenses/MIT) | org.rnorth.duct-tape # duct-tape # 1.0.7 | <notextile></notextile>
|
||||||
|
MIT | [MIT](http://opensource.org/licenses/MIT) | org.rnorth.visible-assertions # visible-assertions # 2.1.1 | <notextile></notextile>
|
||||||
|
MIT | [MIT](http://opensource.org/licenses/MIT) | org.testcontainers # database-commons # 1.10.3 | <notextile></notextile>
|
||||||
|
MIT | [MIT](http://opensource.org/licenses/MIT) | org.testcontainers # jdbc # 1.10.3 | <notextile></notextile>
|
||||||
|
MIT | [MIT](http://opensource.org/licenses/MIT) | org.testcontainers # mysql # 1.10.3 | <notextile></notextile>
|
||||||
|
MIT | [MIT](http://opensource.org/licenses/MIT) | org.testcontainers # postgresql # 1.10.3 | <notextile></notextile>
|
||||||
|
MIT | [MIT](http://opensource.org/licenses/MIT) | org.testcontainers # testcontainers # 1.10.3 | <notextile></notextile>
|
||||||
MIT | [MIT License](http://www.opensource.org/licenses/mit-license.php) | org.slf4j # slf4j-api # 1.7.25 | <notextile></notextile>
|
MIT | [MIT License](http://www.opensource.org/licenses/mit-license.php) | org.slf4j # slf4j-api # 1.7.25 | <notextile></notextile>
|
||||||
MIT | [The MIT License](http://www.opensource.org/licenses/mit-license.php) | com.github.zafarkhaja # java-semver # 0.9.0 | <notextile></notextile>
|
MIT | [The MIT License](http://www.opensource.org/licenses/mit-license.php) | com.github.zafarkhaja # java-semver # 0.9.0 | <notextile></notextile>
|
||||||
MIT | [The MIT License](https://jsoup.org/license) | org.jsoup # jsoup # 1.10.2 | <notextile></notextile>
|
MIT | [The MIT License](https://jsoup.org/license) | org.jsoup # jsoup # 1.10.2 | <notextile></notextile>
|
||||||
MIT | [The MIT License](http://github.com/mockito/mockito/blob/master/LICENSE) | org.mockito # mockito-all # 1.10.19 | <notextile></notextile>
|
MIT | [The MIT License](https://github.com/mockito/mockito/blob/master/LICENSE) | org.mockito # mockito-core # 2.23.4 | <notextile></notextile>
|
||||||
MIT | [The MIT License](http://github.com/mockito/mockito/blob/master/LICENSE) | org.mockito # mockito-core # 2.7.22 | <notextile></notextile>
|
MIT | [The MIT License (MIT)](https://opensource.org/licenses/MIT) | com.dimafeng # testcontainers-scala_2.12 # 0.22.0 | <notextile></notextile>
|
||||||
MIT | [The MIT License (MIT)](http://www.opensource.org/licenses/mit-license.html) | net.coobird # thumbnailator # 0.4.8 | <notextile></notextile>
|
MIT | [The MIT License (MIT)](http://www.opensource.org/licenses/mit-license.html) | net.coobird # thumbnailator # 0.4.8 | <notextile></notextile>
|
||||||
Mozilla | [MPL 2.0 or EPL 1.0](http://h2database.com/html/license.html) | com.h2database # h2 # 1.4.195 | <notextile></notextile>
|
Mozilla | [MPL 2.0 or EPL 1.0](http://h2database.com/html/license.html) | com.h2database # h2 # 1.4.197 | <notextile></notextile>
|
||||||
Mozilla | [Mozilla Public License 1.1 (MPL 1.1)](http://www.mozilla.org/MPL/MPL-1.1.html) | com.googlecode.juniversalchardet # juniversalchardet # 1.0.3 | <notextile></notextile>
|
Mozilla | [Mozilla Public License 1.1 (MPL 1.1)](http://www.mozilla.org/MPL/MPL-1.1.html) | com.googlecode.juniversalchardet # juniversalchardet # 1.0.3 | <notextile></notextile>
|
||||||
Public Domain | [Public Domain](http://en.wikipedia.org/wiki/Public_domain) | net.i2p.crypto # eddsa # 0.1.0 | <notextile></notextile>
|
unrecognized | [Bouncy Castle Licence](http://www.bouncycastle.org/licence.html) | org.bouncycastle # bcpkix-jdk15on # 1.60 | <notextile></notextile>
|
||||||
unrecognized | [Bouncy Castle Licence](http://www.bouncycastle.org/licence.html) | org.bouncycastle # bcpkix-jdk15on # 1.56 | <notextile></notextile>
|
unrecognized | [Bouncy Castle Licence](http://www.bouncycastle.org/licence.html) | org.bouncycastle # bcprov-jdk15on # 1.60 | <notextile></notextile>
|
||||||
unrecognized | [Bouncy Castle Licence](http://www.bouncycastle.org/licence.html) | org.bouncycastle # bcprov-jdk15on # 1.56 | <notextile></notextile>
|
|
||||||
unrecognized | [Eclipse Public License 1.0](http://www.eclipse.org/legal/epl-v10.html) | junit # junit # 4.12 | <notextile></notextile>
|
unrecognized | [Eclipse Public License 1.0](http://www.eclipse.org/legal/epl-v10.html) | junit # junit # 4.12 | <notextile></notextile>
|
||||||
unrecognized | [The OpenLDAP Public License](http://www.openldap.org/software/release/license.html) | com.novell.ldap # jldap # 2009-10-07 | <notextile></notextile>
|
unrecognized | [The OpenLDAP Public License](http://www.openldap.org/software/release/license.html) | com.novell.ldap # jldap # 2009-10-07 | <notextile></notextile>
|
||||||
|
unrecognized | [none specified](none specified) | com.thoughtworks.paranamer # paranamer # 2.8 | <notextile></notextile>
|
||||||
|
unrecognized | [none specified](none specified) | commons-codec # commons-codec # 1.10 | <notextile></notextile>
|
||||||
|
unrecognized | [none specified](none specified) | commons-logging # commons-logging # 1.2 | <notextile></notextile>
|
||||||
|
unrecognized | [none specified](none specified) | fr.brouillard.oss.security.xhub # xhub4j-core # 1.0.0 | <notextile></notextile>
|
||||||
|
unrecognized | [none specified](none specified) | org.ow2.asm # asm # 5.0.4 | <notextile></notextile>
|
||||||
|
unrecognized | [none specified](none specified) | tomcat # tomcat-apr # 5.5.23 | <notextile></notextile>
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
Developer's Guide
|
Developer's Guide
|
||||||
========
|
========
|
||||||
* [How to run from source tree](how_to_run.md)
|
* [Build from source tree](build.md)
|
||||||
|
* [Debug on IntelliJ](debug.md)
|
||||||
* [Directory Structure](directory.md)
|
* [Directory Structure](directory.md)
|
||||||
* [Mapping and Validation](validation.md)
|
* [Mapping and Validation](validation.md)
|
||||||
* [Authentication in Controller](authenticator.md)
|
* [Authentication in Controller](authenticator.md)
|
||||||
|
|||||||
@@ -34,6 +34,20 @@ object GitBucketCoreModule extends Module("gitbucket-core",
|
|||||||
Generate release files
|
Generate release files
|
||||||
--------
|
--------
|
||||||
|
|
||||||
|
### Deploy assembly jar file
|
||||||
|
|
||||||
|
For plug-in development, we have to publish the GitBucket jar file to the Maven central repository before release GitBucket itself.
|
||||||
|
|
||||||
|
First, hit following command to publish artifacts to the sonatype OSS repository:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ sbt publishSigned
|
||||||
|
```
|
||||||
|
|
||||||
|
Then logged-in to https://oss.sonatype.org/, close and release the repository.
|
||||||
|
|
||||||
|
You need to wait up to a day until [gitbucket-notification-plugin](https://plugins.gitbucket-community.org/) which is default bundled plugin is built for new version of GitBucket.
|
||||||
|
|
||||||
### Make release war file
|
### Make release war file
|
||||||
|
|
||||||
Run `sbt executable`. The release war file and fingerprint are generated into `target/executable/gitbucket.war`.
|
Run `sbt executable`. The release war file and fingerprint are generated into `target/executable/gitbucket.war`.
|
||||||
@@ -42,20 +56,4 @@ Run `sbt executable`. The release war file and fingerprint are generated into `t
|
|||||||
$ sbt executable
|
$ sbt executable
|
||||||
```
|
```
|
||||||
|
|
||||||
### Deploy assembly jar file
|
Create new release from the corresponded tag on GitHub, then upload generated jar file and fingerprints to the release.
|
||||||
|
|
||||||
For plug-in development, we have to publish the GitBucket jar file to the Maven central repository as well. At first, hit following command to publish artifacts to the sonatype OSS repository:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ sbt publishSigned
|
|
||||||
```
|
|
||||||
|
|
||||||
Then logged-in https://oss.sonatype.org/ and delete following files from the staging repository:
|
|
||||||
|
|
||||||
- gitbucket_2.12-x.x.x.war
|
|
||||||
- gitbucket_2.12-x.x.x.war.asc
|
|
||||||
- gitbucket_2.12-x.x.x.war.asc.md5
|
|
||||||
- gitbucket_2.12-x.x.x.war.asc.sha1
|
|
||||||
- gitbucket_2.12-x.x.x.war.md5
|
|
||||||
|
|
||||||
At last, close and release the repository.
|
|
||||||
|
|||||||
BIN
doc/remote_debug.png
Normal file
BIN
doc/remote_debug.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 203 KiB |
@@ -1,11 +1,11 @@
|
|||||||
Mapping and Validation
|
Mapping and Validation
|
||||||
========
|
========
|
||||||
GitBucket uses [scalatra-forms](https://github.com/takezoe/scalatra-forms) to validate request parameters and map them to the scala object. This is inspired by Play2 form mapping / validation.
|
GitBucket uses [scalatra-forms](http://scalatra.org/guides/2.6/formats/forms.html) to validate request parameters and map them to the scala object. This is inspired by Play2 form mapping / validation.
|
||||||
|
|
||||||
At first, define the mapping as following:
|
At first, define the mapping as following:
|
||||||
|
|
||||||
```scala
|
```scala
|
||||||
import jp.sf.amateras.scalatra.forms._
|
import org.scalatra.forms._
|
||||||
|
|
||||||
case class RegisterForm(name: String, description: String)
|
case class RegisterForm(name: String, description: String)
|
||||||
|
|
||||||
@@ -15,17 +15,17 @@ val form = mapping(
|
|||||||
)(RegisterForm.apply)
|
)(RegisterForm.apply)
|
||||||
```
|
```
|
||||||
|
|
||||||
The servlet have to mixed in ```jp.sf.amateras.scalatra.forms.ClientSideValidationFormSupport``` to validate request parameters and take mapped object. It validates request parameters before action. If any errors are detected, it throws an exception.
|
The servlet have to mixed in `gitbucket.core.controller.ValidationFormSupport` to validate request parameters and take mapped object. It validates request parameters before action. If any errors are detected, it throws an exception.
|
||||||
|
|
||||||
```scala
|
```scala
|
||||||
class RegisterServlet extends ScalatraServlet with ClientSideValidationFormSupport {
|
class RegisterServlet extends ScalatraServlet with ValidationFormSupport {
|
||||||
post("/register", form) { form: RegisterForm =>
|
post("/register", form) { form: RegisterForm =>
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
In the view template, you can add client-side validation by adding ```validate="true"``` to your form. Error messages are set to ```span#error-<fieldname>```.
|
In the view template, you can add client-side validation by adding `validate="true"` to your form. Error messages are set to `span#error-<fieldname>`.
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<form method="POST" action="/register" validate="true">
|
<form method="POST" action="/register" validate="true">
|
||||||
@@ -39,9 +39,9 @@ In the view template, you can add client-side validation by adding ```validate="
|
|||||||
</form>
|
</form>
|
||||||
```
|
```
|
||||||
|
|
||||||
Client-side validation calls ```<form-action>/validate``` to validate form contents. It returns a validation result as JSON. In this case, form action is ```/register```, so ```/register/validate``` is called before submitting a form. ```ClientSideValidationFormSupport``` adds this JSON API automatically.
|
Client-side validation calls `<form-action>/validate` to validate form contents. It returns a validation result as JSON. In this case, form action is `/register`, so `/register/validate` is called before submitting a form. `ValidationFormSupport` adds this JSON API automatically.
|
||||||
|
|
||||||
For Ajax request, you have to use '''ajaxGet''' or '''ajaxPost''' to define action. It almost same as '''get''' or '''post'''. You can implement actions which handle Ajax request as same as normal actions.
|
For Ajax request, you have to use `ajaxGet` or `ajaxPost` to define action. It almost same as '''get''' or '''post'''. You can implement actions which handle Ajax request as same as normal actions.
|
||||||
Small difference is they return validation errors as JSON.
|
Small difference is they return validation errors as JSON.
|
||||||
|
|
||||||
```scala
|
```scala
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
sbt.version=1.2.6
|
sbt.version=1.3.12
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
scalacOptions ++= Seq("-unchecked", "-deprecation", "-feature")
|
scalacOptions ++= Seq("-unchecked", "-deprecation", "-feature")
|
||||||
|
|
||||||
addSbtPlugin("com.geirsson" % "sbt-scalafmt" % "1.5.0")
|
addSbtPlugin("com.geirsson" % "sbt-scalafmt" % "1.5.1")
|
||||||
addSbtPlugin("com.typesafe.sbt" % "sbt-twirl" % "1.3.15")
|
addSbtPlugin("com.typesafe.sbt" % "sbt-twirl" % "1.5.0")
|
||||||
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.9")
|
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.15.0")
|
||||||
addSbtPlugin("org.scalatra.sbt" % "sbt-scalatra" % "1.0.3")
|
addSbtPlugin("org.scalatra.sbt" % "sbt-scalatra" % "1.0.4")
|
||||||
addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.1.2")
|
addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.1.2")
|
||||||
addSbtCoursier
|
|
||||||
addSbtPlugin("com.typesafe.sbt" % "sbt-license-report" % "1.2.0")
|
addSbtPlugin("com.typesafe.sbt" % "sbt-license-report" % "1.2.0")
|
||||||
addSbtPlugin("net.virtual-void" % "sbt-dependency-graph" % "0.9.2")
|
addSbtPlugin("net.virtual-void" % "sbt-dependency-graph" % "0.9.2")
|
||||||
|
addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.6.1")
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
addSbtPlugin("io.get-coursier" % "sbt-coursier" % "1.0.0")
|
|
||||||
@@ -4,6 +4,10 @@ import org.eclipse.jetty.server.Handler;
|
|||||||
import org.eclipse.jetty.server.HttpConnectionFactory;
|
import org.eclipse.jetty.server.HttpConnectionFactory;
|
||||||
import org.eclipse.jetty.server.Server;
|
import org.eclipse.jetty.server.Server;
|
||||||
import org.eclipse.jetty.server.handler.StatisticsHandler;
|
import org.eclipse.jetty.server.handler.StatisticsHandler;
|
||||||
|
import org.eclipse.jetty.server.session.DefaultSessionCache;
|
||||||
|
import org.eclipse.jetty.server.session.FileSessionDataStore;
|
||||||
|
import org.eclipse.jetty.server.session.SessionCache;
|
||||||
|
import org.eclipse.jetty.server.session.SessionHandler;
|
||||||
import org.eclipse.jetty.webapp.WebAppContext;
|
import org.eclipse.jetty.webapp.WebAppContext;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@@ -16,13 +20,22 @@ public class JettyLauncher {
|
|||||||
System.setProperty("java.awt.headless", "true");
|
System.setProperty("java.awt.headless", "true");
|
||||||
|
|
||||||
String host = null;
|
String host = null;
|
||||||
int port = 8080;
|
String port = null;
|
||||||
InetSocketAddress address = null;
|
InetSocketAddress address = null;
|
||||||
String contextPath = "/";
|
String contextPath = "/";
|
||||||
String tmpDirPath="";
|
String tmpDirPath="";
|
||||||
boolean forceHttps = false;
|
boolean forceHttps = false;
|
||||||
|
boolean saveSessions = false;
|
||||||
|
|
||||||
|
host = getEnvironmentVariable("gitbucket.host");
|
||||||
|
port = getEnvironmentVariable("gitbucket.port");
|
||||||
|
contextPath = getEnvironmentVariable("gitbucket.prefix");
|
||||||
|
tmpDirPath = getEnvironmentVariable("gitbucket.tempDir");
|
||||||
|
|
||||||
for(String arg: args) {
|
for(String arg: args) {
|
||||||
|
if(arg.equals("--save_sessions")) {
|
||||||
|
saveSessions = true;
|
||||||
|
}
|
||||||
if(arg.startsWith("--") && arg.contains("=")) {
|
if(arg.startsWith("--") && arg.contains("=")) {
|
||||||
String[] dim = arg.split("=");
|
String[] dim = arg.split("=");
|
||||||
if(dim.length >= 2) {
|
if(dim.length >= 2) {
|
||||||
@@ -31,16 +44,10 @@ public class JettyLauncher {
|
|||||||
host = dim[1];
|
host = dim[1];
|
||||||
break;
|
break;
|
||||||
case "--port":
|
case "--port":
|
||||||
port = Integer.parseInt(dim[1]);
|
port = dim[1];
|
||||||
break;
|
break;
|
||||||
case "--prefix":
|
case "--prefix":
|
||||||
contextPath = dim[1];
|
contextPath = dim[1];
|
||||||
if (!contextPath.startsWith("/")) {
|
|
||||||
contextPath = "/" + contextPath;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "--max_file_size":
|
|
||||||
System.setProperty("gitbucket.maxFileSize", dim[1]);
|
|
||||||
break;
|
break;
|
||||||
case "--gitbucket.home":
|
case "--gitbucket.home":
|
||||||
System.setProperty("gitbucket.home", dim[1]);
|
System.setProperty("gitbucket.home", dim[1]);
|
||||||
@@ -51,18 +58,19 @@ public class JettyLauncher {
|
|||||||
case "--plugin_dir":
|
case "--plugin_dir":
|
||||||
System.setProperty("gitbucket.pluginDir", dim[1]);
|
System.setProperty("gitbucket.pluginDir", dim[1]);
|
||||||
break;
|
break;
|
||||||
case "--validate_password":
|
|
||||||
System.setProperty("gitbucket.validate.password", dim[1]);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (contextPath != null && !contextPath.startsWith("/")) {
|
||||||
|
contextPath = "/" + contextPath;
|
||||||
|
}
|
||||||
|
|
||||||
if(host != null) {
|
if(host != null) {
|
||||||
address = new InetSocketAddress(host, port);
|
address = new InetSocketAddress(host, getPort(port));
|
||||||
} else {
|
} else {
|
||||||
address = new InetSocketAddress(port);
|
address = new InetSocketAddress(getPort(port));
|
||||||
}
|
}
|
||||||
|
|
||||||
Server server = new Server(address);
|
Server server = new Server(address);
|
||||||
@@ -87,8 +95,21 @@ public class JettyLauncher {
|
|||||||
|
|
||||||
WebAppContext context = new WebAppContext();
|
WebAppContext context = new WebAppContext();
|
||||||
|
|
||||||
|
if(saveSessions) {
|
||||||
|
File sessDir = new File(getGitBucketHome(), "sessions");
|
||||||
|
if(!sessDir.exists()){
|
||||||
|
sessDir.mkdirs();
|
||||||
|
}
|
||||||
|
SessionHandler sessions = context.getSessionHandler();
|
||||||
|
SessionCache cache = new DefaultSessionCache(sessions);
|
||||||
|
FileSessionDataStore fsds = new FileSessionDataStore();
|
||||||
|
fsds.setStoreDir(sessDir);
|
||||||
|
cache.setSessionDataStore(fsds);
|
||||||
|
sessions.setSessionCache(cache);
|
||||||
|
}
|
||||||
|
|
||||||
File tmpDir;
|
File tmpDir;
|
||||||
if(tmpDirPath.equals("")){
|
if(tmpDirPath == null || tmpDirPath.equals("")){
|
||||||
tmpDir = new File(getGitBucketHome(), "tmp");
|
tmpDir = new File(getGitBucketHome(), "tmp");
|
||||||
if(!tmpDir.exists()){
|
if(!tmpDir.exists()){
|
||||||
tmpDir.mkdirs();
|
tmpDir.mkdirs();
|
||||||
@@ -111,7 +132,7 @@ public class JettyLauncher {
|
|||||||
ProtectionDomain domain = JettyLauncher.class.getProtectionDomain();
|
ProtectionDomain domain = JettyLauncher.class.getProtectionDomain();
|
||||||
URL location = domain.getCodeSource().getLocation();
|
URL location = domain.getCodeSource().getLocation();
|
||||||
|
|
||||||
context.setContextPath(contextPath);
|
context.setContextPath(contextPath == null ? "" : contextPath);
|
||||||
context.setDescriptor(location.toExternalForm() + "/WEB-INF/web.xml");
|
context.setDescriptor(location.toExternalForm() + "/WEB-INF/web.xml");
|
||||||
context.setServer(server);
|
context.setServer(server);
|
||||||
context.setWar(location.toExternalForm());
|
context.setWar(location.toExternalForm());
|
||||||
@@ -140,6 +161,23 @@ public class JettyLauncher {
|
|||||||
return new File(System.getProperty("user.home"), ".gitbucket");
|
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) {
|
private static Handler addStatisticsHandler(Handler handler) {
|
||||||
// The graceful shutdown is implemented via the statistics handler.
|
// The graceful shutdown is implemented via the statistics handler.
|
||||||
// See the following: https://bugs.eclipse.org/bugs/show_bug.cgi?id=420142
|
// See the following: https://bugs.eclipse.org/bugs/show_bug.cgi?id=420142
|
||||||
|
|||||||
@@ -1 +1,4 @@
|
|||||||
notifications:1.6.0
|
notifications:1.8.0
|
||||||
|
gist:4.18.0
|
||||||
|
emoji:4.6.0
|
||||||
|
pages:1.8.0
|
||||||
|
|||||||
@@ -60,13 +60,12 @@
|
|||||||
<!-- ACCESS_TOKEN -->
|
<!-- ACCESS_TOKEN -->
|
||||||
<!--================================================================================================-->
|
<!--================================================================================================-->
|
||||||
<createTable tableName="ACCESS_TOKEN">
|
<createTable tableName="ACCESS_TOKEN">
|
||||||
<column name="ACCESS_TOKEN_ID" type="int" nullable="false" autoIncrement="true" unique="true"/>
|
<column name="ACCESS_TOKEN_ID" type="int" nullable="false" autoIncrement="true" unique="true" primaryKeyName="IDX_ACCESS_TOKEN_PK" primaryKey="true" />
|
||||||
<column name="TOKEN_HASH" type="varchar(40)" nullable="false"/>
|
<column name="TOKEN_HASH" type="varchar(40)" nullable="false"/>
|
||||||
<column name="USER_NAME" type="varchar(100)" nullable="false"/>
|
<column name="USER_NAME" type="varchar(100)" nullable="false"/>
|
||||||
<column name="NOTE" type="text" nullable="false"/>
|
<column name="NOTE" type="text" nullable="false"/>
|
||||||
</createTable>
|
</createTable>
|
||||||
|
|
||||||
<addPrimaryKey constraintName="IDX_ACCESS_TOKEN_PK" tableName="ACCESS_TOKEN" columnNames="ACCESS_TOKEN_ID"/>
|
|
||||||
<addUniqueConstraint constraintName="IDX_ACCESS_TOKEN_TOKEN_HASH" tableName="ACCESS_TOKEN" columnNames="TOKEN_HASH"/>
|
<addUniqueConstraint constraintName="IDX_ACCESS_TOKEN_TOKEN_HASH" tableName="ACCESS_TOKEN" columnNames="TOKEN_HASH"/>
|
||||||
<addForeignKeyConstraint constraintName="IDX_ACCESS_TOKEN_FK0" baseTableName="ACCESS_TOKEN" baseColumnNames="USER_NAME" referencedTableName="ACCOUNT" referencedColumnNames="USER_NAME" onDelete="CASCADE" onUpdate="CASCADE"/>
|
<addForeignKeyConstraint constraintName="IDX_ACCESS_TOKEN_FK0" baseTableName="ACCESS_TOKEN" baseColumnNames="USER_NAME" referencedTableName="ACCOUNT" referencedColumnNames="USER_NAME" onDelete="CASCADE" onUpdate="CASCADE"/>
|
||||||
|
|
||||||
@@ -74,7 +73,7 @@
|
|||||||
<!-- ACTIVITY -->
|
<!-- ACTIVITY -->
|
||||||
<!--================================================================================================-->
|
<!--================================================================================================-->
|
||||||
<createTable tableName="ACTIVITY">
|
<createTable tableName="ACTIVITY">
|
||||||
<column name="ACTIVITY_ID" type="int" nullable="false" autoIncrement="true" unique="true"/>
|
<column name="ACTIVITY_ID" type="int" nullable="false" autoIncrement="true" unique="true" primaryKeyName="IDX_ACTIVITY_PK" primaryKey="true"/>
|
||||||
<column name="USER_NAME" type="varchar(100)" nullable="false"/>
|
<column name="USER_NAME" type="varchar(100)" nullable="false"/>
|
||||||
<column name="REPOSITORY_NAME" type="varchar(100)" nullable="false"/>
|
<column name="REPOSITORY_NAME" type="varchar(100)" nullable="false"/>
|
||||||
<column name="ACTIVITY_USER_NAME" type="varchar(100)" nullable="false"/>
|
<column name="ACTIVITY_USER_NAME" type="varchar(100)" nullable="false"/>
|
||||||
@@ -84,7 +83,6 @@
|
|||||||
<column name="ACTIVITY_DATE" type="datetime" nullable="false"/>
|
<column name="ACTIVITY_DATE" type="datetime" nullable="false"/>
|
||||||
</createTable>
|
</createTable>
|
||||||
|
|
||||||
<addPrimaryKey constraintName="IDX_ACTIVITY_PK" tableName="ACTIVITY" columnNames="ACTIVITY_ID"/>
|
|
||||||
<addForeignKeyConstraint constraintName="IDX_ACTIVITY_FK1" baseTableName="ACTIVITY" baseColumnNames="ACTIVITY_USER_NAME" referencedTableName="ACCOUNT" referencedColumnNames="USER_NAME"/>
|
<addForeignKeyConstraint constraintName="IDX_ACTIVITY_FK1" baseTableName="ACTIVITY" baseColumnNames="ACTIVITY_USER_NAME" referencedTableName="ACCOUNT" referencedColumnNames="USER_NAME"/>
|
||||||
<addForeignKeyConstraint constraintName="IDX_ACTIVITY_FK0" baseTableName="ACTIVITY" baseColumnNames="USER_NAME, REPOSITORY_NAME" referencedTableName="REPOSITORY" referencedColumnNames="USER_NAME, REPOSITORY_NAME"/>
|
<addForeignKeyConstraint constraintName="IDX_ACTIVITY_FK0" baseTableName="ACTIVITY" baseColumnNames="USER_NAME, REPOSITORY_NAME" referencedTableName="REPOSITORY" referencedColumnNames="USER_NAME, REPOSITORY_NAME"/>
|
||||||
|
|
||||||
@@ -108,7 +106,7 @@
|
|||||||
<column name="USER_NAME" type="varchar(100)" nullable="false"/>
|
<column name="USER_NAME" type="varchar(100)" nullable="false"/>
|
||||||
<column name="REPOSITORY_NAME" type="varchar(100)" nullable="false"/>
|
<column name="REPOSITORY_NAME" type="varchar(100)" nullable="false"/>
|
||||||
<column name="COMMIT_ID" type="varchar(100)" nullable="false"/>
|
<column name="COMMIT_ID" type="varchar(100)" nullable="false"/>
|
||||||
<column name="COMMENT_ID" type="int" nullable="false" autoIncrement="true" unique="true"/>
|
<column name="COMMENT_ID" type="int" nullable="false" autoIncrement="true" unique="true" primaryKeyName="IDX_COMMIT_COMMENT_PK" primaryKey="true" />
|
||||||
<column name="COMMENTED_USER_NAME" type="varchar(100)" nullable="false"/>
|
<column name="COMMENTED_USER_NAME" type="varchar(100)" nullable="false"/>
|
||||||
<column name="CONTENT" type="text" nullable="false"/>
|
<column name="CONTENT" type="text" nullable="false"/>
|
||||||
<column name="FILE_NAME" type="varchar(260)" nullable="true"/>
|
<column name="FILE_NAME" type="varchar(260)" nullable="true"/>
|
||||||
@@ -119,14 +117,13 @@
|
|||||||
<column name="ISSUE_ID" type="int" nullable="true"/>
|
<column name="ISSUE_ID" type="int" nullable="true"/>
|
||||||
</createTable>
|
</createTable>
|
||||||
|
|
||||||
<addPrimaryKey constraintName="IDX_COMMIT_COMMENT_PK" tableName="COMMIT_COMMENT" columnNames="COMMENT_ID"/>
|
|
||||||
<addForeignKeyConstraint constraintName="IDX_COMMIT_COMMENT_FK0" baseTableName="COMMIT_COMMENT" baseColumnNames="USER_NAME, REPOSITORY_NAME" referencedTableName="REPOSITORY" referencedColumnNames="USER_NAME, REPOSITORY_NAME"/>
|
<addForeignKeyConstraint constraintName="IDX_COMMIT_COMMENT_FK0" baseTableName="COMMIT_COMMENT" baseColumnNames="USER_NAME, REPOSITORY_NAME" referencedTableName="REPOSITORY" referencedColumnNames="USER_NAME, REPOSITORY_NAME"/>
|
||||||
|
|
||||||
<!--================================================================================================-->
|
<!--================================================================================================-->
|
||||||
<!-- COMMIT_STATUS -->
|
<!-- COMMIT_STATUS -->
|
||||||
<!--================================================================================================-->
|
<!--================================================================================================-->
|
||||||
<createTable tableName="COMMIT_STATUS">
|
<createTable tableName="COMMIT_STATUS">
|
||||||
<column name="COMMIT_STATUS_ID" type="int" nullable="false" autoIncrement="true" unique="true"/>
|
<column name="COMMIT_STATUS_ID" type="int" nullable="false" autoIncrement="true" unique="true" primaryKeyName="IDX_COMMIT_STATUS_PK" primaryKey="true" />
|
||||||
<column name="USER_NAME" type="varchar(100)" nullable="false"/>
|
<column name="USER_NAME" type="varchar(100)" nullable="false"/>
|
||||||
<column name="REPOSITORY_NAME" type="varchar(100)" nullable="false"/>
|
<column name="REPOSITORY_NAME" type="varchar(100)" nullable="false"/>
|
||||||
<column name="COMMIT_ID" type="varchar(40)" nullable="false"/>
|
<column name="COMMIT_ID" type="varchar(40)" nullable="false"/>
|
||||||
@@ -139,7 +136,6 @@
|
|||||||
<column name="UPDATED_DATE" type="datetime" nullable="false"/>
|
<column name="UPDATED_DATE" type="datetime" nullable="false"/>
|
||||||
</createTable>
|
</createTable>
|
||||||
|
|
||||||
<addPrimaryKey constraintName="IDX_COMMIT_STATUS_PK" tableName="COMMIT_STATUS" columnNames="COMMIT_STATUS_ID"/>
|
|
||||||
<addUniqueConstraint constraintName="IDX_COMMIT_STATUS_1" tableName="COMMIT_STATUS" columnNames="USER_NAME, REPOSITORY_NAME, COMMIT_ID, CONTEXT"/>
|
<addUniqueConstraint constraintName="IDX_COMMIT_STATUS_1" tableName="COMMIT_STATUS" columnNames="USER_NAME, REPOSITORY_NAME, COMMIT_ID, CONTEXT"/>
|
||||||
<addForeignKeyConstraint constraintName="IDX_COMMIT_STATUS_FK3" baseTableName="COMMIT_STATUS" baseColumnNames="CREATOR" referencedTableName="ACCOUNT" referencedColumnNames="USER_NAME" onDelete="CASCADE" onUpdate="CASCADE"/>
|
<addForeignKeyConstraint constraintName="IDX_COMMIT_STATUS_FK3" baseTableName="COMMIT_STATUS" baseColumnNames="CREATOR" referencedTableName="ACCOUNT" referencedColumnNames="USER_NAME" onDelete="CASCADE" onUpdate="CASCADE"/>
|
||||||
<addForeignKeyConstraint constraintName="IDX_COMMIT_STATUS_FK2" baseTableName="COMMIT_STATUS" baseColumnNames="USER_NAME" referencedTableName="ACCOUNT" referencedColumnNames="USER_NAME" onDelete="CASCADE" onUpdate="CASCADE"/>
|
<addForeignKeyConstraint constraintName="IDX_COMMIT_STATUS_FK2" baseTableName="COMMIT_STATUS" baseColumnNames="USER_NAME" referencedTableName="ACCOUNT" referencedColumnNames="USER_NAME" onDelete="CASCADE" onUpdate="CASCADE"/>
|
||||||
@@ -206,7 +202,7 @@
|
|||||||
<column name="PULL_REQUEST" type="boolean" nullable="false"/>
|
<column name="PULL_REQUEST" type="boolean" nullable="false"/>
|
||||||
</createTable>
|
</createTable>
|
||||||
|
|
||||||
<addPrimaryKey constraintName="IDX_ISSUE_PK" tableName="ISSUE" columnNames="ISSUE_ID, USER_NAME, REPOSITORY_NAME"/>
|
<addPrimaryKey constraintName="IDX_ISSUE_PK" tableName="ISSUE" columnNames="USER_NAME, REPOSITORY_NAME, ISSUE_ID"/>
|
||||||
<addForeignKeyConstraint constraintName="IDX_ISSUE_FK0" baseTableName="ISSUE" baseColumnNames="USER_NAME, REPOSITORY_NAME" referencedTableName="REPOSITORY" referencedColumnNames="USER_NAME, REPOSITORY_NAME"/>
|
<addForeignKeyConstraint constraintName="IDX_ISSUE_FK0" baseTableName="ISSUE" baseColumnNames="USER_NAME, REPOSITORY_NAME" referencedTableName="REPOSITORY" referencedColumnNames="USER_NAME, REPOSITORY_NAME"/>
|
||||||
<addForeignKeyConstraint constraintName="IDX_ISSUE_FK2" baseTableName="ISSUE" baseColumnNames="MILESTONE_ID" referencedTableName="MILESTONE" referencedColumnNames="MILESTONE_ID"/>
|
<addForeignKeyConstraint constraintName="IDX_ISSUE_FK2" baseTableName="ISSUE" baseColumnNames="MILESTONE_ID" referencedTableName="MILESTONE" referencedColumnNames="MILESTONE_ID"/>
|
||||||
<addForeignKeyConstraint constraintName="IDX_ISSUE_FK1" baseTableName="ISSUE" baseColumnNames="OPENED_USER_NAME" referencedTableName="ACCOUNT" referencedColumnNames="USER_NAME"/>
|
<addForeignKeyConstraint constraintName="IDX_ISSUE_FK1" baseTableName="ISSUE" baseColumnNames="OPENED_USER_NAME" referencedTableName="ACCOUNT" referencedColumnNames="USER_NAME"/>
|
||||||
@@ -218,7 +214,7 @@
|
|||||||
<column name="USER_NAME" type="varchar(100)" nullable="false"/>
|
<column name="USER_NAME" type="varchar(100)" nullable="false"/>
|
||||||
<column name="REPOSITORY_NAME" type="varchar(100)" nullable="false"/>
|
<column name="REPOSITORY_NAME" type="varchar(100)" nullable="false"/>
|
||||||
<column name="ISSUE_ID" type="int" nullable="false"/>
|
<column name="ISSUE_ID" type="int" nullable="false"/>
|
||||||
<column name="COMMENT_ID" type="int" nullable="false" autoIncrement="true" unique="true"/>
|
<column name="COMMENT_ID" type="int" nullable="false" autoIncrement="true" unique="true" primaryKeyName="IDX_ISSUE_COMMENT_PK" primaryKey="true" />
|
||||||
<column name="ACTION" type="varchar(20)" nullable="false"/>
|
<column name="ACTION" type="varchar(20)" nullable="false"/>
|
||||||
<column name="COMMENTED_USER_NAME" type="varchar(100)" nullable="false"/>
|
<column name="COMMENTED_USER_NAME" type="varchar(100)" nullable="false"/>
|
||||||
<column name="CONTENT" type="text" nullable="false"/>
|
<column name="CONTENT" type="text" nullable="false"/>
|
||||||
@@ -226,7 +222,6 @@
|
|||||||
<column name="UPDATED_DATE" type="datetime" nullable="false"/>
|
<column name="UPDATED_DATE" type="datetime" nullable="false"/>
|
||||||
</createTable>
|
</createTable>
|
||||||
|
|
||||||
<addPrimaryKey constraintName="IDX_ISSUE_COMMENT_PK" tableName="ISSUE_COMMENT" columnNames="COMMENT_ID"/>
|
|
||||||
<addForeignKeyConstraint constraintName="IDX_ISSUE_COMMENT_FK0" baseTableName="ISSUE_COMMENT" baseColumnNames="USER_NAME, REPOSITORY_NAME, ISSUE_ID" referencedTableName="ISSUE" referencedColumnNames="USER_NAME, REPOSITORY_NAME, ISSUE_ID"/>
|
<addForeignKeyConstraint constraintName="IDX_ISSUE_COMMENT_FK0" baseTableName="ISSUE_COMMENT" baseColumnNames="USER_NAME, REPOSITORY_NAME, ISSUE_ID" referencedTableName="ISSUE" referencedColumnNames="USER_NAME, REPOSITORY_NAME, ISSUE_ID"/>
|
||||||
|
|
||||||
<!--================================================================================================-->
|
<!--================================================================================================-->
|
||||||
|
|||||||
16
src/main/resources/update/gitbucket-core_4.31.xml
Normal file
16
src/main/resources/update/gitbucket-core_4.31.xml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<changeSet>
|
||||||
|
<!--================================================================================================-->
|
||||||
|
<!-- SSH_KEY -->
|
||||||
|
<!--================================================================================================-->
|
||||||
|
<createTable tableName="GPG_KEY">
|
||||||
|
<column name="USER_NAME" type="varchar(100)" nullable="false"/>
|
||||||
|
<column name="KEY_ID" type="int" nullable="false" autoIncrement="true" unique="true"/>
|
||||||
|
<column name="GPG_KEY_ID" type="bigint" nullable="false"/>
|
||||||
|
<column name="TITLE" type="varchar(100)" nullable="false"/>
|
||||||
|
<column name="PUBLIC_KEY" type="text" nullable="false"/>
|
||||||
|
</createTable>
|
||||||
|
|
||||||
|
<addPrimaryKey constraintName="IDX_GPG_KEY_PK" tableName="GPG_KEY" columnNames="USER_NAME, GPG_KEY_ID"/>
|
||||||
|
<addForeignKeyConstraint constraintName="IDX_GPG_KEY_FK0" baseTableName="GPG_KEY" baseColumnNames="USER_NAME" referencedTableName="ACCOUNT" referencedColumnNames="USER_NAME"/>
|
||||||
|
</changeSet>
|
||||||
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>
|
||||||
@@ -16,7 +16,7 @@ class ScalatraBootstrap extends LifeCycle with SystemSettingsService {
|
|||||||
context.getSessionCookieConfig.setSecure(true)
|
context.getSessionCookieConfig.setSecure(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register TransactionFilter and BasicAuthenticationFilter at first
|
// Register TransactionFilter at first
|
||||||
context.addFilter("transactionFilter", new TransactionFilter)
|
context.addFilter("transactionFilter", new TransactionFilter)
|
||||||
context
|
context
|
||||||
.getFilterRegistration("transactionFilter")
|
.getFilterRegistration("transactionFilter")
|
||||||
|
|||||||
@@ -59,5 +59,11 @@ object GitBucketCoreModule
|
|||||||
new Version("4.27.0", new LiquibaseMigration("update/gitbucket-core_4.27.xml")),
|
new Version("4.27.0", new LiquibaseMigration("update/gitbucket-core_4.27.xml")),
|
||||||
new Version("4.28.0"),
|
new Version("4.28.0"),
|
||||||
new Version("4.29.0"),
|
new Version("4.29.0"),
|
||||||
new Version("4.30.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.1"),
|
||||||
|
new Version("4.31.2"),
|
||||||
|
new Version("4.32.0", new LiquibaseMigration("update/gitbucket-core_4.32.xml")),
|
||||||
|
new Version("4.33.0")
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import gitbucket.core.util.RepositoryName
|
|||||||
case class ApiBranch(name: String, commit: ApiBranchCommit, protection: ApiBranchProtection)(
|
case class ApiBranch(name: String, commit: ApiBranchCommit, protection: ApiBranchProtection)(
|
||||||
repositoryName: RepositoryName
|
repositoryName: RepositoryName
|
||||||
) extends FieldSerializable {
|
) extends FieldSerializable {
|
||||||
def _links =
|
val _links =
|
||||||
Map(
|
Map(
|
||||||
"self" -> ApiPath(s"/api/v3/repos/${repositoryName.fullName}/branches/${name}"),
|
"self" -> ApiPath(s"/api/v3/repos/${repositoryName.fullName}/branches/${name}"),
|
||||||
"html" -> ApiPath(s"/${repositoryName.fullName}/tree/${name}")
|
"html" -> ApiPath(s"/${repositoryName.fullName}/tree/${name}")
|
||||||
|
|||||||
@@ -21,22 +21,14 @@ case class ApiCommit(
|
|||||||
modified: List[String],
|
modified: List[String],
|
||||||
author: ApiPersonIdent,
|
author: ApiPersonIdent,
|
||||||
committer: ApiPersonIdent
|
committer: ApiPersonIdent
|
||||||
)(repositoryName: RepositoryName, urlIsHtmlUrl: Boolean)
|
)(repositoryName: RepositoryName)
|
||||||
extends FieldSerializable {
|
extends FieldSerializable {
|
||||||
val url = if (urlIsHtmlUrl) {
|
val url = ApiPath(s"/api/v3/${repositoryName.fullName}/commits/${id}")
|
||||||
ApiPath(s"/${repositoryName.fullName}/commit/${id}")
|
val html_url = ApiPath(s"/${repositoryName.fullName}/commit/${id}")
|
||||||
} else {
|
|
||||||
ApiPath(s"/api/v3/${repositoryName.fullName}/commits/${id}")
|
|
||||||
}
|
|
||||||
val html_url = if (urlIsHtmlUrl) {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(ApiPath(s"/${repositoryName.fullName}/commit/${id}"))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
object ApiCommit {
|
object ApiCommit {
|
||||||
def apply(git: Git, repositoryName: RepositoryName, commit: CommitInfo, urlIsHtmlUrl: Boolean = false): ApiCommit = {
|
def apply(git: Git, repositoryName: RepositoryName, commit: CommitInfo): ApiCommit = {
|
||||||
val diffs = JGitUtil.getDiffs(git, None, commit.id, false, false)
|
val diffs = JGitUtil.getDiffs(git, None, commit.id, false, false)
|
||||||
ApiCommit(
|
ApiCommit(
|
||||||
id = commit.id,
|
id = commit.id,
|
||||||
@@ -53,8 +45,6 @@ object ApiCommit {
|
|||||||
},
|
},
|
||||||
author = ApiPersonIdent.author(commit),
|
author = ApiPersonIdent.author(commit),
|
||||||
committer = ApiPersonIdent.committer(commit)
|
committer = ApiPersonIdent.committer(commit)
|
||||||
)(repositoryName, urlIsHtmlUrl)
|
)(repositoryName)
|
||||||
}
|
}
|
||||||
def forWebhookPayload(git: Git, repositoryName: RepositoryName, commit: CommitInfo): ApiCommit =
|
|
||||||
apply(git, repositoryName, commit, true)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ object ApiCommits {
|
|||||||
url = ApiPath(s"/api/v3/repos/${repositoryName.fullName}/commits/${commitInfo.id}"),
|
url = ApiPath(s"/api/v3/repos/${repositoryName.fullName}/commits/${commitInfo.id}"),
|
||||||
sha = commitInfo.id,
|
sha = commitInfo.id,
|
||||||
html_url = ApiPath(s"${repositoryName.fullName}/commit/${commitInfo.id}"),
|
html_url = ApiPath(s"${repositoryName.fullName}/commit/${commitInfo.id}"),
|
||||||
comment_url = ApiPath(""),
|
comment_url = ApiPath(""), // TODO no API for commit comment
|
||||||
commit = Commit(
|
commit = Commit(
|
||||||
url = ApiPath(s"/api/v3/repos/${repositoryName.fullName}/commits/${commitInfo.id}"),
|
url = ApiPath(s"/api/v3/repos/${repositoryName.fullName}/commits/${commitInfo.id}"),
|
||||||
author = ApiPersonIdent.author(commitInfo),
|
author = ApiPersonIdent.author(commitInfo),
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ case class ApiIssue(
|
|||||||
number: Int,
|
number: Int,
|
||||||
title: String,
|
title: String,
|
||||||
user: ApiUser,
|
user: ApiUser,
|
||||||
|
assignee: Option[ApiUser],
|
||||||
labels: List[ApiLabel],
|
labels: List[ApiLabel],
|
||||||
state: String,
|
state: String,
|
||||||
created_at: Date,
|
created_at: Date,
|
||||||
@@ -19,6 +20,7 @@ case class ApiIssue(
|
|||||||
body: String
|
body: String
|
||||||
)(repositoryName: RepositoryName, isPullRequest: Boolean) {
|
)(repositoryName: RepositoryName, isPullRequest: Boolean) {
|
||||||
val id = 0 // dummy id
|
val id = 0 // dummy id
|
||||||
|
val assignees = List(assignee).flatten
|
||||||
val comments_url = ApiPath(s"/api/v3/repos/${repositoryName.fullName}/issues/${number}/comments")
|
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 html_url = ApiPath(s"/${repositoryName.fullName}/${if (isPullRequest) { "pull" } else { "issues" }}/${number}")
|
||||||
val pull_request = if (isPullRequest) {
|
val pull_request = if (isPullRequest) {
|
||||||
@@ -36,11 +38,18 @@ case class ApiIssue(
|
|||||||
}
|
}
|
||||||
|
|
||||||
object 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(
|
ApiIssue(
|
||||||
number = issue.issueId,
|
number = issue.issueId,
|
||||||
title = issue.title,
|
title = issue.title,
|
||||||
user = user,
|
user = user,
|
||||||
|
assignee = assignee,
|
||||||
labels = labels,
|
labels = labels,
|
||||||
state = if (issue.closed) { "closed" } else { "open" },
|
state = if (issue.closed) { "closed" } else { "open" },
|
||||||
body = issue.content.getOrElse(""),
|
body = issue.content.getOrElse(""),
|
||||||
|
|||||||
@@ -21,7 +21,8 @@ case class ApiPullRequest(
|
|||||||
body: String,
|
body: String,
|
||||||
user: ApiUser,
|
user: ApiUser,
|
||||||
labels: List[ApiLabel],
|
labels: List[ApiLabel],
|
||||||
assignee: Option[ApiUser]
|
assignee: Option[ApiUser],
|
||||||
|
draft: Option[Boolean]
|
||||||
) {
|
) {
|
||||||
val id = 0 // dummy id
|
val id = 0 // dummy id
|
||||||
val html_url = ApiPath(s"${base.repo.html_url.path}/pull/${number}")
|
val html_url = ApiPath(s"${base.repo.html_url.path}/pull/${number}")
|
||||||
@@ -62,7 +63,8 @@ object ApiPullRequest {
|
|||||||
body = issue.content.getOrElse(""),
|
body = issue.content.getOrElse(""),
|
||||||
user = user,
|
user = user,
|
||||||
labels = labels,
|
labels = labels,
|
||||||
assignee = assignee
|
assignee = assignee,
|
||||||
|
draft = Some(pullRequest.isDraft)
|
||||||
)
|
)
|
||||||
|
|
||||||
case class Commit(sha: String, ref: String, repo: ApiRepository)(baseOwner: String) {
|
case class Commit(sha: String, ref: String, repo: ApiRepository)(baseOwner: String) {
|
||||||
|
|||||||
42
src/main/scala/gitbucket/core/api/ApiRelease.scala
Normal file
42
src/main/scala/gitbucket/core/api/ApiRelease.scala
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
package gitbucket.core.api
|
||||||
|
import gitbucket.core.model.{Account, ReleaseAsset, ReleaseTag}
|
||||||
|
import gitbucket.core.util.RepositoryName
|
||||||
|
|
||||||
|
case class ApiReleaseAsset(name: String, size: Long)(tag: String, fileName: String, repositoryName: RepositoryName) {
|
||||||
|
val label = name
|
||||||
|
val file_id = fileName
|
||||||
|
val browser_download_url = ApiPath(
|
||||||
|
s"/${repositoryName.fullName}/releases/${tag}/assets/${fileName}"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
object ApiReleaseAsset {
|
||||||
|
def apply(asset: ReleaseAsset, repositoryName: RepositoryName): ApiReleaseAsset =
|
||||||
|
ApiReleaseAsset(asset.label, asset.size)(asset.tag, asset.fileName, repositoryName)
|
||||||
|
}
|
||||||
|
|
||||||
|
case class ApiRelease(
|
||||||
|
name: String,
|
||||||
|
tag_name: String,
|
||||||
|
body: Option[String],
|
||||||
|
author: ApiUser,
|
||||||
|
assets: Seq[ApiReleaseAsset]
|
||||||
|
)
|
||||||
|
|
||||||
|
object ApiRelease {
|
||||||
|
def apply(
|
||||||
|
release: ReleaseTag,
|
||||||
|
assets: Seq[ReleaseAsset],
|
||||||
|
author: Account,
|
||||||
|
repositoryName: RepositoryName
|
||||||
|
): ApiRelease =
|
||||||
|
ApiRelease(
|
||||||
|
release.name,
|
||||||
|
release.tag,
|
||||||
|
release.content,
|
||||||
|
ApiUser(author),
|
||||||
|
assets.map { asset =>
|
||||||
|
ApiReleaseAsset(asset, repositoryName)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -13,15 +13,11 @@ case class ApiRepository(
|
|||||||
`private`: Boolean,
|
`private`: Boolean,
|
||||||
default_branch: String,
|
default_branch: String,
|
||||||
owner: ApiUser
|
owner: ApiUser
|
||||||
)(urlIsHtmlUrl: Boolean) {
|
) {
|
||||||
val id = 0 // dummy id
|
val id = 0 // dummy id
|
||||||
val forks_count = forks
|
val forks_count = forks
|
||||||
val watchers_count = watchers
|
val watchers_count = watchers
|
||||||
val url = if (urlIsHtmlUrl) {
|
val url = ApiPath(s"/api/v3/repos/${full_name}")
|
||||||
ApiPath(s"/${full_name}")
|
|
||||||
} else {
|
|
||||||
ApiPath(s"/api/v3/repos/${full_name}")
|
|
||||||
}
|
|
||||||
val http_url = ApiPath(s"/git/${full_name}.git")
|
val http_url = ApiPath(s"/git/${full_name}.git")
|
||||||
val clone_url = ApiPath(s"/git/${full_name}.git")
|
val clone_url = ApiPath(s"/git/${full_name}.git")
|
||||||
val html_url = ApiPath(s"/${full_name}")
|
val html_url = ApiPath(s"/${full_name}")
|
||||||
@@ -33,8 +29,7 @@ object ApiRepository {
|
|||||||
repository: Repository,
|
repository: Repository,
|
||||||
owner: ApiUser,
|
owner: ApiUser,
|
||||||
forkedCount: Int = 0,
|
forkedCount: Int = 0,
|
||||||
watchers: Int = 0,
|
watchers: Int = 0
|
||||||
urlIsHtmlUrl: Boolean = false
|
|
||||||
): ApiRepository =
|
): ApiRepository =
|
||||||
ApiRepository(
|
ApiRepository(
|
||||||
name = repository.repositoryName,
|
name = repository.repositoryName,
|
||||||
@@ -45,16 +40,13 @@ object ApiRepository {
|
|||||||
`private` = repository.isPrivate,
|
`private` = repository.isPrivate,
|
||||||
default_branch = repository.defaultBranch,
|
default_branch = repository.defaultBranch,
|
||||||
owner = owner
|
owner = owner
|
||||||
)(urlIsHtmlUrl)
|
)
|
||||||
|
|
||||||
def apply(repositoryInfo: RepositoryInfo, owner: ApiUser): ApiRepository =
|
def apply(repositoryInfo: RepositoryInfo, owner: ApiUser): ApiRepository =
|
||||||
ApiRepository(repositoryInfo.repository, owner, forkedCount = repositoryInfo.forkedCount)
|
ApiRepository(repositoryInfo.repository, owner, forkedCount = repositoryInfo.forkedCount)
|
||||||
|
|
||||||
def apply(repositoryInfo: RepositoryInfo, owner: Account): ApiRepository =
|
def apply(repositoryInfo: RepositoryInfo, owner: Account): ApiRepository =
|
||||||
this(repositoryInfo.repository, ApiUser(owner))
|
this(repositoryInfo, ApiUser(owner))
|
||||||
|
|
||||||
def forWebhookPayload(repositoryInfo: RepositoryInfo, owner: ApiUser): ApiRepository =
|
|
||||||
ApiRepository(repositoryInfo.repository, owner, forkedCount = repositoryInfo.forkedCount, urlIsHtmlUrl = true)
|
|
||||||
|
|
||||||
def forDummyPayload(owner: ApiUser): ApiRepository =
|
def forDummyPayload(owner: ApiUser): ApiRepository =
|
||||||
ApiRepository(
|
ApiRepository(
|
||||||
@@ -66,5 +58,5 @@ object ApiRepository {
|
|||||||
`private` = false,
|
`private` = false,
|
||||||
default_branch = "master",
|
default_branch = "master",
|
||||||
owner = owner
|
owner = owner
|
||||||
)(true)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
13
src/main/scala/gitbucket/core/api/CreateAFile.scala
Normal file
13
src/main/scala/gitbucket/core/api/CreateAFile.scala
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
package gitbucket.core.api
|
||||||
|
|
||||||
|
/**
|
||||||
|
* https://developer.github.com/v3/repos/contents/#create-a-file
|
||||||
|
*/
|
||||||
|
case class CreateAFile(
|
||||||
|
message: String,
|
||||||
|
content: String,
|
||||||
|
sha: Option[String],
|
||||||
|
branch: Option[String],
|
||||||
|
committer: Option[ApiPusher],
|
||||||
|
author: Option[ApiPusher]
|
||||||
|
)
|
||||||
8
src/main/scala/gitbucket/core/api/CreateAGroup.scala
Normal file
8
src/main/scala/gitbucket/core/api/CreateAGroup.scala
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
package gitbucket.core.api
|
||||||
|
|
||||||
|
case class CreateAGroup(
|
||||||
|
login: String,
|
||||||
|
admin: String,
|
||||||
|
profile_name: Option[String],
|
||||||
|
url: Option[String]
|
||||||
|
)
|
||||||
@@ -5,7 +5,8 @@ case class CreateAPullRequest(
|
|||||||
head: String,
|
head: String,
|
||||||
base: String,
|
base: String,
|
||||||
body: Option[String],
|
body: Option[String],
|
||||||
maintainer_can_modify: Option[Boolean]
|
maintainer_can_modify: Option[Boolean],
|
||||||
|
draft: Option[Boolean]
|
||||||
)
|
)
|
||||||
|
|
||||||
case class CreateAPullRequestAlt(
|
case class CreateAPullRequestAlt(
|
||||||
|
|||||||
10
src/main/scala/gitbucket/core/api/CreateARelease.scala
Normal file
10
src/main/scala/gitbucket/core/api/CreateARelease.scala
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
package gitbucket.core.api
|
||||||
|
|
||||||
|
case class CreateARelease(
|
||||||
|
tag_name: String,
|
||||||
|
target_commitish: Option[String],
|
||||||
|
name: Option[String],
|
||||||
|
body: Option[String],
|
||||||
|
draft: Option[Boolean],
|
||||||
|
prerelease: Option[Boolean]
|
||||||
|
)
|
||||||
@@ -43,6 +43,8 @@ object JsonFormat {
|
|||||||
FieldSerializer[ApiCommits.Tree]() +
|
FieldSerializer[ApiCommits.Tree]() +
|
||||||
FieldSerializer[ApiCommits.Stats]() +
|
FieldSerializer[ApiCommits.Stats]() +
|
||||||
FieldSerializer[ApiCommits.File]() +
|
FieldSerializer[ApiCommits.File]() +
|
||||||
|
FieldSerializer[ApiRelease]() +
|
||||||
|
FieldSerializer[ApiReleaseAsset]() +
|
||||||
ApiBranchProtection.enforcementLevelSerializer
|
ApiBranchProtection.enforcementLevelSerializer
|
||||||
|
|
||||||
def apiPathSerializer(c: Context) =
|
def apiPathSerializer(c: Context) =
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import java.io.File
|
|||||||
import gitbucket.core.account.html
|
import gitbucket.core.account.html
|
||||||
import gitbucket.core.helper
|
import gitbucket.core.helper
|
||||||
import gitbucket.core.model._
|
import gitbucket.core.model._
|
||||||
import gitbucket.core.plugin.PluginRegistry
|
|
||||||
import gitbucket.core.service._
|
import gitbucket.core.service._
|
||||||
import gitbucket.core.service.WebHookService._
|
import gitbucket.core.service.WebHookService._
|
||||||
import gitbucket.core.ssh.SshUtil
|
import gitbucket.core.ssh.SshUtil
|
||||||
@@ -17,6 +16,7 @@ import gitbucket.core.util._
|
|||||||
import org.scalatra.i18n.Messages
|
import org.scalatra.i18n.Messages
|
||||||
import org.scalatra.BadRequest
|
import org.scalatra.BadRequest
|
||||||
import org.scalatra.forms._
|
import org.scalatra.forms._
|
||||||
|
import org.scalatra.Forbidden
|
||||||
|
|
||||||
class AccountController
|
class AccountController
|
||||||
extends AccountControllerBase
|
extends AccountControllerBase
|
||||||
@@ -26,6 +26,7 @@ class AccountController
|
|||||||
with WikiService
|
with WikiService
|
||||||
with LabelsService
|
with LabelsService
|
||||||
with SshKeyService
|
with SshKeyService
|
||||||
|
with GpgKeyService
|
||||||
with OneselfAuthenticator
|
with OneselfAuthenticator
|
||||||
with UsersAuthenticator
|
with UsersAuthenticator
|
||||||
with GroupManagerAuthenticator
|
with GroupManagerAuthenticator
|
||||||
@@ -42,6 +43,7 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
|||||||
with WikiService
|
with WikiService
|
||||||
with LabelsService
|
with LabelsService
|
||||||
with SshKeyService
|
with SshKeyService
|
||||||
|
with GpgKeyService
|
||||||
with OneselfAuthenticator
|
with OneselfAuthenticator
|
||||||
with UsersAuthenticator
|
with UsersAuthenticator
|
||||||
with GroupManagerAuthenticator
|
with GroupManagerAuthenticator
|
||||||
@@ -75,11 +77,13 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
|||||||
|
|
||||||
case class SshKeyForm(title: String, publicKey: String)
|
case class SshKeyForm(title: String, publicKey: String)
|
||||||
|
|
||||||
|
case class GpgKeyForm(title: String, publicKey: String)
|
||||||
|
|
||||||
case class PersonalTokenForm(note: String)
|
case class PersonalTokenForm(note: String)
|
||||||
|
|
||||||
val newForm = mapping(
|
val newForm = mapping(
|
||||||
"userName" -> trim(label("User name", text(required, maxlength(100), identifier, uniqueUserName, reservedNames))),
|
"userName" -> trim(label("User name", text(required, maxlength(100), identifier, uniqueUserName, reservedNames))),
|
||||||
"password" -> trim(label("Password", text(required, maxlength(20), password))),
|
"password" -> trim(label("Password", text(required, maxlength(20)))),
|
||||||
"fullName" -> trim(label("Full Name", text(required, maxlength(100)))),
|
"fullName" -> trim(label("Full Name", text(required, maxlength(100)))),
|
||||||
"mailAddress" -> trim(label("Mail Address", text(required, maxlength(100), uniqueMailAddress()))),
|
"mailAddress" -> trim(label("Mail Address", text(required, maxlength(100), uniqueMailAddress()))),
|
||||||
"extraMailAddresses" -> list(
|
"extraMailAddresses" -> list(
|
||||||
@@ -91,7 +95,7 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
|||||||
)(AccountNewForm.apply)
|
)(AccountNewForm.apply)
|
||||||
|
|
||||||
val editForm = mapping(
|
val editForm = mapping(
|
||||||
"password" -> trim(label("Password", optional(text(maxlength(20), password)))),
|
"password" -> trim(label("Password", optional(text(maxlength(20))))),
|
||||||
"fullName" -> trim(label("Full Name", text(required, maxlength(100)))),
|
"fullName" -> trim(label("Full Name", text(required, maxlength(100)))),
|
||||||
"mailAddress" -> trim(label("Mail Address", text(required, maxlength(100), uniqueMailAddress("userName")))),
|
"mailAddress" -> trim(label("Mail Address", text(required, maxlength(100), uniqueMailAddress("userName")))),
|
||||||
"extraMailAddresses" -> list(
|
"extraMailAddresses" -> list(
|
||||||
@@ -108,6 +112,11 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
|||||||
"publicKey" -> trim2(label("Key", text(required, validPublicKey)))
|
"publicKey" -> trim2(label("Key", text(required, validPublicKey)))
|
||||||
)(SshKeyForm.apply)
|
)(SshKeyForm.apply)
|
||||||
|
|
||||||
|
val gpgKeyForm = mapping(
|
||||||
|
"title" -> trim(label("Title", text(required, maxlength(100)))),
|
||||||
|
"publicKey" -> label("Key", text(required, validGpgPublicKey))
|
||||||
|
)(GpgKeyForm.apply)
|
||||||
|
|
||||||
val personalTokenForm = mapping(
|
val personalTokenForm = mapping(
|
||||||
"note" -> trim(label("Token", text(required, maxlength(100))))
|
"note" -> trim(label("Token", text(required, maxlength(100))))
|
||||||
)(PersonalTokenForm.apply)
|
)(PersonalTokenForm.apply)
|
||||||
@@ -247,12 +256,7 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
|||||||
account,
|
account,
|
||||||
members,
|
members,
|
||||||
extraMailAddresses,
|
extraMailAddresses,
|
||||||
context.loginAccount.exists(
|
isGroupManager(context.loginAccount, members)
|
||||||
x =>
|
|
||||||
members.exists { member =>
|
|
||||||
member.userName == x.userName && member.isManager
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -264,12 +268,7 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
|||||||
if (account.isGroupAccount) Nil else getGroupsByUserName(userName),
|
if (account.isGroupAccount) Nil else getGroupsByUserName(userName),
|
||||||
getVisibleRepositories(context.loginAccount, Some(userName)),
|
getVisibleRepositories(context.loginAccount, Some(userName)),
|
||||||
extraMailAddresses,
|
extraMailAddresses,
|
||||||
context.loginAccount.exists(
|
isGroupManager(context.loginAccount, members)
|
||||||
x =>
|
|
||||||
members.exists { member =>
|
|
||||||
member.userName == x.userName && member.isManager
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -338,7 +337,7 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
|||||||
|
|
||||||
updateImage(userName, form.fileId, form.clearImage)
|
updateImage(userName, form.fileId, form.clearImage)
|
||||||
updateAccountExtraMailAddresses(userName, form.extraMailAddresses.filter(_ != ""))
|
updateAccountExtraMailAddresses(userName, form.extraMailAddresses.filter(_ != ""))
|
||||||
flash += "info" -> "Account information has been updated."
|
flash.update("info", "Account information has been updated.")
|
||||||
redirect(s"/${userName}/_edit")
|
redirect(s"/${userName}/_edit")
|
||||||
|
|
||||||
} getOrElse NotFound()
|
} getOrElse NotFound()
|
||||||
@@ -350,7 +349,7 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
|||||||
getAccountByUserName(userName, true).map {
|
getAccountByUserName(userName, true).map {
|
||||||
account =>
|
account =>
|
||||||
if (isLastAdministrator(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")
|
redirect(s"/${userName}/_edit")
|
||||||
} else {
|
} else {
|
||||||
// // Remove repositories
|
// // Remove repositories
|
||||||
@@ -387,6 +386,27 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
|||||||
redirect(s"/${userName}/_ssh")
|
redirect(s"/${userName}/_ssh")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
get("/:userName/_gpg")(oneselfOnly {
|
||||||
|
val userName = params("userName")
|
||||||
|
getAccountByUserName(userName).map { x =>
|
||||||
|
//html.ssh(x, getPublicKeys(x.userName))
|
||||||
|
html.gpg(x, getGpgPublicKeys(x.userName))
|
||||||
|
} getOrElse NotFound()
|
||||||
|
})
|
||||||
|
|
||||||
|
post("/:userName/_gpg", gpgKeyForm)(oneselfOnly { form =>
|
||||||
|
val userName = params("userName")
|
||||||
|
addGpgPublicKey(userName, form.title, form.publicKey)
|
||||||
|
redirect(s"/${userName}/_gpg")
|
||||||
|
})
|
||||||
|
|
||||||
|
get("/:userName/_gpg/delete/:id")(oneselfOnly {
|
||||||
|
val userName = params("userName")
|
||||||
|
val keyId = params("id").toInt
|
||||||
|
deleteGpgPublicKey(userName, keyId)
|
||||||
|
redirect(s"/${userName}/_gpg")
|
||||||
|
})
|
||||||
|
|
||||||
get("/:userName/_application")(oneselfOnly {
|
get("/:userName/_application")(oneselfOnly {
|
||||||
val userName = params("userName")
|
val userName = params("userName")
|
||||||
getAccountByUserName(userName).map { x =>
|
getAccountByUserName(userName).map { x =>
|
||||||
@@ -409,7 +429,7 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
|||||||
val userName = params("userName")
|
val userName = params("userName")
|
||||||
getAccountByUserName(userName).map { x =>
|
getAccountByUserName(userName).map { x =>
|
||||||
val (tokenId, token) = generateAccessToken(userName, form.note)
|
val (tokenId, token) = generateAccessToken(userName, form.note)
|
||||||
flash += "generatedToken" -> (tokenId, token)
|
flash.update("generatedToken", (tokenId, token))
|
||||||
}
|
}
|
||||||
redirect(s"/${userName}/_application")
|
redirect(s"/${userName}/_application")
|
||||||
})
|
})
|
||||||
@@ -445,7 +465,7 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
|||||||
post("/:userName/_hooks/new", accountWebHookForm(false))(managersOnly { form =>
|
post("/:userName/_hooks/new", accountWebHookForm(false))(managersOnly { form =>
|
||||||
val userName = params("userName")
|
val userName = params("userName")
|
||||||
addAccountWebHook(userName, form.url, form.events, form.ctype, form.token)
|
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")
|
redirect(s"/${userName}/_hooks")
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -455,7 +475,7 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
|||||||
get("/:userName/_hooks/delete")(managersOnly {
|
get("/:userName/_hooks/delete")(managersOnly {
|
||||||
val userName = params("userName")
|
val userName = params("userName")
|
||||||
deleteAccountWebHook(userName, params("url"))
|
deleteAccountWebHook(userName, params("url"))
|
||||||
flash += "info" -> s"Webhook ${params("url")} deleted"
|
flash.update("info", s"Webhook ${params("url")} deleted")
|
||||||
redirect(s"/${userName}/_hooks")
|
redirect(s"/${userName}/_hooks")
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -478,7 +498,7 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
|||||||
post("/:userName/_hooks/edit", accountWebHookForm(true))(managersOnly { form =>
|
post("/:userName/_hooks/edit", accountWebHookForm(true))(managersOnly { form =>
|
||||||
val userName = params("userName")
|
val userName = params("userName")
|
||||||
updateAccountWebHook(userName, form.url, form.events, form.ctype, form.token)
|
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")
|
redirect(s"/${userName}/_hooks")
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -507,13 +527,14 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
|||||||
WebHookPushPayload.createDummyPayload(ownerAccount)
|
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]] = {
|
val toErrorMap: PartialFunction[Throwable, Map[String, String]] = {
|
||||||
case e: java.net.UnknownHostException => Map("error" -> ("Unknown host " + e.getMessage))
|
case e: java.net.UnknownHostException => Map("error" -> ("Unknown host " + e.getMessage))
|
||||||
case e: java.lang.IllegalArgumentException => Map("error" -> ("invalid url"))
|
case e: java.lang.IllegalArgumentException => Map("error" -> ("invalid url"))
|
||||||
case e: org.apache.http.client.ClientProtocolException => 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")
|
contentType = formats("json")
|
||||||
@@ -653,7 +674,7 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
|||||||
|
|
||||||
updateImage(form.groupName, form.fileId, form.clearImage)
|
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")
|
redirect(s"/${groupName}/_editgroup")
|
||||||
|
|
||||||
} getOrElse NotFound()
|
} getOrElse NotFound()
|
||||||
@@ -671,6 +692,7 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
|||||||
* Create new repository.
|
* Create new repository.
|
||||||
*/
|
*/
|
||||||
post("/new", newRepositoryForm)(usersOnly { form =>
|
post("/new", newRepositoryForm)(usersOnly { form =>
|
||||||
|
if (context.settings.repositoryOperation.create || context.loginAccount.get.isAdmin) {
|
||||||
LockUtil.lock(s"${form.owner}/${form.name}") {
|
LockUtil.lock(s"${form.owner}/${form.name}") {
|
||||||
if (getRepository(form.owner, form.name).isEmpty) {
|
if (getRepository(form.owner, form.name).isEmpty) {
|
||||||
createRepository(
|
createRepository(
|
||||||
@@ -684,14 +706,14 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// redirect to the repository
|
// redirect to the repository
|
||||||
redirect(s"/${form.owner}/${form.name}")
|
redirect(s"/${form.owner}/${form.name}")
|
||||||
|
} else Forbidden()
|
||||||
})
|
})
|
||||||
|
|
||||||
get("/:owner/:repository/fork")(readableUsersOnly { repository =>
|
get("/:owner/:repository/fork")(readableUsersOnly { repository =>
|
||||||
if (repository.repository.options.allowFork) {
|
|
||||||
val loginAccount = context.loginAccount.get
|
val loginAccount = context.loginAccount.get
|
||||||
|
if (repository.repository.options.allowFork && (context.settings.repositoryOperation.fork || loginAccount.isAdmin)) {
|
||||||
val loginUserName = loginAccount.userName
|
val loginUserName = loginAccount.userName
|
||||||
val groups = getGroupsByUserName(loginUserName)
|
val groups = getGroupsByUserName(loginUserName)
|
||||||
groups match {
|
groups match {
|
||||||
@@ -715,8 +737,8 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
|||||||
})
|
})
|
||||||
|
|
||||||
post("/:owner/:repository/fork", accountForm)(readableUsersOnly { (form, repository) =>
|
post("/:owner/:repository/fork", accountForm)(readableUsersOnly { (form, repository) =>
|
||||||
if (repository.repository.options.allowFork) {
|
|
||||||
val loginAccount = context.loginAccount.get
|
val loginAccount = context.loginAccount.get
|
||||||
|
if (repository.repository.options.allowFork && (context.settings.repositoryOperation.fork || loginAccount.isAdmin)) {
|
||||||
val loginUserName = loginAccount.userName
|
val loginUserName = loginAccount.userName
|
||||||
val accountName = form.accountName
|
val accountName = form.accountName
|
||||||
|
|
||||||
@@ -730,7 +752,7 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
|||||||
// redirect to the repository
|
// redirect to the repository
|
||||||
redirect(s"/${accountName}/${repository.name}")
|
redirect(s"/${accountName}/${repository.name}")
|
||||||
}
|
}
|
||||||
} else BadRequest()
|
} else Forbidden()
|
||||||
})
|
})
|
||||||
|
|
||||||
private def existsAccount: Constraint = new Constraint() {
|
private def existsAccount: Constraint = new Constraint() {
|
||||||
@@ -771,6 +793,20 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private def validGpgPublicKey: Constraint = new Constraint() {
|
||||||
|
override def validate(name: String, value: String, messages: Messages): Option[String] = {
|
||||||
|
GpgUtil.str2GpgKeyId(value) match {
|
||||||
|
case Some(s) if GpgUtil.getGpgKey(s).isEmpty =>
|
||||||
|
None
|
||||||
|
case Some(_) =>
|
||||||
|
Some("GPG key is duplicated.")
|
||||||
|
case None =>
|
||||||
|
Some("GPG key is invalid.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private def validAccountName: Constraint = new Constraint() {
|
private def validAccountName: Constraint = new Constraint() {
|
||||||
override def validate(name: String, value: String, messages: Messages): Option[String] = {
|
override def validate(name: String, value: String, messages: Messages): Option[String] = {
|
||||||
getAccountByUserName(value) match {
|
getAccountByUserName(value) match {
|
||||||
@@ -779,4 +815,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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,9 +15,11 @@ class ApiController
|
|||||||
with ApiIssueLabelControllerBase
|
with ApiIssueLabelControllerBase
|
||||||
with ApiOrganizationControllerBase
|
with ApiOrganizationControllerBase
|
||||||
with ApiPullRequestControllerBase
|
with ApiPullRequestControllerBase
|
||||||
|
with ApiReleaseControllerBase
|
||||||
with ApiRepositoryBranchControllerBase
|
with ApiRepositoryBranchControllerBase
|
||||||
with ApiRepositoryCollaboratorControllerBase
|
with ApiRepositoryCollaboratorControllerBase
|
||||||
with ApiRepositoryCommitControllerBase
|
with ApiRepositoryCommitControllerBase
|
||||||
|
with ApiRepositoryContentsControllerBase
|
||||||
with ApiRepositoryControllerBase
|
with ApiRepositoryControllerBase
|
||||||
with ApiRepositoryStatusControllerBase
|
with ApiRepositoryStatusControllerBase
|
||||||
with ApiUserControllerBase
|
with ApiUserControllerBase
|
||||||
@@ -30,7 +32,9 @@ class ApiController
|
|||||||
with PullRequestService
|
with PullRequestService
|
||||||
with CommitsService
|
with CommitsService
|
||||||
with CommitStatusService
|
with CommitStatusService
|
||||||
|
with ReleaseService
|
||||||
with RepositoryCreationService
|
with RepositoryCreationService
|
||||||
|
with RepositoryCommitFileService
|
||||||
with IssueCreationService
|
with IssueCreationService
|
||||||
with HandleCommentService
|
with HandleCommentService
|
||||||
with MergeService
|
with MergeService
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import javax.servlet.{FilterChain, ServletRequest, ServletResponse}
|
|||||||
import is.tagomor.woothee.Classifier
|
import is.tagomor.woothee.Classifier
|
||||||
|
|
||||||
import scala.util.Try
|
import scala.util.Try
|
||||||
|
import scala.util.Using
|
||||||
import net.coobird.thumbnailator.Thumbnails
|
import net.coobird.thumbnailator.Thumbnails
|
||||||
import org.eclipse.jgit.api.Git
|
import org.eclipse.jgit.api.Git
|
||||||
import org.eclipse.jgit.lib.ObjectId
|
import org.eclipse.jgit.lib.ObjectId
|
||||||
@@ -240,7 +241,7 @@ abstract class ControllerBase
|
|||||||
case false => None
|
case false => None
|
||||||
}
|
}
|
||||||
|
|
||||||
using(new TreeWalk(git.getRepository)) { treeWalk =>
|
Using.resource(new TreeWalk(git.getRepository)) { treeWalk =>
|
||||||
treeWalk.addTree(revCommit.getTree)
|
treeWalk.addTree(revCommit.getTree)
|
||||||
treeWalk.setRecursive(true)
|
treeWalk.setRecursive(true)
|
||||||
_getPathObjectId(path, treeWalk)
|
_getPathObjectId(path, treeWalk)
|
||||||
@@ -268,7 +269,7 @@ abstract class ControllerBase
|
|||||||
response.setContentLength(attrs("size").toInt)
|
response.setContentLength(attrs("size").toInt)
|
||||||
val oid = attrs("oid").split(":")(1)
|
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)
|
IOUtils.copy(in, response.getOutputStream)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -324,6 +325,8 @@ case class Context(
|
|||||||
trait AccountManagementControllerBase extends ControllerBase {
|
trait AccountManagementControllerBase extends ControllerBase {
|
||||||
self: AccountService =>
|
self: AccountService =>
|
||||||
|
|
||||||
|
private val logger = LoggerFactory.getLogger(getClass)
|
||||||
|
|
||||||
protected def updateImage(userName: String, fileId: Option[String], clearImage: Boolean): Unit =
|
protected def updateImage(userName: String, fileId: Option[String], clearImage: Boolean): Unit =
|
||||||
if (clearImage) {
|
if (clearImage) {
|
||||||
getAccountByUserName(userName).flatMap(_.image).foreach { image =>
|
getAccountByUserName(userName).flatMap(_.image).foreach { image =>
|
||||||
@@ -331,6 +334,7 @@ trait AccountManagementControllerBase extends ControllerBase {
|
|||||||
updateAvatarImage(userName, None)
|
updateAvatarImage(userName, None)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
try {
|
||||||
fileId.foreach { fileId =>
|
fileId.foreach { fileId =>
|
||||||
val filename = "avatar." + FileUtil.getExtension(session.getAndRemove(Keys.Session.Upload(fileId)).get)
|
val filename = "avatar." + FileUtil.getExtension(session.getAndRemove(Keys.Session.Upload(fileId)).get)
|
||||||
val uploadDir = getUserUploadDir(userName)
|
val uploadDir = getUserUploadDir(userName)
|
||||||
@@ -343,6 +347,9 @@ trait AccountManagementControllerBase extends ControllerBase {
|
|||||||
.toFile(new File(uploadDir, FileUtil.checkFilename(filename)))
|
.toFile(new File(uploadDir, FileUtil.checkFilename(filename)))
|
||||||
updateAvatarImage(userName, Some(filename))
|
updateAvatarImage(userName, Some(filename))
|
||||||
}
|
}
|
||||||
|
} catch {
|
||||||
|
case e: Exception => logger.info("Error while updateImage" + e.getMessage)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected def uniqueUserName: Constraint = new Constraint() {
|
protected def uniqueUserName: Constraint = new Constraint() {
|
||||||
@@ -359,7 +366,7 @@ trait AccountManagementControllerBase extends ControllerBase {
|
|||||||
params: Map[String, Seq[String]],
|
params: Map[String, Seq[String]],
|
||||||
messages: Messages
|
messages: Messages
|
||||||
): Option[String] = {
|
): Option[String] = {
|
||||||
val extraMailAddresses = params.filterKeys(k => k.startsWith("extraMailAddresses"))
|
val extraMailAddresses = params.view.filterKeys(k => k.startsWith("extraMailAddresses"))
|
||||||
if (extraMailAddresses.exists {
|
if (extraMailAddresses.exists {
|
||||||
case (k, v) =>
|
case (k, v) =>
|
||||||
v.contains(value)
|
v.contains(value)
|
||||||
@@ -382,7 +389,7 @@ trait AccountManagementControllerBase extends ControllerBase {
|
|||||||
params: Map[String, Seq[String]],
|
params: Map[String, Seq[String]],
|
||||||
messages: Messages
|
messages: Messages
|
||||||
): Option[String] = {
|
): 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 {
|
if (Some(value) == params.optionValue("mailAddress") || extraMailAddresses.count {
|
||||||
case (k, v) =>
|
case (k, v) =>
|
||||||
v.contains(value)
|
v.contains(value)
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import gitbucket.core.service.IssuesService._
|
|||||||
class DashboardController
|
class DashboardController
|
||||||
extends DashboardControllerBase
|
extends DashboardControllerBase
|
||||||
with IssuesService
|
with IssuesService
|
||||||
|
with MergeService
|
||||||
with PullRequestService
|
with PullRequestService
|
||||||
with RepositoryService
|
with RepositoryService
|
||||||
with AccountService
|
with AccountService
|
||||||
@@ -26,7 +27,12 @@ trait DashboardControllerBase extends ControllerBase {
|
|||||||
self: IssuesService with PullRequestService with RepositoryService with AccountService with UsersAuthenticator =>
|
self: IssuesService with PullRequestService with RepositoryService with AccountService with UsersAuthenticator =>
|
||||||
|
|
||||||
get("/dashboard/repos")(usersOnly {
|
get("/dashboard/repos")(usersOnly {
|
||||||
val repos = getVisibleRepositories(context.loginAccount, withoutPhysicalInfo = true)
|
val repos = getVisibleRepositories(
|
||||||
|
context.loginAccount,
|
||||||
|
None,
|
||||||
|
withoutPhysicalInfo = true,
|
||||||
|
limit = context.settings.limitVisibleRepositories
|
||||||
|
)
|
||||||
html.repos(getGroupNames(context.loginAccount.get.userName), repos, repos)
|
html.repos(getGroupNames(context.loginAccount.get.userName), repos, repos)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -92,7 +98,12 @@ trait DashboardControllerBase extends ControllerBase {
|
|||||||
},
|
},
|
||||||
filter,
|
filter,
|
||||||
getGroupNames(userName),
|
getGroupNames(userName),
|
||||||
getVisibleRepositories(context.loginAccount, withoutPhysicalInfo = true)
|
getVisibleRepositories(
|
||||||
|
context.loginAccount,
|
||||||
|
None,
|
||||||
|
withoutPhysicalInfo = true,
|
||||||
|
limit = context.settings.limitVisibleRepositories
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,7 +128,12 @@ trait DashboardControllerBase extends ControllerBase {
|
|||||||
},
|
},
|
||||||
filter,
|
filter,
|
||||||
getGroupNames(userName),
|
getGroupNames(userName),
|
||||||
getVisibleRepositories(context.loginAccount, withoutPhysicalInfo = true)
|
getVisibleRepositories(
|
||||||
|
context.loginAccount,
|
||||||
|
None,
|
||||||
|
withoutPhysicalInfo = true,
|
||||||
|
limit = context.settings.limitVisibleRepositories
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,9 @@ import org.scalatra._
|
|||||||
import org.scalatra.servlet.{FileItem, FileUploadSupport, MultipartConfig}
|
import org.scalatra.servlet.{FileItem, FileUploadSupport, MultipartConfig}
|
||||||
import org.apache.commons.io.{FileUtils, IOUtils}
|
import org.apache.commons.io.{FileUtils, IOUtils}
|
||||||
|
|
||||||
|
import scala.util.Using
|
||||||
|
import gitbucket.core.service.SystemSettingsService
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides Ajax based file upload functionality.
|
* Provides Ajax based file upload functionality.
|
||||||
*
|
*
|
||||||
@@ -26,11 +29,11 @@ class FileUploadController
|
|||||||
with FileUploadSupport
|
with FileUploadSupport
|
||||||
with RepositoryService
|
with RepositoryService
|
||||||
with AccountService
|
with AccountService
|
||||||
with ReleaseService {
|
with ReleaseService
|
||||||
|
with SystemSettingsService {
|
||||||
configureMultipartHandling(MultipartConfig(maxFileSize = Some(FileUtil.MaxFileSize)))
|
|
||||||
|
|
||||||
post("/image") {
|
post("/image") {
|
||||||
|
setMultipartConfig()
|
||||||
execute(
|
execute(
|
||||||
{ (file, fileId) =>
|
{ (file, fileId) =>
|
||||||
FileUtils
|
FileUtils
|
||||||
@@ -42,6 +45,7 @@ class FileUploadController
|
|||||||
}
|
}
|
||||||
|
|
||||||
post("/tmp") {
|
post("/tmp") {
|
||||||
|
setMultipartConfig()
|
||||||
execute(
|
execute(
|
||||||
{ (file, fileId) =>
|
{ (file, fileId) =>
|
||||||
FileUtils
|
FileUtils
|
||||||
@@ -53,6 +57,7 @@ class FileUploadController
|
|||||||
}
|
}
|
||||||
|
|
||||||
post("/file/:owner/:repository") {
|
post("/file/:owner/:repository") {
|
||||||
|
setMultipartConfig()
|
||||||
execute(
|
execute(
|
||||||
{ (file, fileId) =>
|
{ (file, fileId) =>
|
||||||
FileUtils.writeByteArrayToFile(
|
FileUtils.writeByteArrayToFile(
|
||||||
@@ -68,6 +73,7 @@ class FileUploadController
|
|||||||
}
|
}
|
||||||
|
|
||||||
post("/wiki/:owner/:repository") {
|
post("/wiki/:owner/:repository") {
|
||||||
|
setMultipartConfig()
|
||||||
// Don't accept not logged-in users
|
// Don't accept not logged-in users
|
||||||
session.get(Keys.Session.LoginAccount).collect {
|
session.get(Keys.Session.LoginAccount).collect {
|
||||||
case loginAccount: Account =>
|
case loginAccount: Account =>
|
||||||
@@ -80,7 +86,7 @@ class FileUploadController
|
|||||||
{ (file, fileId) =>
|
{ (file, fileId) =>
|
||||||
val fileName = file.getName
|
val fileName = file.getName
|
||||||
LockUtil.lock(s"${owner}/${repository}/wiki") {
|
LockUtil.lock(s"${owner}/${repository}/wiki") {
|
||||||
using(Git.open(Directory.getWikiRepositoryDir(owner, repository))) {
|
Using.resource(Git.open(Directory.getWikiRepositoryDir(owner, repository))) {
|
||||||
git =>
|
git =>
|
||||||
val builder = DirCache.newInCore.builder()
|
val builder = DirCache.newInCore.builder()
|
||||||
val inserter = git.getRepository.newObjectInserter()
|
val inserter = git.getRepository.newObjectInserter()
|
||||||
@@ -126,6 +132,7 @@ class FileUploadController
|
|||||||
}
|
}
|
||||||
|
|
||||||
post("/release/:owner/:repository/:tag") {
|
post("/release/:owner/:repository/:tag") {
|
||||||
|
setMultipartConfigForLargeFile()
|
||||||
session
|
session
|
||||||
.get(Keys.Session.LoginAccount)
|
.get(Keys.Session.LoginAccount)
|
||||||
.collect {
|
.collect {
|
||||||
@@ -148,6 +155,7 @@ class FileUploadController
|
|||||||
|
|
||||||
post("/import") {
|
post("/import") {
|
||||||
import JDBCUtil._
|
import JDBCUtil._
|
||||||
|
setMultipartConfig()
|
||||||
session.get(Keys.Session.LoginAccount).collect {
|
session.get(Keys.Session.LoginAccount).collect {
|
||||||
case loginAccount: Account if loginAccount.isAdmin =>
|
case loginAccount: Account if loginAccount.isAdmin =>
|
||||||
execute({ (file, fileId) =>
|
execute({ (file, fileId) =>
|
||||||
@@ -157,6 +165,18 @@ class FileUploadController
|
|||||||
redirect("/admin/data")
|
redirect("/admin/data")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private def setMultipartConfig(): Unit = {
|
||||||
|
val settings = loadSystemSettings()
|
||||||
|
val config = MultipartConfig(maxFileSize = Some(settings.upload.maxFileSize))
|
||||||
|
config.apply(request.getServletContext())
|
||||||
|
}
|
||||||
|
|
||||||
|
private def setMultipartConfigForLargeFile(): Unit = {
|
||||||
|
val settings = loadSystemSettings()
|
||||||
|
val config = MultipartConfig(maxFileSize = Some(settings.upload.largeMaxFileSize))
|
||||||
|
config.apply(request.getServletContext())
|
||||||
|
}
|
||||||
|
|
||||||
private def onlyWikiEditable(owner: String, repository: String, loginAccount: Account)(action: => Any): Any = {
|
private def onlyWikiEditable(owner: String, repository: String, loginAccount: Account)(action: => Any): Any = {
|
||||||
implicit val session = Database.getSession(request)
|
implicit val session = Database.getSession(request)
|
||||||
getRepository(owner, repository) match {
|
getRepository(owner, repository) match {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import gitbucket.core.service._
|
|||||||
import gitbucket.core.util.Implicits._
|
import gitbucket.core.util.Implicits._
|
||||||
import gitbucket.core.util.SyntaxSugars._
|
import gitbucket.core.util.SyntaxSugars._
|
||||||
import gitbucket.core.util._
|
import gitbucket.core.util._
|
||||||
|
import gitbucket.core.view.helpers._
|
||||||
import org.scalatra.Ok
|
import org.scalatra.Ok
|
||||||
import org.scalatra.forms._
|
import org.scalatra.forms._
|
||||||
|
|
||||||
@@ -64,7 +65,12 @@ trait IndexControllerBase extends ControllerBase {
|
|||||||
val visibleOwnerSet: Set[String] = Set(account.userName) ++ getGroupsByUserName(account.userName)
|
val visibleOwnerSet: Set[String] = Set(account.userName) ++ getGroupsByUserName(account.userName)
|
||||||
gitbucket.core.html.index(
|
gitbucket.core.html.index(
|
||||||
getRecentActivitiesByOwners(visibleOwnerSet),
|
getRecentActivitiesByOwners(visibleOwnerSet),
|
||||||
getVisibleRepositories(Some(account), withoutPhysicalInfo = true),
|
getVisibleRepositories(
|
||||||
|
Some(account),
|
||||||
|
None,
|
||||||
|
withoutPhysicalInfo = true,
|
||||||
|
limit = context.settings.limitVisibleRepositories
|
||||||
|
),
|
||||||
showBannerToCreatePersonalAccessToken = hasAccountFederation(account.userName) && !hasAccessToken(
|
showBannerToCreatePersonalAccessToken = hasAccountFederation(account.userName) && !hasAccessToken(
|
||||||
account.userName
|
account.userName
|
||||||
)
|
)
|
||||||
@@ -82,7 +88,7 @@ trait IndexControllerBase extends ControllerBase {
|
|||||||
get("/signin") {
|
get("/signin") {
|
||||||
val redirect = params.get("redirect")
|
val redirect = params.get("redirect")
|
||||||
if (redirect.isDefined && redirect.get.startsWith("/")) {
|
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"))
|
gitbucket.core.html.signin(flash.get("userName"), flash.get("password"), flash.get("error"))
|
||||||
}
|
}
|
||||||
@@ -95,9 +101,9 @@ trait IndexControllerBase extends ControllerBase {
|
|||||||
case _ => signin(account)
|
case _ => signin(account)
|
||||||
}
|
}
|
||||||
case None =>
|
case None =>
|
||||||
flash += "userName" -> form.userName
|
flash.update("userName", form.userName)
|
||||||
flash += "password" -> form.password
|
flash.update("password", form.password)
|
||||||
flash += "error" -> "Sorry, your Username and/or Password is incorrect. Please try again."
|
flash.update("error", "Sorry, your Username and/or Password is incorrect. Please try again.")
|
||||||
redirect("/signin")
|
redirect("/signin")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -131,15 +137,15 @@ trait IndexControllerBase extends ControllerBase {
|
|||||||
val redirectURI = new URI(s"$baseUrl/signin/oidc")
|
val redirectURI = new URI(s"$baseUrl/signin/oidc")
|
||||||
session.get(Keys.Session.OidcContext) match {
|
session.get(Keys.Session.OidcContext) match {
|
||||||
case Some(context: OidcContext) =>
|
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)
|
signin(account, context.redirectBackURI)
|
||||||
} orElse {
|
} orElse {
|
||||||
flash += "error" -> "Sorry, authentication failed. Please try again."
|
flash.update("error", "Sorry, authentication failed. Please try again.")
|
||||||
session.invalidate()
|
session.invalidate()
|
||||||
redirect("/signin")
|
redirect("/signin")
|
||||||
}
|
}
|
||||||
case _ =>
|
case _ =>
|
||||||
flash += "error" -> "Sorry, something wrong. Please try again."
|
flash.update("error", "Sorry, something wrong. Please try again.")
|
||||||
session.invalidate()
|
session.invalidate()
|
||||||
redirect("/signin")
|
redirect("/signin")
|
||||||
}
|
}
|
||||||
@@ -206,7 +212,8 @@ trait IndexControllerBase extends ControllerBase {
|
|||||||
}
|
}
|
||||||
.map { t =>
|
.map { t =>
|
||||||
Map(
|
Map(
|
||||||
"label" -> s"<b>@${StringUtil.escapeHtml(t.userName)}</b> ${StringUtil.escapeHtml(t.fullName)}",
|
"label" -> s"${avatar(t.userName, 16)}<b>@${StringUtil.escapeHtml(t.userName)}</b> ${StringUtil
|
||||||
|
.escapeHtml(t.fullName)}",
|
||||||
"value" -> t.userName
|
"value" -> t.userName
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -225,7 +232,7 @@ trait IndexControllerBase extends ControllerBase {
|
|||||||
} getOrElse ""
|
} getOrElse ""
|
||||||
})
|
})
|
||||||
|
|
||||||
// TODO Move to RepositoryViwerController?
|
// TODO Move to RepositoryViewrController?
|
||||||
get("/:owner/:repository/search")(referrersOnly { repository =>
|
get("/:owner/:repository/search")(referrersOnly { repository =>
|
||||||
defining(params.getOrElse("q", "").trim, params.getOrElse("type", "code")) {
|
defining(params.getOrElse("q", "").trim, params.getOrElse("type", "code")) {
|
||||||
case (query, target) =>
|
case (query, target) =>
|
||||||
@@ -277,11 +284,28 @@ trait IndexControllerBase extends ControllerBase {
|
|||||||
get("/search") {
|
get("/search") {
|
||||||
val query = params.getOrElse("query", "").trim.toLowerCase
|
val query = params.getOrElse("query", "").trim.toLowerCase
|
||||||
val visibleRepositories =
|
val visibleRepositories =
|
||||||
getVisibleRepositories(context.loginAccount, repositoryUserName = None, withoutPhysicalInfo = true)
|
getVisibleRepositories(
|
||||||
val repositories = visibleRepositories.filter { repository =>
|
context.loginAccount,
|
||||||
|
None,
|
||||||
|
withoutPhysicalInfo = true,
|
||||||
|
limit = context.settings.limitVisibleRepositories
|
||||||
|
)
|
||||||
|
|
||||||
|
val repositories = {
|
||||||
|
context.settings.limitVisibleRepositories match {
|
||||||
|
case true =>
|
||||||
|
getVisibleRepositories(
|
||||||
|
context.loginAccount,
|
||||||
|
None,
|
||||||
|
withoutPhysicalInfo = true,
|
||||||
|
limit = false
|
||||||
|
)
|
||||||
|
case false => visibleRepositories
|
||||||
|
}
|
||||||
|
}.filter { repository =>
|
||||||
repository.name.toLowerCase.indexOf(query) >= 0 || repository.owner.toLowerCase.indexOf(query) >= 0
|
repository.name.toLowerCase.indexOf(query) >= 0 || repository.owner.toLowerCase.indexOf(query) >= 0
|
||||||
}
|
}
|
||||||
|
|
||||||
gitbucket.core.search.html.repositories(query, repositories, visibleRepositories)
|
gitbucket.core.search.html.repositories(query, repositories, visibleRepositories)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ class IssuesController
|
|||||||
with ReadableUsersAuthenticator
|
with ReadableUsersAuthenticator
|
||||||
with ReferrerAuthenticator
|
with ReferrerAuthenticator
|
||||||
with WritableUsersAuthenticator
|
with WritableUsersAuthenticator
|
||||||
|
with MergeService
|
||||||
with PullRequestService
|
with PullRequestService
|
||||||
with WebHookIssueCommentService
|
with WebHookIssueCommentService
|
||||||
with WebHookPullRequestReviewCommentService
|
with WebHookPullRequestReviewCommentService
|
||||||
@@ -144,7 +145,7 @@ trait IssuesControllerBase extends ControllerBase {
|
|||||||
form.assignedUserName,
|
form.assignedUserName,
|
||||||
form.milestoneId,
|
form.milestoneId,
|
||||||
form.priorityId,
|
form.priorityId,
|
||||||
form.labelNames.toArray.flatMap(_.split(",")),
|
form.labelNames.toSeq.flatMap(_.split(",")),
|
||||||
context.loginAccount.get
|
context.loginAccount.get
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
package gitbucket.core.controller
|
package gitbucket.core.controller
|
||||||
|
|
||||||
import gitbucket.core.issues.milestones.html
|
import gitbucket.core.issues.milestones.html
|
||||||
import gitbucket.core.service.{RepositoryService, MilestonesService, AccountService}
|
import gitbucket.core.service.{AccountService, MilestonesService, RepositoryService}
|
||||||
import gitbucket.core.util.{ReferrerAuthenticator, WritableUsersAuthenticator}
|
|
||||||
import gitbucket.core.util.Implicits._
|
import gitbucket.core.util.Implicits._
|
||||||
|
import gitbucket.core.util.{ReferrerAuthenticator, WritableUsersAuthenticator}
|
||||||
|
import gitbucket.core.util.SyntaxSugars._
|
||||||
import org.scalatra.forms._
|
import org.scalatra.forms._
|
||||||
|
import org.scalatra.i18n.Messages
|
||||||
|
|
||||||
class MilestonesController
|
class MilestonesController
|
||||||
extends MilestonesControllerBase
|
extends MilestonesControllerBase
|
||||||
@@ -20,7 +22,7 @@ trait MilestonesControllerBase extends ControllerBase {
|
|||||||
case class MilestoneForm(title: String, description: Option[String], dueDate: Option[java.util.Date])
|
case class MilestoneForm(title: String, description: Option[String], dueDate: Option[java.util.Date])
|
||||||
|
|
||||||
val milestoneForm = mapping(
|
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()))),
|
"description" -> trim(label("Description", optional(text()))),
|
||||||
"dueDate" -> trim(label("Due Date", optional(date())))
|
"dueDate" -> trim(label("Due Date", optional(date())))
|
||||||
)(MilestoneForm.apply)
|
)(MilestoneForm.apply)
|
||||||
@@ -86,4 +88,29 @@ trait MilestonesControllerBase extends ControllerBase {
|
|||||||
} getOrElse NotFound()
|
} 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
|
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.pulls.html
|
||||||
import gitbucket.core.service.CommitStatusService
|
import gitbucket.core.service.CommitStatusService
|
||||||
import gitbucket.core.service.MergeService
|
import gitbucket.core.service.MergeService
|
||||||
@@ -15,11 +13,9 @@ import gitbucket.core.util.Implicits._
|
|||||||
import gitbucket.core.util._
|
import gitbucket.core.util._
|
||||||
import org.scalatra.forms._
|
import org.scalatra.forms._
|
||||||
import org.eclipse.jgit.api.Git
|
import org.eclipse.jgit.api.Git
|
||||||
import org.eclipse.jgit.lib.{ObjectId, PersonIdent}
|
|
||||||
import org.eclipse.jgit.revwalk.RevWalk
|
|
||||||
import org.scalatra.BadRequest
|
import org.scalatra.BadRequest
|
||||||
|
|
||||||
import scala.collection.JavaConverters._
|
import scala.util.Using
|
||||||
|
|
||||||
class PullRequestsController
|
class PullRequestsController
|
||||||
extends PullRequestsControllerBase
|
extends PullRequestsControllerBase
|
||||||
@@ -69,6 +65,7 @@ trait PullRequestsControllerBase extends ControllerBase {
|
|||||||
"requestBranch" -> trim(text(required, maxlength(100))),
|
"requestBranch" -> trim(text(required, maxlength(100))),
|
||||||
"commitIdFrom" -> trim(text(required, maxlength(40))),
|
"commitIdFrom" -> trim(text(required, maxlength(40))),
|
||||||
"commitIdTo" -> trim(text(required, maxlength(40))),
|
"commitIdTo" -> trim(text(required, maxlength(40))),
|
||||||
|
"isDraft" -> trim(boolean(required)),
|
||||||
"assignedUserName" -> trim(optional(text())),
|
"assignedUserName" -> trim(optional(text())),
|
||||||
"milestoneId" -> trim(optional(number())),
|
"milestoneId" -> trim(optional(number())),
|
||||||
"priorityId" -> trim(optional(number())),
|
"priorityId" -> trim(optional(number())),
|
||||||
@@ -77,7 +74,8 @@ trait PullRequestsControllerBase extends ControllerBase {
|
|||||||
|
|
||||||
val mergeForm = mapping(
|
val mergeForm = mapping(
|
||||||
"message" -> trim(label("Message", text(required))),
|
"message" -> trim(label("Message", text(required))),
|
||||||
"strategy" -> trim(label("Strategy", text(required)))
|
"strategy" -> trim(label("Strategy", text(required))),
|
||||||
|
"isDraft" -> trim(boolean(required))
|
||||||
)(MergeForm.apply)
|
)(MergeForm.apply)
|
||||||
|
|
||||||
case class PullRequestForm(
|
case class PullRequestForm(
|
||||||
@@ -90,13 +88,14 @@ trait PullRequestsControllerBase extends ControllerBase {
|
|||||||
requestBranch: String,
|
requestBranch: String,
|
||||||
commitIdFrom: String,
|
commitIdFrom: String,
|
||||||
commitIdTo: String,
|
commitIdTo: String,
|
||||||
|
isDraft: Boolean,
|
||||||
assignedUserName: Option[String],
|
assignedUserName: Option[String],
|
||||||
milestoneId: Option[Int],
|
milestoneId: Option[Int],
|
||||||
priorityId: Option[Int],
|
priorityId: Option[Int],
|
||||||
labelNames: Option[String]
|
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 =>
|
get("/:owner/:repository/pulls")(referrersOnly { repository =>
|
||||||
val q = request.getParameter("q")
|
val q = request.getParameter("q")
|
||||||
@@ -133,7 +132,7 @@ trait PullRequestsControllerBase extends ControllerBase {
|
|||||||
hasDeveloperRole(pullreq.requestUserName, pullreq.requestRepositoryName, context.loginAccount),
|
hasDeveloperRole(pullreq.requestUserName, pullreq.requestRepositoryName, context.loginAccount),
|
||||||
repository,
|
repository,
|
||||||
getRepository(pullreq.requestUserName, pullreq.requestRepositoryName),
|
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(
|
// html.pullreq(
|
||||||
@@ -266,11 +265,11 @@ trait PullRequestsControllerBase extends ControllerBase {
|
|||||||
val repository = getRepository(owner, name).get
|
val repository = getRepository(owner, name).get
|
||||||
val branchProtection = getProtectedBranchInfo(owner, name, pullreq.requestBranch)
|
val branchProtection = getProtectedBranchInfo(owner, name, pullreq.requestBranch)
|
||||||
if (branchProtection.enabled) {
|
if (branchProtection.enabled) {
|
||||||
flash += "error" -> s"branch ${pullreq.requestBranch} is protected."
|
flash.update("error", s"branch ${pullreq.requestBranch} is protected.")
|
||||||
} else {
|
} else {
|
||||||
if (repository.repository.defaultBranch != pullreq.requestBranch) {
|
if (repository.repository.defaultBranch != pullreq.requestBranch) {
|
||||||
val userName = context.loginAccount.get.userName
|
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()
|
git.branchDelete().setForce(true).setBranchNames(pullreq.requestBranch).call()
|
||||||
recordDeleteBranchActivity(repository.owner, repository.name, userName, pullreq.requestBranch)
|
recordDeleteBranchActivity(repository.owner, repository.name, userName, pullreq.requestBranch)
|
||||||
}
|
}
|
||||||
@@ -283,9 +282,10 @@ trait PullRequestsControllerBase extends ControllerBase {
|
|||||||
"delete_branch"
|
"delete_branch"
|
||||||
)
|
)
|
||||||
} else {
|
} 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}")
|
redirect(s"/${baseRepository.owner}/${baseRepository.name}/pull/${issueId}")
|
||||||
}) getOrElse NotFound()
|
}) getOrElse NotFound()
|
||||||
})
|
})
|
||||||
@@ -303,7 +303,7 @@ trait PullRequestsControllerBase extends ControllerBase {
|
|||||||
} yield {
|
} yield {
|
||||||
val branchProtection = getProtectedBranchInfo(owner, name, pullreq.requestBranch)
|
val branchProtection = getProtectedBranchInfo(owner, name, pullreq.requestBranch)
|
||||||
if (branchProtection.needStatusCheck(loginAccount.userName)) {
|
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 {
|
} else {
|
||||||
LockUtil.lock(s"${owner}/${name}") {
|
LockUtil.lock(s"${owner}/${name}") {
|
||||||
val alias =
|
val alias =
|
||||||
@@ -312,9 +312,11 @@ trait PullRequestsControllerBase extends ControllerBase {
|
|||||||
} else {
|
} else {
|
||||||
s"${pullreq.userName}:${pullreq.branch}"
|
s"${pullreq.userName}:${pullreq.branch}"
|
||||||
}
|
}
|
||||||
val existIds = using(Git.open(Directory.getRepositoryDir(owner, name))) { git =>
|
val existIds = Using
|
||||||
|
.resource(Git.open(Directory.getRepositoryDir(owner, name))) { git =>
|
||||||
JGitUtil.getAllCommitIds(git)
|
JGitUtil.getAllCommitIds(git)
|
||||||
}.toSet
|
}
|
||||||
|
.toSet
|
||||||
pullRemote(
|
pullRemote(
|
||||||
repository,
|
repository,
|
||||||
pullreq.requestBranch,
|
pullreq.requestBranch,
|
||||||
@@ -322,14 +324,15 @@ trait PullRequestsControllerBase extends ControllerBase {
|
|||||||
pullreq.branch,
|
pullreq.branch,
|
||||||
loginAccount,
|
loginAccount,
|
||||||
s"Merge branch '${alias}' into ${pullreq.requestBranch}",
|
s"Merge branch '${alias}' into ${pullreq.requestBranch}",
|
||||||
Some(pullreq)
|
Some(pullreq),
|
||||||
|
context.settings
|
||||||
) match {
|
) match {
|
||||||
case None => // conflict
|
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) =>
|
case Some(oldId) =>
|
||||||
// update pull request
|
// update pull request
|
||||||
updatePullRequests(owner, name, pullreq.requestBranch, loginAccount, "synchronize")
|
updatePullRequests(owner, name, pullreq.requestBranch, loginAccount, "synchronize", context.settings)
|
||||||
flash += "info" -> s"Merge branch '${alias}' into ${pullreq.requestBranch}"
|
flash.update("info", s"Merge branch '${alias}' into ${pullreq.requestBranch}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -338,14 +341,34 @@ trait PullRequestsControllerBase extends ControllerBase {
|
|||||||
}) getOrElse NotFound()
|
}) 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) =>
|
post("/:owner/:repository/pull/:id/merge", mergeForm)(writableUsersOnly { (form, repository) =>
|
||||||
params("id").toIntOpt.flatMap { issueId =>
|
params("id").toIntOpt.flatMap { issueId =>
|
||||||
val owner = repository.owner
|
val owner = repository.owner
|
||||||
val name = repository.name
|
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 Right(objectId) => redirect(s"/${owner}/${name}/pull/${issueId}")
|
||||||
case Left(message) => Some(BadRequest())
|
case Left(message) => Some(BadRequest(message))
|
||||||
}
|
}
|
||||||
} getOrElse NotFound()
|
} getOrElse NotFound()
|
||||||
})
|
})
|
||||||
@@ -356,7 +379,7 @@ trait PullRequestsControllerBase extends ControllerBase {
|
|||||||
case (Some(originUserName), Some(originRepositoryName)) => {
|
case (Some(originUserName), Some(originRepositoryName)) => {
|
||||||
getRepository(originUserName, originRepositoryName).map {
|
getRepository(originUserName, originRepositoryName).map {
|
||||||
originRepository =>
|
originRepository =>
|
||||||
using(
|
Using.resources(
|
||||||
Git.open(getRepositoryDir(originUserName, originRepositoryName)),
|
Git.open(getRepositoryDir(originUserName, originRepositoryName)),
|
||||||
Git.open(getRepositoryDir(forkedRepository.owner, forkedRepository.name))
|
Git.open(getRepositoryDir(forkedRepository.owner, forkedRepository.name))
|
||||||
) { (oldGit, newGit) =>
|
) { (oldGit, newGit) =>
|
||||||
@@ -372,7 +395,7 @@ trait PullRequestsControllerBase extends ControllerBase {
|
|||||||
} getOrElse NotFound()
|
} getOrElse NotFound()
|
||||||
}
|
}
|
||||||
case _ => {
|
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 {
|
JGitUtil.getDefaultBranch(git, forkedRepository).map {
|
||||||
case (_, defaultBranch) =>
|
case (_, defaultBranch) =>
|
||||||
redirect(
|
redirect(
|
||||||
@@ -464,6 +487,7 @@ trait PullRequestsControllerBase extends ControllerBase {
|
|||||||
getAssignableUserNames(originRepository.owner, originRepository.name),
|
getAssignableUserNames(originRepository.owner, originRepository.name),
|
||||||
getMilestones(originRepository.owner, originRepository.name),
|
getMilestones(originRepository.owner, originRepository.name),
|
||||||
getPriorities(originRepository.owner, originRepository.name),
|
getPriorities(originRepository.owner, originRepository.name),
|
||||||
|
getDefaultPriority(originRepository.owner, originRepository.name),
|
||||||
getLabels(originRepository.owner, originRepository.name)
|
getLabels(originRepository.owner, originRepository.name)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -493,7 +517,7 @@ trait PullRequestsControllerBase extends ControllerBase {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
originRepository <- getRepository(originOwner, originRepositoryName)) yield {
|
originRepository <- getRepository(originOwner, originRepositoryName)) yield {
|
||||||
using(
|
Using.resources(
|
||||||
Git.open(getRepositoryDir(originRepository.owner, originRepository.name)),
|
Git.open(getRepositoryDir(originRepository.owner, originRepository.name)),
|
||||||
Git.open(getRepositoryDir(forkedRepository.owner, forkedRepository.name))
|
Git.open(getRepositoryDir(forkedRepository.owner, forkedRepository.name))
|
||||||
) {
|
) {
|
||||||
@@ -534,15 +558,17 @@ trait PullRequestsControllerBase extends ControllerBase {
|
|||||||
)
|
)
|
||||||
|
|
||||||
createPullRequest(
|
createPullRequest(
|
||||||
originUserName = repository.owner,
|
originRepository = repository,
|
||||||
originRepositoryName = repository.name,
|
|
||||||
issueId = issueId,
|
issueId = issueId,
|
||||||
originBranch = form.targetBranch,
|
originBranch = form.targetBranch,
|
||||||
requestUserName = form.requestUserName,
|
requestUserName = form.requestUserName,
|
||||||
requestRepositoryName = form.requestRepositoryName,
|
requestRepositoryName = form.requestRepositoryName,
|
||||||
requestBranch = form.requestBranch,
|
requestBranch = form.requestBranch,
|
||||||
commitIdFrom = form.commitIdFrom,
|
commitIdFrom = form.commitIdFrom,
|
||||||
commitIdTo = form.commitIdTo
|
commitIdTo = form.commitIdTo,
|
||||||
|
isDraft = form.isDraft,
|
||||||
|
loginAccount = context.loginAccount.get,
|
||||||
|
settings = context.settings
|
||||||
)
|
)
|
||||||
|
|
||||||
// insert labels
|
// insert labels
|
||||||
@@ -557,29 +583,6 @@ trait PullRequestsControllerBase extends ControllerBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// fetch requested branch
|
|
||||||
fetchAsPullRequest(owner, name, form.requestUserName, form.requestRepositoryName, form.requestBranch, issueId)
|
|
||||||
|
|
||||||
// record activity
|
|
||||||
recordPullRequestActivity(owner, name, loginUserName, issueId, form.title)
|
|
||||||
|
|
||||||
// call web hook
|
|
||||||
callPullRequestWebHook("opened", repository, issueId, context.loginAccount.get)
|
|
||||||
|
|
||||||
getIssue(owner, name, issueId.toString) foreach { issue =>
|
|
||||||
// extract references and create refer comment
|
|
||||||
createReferComment(
|
|
||||||
owner,
|
|
||||||
name,
|
|
||||||
issue,
|
|
||||||
form.title + " " + form.content.getOrElse(""),
|
|
||||||
context.loginAccount.get
|
|
||||||
)
|
|
||||||
|
|
||||||
// call hooks
|
|
||||||
PluginRegistry().getPullRequestHooks.foreach(_.created(issue, repository))
|
|
||||||
}
|
|
||||||
|
|
||||||
redirect(s"/${owner}/${name}/pull/${issueId}")
|
redirect(s"/${owner}/${name}/pull/${issueId}")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -589,10 +592,12 @@ trait PullRequestsControllerBase extends ControllerBase {
|
|||||||
val mailAddresses =
|
val mailAddresses =
|
||||||
context.loginAccount.map(x => Seq(x.mailAddress) ++ getAccountExtraMailAddresses(x.userName)).getOrElse(Nil)
|
context.loginAccount.map(x => Seq(x.mailAddress) ++ getAccountExtraMailAddresses(x.userName)).getOrElse(Nil)
|
||||||
|
|
||||||
val branches = JGitUtil
|
val branches =
|
||||||
|
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) {
|
||||||
|
git =>
|
||||||
|
JGitUtil
|
||||||
.getBranches(
|
.getBranches(
|
||||||
owner = repository.owner,
|
git = git,
|
||||||
name = repository.name,
|
|
||||||
defaultBranch = repository.repository.defaultBranch,
|
defaultBranch = repository.repository.defaultBranch,
|
||||||
origin = repository.repository.originUserName.isEmpty
|
origin = repository.repository.originUserName.isEmpty
|
||||||
)
|
)
|
||||||
@@ -606,6 +611,7 @@ trait PullRequestsControllerBase extends ControllerBase {
|
|||||||
}
|
}
|
||||||
.map(_.name)
|
.map(_.name)
|
||||||
.reverse
|
.reverse
|
||||||
|
}
|
||||||
|
|
||||||
val targetRepository = (for {
|
val targetRepository = (for {
|
||||||
parentUserName <- repository.repository.parentUserName
|
parentUserName <- repository.repository.parentUserName
|
||||||
|
|||||||
@@ -2,16 +2,17 @@ package gitbucket.core.controller
|
|||||||
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
import gitbucket.core.service.{AccountService, ActivityService, ReleaseService, RepositoryService}
|
import gitbucket.core.service.{AccountService, ActivityService, PaginationHelper, ReleaseService, RepositoryService}
|
||||||
import gitbucket.core.util._
|
import gitbucket.core.util._
|
||||||
import gitbucket.core.util.Directory._
|
import gitbucket.core.util.Directory._
|
||||||
import gitbucket.core.util.Implicits._
|
import gitbucket.core.util.Implicits._
|
||||||
import org.scalatra.forms._
|
import org.scalatra.forms._
|
||||||
import gitbucket.core.releases.html
|
import gitbucket.core.releases.html
|
||||||
import gitbucket.core.util.SyntaxSugars.using
|
|
||||||
import org.apache.commons.io.FileUtils
|
import org.apache.commons.io.FileUtils
|
||||||
import org.eclipse.jgit.api.Git
|
import org.eclipse.jgit.api.Git
|
||||||
|
|
||||||
|
import scala.util.Using
|
||||||
|
|
||||||
class ReleaseController
|
class ReleaseController
|
||||||
extends ReleaseControllerBase
|
extends ReleaseControllerBase
|
||||||
with RepositoryService
|
with RepositoryService
|
||||||
@@ -42,17 +43,14 @@ trait ReleaseControllerBase extends ControllerBase {
|
|||||||
)(ReleaseForm.apply)
|
)(ReleaseForm.apply)
|
||||||
|
|
||||||
get("/:owner/:repository/releases")(referrersOnly { repository =>
|
get("/:owner/:repository/releases")(referrersOnly { repository =>
|
||||||
val releases = getReleases(repository.owner, repository.name)
|
val page = PaginationHelper.page(params.get("page"))
|
||||||
val assets = getReleaseAssetsMap(repository.owner, repository.name)
|
|
||||||
|
|
||||||
html.list(
|
html.list(
|
||||||
repository,
|
repository,
|
||||||
repository.tags.reverse.map { tag =>
|
fetchReleases(repository, page),
|
||||||
(tag, releases.find(_.tag == tag.name).map { release =>
|
hasDeveloperRole(repository.owner, repository.name, context.loginAccount),
|
||||||
(release, assets(release))
|
page,
|
||||||
})
|
repository.tags.size
|
||||||
},
|
|
||||||
hasDeveloperRole(repository.owner, repository.name, context.loginAccount)
|
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -106,7 +104,7 @@ trait ReleaseControllerBase extends ControllerBase {
|
|||||||
createRelease(repository.owner, repository.name, form.name, form.content, tagName, loginAccount)
|
createRelease(repository.owner, repository.name, form.name, form.content, tagName, loginAccount)
|
||||||
|
|
||||||
// Insert into RELEASE_ASSET
|
// Insert into RELEASE_ASSET
|
||||||
val files = params.collect {
|
val files = params.toMap.collect {
|
||||||
case (name, value) if name.startsWith("file:") =>
|
case (name, value) if name.startsWith("file:") =>
|
||||||
val Array(_, fileId) = name.split(":")
|
val Array(_, fileId) = name.split(":")
|
||||||
(fileId, value)
|
(fileId, value)
|
||||||
@@ -121,7 +119,7 @@ trait ReleaseControllerBase extends ControllerBase {
|
|||||||
createReleaseAsset(repository.owner, repository.name, tagName, fileId, fileName, size, loginAccount)
|
createReleaseAsset(repository.owner, repository.name, tagName, fileId, fileName, size, loginAccount)
|
||||||
}
|
}
|
||||||
|
|
||||||
recordReleaseActivity(repository.owner, repository.name, loginAccount.userName, form.name)
|
recordReleaseActivity(repository.owner, repository.name, loginAccount.userName, form.name, tagName)
|
||||||
|
|
||||||
redirect(s"/${repository.owner}/${repository.name}/releases/${tagName}")
|
redirect(s"/${repository.owner}/${repository.name}/releases/${tagName}")
|
||||||
})
|
})
|
||||||
@@ -130,7 +128,7 @@ trait ReleaseControllerBase extends ControllerBase {
|
|||||||
val Seq(previousTag, currentTag) = multiParams("splat")
|
val Seq(previousTag, currentTag) = multiParams("splat")
|
||||||
val previousTagId = repository.tags.collectFirst { case x if x.name == previousTag => x.id }.getOrElse("")
|
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
|
val commits = JGitUtil.getCommitLog(git, previousTagId, currentTag).reverse
|
||||||
commits
|
commits
|
||||||
.map { commit =>
|
.map { commit =>
|
||||||
@@ -174,7 +172,7 @@ trait ReleaseControllerBase extends ControllerBase {
|
|||||||
val assets = getReleaseAssets(repository.owner, repository.name, tagName)
|
val assets = getReleaseAssets(repository.owner, repository.name, tagName)
|
||||||
deleteReleaseAssets(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:") =>
|
case (name, value) if name.startsWith("file:") =>
|
||||||
val Array(_, fileId) = name.split(":")
|
val Array(_, fileId) = name.split(":")
|
||||||
(fileId, value)
|
(fileId, value)
|
||||||
@@ -215,4 +213,21 @@ trait ReleaseControllerBase extends ControllerBase {
|
|||||||
redirect(s"/${repository.owner}/${repository.name}/releases")
|
redirect(s"/${repository.owner}/${repository.name}/releases")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
private def fetchReleases(repository: RepositoryService.RepositoryInfo, page: Int) = {
|
||||||
|
|
||||||
|
import gitbucket.core.service.ReleaseService._
|
||||||
|
|
||||||
|
val (offset, limit) = ((page - 1) * ReleaseLimit, ReleaseLimit)
|
||||||
|
val tagsToDisplay = repository.tags.reverse.slice(offset, offset + limit)
|
||||||
|
|
||||||
|
val releases = getReleases(repository.owner, repository.name, tagsToDisplay)
|
||||||
|
val assets = getReleaseAssetsMap(repository.owner, repository.name, releases)
|
||||||
|
|
||||||
|
val tagsWithReleases = tagsToDisplay.map { tag =>
|
||||||
|
(tag, releases.find(_.tag == tag.name).map { release =>
|
||||||
|
(release, assets(release))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
tagsWithReleases
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,12 +12,15 @@ import gitbucket.core.util.JGitUtil._
|
|||||||
import gitbucket.core.util.SyntaxSugars._
|
import gitbucket.core.util.SyntaxSugars._
|
||||||
import gitbucket.core.util.Implicits._
|
import gitbucket.core.util.Implicits._
|
||||||
import gitbucket.core.util.Directory._
|
import gitbucket.core.util.Directory._
|
||||||
|
import gitbucket.core.model.WebHookContentType
|
||||||
import org.scalatra.forms._
|
import org.scalatra.forms._
|
||||||
import org.scalatra.i18n.Messages
|
import org.scalatra.i18n.Messages
|
||||||
import org.eclipse.jgit.api.Git
|
import org.eclipse.jgit.api.Git
|
||||||
import org.eclipse.jgit.lib.Constants
|
import org.eclipse.jgit.lib.Constants
|
||||||
import org.eclipse.jgit.lib.ObjectId
|
import org.eclipse.jgit.lib.ObjectId
|
||||||
import gitbucket.core.model.WebHookContentType
|
|
||||||
|
import scala.util.Using
|
||||||
|
import org.scalatra.Forbidden
|
||||||
|
|
||||||
class RepositorySettingsController
|
class RepositorySettingsController
|
||||||
extends RepositorySettingsControllerBase
|
extends RepositorySettingsControllerBase
|
||||||
@@ -42,7 +45,6 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
|||||||
|
|
||||||
// for repository options
|
// for repository options
|
||||||
case class OptionsForm(
|
case class OptionsForm(
|
||||||
repositoryName: String,
|
|
||||||
description: Option[String],
|
description: Option[String],
|
||||||
isPrivate: Boolean,
|
isPrivate: Boolean,
|
||||||
issuesOption: String,
|
issuesOption: String,
|
||||||
@@ -55,9 +57,6 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
|||||||
)
|
)
|
||||||
|
|
||||||
val optionsForm = mapping(
|
val optionsForm = mapping(
|
||||||
"repositoryName" -> trim(
|
|
||||||
label("Repository Name", text(required, maxlength(100), repository, renameRepositoryName))
|
|
||||||
),
|
|
||||||
"description" -> trim(label("Description", optional(text()))),
|
"description" -> trim(label("Description", optional(text()))),
|
||||||
"isPrivate" -> trim(label("Repository Type", boolean())),
|
"isPrivate" -> trim(label("Repository Type", boolean())),
|
||||||
"issuesOption" -> trim(label("Issues Option", text(required, featureOption))),
|
"issuesOption" -> trim(label("Issues Option", text(required, featureOption))),
|
||||||
@@ -102,6 +101,15 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
|||||||
(url, events, ctype, token) => WebHookForm(url, events, WebHookContentType.valueOf(ctype), token)
|
(url, events, ctype, token) => WebHookForm(url, events, WebHookContentType.valueOf(ctype), token)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// for rename repository
|
||||||
|
case class RenameRepositoryForm(repositoryName: String)
|
||||||
|
|
||||||
|
val renameForm = mapping(
|
||||||
|
"repositoryName" -> trim(
|
||||||
|
label("New repository name", text(required, maxlength(100), repository, renameRepositoryName))
|
||||||
|
)
|
||||||
|
)(RenameRepositoryForm.apply)
|
||||||
|
|
||||||
// for transfer ownership
|
// for transfer ownership
|
||||||
case class TransferOwnerShipForm(newOwner: String)
|
case class TransferOwnerShipForm(newOwner: String)
|
||||||
|
|
||||||
@@ -142,13 +150,8 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
|||||||
form.mergeOptions,
|
form.mergeOptions,
|
||||||
form.defaultMergeOption
|
form.defaultMergeOption
|
||||||
)
|
)
|
||||||
// Change repository name
|
flash.update("info", "Repository settings has been updated.")
|
||||||
if (repository.name != form.repositoryName) {
|
redirect(s"/${repository.owner}/${repository.name}/settings/options")
|
||||||
// Update database
|
|
||||||
renameRepository(repository.owner, repository.name, repository.owner, form.repositoryName)
|
|
||||||
}
|
|
||||||
flash += "info" -> "Repository settings has been updated."
|
|
||||||
redirect(s"/${repository.owner}/${form.repositoryName}/settings/options")
|
|
||||||
})
|
})
|
||||||
|
|
||||||
/** branch settings */
|
/** branch settings */
|
||||||
@@ -164,10 +167,10 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
|||||||
} else {
|
} else {
|
||||||
saveRepositoryDefaultBranch(repository.owner, repository.name, form.defaultBranch)
|
saveRepositoryDefaultBranch(repository.owner, repository.name, form.defaultBranch)
|
||||||
// Change repository HEAD
|
// 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)
|
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")
|
redirect(s"/${repository.owner}/${repository.name}/settings/branches")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -231,7 +234,7 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
|||||||
*/
|
*/
|
||||||
post("/:owner/:repository/settings/hooks/new", webHookForm(false))(ownerOnly { (form, repository) =>
|
post("/:owner/:repository/settings/hooks/new", webHookForm(false))(ownerOnly { (form, repository) =>
|
||||||
addWebHook(repository.owner, repository.name, form.url, form.events, form.ctype, form.token)
|
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")
|
redirect(s"/${repository.owner}/${repository.name}/settings/hooks")
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -240,7 +243,7 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
|||||||
*/
|
*/
|
||||||
get("/:owner/:repository/settings/hooks/delete")(ownerOnly { repository =>
|
get("/:owner/:repository/settings/hooks/delete")(ownerOnly { repository =>
|
||||||
deleteWebHook(repository.owner, repository.name, params("url"))
|
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")
|
redirect(s"/${repository.owner}/${repository.name}/settings/hooks")
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -252,11 +255,11 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
|||||||
Array(h.getName, h.getValue)
|
Array(h.getName, h.getValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))) {
|
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) {
|
||||||
git =>
|
git =>
|
||||||
import scala.collection.JavaConverters._
|
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
import scala.concurrent._
|
import scala.concurrent._
|
||||||
|
import scala.jdk.CollectionConverters._
|
||||||
import scala.util.control.NonFatal
|
import scala.util.control.NonFatal
|
||||||
import org.apache.http.util.EntityUtils
|
import org.apache.http.util.EntityUtils
|
||||||
import scala.concurrent.ExecutionContext.Implicits.global
|
import scala.concurrent.ExecutionContext.Implicits.global
|
||||||
@@ -292,13 +295,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]] = {
|
val toErrorMap: PartialFunction[Throwable, Map[String, String]] = {
|
||||||
case e: java.net.UnknownHostException => Map("error" -> ("Unknown host " + e.getMessage))
|
case e: java.net.UnknownHostException => Map("error" -> ("Unknown host " + e.getMessage))
|
||||||
case e: java.lang.IllegalArgumentException => Map("error" -> ("invalid url"))
|
case e: java.lang.IllegalArgumentException => Map("error" -> ("invalid url"))
|
||||||
case e: org.apache.http.client.ClientProtocolException => 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")
|
contentType = formats("json")
|
||||||
@@ -350,7 +354,7 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
|||||||
*/
|
*/
|
||||||
post("/:owner/:repository/settings/hooks/edit", webHookForm(true))(ownerOnly { (form, repository) =>
|
post("/:owner/:repository/settings/hooks/edit", webHookForm(true))(ownerOnly { (form, repository) =>
|
||||||
updateWebHook(repository.owner, repository.name, form.url, form.events, form.ctype, form.token)
|
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")
|
redirect(s"/${repository.owner}/${repository.name}/settings/hooks")
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -361,24 +365,40 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
|||||||
html.danger(_, flash.get("info"))
|
html.danger(_, flash.get("info"))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rename repository.
|
||||||
|
*/
|
||||||
|
post("/:owner/:repository/settings/rename", renameForm)(ownerOnly { (form, repository) =>
|
||||||
|
if (context.settings.repositoryOperation.rename || context.loginAccount.get.isAdmin) {
|
||||||
|
if (repository.name != form.repositoryName) {
|
||||||
|
renameRepository(repository.owner, repository.name, repository.owner, form.repositoryName)
|
||||||
|
}
|
||||||
|
redirect(s"/${repository.owner}/${form.repositoryName}")
|
||||||
|
} else Forbidden()
|
||||||
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transfer repository ownership.
|
* Transfer repository ownership.
|
||||||
*/
|
*/
|
||||||
post("/:owner/:repository/settings/transfer", transferForm)(ownerOnly { (form, repository) =>
|
post("/:owner/:repository/settings/transfer", transferForm)(ownerOnly { (form, repository) =>
|
||||||
|
if (context.settings.repositoryOperation.transfer || context.loginAccount.get.isAdmin) {
|
||||||
// Change repository owner
|
// Change repository owner
|
||||||
if (repository.owner != form.newOwner) {
|
if (repository.owner != form.newOwner) {
|
||||||
renameRepository(repository.owner, repository.name, form.newOwner, repository.name)
|
renameRepository(repository.owner, repository.name, form.newOwner, repository.name)
|
||||||
}
|
}
|
||||||
redirect(s"/${form.newOwner}/${repository.name}")
|
redirect(s"/${form.newOwner}/${repository.name}")
|
||||||
|
} else Forbidden()
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete the repository.
|
* Delete the repository.
|
||||||
*/
|
*/
|
||||||
post("/:owner/:repository/settings/delete")(ownerOnly { repository =>
|
post("/:owner/:repository/settings/delete")(ownerOnly { repository =>
|
||||||
|
if (context.settings.repositoryOperation.delete || context.loginAccount.get.isAdmin) {
|
||||||
// Delete the repository and related files
|
// Delete the repository and related files
|
||||||
deleteRepository(repository.repository)
|
deleteRepository(repository.repository)
|
||||||
redirect(s"/${repository.owner}")
|
redirect(s"/${repository.owner}")
|
||||||
|
} else Forbidden()
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -386,11 +406,11 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
|||||||
*/
|
*/
|
||||||
post("/:owner/:repository/settings/gc")(ownerOnly { repository =>
|
post("/:owner/:repository/settings/gc")(ownerOnly { repository =>
|
||||||
LockUtil.lock(s"${repository.owner}/${repository.name}") {
|
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()
|
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")
|
redirect(s"/${repository.owner}/${repository.name}/settings/danger")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
package gitbucket.core.controller
|
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 javax.servlet.http.{HttpServletRequest, HttpServletResponse}
|
||||||
import gitbucket.core.plugin.PluginRegistry
|
import gitbucket.core.plugin.PluginRegistry
|
||||||
import gitbucket.core.repo.html
|
import gitbucket.core.repo.html
|
||||||
@@ -14,6 +15,7 @@ import gitbucket.core.util.SyntaxSugars._
|
|||||||
import gitbucket.core.util.Implicits._
|
import gitbucket.core.util.Implicits._
|
||||||
import gitbucket.core.util.Directory._
|
import gitbucket.core.util.Directory._
|
||||||
import gitbucket.core.model.{Account, CommitState, CommitStatus}
|
import gitbucket.core.model.{Account, CommitState, CommitStatus}
|
||||||
|
import gitbucket.core.util.JGitUtil.CommitInfo
|
||||||
import gitbucket.core.view
|
import gitbucket.core.view
|
||||||
import gitbucket.core.view.helpers
|
import gitbucket.core.view.helpers
|
||||||
import org.apache.commons.compress.archivers.{ArchiveEntry, ArchiveOutputStream}
|
import org.apache.commons.compress.archivers.{ArchiveEntry, ArchiveOutputStream}
|
||||||
@@ -29,8 +31,10 @@ import org.eclipse.jgit.api.{ArchiveCommand, Git}
|
|||||||
import org.eclipse.jgit.archive.{TgzFormat, ZipFormat}
|
import org.eclipse.jgit.archive.{TgzFormat, ZipFormat}
|
||||||
import org.eclipse.jgit.errors.MissingObjectException
|
import org.eclipse.jgit.errors.MissingObjectException
|
||||||
import org.eclipse.jgit.lib._
|
import org.eclipse.jgit.lib._
|
||||||
import org.eclipse.jgit.treewalk.TreeWalk
|
import org.eclipse.jgit.treewalk.{TreeWalk, WorkingTreeOptions}
|
||||||
|
import org.eclipse.jgit.treewalk.TreeWalk.OperationType
|
||||||
import org.eclipse.jgit.treewalk.filter.PathFilter
|
import org.eclipse.jgit.treewalk.filter.PathFilter
|
||||||
|
import org.eclipse.jgit.util.io.EolStreamTypeUtil
|
||||||
import org.json4s.jackson.Serialization
|
import org.json4s.jackson.Serialization
|
||||||
import org.scalatra._
|
import org.scalatra._
|
||||||
import org.scalatra.i18n.Messages
|
import org.scalatra.i18n.Messages
|
||||||
@@ -50,6 +54,7 @@ class RepositoryViewerController
|
|||||||
with ReadableUsersAuthenticator
|
with ReadableUsersAuthenticator
|
||||||
with ReferrerAuthenticator
|
with ReferrerAuthenticator
|
||||||
with WritableUsersAuthenticator
|
with WritableUsersAuthenticator
|
||||||
|
with MergeService
|
||||||
with PullRequestService
|
with PullRequestService
|
||||||
with CommitStatusService
|
with CommitStatusService
|
||||||
with WebHookPullRequestService
|
with WebHookPullRequestService
|
||||||
@@ -254,11 +259,11 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
def getSummary(statuses: List[CommitStatus]): (CommitState, String) = {
|
def getSummary(statuses: List[CommitStatus]): (CommitState, String) = {
|
||||||
val stateMap = statuses.groupBy(_.state)
|
val stateMap = statuses.groupBy(_.state)
|
||||||
val state = CommitState.combine(stateMap.keySet)
|
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
|
state -> summary
|
||||||
}
|
}
|
||||||
|
|
||||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))) {
|
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) {
|
||||||
git =>
|
git =>
|
||||||
def getTags(sha: String): List[String] = {
|
def getTags(sha: String): List[String] = {
|
||||||
JGitUtil.getTagsOnCommit(git, sha)
|
JGitUtil.getTagsOnCommit(git, sha)
|
||||||
@@ -270,7 +275,28 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
if (path.isEmpty) Nil else path.split("/").toList,
|
if (path.isEmpty) Nil else path.split("/").toList,
|
||||||
branchName,
|
branchName,
|
||||||
repository,
|
repository,
|
||||||
logs.splitWith { (commit1, commit2) =>
|
logs
|
||||||
|
.map {
|
||||||
|
c =>
|
||||||
|
CommitInfo(
|
||||||
|
id = c.id,
|
||||||
|
shortMessage = c.shortMessage,
|
||||||
|
fullMessage = c.fullMessage,
|
||||||
|
parents = c.parents,
|
||||||
|
authorTime = c.authorTime,
|
||||||
|
authorName = c.authorName,
|
||||||
|
authorEmailAddress = c.authorEmailAddress,
|
||||||
|
commitTime = c.commitTime,
|
||||||
|
committerName = c.committerName,
|
||||||
|
committerEmailAddress = c.committerEmailAddress,
|
||||||
|
commitSign = c.commitSign,
|
||||||
|
verified = c.commitSign
|
||||||
|
.flatMap { s =>
|
||||||
|
GpgUtil.verifySign(s)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.splitWith { (commit1, commit2) =>
|
||||||
view.helpers.date(commit1.commitTime) == view.helpers.date(commit2.commitTime)
|
view.helpers.date(commit1.commitTime) == view.helpers.date(commit2.commitTime)
|
||||||
},
|
},
|
||||||
page,
|
page,
|
||||||
@@ -290,7 +316,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
val protectedBranch = getProtectedBranchInfo(repository.owner, repository.name, branch)
|
val protectedBranch = getProtectedBranchInfo(repository.owner, repository.name, branch)
|
||||||
.needStatusCheck(context.loginAccount.get.userName)
|
.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))
|
val revCommit = JGitUtil.getRevCommitFromId(git, git.getRepository.resolve(branch))
|
||||||
|
|
||||||
html.editor(
|
html.editor(
|
||||||
@@ -326,9 +352,10 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
repository = repository,
|
repository = repository,
|
||||||
branch = form.branch,
|
branch = form.branch,
|
||||||
path = form.path,
|
path = form.path,
|
||||||
files = files,
|
files = files.toIndexedSeq,
|
||||||
message = form.message.getOrElse("Add files via upload"),
|
message = form.message.getOrElse("Add files via upload"),
|
||||||
loginAccount = context.loginAccount.get
|
loginAccount = context.loginAccount.get,
|
||||||
|
settings = context.settings
|
||||||
) {
|
) {
|
||||||
case (git, headTip, builder, inserter) =>
|
case (git, headTip, builder, inserter) =>
|
||||||
JGitUtil.processTree(git, headTip) { (path, tree) =>
|
JGitUtil.processTree(git, headTip) { (path, tree) =>
|
||||||
@@ -359,7 +386,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
val protectedBranch = getProtectedBranchInfo(repository.owner, repository.name, branch)
|
val protectedBranch = getProtectedBranchInfo(repository.owner, repository.name, branch)
|
||||||
.needStatusCheck(context.loginAccount.get.userName)
|
.needStatusCheck(context.loginAccount.get.userName)
|
||||||
|
|
||||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))) {
|
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) {
|
||||||
git =>
|
git =>
|
||||||
val revCommit = JGitUtil.getRevCommitFromId(git, git.getRepository.resolve(branch))
|
val revCommit = JGitUtil.getRevCommitFromId(git, git.getRepository.resolve(branch))
|
||||||
|
|
||||||
@@ -386,7 +413,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
|
|
||||||
get("/:owner/:repository/remove/*")(writableUsersOnly { repository =>
|
get("/:owner/:repository/remove/*")(writableUsersOnly { repository =>
|
||||||
val (branch, path) = repository.splitPath(multiParams("splat").head)
|
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 =>
|
git =>
|
||||||
val revCommit = JGitUtil.getRevCommitFromId(git, git.getRepository.resolve(branch))
|
val revCommit = JGitUtil.getRevCommitFromId(git, git.getRepository.resolve(branch))
|
||||||
|
|
||||||
@@ -415,7 +442,8 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
charset = form.charset,
|
charset = form.charset,
|
||||||
message = form.message.getOrElse(s"Create ${form.newFileName}"),
|
message = form.message.getOrElse(s"Create ${form.newFileName}"),
|
||||||
commit = form.commit,
|
commit = form.commit,
|
||||||
loginAccount = context.loginAccount.get
|
loginAccount = context.loginAccount.get,
|
||||||
|
settings = context.settings
|
||||||
)
|
)
|
||||||
|
|
||||||
redirect(
|
redirect(
|
||||||
@@ -439,7 +467,8 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
form.message.getOrElse(s"Rename ${form.oldFileName.get} to ${form.newFileName}")
|
form.message.getOrElse(s"Rename ${form.oldFileName.get} to ${form.newFileName}")
|
||||||
},
|
},
|
||||||
commit = form.commit,
|
commit = form.commit,
|
||||||
loginAccount = context.loginAccount.get
|
loginAccount = context.loginAccount.get,
|
||||||
|
settings = context.settings
|
||||||
)
|
)
|
||||||
|
|
||||||
redirect(
|
redirect(
|
||||||
@@ -459,11 +488,10 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
charset = "",
|
charset = "",
|
||||||
message = form.message.getOrElse(s"Delete ${form.fileName}"),
|
message = form.message.getOrElse(s"Delete ${form.fileName}"),
|
||||||
commit = form.commit,
|
commit = form.commit,
|
||||||
loginAccount = context.loginAccount.get
|
loginAccount = context.loginAccount.get,
|
||||||
|
settings = context.settings
|
||||||
)
|
)
|
||||||
|
|
||||||
println(form.path)
|
|
||||||
|
|
||||||
redirect(
|
redirect(
|
||||||
s"/${repository.owner}/${repository.name}/tree/${form.branch}${if (form.path.length == 0) "" else "/" + form.path}"
|
s"/${repository.owner}/${repository.name}/tree/${form.branch}${if (form.path.length == 0) "" else "/" + form.path}"
|
||||||
)
|
)
|
||||||
@@ -471,7 +499,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
|
|
||||||
get("/:owner/:repository/raw/*")(referrersOnly { repository =>
|
get("/:owner/:repository/raw/*")(referrersOnly { repository =>
|
||||||
val (id, path) = repository.splitPath(multiParams("splat").head)
|
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))
|
val revCommit = JGitUtil.getRevCommitFromId(git, git.getRepository.resolve(id))
|
||||||
|
|
||||||
getPathObjectId(git, path, revCommit).map { objectId =>
|
getPathObjectId(git, path, revCommit).map { objectId =>
|
||||||
@@ -486,7 +514,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
val blobRoute = get("/:owner/:repository/blob/*")(referrersOnly { repository =>
|
val blobRoute = get("/:owner/:repository/blob/*")(referrersOnly { repository =>
|
||||||
val (id, path) = repository.splitPath(multiParams("splat").head)
|
val (id, path) = repository.splitPath(multiParams("splat").head)
|
||||||
val raw = params.get("raw").getOrElse("false").toBoolean
|
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 =>
|
git =>
|
||||||
val revCommit = JGitUtil.getRevCommitFromId(git, git.getRepository.resolve(id))
|
val revCommit = JGitUtil.getRevCommitFromId(git, git.getRepository.resolve(id))
|
||||||
getPathObjectId(git, path, revCommit).map {
|
getPathObjectId(git, path, revCommit).map {
|
||||||
@@ -526,7 +554,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
ajaxGet("/:owner/:repository/get-blame/*")(referrersOnly { repository =>
|
ajaxGet("/:owner/:repository/get-blame/*")(referrersOnly { repository =>
|
||||||
val (id, path) = repository.splitPath(multiParams("splat").head)
|
val (id, path) = repository.splitPath(multiParams("splat").head)
|
||||||
contentType = formats("json")
|
contentType = formats("json")
|
||||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))) {
|
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) {
|
||||||
git =>
|
git =>
|
||||||
val last = git.log.add(git.getRepository.resolve(id)).addPath(path).setMaxCount(1).call.iterator.next.name
|
val last = git.log.add(git.getRepository.resolve(id)).addPath(path).setMaxCount(1).call.iterator.next.name
|
||||||
Serialization.write(
|
Serialization.write(
|
||||||
@@ -561,7 +589,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
val id = params("id")
|
val id = params("id")
|
||||||
|
|
||||||
try {
|
try {
|
||||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))) {
|
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) {
|
||||||
git =>
|
git =>
|
||||||
defining(JGitUtil.getRevCommitFromId(git, git.getRepository.resolve(id))) {
|
defining(JGitUtil.getRevCommitFromId(git, git.getRepository.resolve(id))) {
|
||||||
revCommit =>
|
revCommit =>
|
||||||
@@ -590,7 +618,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
|
|
||||||
get("/:owner/:repository/patch/:id")(referrersOnly { repository =>
|
get("/:owner/:repository/patch/:id")(referrersOnly { repository =>
|
||||||
try {
|
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"))
|
val diff = JGitUtil.getPatch(git, None, params("id"))
|
||||||
contentType = formats("txt")
|
contentType = formats("txt")
|
||||||
diff
|
diff
|
||||||
@@ -603,7 +631,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
get("/:owner/:repository/patch/*...*")(referrersOnly { repository =>
|
get("/:owner/:repository/patch/*...*")(referrersOnly { repository =>
|
||||||
try {
|
try {
|
||||||
val Seq(fromId, toId) = multiParams("splat")
|
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)
|
val diff = JGitUtil.getPatch(git, Some(fromId), toId)
|
||||||
contentType = formats("txt")
|
contentType = formats("txt")
|
||||||
diff
|
diff
|
||||||
@@ -723,10 +751,11 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
*/
|
*/
|
||||||
get("/:owner/:repository/branches")(referrersOnly { repository =>
|
get("/:owner/:repository/branches")(referrersOnly { repository =>
|
||||||
val protectedBranches = getProtectedBranchList(repository.owner, repository.name).toSet
|
val protectedBranches = getProtectedBranchList(repository.owner, repository.name).toSet
|
||||||
val branches = JGitUtil
|
val branches = Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) {
|
||||||
|
git =>
|
||||||
|
JGitUtil
|
||||||
.getBranches(
|
.getBranches(
|
||||||
owner = repository.owner,
|
git = git,
|
||||||
name = repository.name,
|
|
||||||
defaultBranch = repository.repository.defaultBranch,
|
defaultBranch = repository.repository.defaultBranch,
|
||||||
origin = repository.repository.originUserName.isEmpty
|
origin = repository.repository.originUserName.isEmpty
|
||||||
)
|
)
|
||||||
@@ -746,6 +775,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
.reverse
|
.reverse
|
||||||
|
}
|
||||||
|
|
||||||
html.branches(branches, hasDeveloperRole(repository.owner, repository.name, context.loginAccount), repository)
|
html.branches(branches, hasDeveloperRole(repository.owner, repository.name, context.loginAccount), repository)
|
||||||
})
|
})
|
||||||
@@ -761,14 +791,14 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
* Creates a tag.
|
* Creates a tag.
|
||||||
*/
|
*/
|
||||||
post("/:owner/:repository/tag", tagForm)(writableUsersOnly { (form, repository) =>
|
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)
|
JGitUtil.createTag(git, form.tagName, form.message, form.commitId)
|
||||||
} match {
|
} match {
|
||||||
case Right(message) =>
|
case Right(message) =>
|
||||||
flash += "info" -> message
|
flash.update("info", message)
|
||||||
redirect(s"/${repository.owner}/${repository.name}/commit/${form.commitId}")
|
redirect(s"/${repository.owner}/${repository.name}/commit/${form.commitId}")
|
||||||
case Left(message) =>
|
case Left(message) =>
|
||||||
flash += "error" -> message
|
flash.update("error", message)
|
||||||
redirect(s"/${repository.owner}/${repository.name}/commit/${form.commitId}")
|
redirect(s"/${repository.owner}/${repository.name}/commit/${form.commitId}")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -779,16 +809,16 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
post("/:owner/:repository/branches")(writableUsersOnly { repository =>
|
post("/:owner/:repository/branches")(writableUsersOnly { repository =>
|
||||||
val newBranchName = params.getOrElse("new", halt(400))
|
val newBranchName = params.getOrElse("new", halt(400))
|
||||||
val fromBranchName = params.getOrElse("from", 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)
|
JGitUtil.createBranch(git, fromBranchName, newBranchName)
|
||||||
} match {
|
} match {
|
||||||
case Right(message) =>
|
case Right(message) =>
|
||||||
flash += "info" -> message
|
flash.update("info", message)
|
||||||
redirect(
|
redirect(
|
||||||
s"/${repository.owner}/${repository.name}/tree/${StringUtil.urlEncode(newBranchName).replace("%2F", "/")}"
|
s"/${repository.owner}/${repository.name}/tree/${StringUtil.urlEncode(newBranchName).replace("%2F", "/")}"
|
||||||
)
|
)
|
||||||
case Left(message) =>
|
case Left(message) =>
|
||||||
flash += "error" -> message
|
flash.update("error", message)
|
||||||
redirect(s"/${repository.owner}/${repository.name}/tree/${fromBranchName}")
|
redirect(s"/${repository.owner}/${repository.name}/tree/${fromBranchName}")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -800,7 +830,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
val branchName = multiParams("splat").head
|
val branchName = multiParams("splat").head
|
||||||
val userName = context.loginAccount.get.userName
|
val userName = context.loginAccount.get.userName
|
||||||
if (repository.repository.defaultBranch != branchName) {
|
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()
|
git.branchDelete().setForce(true).setBranchNames(branchName).call()
|
||||||
recordDeleteBranchActivity(repository.owner, repository.name, userName, branchName)
|
recordDeleteBranchActivity(repository.owner, repository.name, userName, branchName)
|
||||||
}
|
}
|
||||||
@@ -852,7 +882,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
* Displays the file find of branch.
|
* Displays the file find of branch.
|
||||||
*/
|
*/
|
||||||
get("/:owner/:repository/find/*")(referrersOnly { repository =>
|
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
|
val ref = multiParams("splat").head
|
||||||
JGitUtil.getTreeId(git, ref).map { treeId =>
|
JGitUtil.getTreeId(git, ref).map { treeId =>
|
||||||
html.find(ref, treeId, repository)
|
html.find(ref, treeId, repository)
|
||||||
@@ -864,7 +894,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
* Get all file list of branch.
|
* Get all file list of branch.
|
||||||
*/
|
*/
|
||||||
ajaxGet("/:owner/:repository/tree-list/:tree")(referrersOnly { repository =>
|
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")
|
val treeId = params("tree")
|
||||||
contentType = formats("json")
|
contentType = formats("json")
|
||||||
Map("paths" -> JGitUtil.getAllFileListByTreeId(git, treeId))
|
Map("paths" -> JGitUtil.getAllFileListByTreeId(git, treeId))
|
||||||
@@ -888,7 +918,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
* @return HTML of the file list
|
* @return HTML of the file list
|
||||||
*/
|
*/
|
||||||
private def fileList(repository: RepositoryService.RepositoryInfo, revstr: String = "", path: String = ".") = {
|
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)) {
|
if (JGitUtil.isEmpty(git)) {
|
||||||
html.guide(repository, hasDeveloperRole(repository.owner, repository.name, context.loginAccount))
|
html.guide(repository, hasDeveloperRole(repository.owner, repository.name, context.loginAccount))
|
||||||
} else {
|
} else {
|
||||||
@@ -898,8 +928,9 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
defining(JGitUtil.getRevCommitFromId(git, objectId)) { revCommit =>
|
defining(JGitUtil.getRevCommitFromId(git, objectId)) { revCommit =>
|
||||||
val lastModifiedCommit =
|
val lastModifiedCommit =
|
||||||
if (path == ".") revCommit else JGitUtil.getLastModifiedCommit(git, revCommit, path)
|
if (path == ".") revCommit else JGitUtil.getLastModifiedCommit(git, revCommit, path)
|
||||||
|
val commitCount = JGitUtil.getCommitCount(git, lastModifiedCommit.getName)
|
||||||
// get files
|
// get files
|
||||||
val files = JGitUtil.getFileList(git, revision, path, context.settings.baseUrl)
|
val files = JGitUtil.getFileList(git, revision, path, context.settings.baseUrl, commitCount)
|
||||||
val parentPath = if (path == ".") Nil else path.split("/").toList
|
val parentPath = if (path == ".") Nil else path.split("/").toList
|
||||||
// process README.md or README.markdown
|
// process README.md or README.markdown
|
||||||
val readme = files
|
val readme = files
|
||||||
@@ -920,7 +951,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
repository,
|
repository,
|
||||||
if (path == ".") Nil else path.split("/").toList, // current path
|
if (path == ".") Nil else path.split("/").toList, // current path
|
||||||
new JGitUtil.CommitInfo(lastModifiedCommit), // last modified commit
|
new JGitUtil.CommitInfo(lastModifiedCommit), // last modified commit
|
||||||
JGitUtil.getCommitCount(repository.owner, repository.name, lastModifiedCommit.getName),
|
commitCount,
|
||||||
files,
|
files,
|
||||||
readme,
|
readme,
|
||||||
hasDeveloperRole(repository.owner, repository.name, context.loginAccount),
|
hasDeveloperRole(repository.owner, repository.name, context.loginAccount),
|
||||||
@@ -947,16 +978,16 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
def archive(revision: String, archiveFormat: String, archive: ArchiveOutputStream)(
|
def archive(revision: String, archiveFormat: String, archive: ArchiveOutputStream)(
|
||||||
entryCreator: (String, Long, java.util.Date, Int) => ArchiveEntry
|
entryCreator: (String, Long, java.util.Date, Int) => ArchiveEntry
|
||||||
): Unit = {
|
): 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 oid = git.getRepository.resolve(revision)
|
||||||
val commit = JGitUtil.getRevCommitFromId(git, oid)
|
val commit = JGitUtil.getRevCommitFromId(git, oid)
|
||||||
val date = commit.getCommitterIdent.getWhen
|
val date = commit.getCommitterIdent.getWhen
|
||||||
val sha1 = oid.getName()
|
val sha1 = oid.getName()
|
||||||
val repositorySuffix = (if (sha1.startsWith(revision)) sha1 else revision).replace('/', '-')
|
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
|
val baseName = repository.name + "-" + repositorySuffix + pathSuffix
|
||||||
|
|
||||||
using(new TreeWalk(git.getRepository)) { treeWalk =>
|
Using.resource(new TreeWalk(git.getRepository)) { treeWalk =>
|
||||||
treeWalk.addTree(commit.getTree)
|
treeWalk.addTree(commit.getTree)
|
||||||
treeWalk.setRecursive(true)
|
treeWalk.setRecursive(true)
|
||||||
if (!path.isEmpty) {
|
if (!path.isEmpty) {
|
||||||
@@ -967,13 +998,31 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
val entryPath =
|
val entryPath =
|
||||||
if (path.isEmpty) baseName + "/" + treeWalk.getPathString
|
if (path.isEmpty) baseName + "/" + treeWalk.getPathString
|
||||||
else path.split("/").last + treeWalk.getPathString.substring(path.length)
|
else path.split("/").last + treeWalk.getPathString.substring(path.length)
|
||||||
val size = JGitUtil.getFileSize(git, repository, treeWalk)
|
|
||||||
val mode = treeWalk.getFileMode.getBits
|
val mode = treeWalk.getFileMode.getBits
|
||||||
val entry: ArchiveEntry = entryCreator(entryPath, size, date, mode)
|
|
||||||
JGitUtil.openFile(git, repository, commit.getTree, treeWalk.getPathString) { in =>
|
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)
|
archive.putArchiveEntry(entry)
|
||||||
|
Using.resource(new FileInputStream(tempFile)) { in =>
|
||||||
IOUtils.copy(in, archive)
|
IOUtils.copy(in, archive)
|
||||||
|
}
|
||||||
archive.closeArchiveEntry()
|
archive.closeArchiveEntry()
|
||||||
|
tempFile.delete()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -994,7 +1043,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
)
|
)
|
||||||
contentType = "application/octet-stream"
|
contentType = "application/octet-stream"
|
||||||
response.setBufferSize(1024 * 1024)
|
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) =>
|
archive(revision, ".zip", zip) { (path, size, date, mode) =>
|
||||||
val entry = new ZipArchiveEntry(path)
|
val entry = new ZipArchiveEntry(path)
|
||||||
entry.setSize(size)
|
entry.setSize(size)
|
||||||
@@ -1011,12 +1060,12 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
)
|
)
|
||||||
contentType = "application/octet-stream"
|
contentType = "application/octet-stream"
|
||||||
response.setBufferSize(1024 * 1024)
|
response.setBufferSize(1024 * 1024)
|
||||||
using(compressor match {
|
Using.resource(compressor match {
|
||||||
case "gz" => new GzipCompressorOutputStream(response.getOutputStream)
|
case "gz" => new GzipCompressorOutputStream(response.getOutputStream)
|
||||||
case "bz2" => new BZip2CompressorOutputStream(response.getOutputStream)
|
case "bz2" => new BZip2CompressorOutputStream(response.getOutputStream)
|
||||||
case "xz" => new XZCompressorOutputStream(response.getOutputStream)
|
case "xz" => new XZCompressorOutputStream(response.getOutputStream)
|
||||||
}) { compressorOutputStream =>
|
}) { compressorOutputStream =>
|
||||||
using(new TarArchiveOutputStream(compressorOutputStream)) { tar =>
|
Using.resource(new TarArchiveOutputStream(compressorOutputStream)) { tar =>
|
||||||
tar.setBigNumberMode(TarArchiveOutputStream.BIGNUMBER_STAR)
|
tar.setBigNumberMode(TarArchiveOutputStream.BIGNUMBER_STAR)
|
||||||
tar.setLongFileMode(TarArchiveOutputStream.LONGFILE_GNU)
|
tar.setLongFileMode(TarArchiveOutputStream.LONGFILE_GNU)
|
||||||
tar.setAddPaxHeadersForNonAsciiNames(true)
|
tar.setAddPaxHeadersForNonAsciiNames(true)
|
||||||
@@ -1045,7 +1094,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
val branch = params("branch")
|
val branch = params("branch")
|
||||||
|
|
||||||
LockUtil.lock(s"${owner}/${repository}") {
|
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 headName = s"refs/heads/${branch}"
|
||||||
val headTip = git.getRepository.resolve(headName)
|
val headTip = git.getRepository.resolve(headName)
|
||||||
if (headTip.getName != value) {
|
if (headTip.getName != value) {
|
||||||
|
|||||||
@@ -2,14 +2,11 @@ package gitbucket.core.controller
|
|||||||
|
|
||||||
import java.io.FileInputStream
|
import java.io.FileInputStream
|
||||||
|
|
||||||
import com.github.zafarkhaja.semver.{Version => Semver}
|
|
||||||
import gitbucket.core.GitBucketCoreModule
|
|
||||||
import gitbucket.core.admin.html
|
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.SystemSettingsService._
|
||||||
import gitbucket.core.service.{AccountService, RepositoryService}
|
import gitbucket.core.service.{AccountService, RepositoryService}
|
||||||
import gitbucket.core.ssh.SshServer
|
import gitbucket.core.ssh.SshServer
|
||||||
import gitbucket.core.util.Directory._
|
|
||||||
import gitbucket.core.util.Implicits._
|
import gitbucket.core.util.Implicits._
|
||||||
import gitbucket.core.util.StringUtil._
|
import gitbucket.core.util.StringUtil._
|
||||||
import gitbucket.core.util.SyntaxSugars._
|
import gitbucket.core.util.SyntaxSugars._
|
||||||
@@ -21,8 +18,8 @@ import org.scalatra._
|
|||||||
import org.scalatra.forms._
|
import org.scalatra.forms._
|
||||||
import org.scalatra.i18n.Messages
|
import org.scalatra.i18n.Messages
|
||||||
|
|
||||||
import scala.collection.JavaConverters._
|
|
||||||
import scala.collection.mutable.ListBuffer
|
import scala.collection.mutable.ListBuffer
|
||||||
|
import scala.util.Using
|
||||||
|
|
||||||
class SystemSettingsController
|
class SystemSettingsController
|
||||||
extends SystemSettingsControllerBase
|
extends SystemSettingsControllerBase
|
||||||
@@ -41,14 +38,22 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
|
|||||||
"information" -> trim(label("Information", optional(text()))),
|
"information" -> trim(label("Information", optional(text()))),
|
||||||
"allowAccountRegistration" -> trim(label("Account registration", boolean())),
|
"allowAccountRegistration" -> trim(label("Account registration", boolean())),
|
||||||
"allowAnonymousAccess" -> trim(label("Anonymous access", boolean())),
|
"allowAnonymousAccess" -> trim(label("Anonymous access", boolean())),
|
||||||
"isCreateRepoOptionPublic" -> trim(label("Default option to create a new repository", boolean())),
|
"isCreateRepoOptionPublic" -> trim(label("Default visibility of new repository", boolean())),
|
||||||
|
"repositoryOperation" -> mapping(
|
||||||
|
"create" -> trim(label("Allow all users to create repository", boolean())),
|
||||||
|
"delete" -> trim(label("Allow all users to delete repository", boolean())),
|
||||||
|
"rename" -> trim(label("Allow all users to rename repository", boolean())),
|
||||||
|
"transfer" -> trim(label("Allow all users to transfer repository", boolean())),
|
||||||
|
"fork" -> trim(label("Allow all users to fork repository", boolean()))
|
||||||
|
)(RepositoryOperation.apply),
|
||||||
"gravatar" -> trim(label("Gravatar", boolean())),
|
"gravatar" -> trim(label("Gravatar", boolean())),
|
||||||
"notification" -> trim(label("Notification", boolean())),
|
"notification" -> trim(label("Notification", boolean())),
|
||||||
"activityLogLimit" -> trim(label("Limit of activity logs", optional(number()))),
|
"activityLogLimit" -> trim(label("Limit of activity logs", optional(number()))),
|
||||||
|
"limitVisibleRepositories" -> trim(label("limitVisibleRepositories", boolean())),
|
||||||
"ssh" -> mapping(
|
"ssh" -> mapping(
|
||||||
"enabled" -> trim(label("SSH access", boolean())),
|
"enabled" -> trim(label("SSH access", boolean())),
|
||||||
"host" -> trim(label("SSH host", optional(text()))),
|
"host" -> trim(label("SSH host", optional(text()))),
|
||||||
"port" -> trim(label("SSH port", optional(number()))),
|
"port" -> trim(label("SSH port", optional(number())))
|
||||||
)(Ssh.apply),
|
)(Ssh.apply),
|
||||||
"useSMTP" -> trim(label("SMTP", boolean())),
|
"useSMTP" -> trim(label("SMTP", boolean())),
|
||||||
"smtp" -> optionalIfNotChecked(
|
"smtp" -> optionalIfNotChecked(
|
||||||
@@ -93,17 +98,18 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
|
|||||||
)(OIDC.apply)
|
)(OIDC.apply)
|
||||||
),
|
),
|
||||||
"skinName" -> trim(label("AdminLTE skin name", text(required))),
|
"skinName" -> trim(label("AdminLTE skin name", text(required))),
|
||||||
|
"userDefinedCss" -> trim(label("User-defined CSS", optional(text()))),
|
||||||
"showMailAddress" -> trim(label("Show mail address", boolean())),
|
"showMailAddress" -> trim(label("Show mail address", boolean())),
|
||||||
"pluginNetworkInstall" -> trim(label("Network plugin installation", boolean())),
|
"webhook" -> mapping(
|
||||||
"proxy" -> optionalIfNotChecked(
|
"blockPrivateAddress" -> trim(label("Block private address", boolean())),
|
||||||
"useProxy",
|
"whitelist" -> trim(label("Whitelist", multiLineText()))
|
||||||
mapping(
|
)(WebHook.apply),
|
||||||
"host" -> trim(label("Proxy host", text(required))),
|
"upload" -> mapping(
|
||||||
"port" -> trim(label("Proxy port", number())),
|
"maxFileSize" -> trim(label("Max file size", long(required))),
|
||||||
"user" -> trim(label("Keystore", optional(text()))),
|
"timeout" -> trim(label("Timeout", long(required))),
|
||||||
"password" -> trim(label("Keystore", optional(text())))
|
"largeMaxFileSize" -> trim(label("Max file size for large file", long(required))),
|
||||||
)(Proxy.apply)
|
"largeTimeout" -> trim(label("Timeout for large file", long(required)))
|
||||||
)
|
)(Upload.apply)
|
||||||
)(SystemSettings.apply).verifying { settings =>
|
)(SystemSettings.apply).verifying { settings =>
|
||||||
Vector(
|
Vector(
|
||||||
if (settings.ssh.enabled && settings.baseUrl.isEmpty) {
|
if (settings.ssh.enabled && settings.baseUrl.isEmpty) {
|
||||||
@@ -179,7 +185,7 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
|
|||||||
|
|
||||||
val newUserForm = mapping(
|
val newUserForm = mapping(
|
||||||
"userName" -> trim(label("Username", text(required, maxlength(100), identifier, uniqueUserName, reservedNames))),
|
"userName" -> trim(label("Username", text(required, maxlength(100), identifier, uniqueUserName, reservedNames))),
|
||||||
"password" -> trim(label("Password", text(required, maxlength(20), password))),
|
"password" -> trim(label("Password", text(required, maxlength(20)))),
|
||||||
"fullName" -> trim(label("Full Name", text(required, maxlength(100)))),
|
"fullName" -> trim(label("Full Name", text(required, maxlength(100)))),
|
||||||
"mailAddress" -> trim(label("Mail Address", text(required, maxlength(100), uniqueMailAddress()))),
|
"mailAddress" -> trim(label("Mail Address", text(required, maxlength(100), uniqueMailAddress()))),
|
||||||
"extraMailAddresses" -> list(
|
"extraMailAddresses" -> list(
|
||||||
@@ -193,7 +199,7 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
|
|||||||
|
|
||||||
val editUserForm = mapping(
|
val editUserForm = mapping(
|
||||||
"userName" -> trim(label("Username", text(required, maxlength(100), identifier))),
|
"userName" -> trim(label("Username", text(required, maxlength(100), identifier))),
|
||||||
"password" -> trim(label("Password", optional(text(maxlength(20), password)))),
|
"password" -> trim(label("Password", optional(text(maxlength(20))))),
|
||||||
"fullName" -> trim(label("Full Name", text(required, maxlength(100)))),
|
"fullName" -> trim(label("Full Name", text(required, maxlength(100)))),
|
||||||
"mailAddress" -> trim(label("Mail Address", text(required, maxlength(100), uniqueMailAddress("userName")))),
|
"mailAddress" -> trim(label("Mail Address", text(required, maxlength(100), uniqueMailAddress("userName")))),
|
||||||
"extraMailAddresses" -> list(
|
"extraMailAddresses" -> list(
|
||||||
@@ -229,30 +235,30 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
|
|||||||
val conn = request2Session(request).conn
|
val conn = request2Session(request).conn
|
||||||
val meta = conn.getMetaData
|
val meta = conn.getMetaData
|
||||||
val tables = ListBuffer[Table]()
|
val tables = ListBuffer[Table]()
|
||||||
using(meta.getTables(null, "%", "%", Array("TABLE", "VIEW"))) {
|
Using.resource(meta.getTables(null, "%", "%", Array("TABLE", "VIEW"))) {
|
||||||
rs =>
|
rs =>
|
||||||
while (rs.next()) {
|
while (rs.next()) {
|
||||||
val tableName = rs.getString("TABLE_NAME")
|
val tableName = rs.getString("TABLE_NAME")
|
||||||
|
|
||||||
val pkColumns = ListBuffer[String]()
|
val pkColumns = ListBuffer[String]()
|
||||||
using(meta.getPrimaryKeys(null, null, tableName)) { rs =>
|
Using.resource(meta.getPrimaryKeys(null, null, tableName)) { rs =>
|
||||||
while (rs.next()) {
|
while (rs.next()) {
|
||||||
pkColumns += rs.getString("COLUMN_NAME").toUpperCase
|
pkColumns += rs.getString("COLUMN_NAME").toUpperCase
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val columns = ListBuffer[Column]()
|
val columns = ListBuffer[Column]()
|
||||||
using(meta.getColumns(null, "%", tableName, "%")) { rs =>
|
Using.resource(meta.getColumns(null, "%", tableName, "%")) { rs =>
|
||||||
while (rs.next()) {
|
while (rs.next()) {
|
||||||
val columnName = rs.getString("COLUMN_NAME").toUpperCase
|
val columnName = rs.getString("COLUMN_NAME").toUpperCase
|
||||||
columns += Column(columnName, pkColumns.contains(columnName))
|
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 {
|
post("/admin/dbviewer/_query")(adminOnly {
|
||||||
@@ -263,10 +269,10 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
|
|||||||
if (trimmedQuery.nonEmpty) {
|
if (trimmedQuery.nonEmpty) {
|
||||||
try {
|
try {
|
||||||
val conn = request2Session(request).conn
|
val conn = request2Session(request).conn
|
||||||
using(conn.prepareStatement(query)) {
|
Using.resource(conn.prepareStatement(query)) {
|
||||||
stmt =>
|
stmt =>
|
||||||
if (trimmedQuery.toUpperCase.startsWith("SELECT")) {
|
if (trimmedQuery.toUpperCase.startsWith("SELECT")) {
|
||||||
using(stmt.executeQuery()) {
|
Using.resource(stmt.executeQuery()) {
|
||||||
rs =>
|
rs =>
|
||||||
val meta = rs.getMetaData
|
val meta = rs.getMetaData
|
||||||
val columns = for (i <- 1 to meta.getColumnCount) yield {
|
val columns = for (i <- 1 to meta.getColumnCount) yield {
|
||||||
@@ -309,7 +315,7 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
|
|||||||
} SshServer.start(sshAddress, baseUrl)
|
} SshServer.start(sshAddress, baseUrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
flash += "info" -> "System settings has been updated."
|
flash.update("info", "System settings has been updated.")
|
||||||
redirect("/admin/system")
|
redirect("/admin/system")
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -332,63 +338,12 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
|
|||||||
})
|
})
|
||||||
|
|
||||||
get("/admin/plugins")(adminOnly {
|
get("/admin/plugins")(adminOnly {
|
||||||
// Installed plugins
|
html.plugins(PluginRegistry().getPlugins(), flash.get("info"))
|
||||||
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"))
|
|
||||||
})
|
})
|
||||||
|
|
||||||
post("/admin/plugins/_reload")(adminOnly {
|
post("/admin/plugins/_reload")(adminOnly {
|
||||||
PluginRegistry.reload(request.getServletContext(), loadSystemSettings(), request2Session(request).conn)
|
PluginRegistry.reload(request.getServletContext(), loadSystemSettings(), request2Session(request).conn)
|
||||||
flash += "info" -> "All plugins were reloaded."
|
flash.update("info", "All plugins were reloaded.")
|
||||||
redirect("/admin/plugins")
|
redirect("/admin/plugins")
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -398,37 +353,7 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
|
|||||||
if (PluginRegistry().getPlugins().exists(_.pluginId == pluginId)) {
|
if (PluginRegistry().getPlugins().exists(_.pluginId == pluginId)) {
|
||||||
PluginRegistry
|
PluginRegistry
|
||||||
.uninstall(pluginId, request.getServletContext, loadSystemSettings(), request2Session(request).conn)
|
.uninstall(pluginId, request.getServletContext, loadSystemSettings(), request2Session(request).conn)
|
||||||
flash += "info" -> s"${pluginId} was uninstalled."
|
flash.update("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."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
redirect("/admin/plugins")
|
redirect("/admin/plugins")
|
||||||
@@ -476,7 +401,7 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
|
|||||||
getAccountByUserName(userName, true).map {
|
getAccountByUserName(userName, true).map {
|
||||||
account =>
|
account =>
|
||||||
if (account.isAdmin && (form.isRemoved || !form.isAdmin) && isLastAdministrator(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")
|
redirect(s"/admin/users/${userName}/_edituser")
|
||||||
} else {
|
} else {
|
||||||
if (form.isRemoved) {
|
if (form.isRemoved) {
|
||||||
@@ -601,14 +526,26 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
|
|||||||
response.setHeader("Content-Disposition", "attachment; filename=" + file.getName)
|
response.setHeader("Content-Disposition", "attachment; filename=" + file.getName)
|
||||||
response.setContentLength(file.length.toInt)
|
response.setContentLength(file.length.toInt)
|
||||||
|
|
||||||
using(new FileInputStream(file)) { in =>
|
Using.resource(new FileInputStream(file)) { in =>
|
||||||
IOUtils.copy(in, response.outputStream)
|
IOUtils.copy(in, response.outputStream)
|
||||||
}
|
}
|
||||||
|
|
||||||
()
|
()
|
||||||
})
|
})
|
||||||
|
|
||||||
private def members: Constraint = new Constraint() {
|
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] = {
|
override def validate(name: String, value: String, messages: Messages): Option[String] = {
|
||||||
if (value.split(",").exists {
|
if (value.split(",").exists {
|
||||||
_.split(":") match { case Array(userName, isManager) => isManager.toBoolean }
|
_.split(":") match { case Array(userName, isManager) => isManager.toBoolean }
|
||||||
@@ -617,7 +554,8 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected def disableByNotYourself(paramName: String): Constraint = new Constraint() {
|
protected def disableByNotYourself(paramName: String): Constraint =
|
||||||
|
new Constraint() {
|
||||||
override def validate(name: String, value: String, messages: Messages): Option[String] = {
|
override def validate(name: String, value: String, messages: Messages): Option[String] = {
|
||||||
params.get(paramName).flatMap { userName =>
|
params.get(paramName).flatMap { userName =>
|
||||||
if (userName == context.loginAccount.get.userName && params.get("removed") == Some("true"))
|
if (userName == context.loginAccount.get.userName && params.get("removed") == Some("true"))
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import gitbucket.core.util.Directory._
|
|||||||
import org.scalatra.forms._
|
import org.scalatra.forms._
|
||||||
import org.eclipse.jgit.api.Git
|
import org.eclipse.jgit.api.Git
|
||||||
import org.scalatra.i18n.Messages
|
import org.scalatra.i18n.Messages
|
||||||
|
import scala.util.Using
|
||||||
|
|
||||||
class WikiController
|
class WikiController
|
||||||
extends WikiControllerBase
|
extends WikiControllerBase
|
||||||
@@ -90,7 +91,7 @@ trait WikiControllerBase extends ControllerBase {
|
|||||||
get("/:owner/:repository/wiki/:page/_history")(referrersOnly { repository =>
|
get("/:owner/:repository/wiki/:page/_history")(referrersOnly { repository =>
|
||||||
val pageName = StringUtil.urlDecode(params("page"))
|
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 {
|
JGitUtil.getCommitLog(git, "master", path = pageName + ".md") match {
|
||||||
case Right((logs, hasNext)) => html.history(Some(pageName), logs, repository, isEditable(repository))
|
case Right((logs, hasNext)) => html.history(Some(pageName), logs, repository, isEditable(repository))
|
||||||
case Left(_) => NotFound()
|
case Left(_) => NotFound()
|
||||||
@@ -102,7 +103,7 @@ trait WikiControllerBase extends ControllerBase {
|
|||||||
val pageName = StringUtil.urlDecode(params("page"))
|
val pageName = StringUtil.urlDecode(params("page"))
|
||||||
val Array(from, to) = params("commitId").split("\\.\\.\\.")
|
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(
|
html.compare(
|
||||||
Some(pageName),
|
Some(pageName),
|
||||||
from,
|
from,
|
||||||
@@ -118,7 +119,7 @@ trait WikiControllerBase extends ControllerBase {
|
|||||||
get("/:owner/:repository/wiki/_compare/:commitId")(referrersOnly { repository =>
|
get("/:owner/:repository/wiki/_compare/:commitId")(referrersOnly { repository =>
|
||||||
val Array(from, to) = params("commitId").split("\\.\\.\\.")
|
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(
|
html.compare(
|
||||||
None,
|
None,
|
||||||
from,
|
from,
|
||||||
@@ -139,7 +140,7 @@ trait WikiControllerBase extends ControllerBase {
|
|||||||
if (revertWikiPage(repository.owner, repository.name, from, to, context.loginAccount.get, Some(pageName))) {
|
if (revertWikiPage(repository.owner, repository.name, from, to, context.loginAccount.get, Some(pageName))) {
|
||||||
redirect(s"/${repository.owner}/${repository.name}/wiki/${StringUtil.urlEncode(pageName)}")
|
redirect(s"/${repository.owner}/${repository.name}/wiki/${StringUtil.urlEncode(pageName)}")
|
||||||
} else {
|
} else {
|
||||||
flash += "info" -> "This patch was not able to be reversed."
|
flash.update("info", "This patch was not able to be reversed.")
|
||||||
redirect(
|
redirect(
|
||||||
s"/${repository.owner}/${repository.name}/wiki/${StringUtil.urlEncode(pageName)}/_compare/${from}...${to}"
|
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)) {
|
if (revertWikiPage(repository.owner, repository.name, from, to, context.loginAccount.get, None)) {
|
||||||
redirect(s"/${repository.owner}/${repository.name}/wiki")
|
redirect(s"/${repository.owner}/${repository.name}/wiki")
|
||||||
} else {
|
} 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}")
|
redirect(s"/${repository.owner}/${repository.name}/wiki/_compare/${from}...${to}")
|
||||||
}
|
}
|
||||||
} else Unauthorized()
|
} else Unauthorized()
|
||||||
@@ -190,7 +191,7 @@ trait WikiControllerBase extends ControllerBase {
|
|||||||
form.pageName,
|
form.pageName,
|
||||||
commitId
|
commitId
|
||||||
)
|
)
|
||||||
callWebHookOf(repository.owner, repository.name, WebHook.Gollum) {
|
callWebHookOf(repository.owner, repository.name, WebHook.Gollum, context.settings) {
|
||||||
getAccountByUserName(repository.owner).map { repositoryUser =>
|
getAccountByUserName(repository.owner).map { repositoryUser =>
|
||||||
WebHookGollumPayload("edited", form.pageName, commitId, repository, repositoryUser, loginAccount)
|
WebHookGollumPayload("edited", form.pageName, commitId, repository, repositoryUser, loginAccount)
|
||||||
}
|
}
|
||||||
@@ -228,7 +229,7 @@ trait WikiControllerBase extends ControllerBase {
|
|||||||
commitId =>
|
commitId =>
|
||||||
updateLastActivityDate(repository.owner, repository.name)
|
updateLastActivityDate(repository.owner, repository.name)
|
||||||
recordCreateWikiPageActivity(repository.owner, repository.name, loginAccount.userName, form.pageName)
|
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 =>
|
getAccountByUserName(repository.owner).map { repositoryUser =>
|
||||||
WebHookGollumPayload("created", form.pageName, commitId, repository, repositoryUser, loginAccount)
|
WebHookGollumPayload("created", form.pageName, commitId, repository, repositoryUser, loginAccount)
|
||||||
}
|
}
|
||||||
@@ -269,7 +270,7 @@ trait WikiControllerBase extends ControllerBase {
|
|||||||
})
|
})
|
||||||
|
|
||||||
get("/:owner/:repository/wiki/_history")(referrersOnly { repository =>
|
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 {
|
JGitUtil.getCommitLog(git, "master") match {
|
||||||
case Right((logs, hasNext)) => html.history(None, logs, repository, isEditable(repository))
|
case Right((logs, hasNext)) => html.history(None, logs, repository, isEditable(repository))
|
||||||
case Left(_) => NotFound()
|
case Left(_) => NotFound()
|
||||||
@@ -279,7 +280,7 @@ trait WikiControllerBase extends ControllerBase {
|
|||||||
|
|
||||||
get("/:owner/:repository/wiki/_blob/*")(referrersOnly { repository =>
|
get("/:owner/:repository/wiki/_blob/*")(referrersOnly { repository =>
|
||||||
val path = multiParams("splat").head
|
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"))
|
val revCommit = JGitUtil.getRevCommitFromId(git, git.getRepository.resolve("master"))
|
||||||
|
|
||||||
getPathObjectId(git, path, revCommit).map { objectId =>
|
getPathObjectId(git, path, revCommit).map { objectId =>
|
||||||
|
|||||||
@@ -3,21 +3,35 @@ import gitbucket.core.api.{ApiObject, ApiRef, JsonFormat}
|
|||||||
import gitbucket.core.controller.ControllerBase
|
import gitbucket.core.controller.ControllerBase
|
||||||
import gitbucket.core.util.Directory.getRepositoryDir
|
import gitbucket.core.util.Directory.getRepositoryDir
|
||||||
import gitbucket.core.util.ReferrerAuthenticator
|
import gitbucket.core.util.ReferrerAuthenticator
|
||||||
import gitbucket.core.util.SyntaxSugars.using
|
|
||||||
import gitbucket.core.util.Implicits._
|
import gitbucket.core.util.Implicits._
|
||||||
import org.eclipse.jgit.api.Git
|
import org.eclipse.jgit.api.Git
|
||||||
import scala.collection.JavaConverters._
|
import org.slf4j.LoggerFactory
|
||||||
|
|
||||||
|
import scala.jdk.CollectionConverters._
|
||||||
|
import scala.util.Using
|
||||||
|
|
||||||
trait ApiGitReferenceControllerBase extends ControllerBase {
|
trait ApiGitReferenceControllerBase extends ControllerBase {
|
||||||
self: ReferrerAuthenticator =>
|
self: ReferrerAuthenticator =>
|
||||||
|
|
||||||
|
private val logger = LoggerFactory.getLogger(classOf[ApiGitReferenceControllerBase])
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* i. Get a reference
|
* i. Get a reference
|
||||||
* https://developer.github.com/v3/git/refs/#get-a-reference
|
* https://developer.github.com/v3/git/refs/#get-a-reference
|
||||||
*/
|
*/
|
||||||
|
get("/api/v3/repos/:owner/:repository/git/ref/*")(referrersOnly { repository =>
|
||||||
|
getRef()
|
||||||
|
})
|
||||||
|
|
||||||
|
// Some versions of GHE support this path
|
||||||
get("/api/v3/repos/:owner/:repository/git/refs/*")(referrersOnly { repository =>
|
get("/api/v3/repos/:owner/:repository/git/refs/*")(referrersOnly { repository =>
|
||||||
|
logger.warn("git/refs/ endpoint may not be compatible with GitHub API v3. Consider using git/ref/ endpoint instead")
|
||||||
|
getRef()
|
||||||
|
})
|
||||||
|
|
||||||
|
private def getRef() = {
|
||||||
val revstr = multiParams("splat").head
|
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)
|
val ref = git.getRepository().findRef(revstr)
|
||||||
|
|
||||||
if (ref != null) {
|
if (ref != null) {
|
||||||
@@ -37,7 +51,8 @@ trait ApiGitReferenceControllerBase extends ControllerBase {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ii. Get all references
|
* ii. Get all references
|
||||||
* https://developer.github.com/v3/git/refs/#get-all-references
|
* https://developer.github.com/v3/git/refs/#get-all-references
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ trait ApiIssueControllerBase extends ControllerBase {
|
|||||||
val condition = IssueSearchCondition(request)
|
val condition = IssueSearchCondition(request)
|
||||||
val baseOwner = getAccountByUserName(repository.owner).get
|
val baseOwner = getAccountByUserName(repository.owner).get
|
||||||
|
|
||||||
val issues: List[(Issue, Account)] =
|
val issues: List[(Issue, Account, Option[Account])] =
|
||||||
searchIssueByApi(
|
searchIssueByApi(
|
||||||
condition = condition,
|
condition = condition,
|
||||||
offset = (page - 1) * PullRequestLimit,
|
offset = (page - 1) * PullRequestLimit,
|
||||||
@@ -40,11 +40,12 @@ trait ApiIssueControllerBase extends ControllerBase {
|
|||||||
)
|
)
|
||||||
|
|
||||||
JsonFormat(issues.map {
|
JsonFormat(issues.map {
|
||||||
case (issue, issueUser) =>
|
case (issue, issueUser, assignedUser) =>
|
||||||
ApiIssue(
|
ApiIssue(
|
||||||
issue = issue,
|
issue = issue,
|
||||||
repositoryName = RepositoryName(repository),
|
repositoryName = RepositoryName(repository),
|
||||||
user = ApiUser(issueUser),
|
user = ApiUser(issueUser),
|
||||||
|
assignee = assignedUser.map(ApiUser(_)),
|
||||||
labels = getIssueLabels(repository.owner, repository.name, issue.issueId)
|
labels = getIssueLabels(repository.owner, repository.name, issue.issueId)
|
||||||
.map(ApiLabel(_, RepositoryName(repository)))
|
.map(ApiLabel(_, RepositoryName(repository)))
|
||||||
)
|
)
|
||||||
@@ -59,13 +60,15 @@ trait ApiIssueControllerBase extends ControllerBase {
|
|||||||
(for {
|
(for {
|
||||||
issueId <- params("id").toIntOpt
|
issueId <- params("id").toIntOpt
|
||||||
issue <- getIssue(repository.owner, repository.name, issueId.toString)
|
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 {
|
} yield {
|
||||||
JsonFormat(
|
JsonFormat(
|
||||||
ApiIssue(
|
ApiIssue(
|
||||||
issue,
|
issue,
|
||||||
RepositoryName(repository),
|
RepositoryName(repository),
|
||||||
ApiUser(openedUser),
|
ApiUser(openedUser),
|
||||||
|
issue.assignedUserName.flatMap(users.get(_)).map(ApiUser(_)),
|
||||||
getIssueLabels(repository.owner, repository.name, issue.issueId).map(ApiLabel(_, RepositoryName(repository)))
|
getIssueLabels(repository.owner, repository.name, issue.issueId).map(ApiLabel(_, RepositoryName(repository)))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -98,6 +101,7 @@ trait ApiIssueControllerBase extends ControllerBase {
|
|||||||
issue,
|
issue,
|
||||||
RepositoryName(repository),
|
RepositoryName(repository),
|
||||||
ApiUser(loginAccount),
|
ApiUser(loginAccount),
|
||||||
|
issue.assignedUserName.flatMap(getAccountByUserName(_)).map(ApiUser(_)),
|
||||||
getIssueLabels(repository.owner, repository.name, issue.issueId)
|
getIssueLabels(repository.owner, repository.name, issue.issueId)
|
||||||
.map(ApiLabel(_, RepositoryName(repository)))
|
.map(ApiLabel(_, RepositoryName(repository)))
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
package gitbucket.core.controller.api
|
package gitbucket.core.controller.api
|
||||||
import gitbucket.core.api.{ApiGroup, ApiRepository, ApiUser, JsonFormat}
|
import gitbucket.core.api.{ApiGroup, CreateAGroup, ApiRepository, ApiUser, JsonFormat}
|
||||||
import gitbucket.core.controller.ControllerBase
|
import gitbucket.core.controller.ControllerBase
|
||||||
import gitbucket.core.service.{AccountService, RepositoryService}
|
import gitbucket.core.service.{AccountService, RepositoryService}
|
||||||
import gitbucket.core.util.Implicits._
|
import gitbucket.core.util.Implicits._
|
||||||
import gitbucket.core.util.UsersAuthenticator
|
import gitbucket.core.util.{AdminAuthenticator, UsersAuthenticator}
|
||||||
|
|
||||||
trait ApiOrganizationControllerBase extends ControllerBase {
|
trait ApiOrganizationControllerBase extends ControllerBase {
|
||||||
self: RepositoryService with AccountService with UsersAuthenticator =>
|
self: RepositoryService with AccountService with AdminAuthenticator with UsersAuthenticator =>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* i. List your organizations
|
* i. List your organizations
|
||||||
@@ -51,6 +51,19 @@ trait ApiOrganizationControllerBase extends ControllerBase {
|
|||||||
* ghe: i. Create an organization
|
* ghe: i. Create an organization
|
||||||
* https://developer.github.com/enterprise/2.14/v3/enterprise-admin/orgs/#create-an-organization
|
* https://developer.github.com/enterprise/2.14/v3/enterprise-admin/orgs/#create-an-organization
|
||||||
*/
|
*/
|
||||||
|
post("/api/v3/admin/organizations")(adminOnly {
|
||||||
|
for {
|
||||||
|
data <- extractFromJsonBody[CreateAGroup]
|
||||||
|
} yield {
|
||||||
|
val group = createGroup(
|
||||||
|
data.login,
|
||||||
|
data.profile_name,
|
||||||
|
data.url
|
||||||
|
)
|
||||||
|
updateGroupMembers(data.login, List(data.admin -> true))
|
||||||
|
JsonFormat(ApiGroup(group))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ghe: ii. Rename an organization
|
* ghe: ii. Rename an organization
|
||||||
|
|||||||
@@ -8,12 +8,12 @@ import gitbucket.core.service.PullRequestService.PullRequestLimit
|
|||||||
import gitbucket.core.util.Directory.getRepositoryDir
|
import gitbucket.core.util.Directory.getRepositoryDir
|
||||||
import gitbucket.core.util.Implicits._
|
import gitbucket.core.util.Implicits._
|
||||||
import gitbucket.core.util.JGitUtil.CommitInfo
|
import gitbucket.core.util.JGitUtil.CommitInfo
|
||||||
import gitbucket.core.util.SyntaxSugars.using
|
|
||||||
import gitbucket.core.util._
|
import gitbucket.core.util._
|
||||||
import org.eclipse.jgit.api.Git
|
import org.eclipse.jgit.api.Git
|
||||||
import org.scalatra.NoContent
|
import org.scalatra.NoContent
|
||||||
|
import scala.util.Using
|
||||||
|
|
||||||
import scala.collection.JavaConverters._
|
import scala.jdk.CollectionConverters._
|
||||||
|
|
||||||
trait ApiPullRequestControllerBase extends ControllerBase {
|
trait ApiPullRequestControllerBase extends ControllerBase {
|
||||||
self: AccountService
|
self: AccountService
|
||||||
@@ -106,15 +106,17 @@ trait ApiPullRequestControllerBase extends ControllerBase {
|
|||||||
)
|
)
|
||||||
|
|
||||||
createPullRequest(
|
createPullRequest(
|
||||||
originUserName = repository.owner,
|
originRepository = repository,
|
||||||
originRepositoryName = repository.name,
|
|
||||||
issueId = issueId,
|
issueId = issueId,
|
||||||
originBranch = createPullReq.base,
|
originBranch = createPullReq.base,
|
||||||
requestUserName = reqOwner,
|
requestUserName = reqOwner,
|
||||||
requestRepositoryName = repository.name,
|
requestRepositoryName = repository.name,
|
||||||
requestBranch = reqBranch,
|
requestBranch = reqBranch,
|
||||||
commitIdFrom = commitIdFrom.getName,
|
commitIdFrom = commitIdFrom.getName,
|
||||||
commitIdTo = commitIdTo.getName
|
commitIdTo = commitIdTo.getName,
|
||||||
|
isDraft = createPullReq.draft.getOrElse(false),
|
||||||
|
loginAccount = context.loginAccount.get,
|
||||||
|
settings = context.settings
|
||||||
)
|
)
|
||||||
getApiPullRequest(repository, issueId).map(JsonFormat(_))
|
getApiPullRequest(repository, issueId).map(JsonFormat(_))
|
||||||
case _ =>
|
case _ =>
|
||||||
@@ -133,15 +135,17 @@ trait ApiPullRequestControllerBase extends ControllerBase {
|
|||||||
case (Some(commitIdFrom), Some(commitIdTo)) =>
|
case (Some(commitIdFrom), Some(commitIdTo)) =>
|
||||||
changeIssueToPullRequest(repository.owner, repository.name, createPullReqAlt.issue)
|
changeIssueToPullRequest(repository.owner, repository.name, createPullReqAlt.issue)
|
||||||
createPullRequest(
|
createPullRequest(
|
||||||
originUserName = repository.owner,
|
originRepository = repository,
|
||||||
originRepositoryName = repository.name,
|
|
||||||
issueId = createPullReqAlt.issue,
|
issueId = createPullReqAlt.issue,
|
||||||
originBranch = createPullReqAlt.base,
|
originBranch = createPullReqAlt.base,
|
||||||
requestUserName = reqOwner,
|
requestUserName = reqOwner,
|
||||||
requestRepositoryName = repository.name,
|
requestRepositoryName = repository.name,
|
||||||
requestBranch = reqBranch,
|
requestBranch = reqBranch,
|
||||||
commitIdFrom = commitIdFrom.getName,
|
commitIdFrom = commitIdFrom.getName,
|
||||||
commitIdTo = commitIdTo.getName
|
commitIdTo = commitIdTo.getName,
|
||||||
|
isDraft = false,
|
||||||
|
loginAccount = context.loginAccount.get,
|
||||||
|
settings = context.settings
|
||||||
)
|
)
|
||||||
getApiPullRequest(repository, createPullReqAlt.issue).map(JsonFormat(_))
|
getApiPullRequest(repository, createPullReqAlt.issue).map(JsonFormat(_))
|
||||||
case _ =>
|
case _ =>
|
||||||
@@ -171,7 +175,7 @@ trait ApiPullRequestControllerBase extends ControllerBase {
|
|||||||
issueId =>
|
issueId =>
|
||||||
getPullRequest(owner, name, issueId) map {
|
getPullRequest(owner, name, issueId) map {
|
||||||
case (issue, pullreq) =>
|
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 oldId = git.getRepository.resolve(pullreq.commitIdFrom)
|
||||||
val newId = git.getRepository.resolve(pullreq.commitIdTo)
|
val newId = git.getRepository.resolve(pullreq.commitIdTo)
|
||||||
val repoFullName = RepositoryName(repository)
|
val repoFullName = RepositoryName(repository)
|
||||||
|
|||||||
@@ -0,0 +1,184 @@
|
|||||||
|
package gitbucket.core.controller.api
|
||||||
|
import java.io.{ByteArrayInputStream, File}
|
||||||
|
|
||||||
|
import gitbucket.core.api._
|
||||||
|
import gitbucket.core.controller.ControllerBase
|
||||||
|
import gitbucket.core.service.{AccountService, ReleaseService}
|
||||||
|
import gitbucket.core.util.Directory.getReleaseFilesDir
|
||||||
|
import gitbucket.core.util.{FileUtil, ReferrerAuthenticator, RepositoryName, WritableUsersAuthenticator}
|
||||||
|
import gitbucket.core.util.Implicits._
|
||||||
|
import gitbucket.core.util.SyntaxSugars.defining
|
||||||
|
import org.apache.commons.io.FileUtils
|
||||||
|
import org.scalatra.{Created, NoContent}
|
||||||
|
|
||||||
|
trait ApiReleaseControllerBase extends ControllerBase {
|
||||||
|
self: AccountService with ReleaseService with ReferrerAuthenticator with WritableUsersAuthenticator =>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* i. List releases for a repository
|
||||||
|
* https://developer.github.com/v3/repos/releases/#list-releases-for-a-repository
|
||||||
|
*/
|
||||||
|
get("/api/v3/repos/:owner/:repository/releases")(referrersOnly { repository =>
|
||||||
|
val releases = getReleases(repository.owner, repository.name)
|
||||||
|
JsonFormat(releases.map { rel =>
|
||||||
|
val assets = getReleaseAssets(repository.owner, repository.name, rel.tag)
|
||||||
|
ApiRelease(rel, assets, getAccountByUserName(rel.author).get, RepositoryName(repository))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ii. Get a single release
|
||||||
|
* https://developer.github.com/v3/repos/releases/#get-a-single-release
|
||||||
|
* GitBucket doesn't have release id
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* iii. Get the latest release
|
||||||
|
* https://developer.github.com/v3/repos/releases/#get-the-latest-release
|
||||||
|
*/
|
||||||
|
get("/api/v3/repos/:owner/:repository/releases/latest")(referrersOnly { repository =>
|
||||||
|
getReleases(repository.owner, repository.name).lastOption
|
||||||
|
.map { release =>
|
||||||
|
val assets = getReleaseAssets(repository.owner, repository.name, release.tag)
|
||||||
|
JsonFormat(ApiRelease(release, assets, getAccountByUserName(release.author).get, RepositoryName(repository)))
|
||||||
|
}
|
||||||
|
.getOrElse {
|
||||||
|
NotFound()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* iv. Get a release by tag name
|
||||||
|
* https://developer.github.com/v3/repos/releases/#get-a-release-by-tag-name
|
||||||
|
*/
|
||||||
|
get("/api/v3/repos/:owner/:repository/releases/tags/:tag")(referrersOnly { repository =>
|
||||||
|
val tag = params("tag")
|
||||||
|
getRelease(repository.owner, repository.name, tag)
|
||||||
|
.map { release =>
|
||||||
|
val assets = getReleaseAssets(repository.owner, repository.name, tag)
|
||||||
|
JsonFormat(ApiRelease(release, assets, getAccountByUserName(release.author).get, RepositoryName(repository)))
|
||||||
|
}
|
||||||
|
.getOrElse {
|
||||||
|
NotFound()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* v. Create a release
|
||||||
|
* https://developer.github.com/v3/repos/releases/#create-a-release
|
||||||
|
*/
|
||||||
|
post("/api/v3/repos/:owner/:repository/releases")(writableUsersOnly { repository =>
|
||||||
|
(for {
|
||||||
|
data <- extractFromJsonBody[CreateARelease]
|
||||||
|
} yield {
|
||||||
|
createRelease(
|
||||||
|
repository.owner,
|
||||||
|
repository.name,
|
||||||
|
data.name.getOrElse(data.tag_name),
|
||||||
|
data.body,
|
||||||
|
data.tag_name,
|
||||||
|
context.loginAccount.get
|
||||||
|
)
|
||||||
|
val release = getRelease(repository.owner, repository.name, data.tag_name).get
|
||||||
|
val assets = getReleaseAssets(repository.owner, repository.name, data.tag_name)
|
||||||
|
JsonFormat(ApiRelease(release, assets, context.loginAccount.get, RepositoryName(repository)))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* vi. Edit a release
|
||||||
|
* https://developer.github.com/v3/repos/releases/#edit-a-release
|
||||||
|
* Incompatiblity info: GitHub API requires :release_id, but GitBucket API requires :tag_name
|
||||||
|
*/
|
||||||
|
patch("/api/v3/repos/:owner/:repository/releases/:tag")(writableUsersOnly { repository =>
|
||||||
|
(for {
|
||||||
|
data <- extractFromJsonBody[CreateARelease]
|
||||||
|
} yield {
|
||||||
|
val tag = params("tag")
|
||||||
|
updateRelease(repository.owner, repository.name, tag, data.name.getOrElse(data.tag_name), data.body)
|
||||||
|
val release = getRelease(repository.owner, repository.name, data.tag_name).get
|
||||||
|
val assets = getReleaseAssets(repository.owner, repository.name, data.tag_name)
|
||||||
|
JsonFormat(ApiRelease(release, assets, context.loginAccount.get, RepositoryName(repository)))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* vii. Delete a release
|
||||||
|
* https://developer.github.com/v3/repos/releases/#delete-a-release
|
||||||
|
* Incompatiblity info: GitHub API requires :release_id, but GitBucket API requires :tag_name
|
||||||
|
*/
|
||||||
|
delete("/api/v3/repos/:owner/:repository/releases/:tag")(writableUsersOnly { repository =>
|
||||||
|
val tag = params("tag")
|
||||||
|
deleteRelease(repository.owner, repository.name, tag)
|
||||||
|
NoContent()
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* viii. List assets for a release
|
||||||
|
* https://developer.github.com/v3/repos/releases/#list-assets-for-a-release
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* ix. Upload a release asset
|
||||||
|
* https://developer.github.com/v3/repos/releases/#upload-a-release-asset
|
||||||
|
*/
|
||||||
|
post("/api/v3/repos/:owner/:repository/releases/:tag/assets")(writableUsersOnly { repository =>
|
||||||
|
val name = params("name")
|
||||||
|
val tag = params("tag")
|
||||||
|
getRelease(repository.owner, repository.name, tag)
|
||||||
|
.map {
|
||||||
|
release =>
|
||||||
|
defining(FileUtil.generateFileId) { fileId =>
|
||||||
|
val buf = new Array[Byte](request.inputStream.available())
|
||||||
|
request.inputStream.read(buf)
|
||||||
|
FileUtils.writeByteArrayToFile(
|
||||||
|
new File(
|
||||||
|
getReleaseFilesDir(repository.owner, repository.name),
|
||||||
|
FileUtil.checkFilename(tag + "/" + fileId)
|
||||||
|
),
|
||||||
|
buf
|
||||||
|
)
|
||||||
|
createReleaseAsset(
|
||||||
|
repository.owner,
|
||||||
|
repository.name,
|
||||||
|
tag,
|
||||||
|
fileId,
|
||||||
|
name,
|
||||||
|
request.contentLength.getOrElse(0),
|
||||||
|
context.loginAccount.get
|
||||||
|
)
|
||||||
|
getReleaseAsset(repository.owner, repository.name, tag, fileId)
|
||||||
|
.map { asset =>
|
||||||
|
JsonFormat(ApiReleaseAsset(asset, RepositoryName(repository)))
|
||||||
|
}
|
||||||
|
.getOrElse {
|
||||||
|
ApiError("Unknown error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.getOrElse(NotFound())
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* x. Get a single release asset
|
||||||
|
* https://developer.github.com/v3/repos/releases/#get-a-single-release-asset
|
||||||
|
* Incompatibility info: GitHub requires only asset_id, but GitBucket requires tag and fileId(file_id).
|
||||||
|
*/
|
||||||
|
get("/api/v3/repos/:owner/:repository/releases/:tag/assets/:fileId")(referrersOnly { repository =>
|
||||||
|
val tag = params("tag")
|
||||||
|
val fileId = params("fileId")
|
||||||
|
getReleaseAsset(repository.owner, repository.name, tag, fileId)
|
||||||
|
.map { asset =>
|
||||||
|
JsonFormat(ApiReleaseAsset(asset, RepositoryName(repository)))
|
||||||
|
}
|
||||||
|
.getOrElse(NotFound())
|
||||||
|
})
|
||||||
|
|
||||||
|
/*
|
||||||
|
* xi. Edit a release asset
|
||||||
|
* https://developer.github.com/v3/repos/releases/#edit-a-release-asset
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* xii. Delete a release asset
|
||||||
|
* https://developer.github.com/v3/repos/releases/#edit-a-release-asset
|
||||||
|
*/
|
||||||
|
}
|
||||||
@@ -3,8 +3,11 @@ import gitbucket.core.api._
|
|||||||
import gitbucket.core.controller.ControllerBase
|
import gitbucket.core.controller.ControllerBase
|
||||||
import gitbucket.core.service.{AccountService, ProtectedBranchService, RepositoryService}
|
import gitbucket.core.service.{AccountService, ProtectedBranchService, RepositoryService}
|
||||||
import gitbucket.core.util._
|
import gitbucket.core.util._
|
||||||
|
import gitbucket.core.util.Directory._
|
||||||
import gitbucket.core.util.Implicits._
|
import gitbucket.core.util.Implicits._
|
||||||
import gitbucket.core.util.JGitUtil.getBranches
|
import gitbucket.core.util.JGitUtil.getBranches
|
||||||
|
import org.eclipse.jgit.api.Git
|
||||||
|
import scala.util.Using
|
||||||
|
|
||||||
trait ApiRepositoryBranchControllerBase extends ControllerBase {
|
trait ApiRepositoryBranchControllerBase extends ControllerBase {
|
||||||
self: RepositoryService
|
self: RepositoryService
|
||||||
@@ -22,11 +25,11 @@ trait ApiRepositoryBranchControllerBase extends ControllerBase {
|
|||||||
* https://developer.github.com/v3/repos/branches/#list-branches
|
* https://developer.github.com/v3/repos/branches/#list-branches
|
||||||
*/
|
*/
|
||||||
get("/api/v3/repos/:owner/:repository/branches")(referrersOnly { repository =>
|
get("/api/v3/repos/:owner/:repository/branches")(referrersOnly { repository =>
|
||||||
|
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||||
JsonFormat(
|
JsonFormat(
|
||||||
JGitUtil
|
JGitUtil
|
||||||
.getBranches(
|
.getBranches(
|
||||||
owner = repository.owner,
|
git = git,
|
||||||
name = repository.name,
|
|
||||||
defaultBranch = repository.repository.defaultBranch,
|
defaultBranch = repository.repository.defaultBranch,
|
||||||
origin = repository.repository.originUserName.isEmpty
|
origin = repository.repository.originUserName.isEmpty
|
||||||
)
|
)
|
||||||
@@ -34,6 +37,7 @@ trait ApiRepositoryBranchControllerBase extends ControllerBase {
|
|||||||
ApiBranchForList(br.name, ApiBranchCommit(br.commitId))
|
ApiBranchForList(br.name, ApiBranchCommit(br.commitId))
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -41,12 +45,12 @@ trait ApiRepositoryBranchControllerBase extends ControllerBase {
|
|||||||
* https://developer.github.com/v3/repos/branches/#get-branch
|
* https://developer.github.com/v3/repos/branches/#get-branch
|
||||||
*/
|
*/
|
||||||
get("/api/v3/repos/:owner/:repository/branches/*")(referrersOnly { repository =>
|
get("/api/v3/repos/:owner/:repository/branches/*")(referrersOnly { repository =>
|
||||||
//import gitbucket.core.api._
|
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) {
|
||||||
|
git =>
|
||||||
(for {
|
(for {
|
||||||
branch <- params.get("splat") if repository.branchList.contains(branch)
|
branch <- params.get("splat") if repository.branchList.contains(branch)
|
||||||
br <- getBranches(
|
br <- getBranches(
|
||||||
repository.owner,
|
git,
|
||||||
repository.name,
|
|
||||||
repository.repository.defaultBranch,
|
repository.repository.defaultBranch,
|
||||||
repository.repository.originUserName.isEmpty
|
repository.repository.originUserName.isEmpty
|
||||||
).find(_.name == branch)
|
).find(_.name == branch)
|
||||||
@@ -56,6 +60,7 @@ trait ApiRepositoryBranchControllerBase extends ControllerBase {
|
|||||||
ApiBranch(branch, ApiBranchCommit(br.commitId), ApiBranchProtection(protection))(RepositoryName(repository))
|
ApiBranch(branch, ApiBranchCommit(br.commitId), ApiBranchProtection(protection))(RepositoryName(repository))
|
||||||
)
|
)
|
||||||
}) getOrElse NotFound()
|
}) getOrElse NotFound()
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -209,12 +214,13 @@ trait ApiRepositoryBranchControllerBase extends ControllerBase {
|
|||||||
*/
|
*/
|
||||||
patch("/api/v3/repos/:owner/:repository/branches/*")(ownerOnly { repository =>
|
patch("/api/v3/repos/:owner/:repository/branches/*")(ownerOnly { repository =>
|
||||||
import gitbucket.core.api._
|
import gitbucket.core.api._
|
||||||
|
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) {
|
||||||
|
git =>
|
||||||
(for {
|
(for {
|
||||||
branch <- params.get("splat") if repository.branchList.contains(branch)
|
branch <- params.get("splat") if repository.branchList.contains(branch)
|
||||||
protection <- extractFromJsonBody[ApiBranchProtection.EnablingAndDisabling].map(_.protection)
|
protection <- extractFromJsonBody[ApiBranchProtection.EnablingAndDisabling].map(_.protection)
|
||||||
br <- getBranches(
|
br <- getBranches(
|
||||||
repository.owner,
|
git,
|
||||||
repository.name,
|
|
||||||
repository.repository.defaultBranch,
|
repository.repository.defaultBranch,
|
||||||
repository.repository.originUserName.isEmpty
|
repository.repository.originUserName.isEmpty
|
||||||
).find(_.name == branch)
|
).find(_.name == branch)
|
||||||
@@ -232,5 +238,6 @@ trait ApiRepositoryBranchControllerBase extends ControllerBase {
|
|||||||
}
|
}
|
||||||
JsonFormat(ApiBranch(branch, ApiBranchCommit(br.commitId), protection)(RepositoryName(repository)))
|
JsonFormat(ApiBranch(branch, ApiBranchCommit(br.commitId), protection)(RepositoryName(repository)))
|
||||||
}) getOrElse NotFound()
|
}) getOrElse NotFound()
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,9 +7,10 @@ import gitbucket.core.util.Directory.getRepositoryDir
|
|||||||
import gitbucket.core.util.Implicits._
|
import gitbucket.core.util.Implicits._
|
||||||
import gitbucket.core.util.JGitUtil.CommitInfo
|
import gitbucket.core.util.JGitUtil.CommitInfo
|
||||||
import gitbucket.core.util.{JGitUtil, ReferrerAuthenticator, RepositoryName}
|
import gitbucket.core.util.{JGitUtil, ReferrerAuthenticator, RepositoryName}
|
||||||
import gitbucket.core.util.SyntaxSugars.using
|
|
||||||
import org.eclipse.jgit.api.Git
|
import org.eclipse.jgit.api.Git
|
||||||
import org.eclipse.jgit.revwalk.RevWalk
|
import org.eclipse.jgit.revwalk.RevWalk
|
||||||
|
import scala.jdk.CollectionConverters._
|
||||||
|
import scala.util.Using
|
||||||
|
|
||||||
trait ApiRepositoryCommitControllerBase extends ControllerBase {
|
trait ApiRepositoryCommitControllerBase extends ControllerBase {
|
||||||
self: AccountService with CommitsService with ReferrerAuthenticator =>
|
self: AccountService with CommitsService with ReferrerAuthenticator =>
|
||||||
@@ -17,6 +18,33 @@ trait ApiRepositoryCommitControllerBase extends ControllerBase {
|
|||||||
* i. List commits on a repository
|
* i. List commits on a repository
|
||||||
* https://developer.github.com/v3/repos/commits/#list-commits-on-a-repository
|
* https://developer.github.com/v3/repos/commits/#list-commits-on-a-repository
|
||||||
*/
|
*/
|
||||||
|
get("/api/v3/repos/:owner/:repository/commits")(referrersOnly { repository =>
|
||||||
|
val owner = repository.owner
|
||||||
|
val name = repository.name
|
||||||
|
// TODO: The following parameters need to be implemented. [:path, :author, :since, :until]
|
||||||
|
val sha = if (request.body.nonEmpty) (parse(request.body) \ "sha").extract[String] else "refs/heads/master";
|
||||||
|
Using.resource(Git.open(getRepositoryDir(owner, name))) {
|
||||||
|
git =>
|
||||||
|
val repo = git.getRepository
|
||||||
|
Using.resource(new RevWalk(repo)) {
|
||||||
|
revWalk =>
|
||||||
|
val objectId = repo.resolve(sha)
|
||||||
|
revWalk.markStart(revWalk.parseCommit(objectId))
|
||||||
|
JsonFormat(revWalk.asScala.take(30).map {
|
||||||
|
commit =>
|
||||||
|
val commitInfo = new CommitInfo(commit)
|
||||||
|
ApiCommits(
|
||||||
|
repositoryName = RepositoryName(repository),
|
||||||
|
commitInfo = commitInfo,
|
||||||
|
diffs = JGitUtil.getDiffs(git, commitInfo.parents.headOption, commitInfo.id, false, true),
|
||||||
|
author = getAccount(commitInfo.authorName, commitInfo.authorEmailAddress),
|
||||||
|
committer = getAccount(commitInfo.committerName, commitInfo.committerEmailAddress),
|
||||||
|
commentCount = getCommitComment(repository.owner, repository.name, commitInfo.id.toString).size
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ii. Get a single commit
|
* ii. Get a single commit
|
||||||
@@ -27,11 +55,11 @@ trait ApiRepositoryCommitControllerBase extends ControllerBase {
|
|||||||
val name = repository.name
|
val name = repository.name
|
||||||
val sha = params("sha")
|
val sha = params("sha")
|
||||||
|
|
||||||
using(Git.open(getRepositoryDir(owner, name))) {
|
Using.resource(Git.open(getRepositoryDir(owner, name))) {
|
||||||
git =>
|
git =>
|
||||||
val repo = git.getRepository
|
val repo = git.getRepository
|
||||||
val objectId = repo.resolve(sha)
|
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))
|
new CommitInfo(revWalk.parseCommit(objectId))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
package gitbucket.core.controller.api
|
package gitbucket.core.controller.api
|
||||||
import gitbucket.core.api.{ApiContents, JsonFormat}
|
import gitbucket.core.api.{ApiContents, ApiError, CreateAFile, JsonFormat}
|
||||||
import gitbucket.core.controller.ControllerBase
|
import gitbucket.core.controller.ControllerBase
|
||||||
import gitbucket.core.service.{AccountService, ProtectedBranchService, RepositoryService}
|
import gitbucket.core.service.{RepositoryCommitFileService, RepositoryService}
|
||||||
import gitbucket.core.util.Directory.getRepositoryDir
|
import gitbucket.core.util.Directory.getRepositoryDir
|
||||||
import gitbucket.core.util.JGitUtil.{FileInfo, getContentFromId, getFileList}
|
import gitbucket.core.util.JGitUtil.{FileInfo, getContentFromId, getFileList}
|
||||||
import gitbucket.core.util._
|
import gitbucket.core.util._
|
||||||
import gitbucket.core.util.SyntaxSugars.using
|
|
||||||
import gitbucket.core.view.helpers.{isRenderable, renderMarkup}
|
import gitbucket.core.view.helpers.{isRenderable, renderMarkup}
|
||||||
import gitbucket.core.util.Implicits._
|
import gitbucket.core.util.Implicits._
|
||||||
import org.eclipse.jgit.api.Git
|
import org.eclipse.jgit.api.Git
|
||||||
|
import scala.util.Using
|
||||||
|
|
||||||
trait ApiRepositoryContentsControllerBase extends ControllerBase {
|
trait ApiRepositoryContentsControllerBase extends ControllerBase {
|
||||||
self: ReferrerAuthenticator =>
|
self: ReferrerAuthenticator with WritableUsersAuthenticator with RepositoryCommitFileService =>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* i. Get the README
|
* i. Get the README
|
||||||
@@ -45,7 +45,7 @@ trait ApiRepositoryContentsControllerBase extends ControllerBase {
|
|||||||
getFileList(git, revision, dirName).find(f => f.name.equals(fileName))
|
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)
|
val fileList = getFileList(git, refStr, path)
|
||||||
if (fileList.isEmpty) { // file or NotFound
|
if (fileList.isEmpty) { // file or NotFound
|
||||||
getFileInfo(git, refStr, path)
|
getFileInfo(git, refStr, path)
|
||||||
@@ -101,16 +101,45 @@ trait ApiRepositoryContentsControllerBase extends ControllerBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
* iii. Create a file
|
* iii. Create a file or iv. Update a file
|
||||||
* https://developer.github.com/v3/repos/contents/#create-a-file
|
* https://developer.github.com/v3/repos/contents/#create-a-file
|
||||||
|
* https://developer.github.com/v3/repos/contents/#update-a-file
|
||||||
|
* if sha is presented, update a file else create a file.
|
||||||
* requested #2112
|
* requested #2112
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
put("/api/v3/repos/:owner/:repository/contents/*")(writableUsersOnly { repository =>
|
||||||
* iv. Update a file
|
JsonFormat(for {
|
||||||
* https://developer.github.com/v3/repos/contents/#update-a-file
|
data <- extractFromJsonBody[CreateAFile]
|
||||||
* requested #2112
|
} yield {
|
||||||
*/
|
val branch = data.branch.getOrElse(repository.repository.defaultBranch)
|
||||||
|
val commit = Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||||
|
val revCommit = JGitUtil.getRevCommitFromId(git, git.getRepository.resolve(branch))
|
||||||
|
revCommit.name
|
||||||
|
}
|
||||||
|
val paths = multiParams("splat").head.split("/")
|
||||||
|
val path = paths.take(paths.size - 1).toList.mkString("/")
|
||||||
|
if (data.sha.isDefined && data.sha.get != commit) {
|
||||||
|
ApiError("The blob SHA is not matched.", Some("https://developer.github.com/v3/repos/contents/#update-a-file"))
|
||||||
|
} else {
|
||||||
|
val objectId = commitFile(
|
||||||
|
repository,
|
||||||
|
branch,
|
||||||
|
path,
|
||||||
|
Some(paths.last),
|
||||||
|
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),
|
||||||
|
context.settings
|
||||||
|
)
|
||||||
|
ApiContents("file", paths.last, path, objectId.name, None, None)(RepositoryName(repository))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* v. Delete a file
|
* v. Delete a file
|
||||||
|
|||||||
@@ -6,12 +6,12 @@ import gitbucket.core.servlet.Database
|
|||||||
import gitbucket.core.util.Directory.getRepositoryDir
|
import gitbucket.core.util.Directory.getRepositoryDir
|
||||||
import gitbucket.core.util._
|
import gitbucket.core.util._
|
||||||
import gitbucket.core.util.Implicits._
|
import gitbucket.core.util.Implicits._
|
||||||
import gitbucket.core.util.SyntaxSugars.using
|
|
||||||
import gitbucket.core.model.Profile.profile.blockingApi._
|
import gitbucket.core.model.Profile.profile.blockingApi._
|
||||||
import org.eclipse.jgit.api.Git
|
import org.eclipse.jgit.api.Git
|
||||||
|
|
||||||
import scala.concurrent.Await
|
import scala.concurrent.Await
|
||||||
import scala.concurrent.duration.Duration
|
import scala.concurrent.duration.Duration
|
||||||
|
import scala.util.Using
|
||||||
|
|
||||||
trait ApiRepositoryControllerBase extends ControllerBase {
|
trait ApiRepositoryControllerBase extends ControllerBase {
|
||||||
self: RepositoryService
|
self: RepositoryService
|
||||||
@@ -193,7 +193,7 @@ trait ApiRepositoryControllerBase extends ControllerBase {
|
|||||||
*/
|
*/
|
||||||
get("/api/v3/repos/:owner/:repository/raw/*")(referrersOnly { repository =>
|
get("/api/v3/repos/:owner/:repository/raw/*")(referrersOnly { repository =>
|
||||||
val (id, path) = repository.splitPath(multiParams("splat").head)
|
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))
|
val revCommit = JGitUtil.getRevCommitFromId(git, git.getRepository.resolve(id))
|
||||||
|
|
||||||
getPathObjectId(git, path, revCommit).map { objectId =>
|
getPathObjectId(git, path, revCommit).map { objectId =>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import gitbucket.core.controller.ControllerBase
|
|||||||
import gitbucket.core.service.{AccountService, RepositoryService}
|
import gitbucket.core.service.{AccountService, RepositoryService}
|
||||||
import gitbucket.core.util.{AdminAuthenticator, UsersAuthenticator}
|
import gitbucket.core.util.{AdminAuthenticator, UsersAuthenticator}
|
||||||
import gitbucket.core.util.Implicits._
|
import gitbucket.core.util.Implicits._
|
||||||
|
import gitbucket.core.util.StringUtil._
|
||||||
import org.scalatra.NoContent
|
import org.scalatra.NoContent
|
||||||
|
|
||||||
trait ApiUserControllerBase extends ControllerBase {
|
trait ApiUserControllerBase extends ControllerBase {
|
||||||
@@ -70,7 +71,7 @@ trait ApiUserControllerBase extends ControllerBase {
|
|||||||
} yield {
|
} yield {
|
||||||
val user = createAccount(
|
val user = createAccount(
|
||||||
data.login,
|
data.login,
|
||||||
data.password,
|
pbkdf2_sha256(data.password),
|
||||||
data.fullName.getOrElse(data.login),
|
data.fullName.getOrElse(data.login),
|
||||||
data.email,
|
data.email,
|
||||||
data.isAdmin.getOrElse(false),
|
data.isAdmin.getOrElse(false),
|
||||||
|
|||||||
29
src/main/scala/gitbucket/core/model/GpgKey.scala
Normal file
29
src/main/scala/gitbucket/core/model/GpgKey.scala
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
package gitbucket.core.model
|
||||||
|
|
||||||
|
trait GpgKeyComponent { self: Profile =>
|
||||||
|
import profile.api._
|
||||||
|
|
||||||
|
lazy val GpgKeys = TableQuery[GpgKeys]
|
||||||
|
|
||||||
|
class GpgKeys(tag: Tag) extends Table[GpgKey](tag, "GPG_KEY") {
|
||||||
|
val userName = column[String]("USER_NAME")
|
||||||
|
val keyId = column[Int]("KEY_ID", O AutoInc)
|
||||||
|
val gpgKeyId = column[Long]("GPG_KEY_ID")
|
||||||
|
val title = column[String]("TITLE")
|
||||||
|
val publicKey = column[String]("PUBLIC_KEY")
|
||||||
|
def * = (userName, keyId, gpgKeyId, title, publicKey) <> (GpgKey.tupled, GpgKey.unapply)
|
||||||
|
|
||||||
|
def byPrimaryKey(userName: String, keyId: Int) =
|
||||||
|
(this.userName === userName.bind) && (this.keyId === keyId.bind)
|
||||||
|
def byGpgKeyId(gpgKeyId: Long) =
|
||||||
|
this.gpgKeyId === gpgKeyId.bind
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case class GpgKey(
|
||||||
|
userName: String,
|
||||||
|
keyId: Int = 0,
|
||||||
|
gpgKeyId: Long,
|
||||||
|
title: String,
|
||||||
|
publicKey: String
|
||||||
|
)
|
||||||
@@ -59,6 +59,7 @@ trait CoreProfile
|
|||||||
with PullRequestComponent
|
with PullRequestComponent
|
||||||
with RepositoryComponent
|
with RepositoryComponent
|
||||||
with SshKeyComponent
|
with SshKeyComponent
|
||||||
|
with GpgKeyComponent
|
||||||
with RepositoryWebHookComponent
|
with RepositoryWebHookComponent
|
||||||
with RepositoryWebHookEventComponent
|
with RepositoryWebHookEventComponent
|
||||||
with AccountWebHookComponent
|
with AccountWebHookComponent
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ trait PullRequestComponent extends TemplateComponent { self: Profile =>
|
|||||||
val requestBranch = column[String]("REQUEST_BRANCH")
|
val requestBranch = column[String]("REQUEST_BRANCH")
|
||||||
val commitIdFrom = column[String]("COMMIT_ID_FROM")
|
val commitIdFrom = column[String]("COMMIT_ID_FROM")
|
||||||
val commitIdTo = column[String]("COMMIT_ID_TO")
|
val commitIdTo = column[String]("COMMIT_ID_TO")
|
||||||
|
val isDraft = column[Boolean]("IS_DRAFT")
|
||||||
def * =
|
def * =
|
||||||
(
|
(
|
||||||
userName,
|
userName,
|
||||||
@@ -22,7 +23,8 @@ trait PullRequestComponent extends TemplateComponent { self: Profile =>
|
|||||||
requestRepositoryName,
|
requestRepositoryName,
|
||||||
requestBranch,
|
requestBranch,
|
||||||
commitIdFrom,
|
commitIdFrom,
|
||||||
commitIdTo
|
commitIdTo,
|
||||||
|
isDraft
|
||||||
) <> (PullRequest.tupled, PullRequest.unapply)
|
) <> (PullRequest.tupled, PullRequest.unapply)
|
||||||
|
|
||||||
def byPrimaryKey(userName: String, repositoryName: String, issueId: Int) =
|
def byPrimaryKey(userName: String, repositoryName: String, issueId: Int) =
|
||||||
@@ -41,5 +43,6 @@ case class PullRequest(
|
|||||||
requestRepositoryName: String,
|
requestRepositoryName: String,
|
||||||
requestBranch: String,
|
requestBranch: String,
|
||||||
commitIdFrom: 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.model.{Account, Issue}
|
||||||
import gitbucket.core.service.RepositoryService.RepositoryInfo
|
import gitbucket.core.service.RepositoryService.RepositoryInfo
|
||||||
import gitbucket.core.service.SystemSettingsService.SystemSettings
|
import gitbucket.core.service.SystemSettingsService.SystemSettings
|
||||||
import gitbucket.core.util.SyntaxSugars._
|
|
||||||
import io.github.gitbucket.solidbase.model.Version
|
import io.github.gitbucket.solidbase.model.Version
|
||||||
import org.apache.sshd.server.command.Command
|
import org.apache.sshd.server.command.Command
|
||||||
import play.twirl.api.Html
|
import play.twirl.api.Html
|
||||||
|
import scala.util.Using
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Trait for define plugin interface.
|
* Trait for define plugin interface.
|
||||||
@@ -434,7 +434,7 @@ abstract class Plugin {
|
|||||||
* Helper method to get a resource from classpath.
|
* Helper method to get a resource from classpath.
|
||||||
*/
|
*/
|
||||||
protected def fromClassPath(path: String): Array[Byte] =
|
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)
|
val bytes = new Array[Byte](in.available)
|
||||||
in.read(bytes)
|
in.read(bytes)
|
||||||
bytes
|
bytes
|
||||||
|
|||||||
@@ -15,19 +15,17 @@ import gitbucket.core.service.ProtectedBranchService.ProtectedBranchReceiveHook
|
|||||||
import gitbucket.core.service.RepositoryService.RepositoryInfo
|
import gitbucket.core.service.RepositoryService.RepositoryInfo
|
||||||
import gitbucket.core.service.SystemSettingsService
|
import gitbucket.core.service.SystemSettingsService
|
||||||
import gitbucket.core.service.SystemSettingsService.SystemSettings
|
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.Directory._
|
||||||
import gitbucket.core.util.HttpClientUtil._
|
|
||||||
import io.github.gitbucket.solidbase.Solidbase
|
import io.github.gitbucket.solidbase.Solidbase
|
||||||
import io.github.gitbucket.solidbase.manager.JDBCVersionManager
|
import io.github.gitbucket.solidbase.manager.JDBCVersionManager
|
||||||
import io.github.gitbucket.solidbase.model.Module
|
import io.github.gitbucket.solidbase.model.Module
|
||||||
import org.apache.commons.io.FileUtils
|
import org.apache.commons.io.FileUtils
|
||||||
import org.apache.http.client.methods.HttpGet
|
|
||||||
import org.apache.sshd.server.command.Command
|
import org.apache.sshd.server.command.Command
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import play.twirl.api.Html
|
import play.twirl.api.Html
|
||||||
|
|
||||||
import scala.collection.JavaConverters._
|
import scala.jdk.CollectionConverters._
|
||||||
|
|
||||||
class PluginRegistry {
|
class PluginRegistry {
|
||||||
|
|
||||||
@@ -227,40 +225,6 @@ object PluginRegistry {
|
|||||||
initialize(context, settings, conn)
|
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] = {
|
private def listPluginJars(dir: File): Seq[File] = {
|
||||||
dir
|
dir
|
||||||
.listFiles(new FilenameFilter {
|
.listFiles(new FilenameFilter {
|
||||||
@@ -271,7 +235,7 @@ object PluginRegistry {
|
|||||||
.reverse
|
.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] = {
|
def getGitBucketVersion(pluginJarFileName: String): Option[String] = {
|
||||||
val regex = ".+-gitbucket\\_(\\d+\\.\\d+\\.\\d+(-SNAPSHOT)?)-.+".r
|
val regex = ".+-gitbucket\\_(\\d+\\.\\d+\\.\\d+(-SNAPSHOT)?)-.+".r
|
||||||
@@ -331,6 +295,7 @@ object PluginRegistry {
|
|||||||
instance.getPlugins().find(_.pluginId == pluginId) match {
|
instance.getPlugins().find(_.pluginId == pluginId) match {
|
||||||
case Some(x) => {
|
case Some(x) => {
|
||||||
logger.warn(s"Plugin ${pluginId} is duplicated. ${x.pluginJar.getName} is available.")
|
logger.warn(s"Plugin ${pluginId} is duplicated. ${x.pluginJar.getName} is available.")
|
||||||
|
classLoader.close()
|
||||||
}
|
}
|
||||||
case None => {
|
case None => {
|
||||||
// Migration
|
// Migration
|
||||||
@@ -370,7 +335,9 @@ object PluginRegistry {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
case e: Throwable => logger.error(s"Error during plugin initialization: ${pluginJar.getName}", e)
|
case e: Throwable =>
|
||||||
|
logger.error(s"Error during plugin initialization: ${pluginJar.getName}", e)
|
||||||
|
classLoader.close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -409,6 +376,13 @@ object PluginRegistry {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def getPluginInfoFromClassLoader(classLoader: ClassLoader): Option[PluginInfo] = {
|
||||||
|
instance
|
||||||
|
.getPlugins()
|
||||||
|
.find { info =>
|
||||||
|
info.classLoader.equals(classLoader)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case class Link(
|
case class Link(
|
||||||
@@ -439,7 +413,6 @@ case class PluginInfo(
|
|||||||
|
|
||||||
class PluginWatchThread(context: ServletContext, dir: String) extends Thread with SystemSettingsService {
|
class PluginWatchThread(context: ServletContext, dir: String) extends Thread with SystemSettingsService {
|
||||||
import gitbucket.core.model.Profile.profile.blockingApi._
|
import gitbucket.core.model.Profile.profile.blockingApi._
|
||||||
import scala.collection.JavaConverters._
|
|
||||||
|
|
||||||
private val logger = LoggerFactory.getLogger(classOf[PluginWatchThread])
|
private val logger = LoggerFactory.getLogger(classOf[PluginWatchThread])
|
||||||
|
|
||||||
@@ -469,7 +442,7 @@ class PluginWatchThread(context: ServletContext, dir: String) extends Thread wit
|
|||||||
}
|
}
|
||||||
if (events.nonEmpty) {
|
if (events.nonEmpty) {
|
||||||
events.foreach { event =>
|
events.foreach { event =>
|
||||||
logger.info(event.kind + ": " + event.context)
|
logger.info(s"${event.kind}: ${event.context}")
|
||||||
}
|
}
|
||||||
new Thread {
|
new Thread {
|
||||||
override def run(): Unit = {
|
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
|
|
||||||
)
|
|
||||||
@@ -240,8 +240,8 @@ trait AccountService {
|
|||||||
def updateLastLoginDate(userName: String)(implicit s: Session): Unit =
|
def updateLastLoginDate(userName: String)(implicit s: Session): Unit =
|
||||||
Accounts.filter(_.userName === userName.bind).map(_.lastLoginDate).update(currentDate)
|
Accounts.filter(_.userName === userName.bind).map(_.lastLoginDate).update(currentDate)
|
||||||
|
|
||||||
def createGroup(groupName: String, description: Option[String], url: Option[String])(implicit s: Session): Unit =
|
def createGroup(groupName: String, description: Option[String], url: Option[String])(implicit s: Session): Account = {
|
||||||
Accounts insert Account(
|
val group = Account(
|
||||||
userName = groupName,
|
userName = groupName,
|
||||||
password = "",
|
password = "",
|
||||||
fullName = groupName,
|
fullName = groupName,
|
||||||
@@ -256,6 +256,9 @@ trait AccountService {
|
|||||||
isRemoved = false,
|
isRemoved = false,
|
||||||
description = description
|
description = description
|
||||||
)
|
)
|
||||||
|
Accounts insert group
|
||||||
|
group
|
||||||
|
}
|
||||||
|
|
||||||
def updateGroup(groupName: String, description: Option[String], url: Option[String], removed: Boolean)(
|
def updateGroup(groupName: String, description: Option[String], url: Option[String], removed: Boolean)(
|
||||||
implicit s: Session
|
implicit s: Session
|
||||||
|
|||||||
@@ -131,6 +131,23 @@ trait ActivityService {
|
|||||||
currentDate
|
currentDate
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def recordReopenPullRequestActivity(
|
||||||
|
userName: String,
|
||||||
|
repositoryName: String,
|
||||||
|
activityUserName: String,
|
||||||
|
issueId: Int,
|
||||||
|
title: String
|
||||||
|
)(implicit s: Session): Unit =
|
||||||
|
Activities insert Activity(
|
||||||
|
userName,
|
||||||
|
repositoryName,
|
||||||
|
activityUserName,
|
||||||
|
"reopen_issue",
|
||||||
|
s"[user:${activityUserName}] reopened pull request [issue:${userName}/${repositoryName}#${issueId}]",
|
||||||
|
Some(title),
|
||||||
|
currentDate
|
||||||
|
)
|
||||||
|
|
||||||
def recordCommentIssueActivity(
|
def recordCommentIssueActivity(
|
||||||
userName: String,
|
userName: String,
|
||||||
repositoryName: String,
|
repositoryName: String,
|
||||||
@@ -352,15 +369,19 @@ trait ActivityService {
|
|||||||
currentDate
|
currentDate
|
||||||
)
|
)
|
||||||
|
|
||||||
def recordReleaseActivity(userName: String, repositoryName: String, activityUserName: String, name: String)(
|
def recordReleaseActivity(
|
||||||
implicit s: Session
|
userName: String,
|
||||||
): Unit =
|
repositoryName: String,
|
||||||
|
activityUserName: String,
|
||||||
|
releaseName: String,
|
||||||
|
tagName: String
|
||||||
|
)(implicit s: Session): Unit =
|
||||||
Activities insert Activity(
|
Activities insert Activity(
|
||||||
userName,
|
userName,
|
||||||
repositoryName,
|
repositoryName,
|
||||||
activityUserName,
|
activityUserName,
|
||||||
"release",
|
"release",
|
||||||
s"[user:${activityUserName}] released [release:${userName}/${repositoryName}/${name}] at [repo:${userName}/${repositoryName}]",
|
s"[user:${activityUserName}] released [release:${userName}/${repositoryName}/${tagName}:${releaseName}] at [repo:${userName}/${repositoryName}]",
|
||||||
None,
|
None,
|
||||||
currentDate
|
currentDate
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -94,7 +94,8 @@ trait CommitsService {
|
|||||||
repository,
|
repository,
|
||||||
issue,
|
issue,
|
||||||
pullRequest,
|
pullRequest,
|
||||||
loginAccount
|
loginAccount,
|
||||||
|
context.settings
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
case None =>
|
case None =>
|
||||||
|
|||||||
29
src/main/scala/gitbucket/core/service/GpgKeyService.scala
Normal file
29
src/main/scala/gitbucket/core/service/GpgKeyService.scala
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
package gitbucket.core.service
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream
|
||||||
|
|
||||||
|
import gitbucket.core.model.GpgKey
|
||||||
|
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
|
||||||
|
|
||||||
|
def addGpgPublicKey(userName: String, title: String, publicKey: String)(implicit s: Session): Unit = {
|
||||||
|
val pubKeyOf = new BcPGPObjectFactory(new ArmoredInputStream(new ByteArrayInputStream(publicKey.getBytes)))
|
||||||
|
pubKeyOf.iterator().asScala.foreach {
|
||||||
|
case keyRing: PGPPublicKeyRing =>
|
||||||
|
val key = keyRing.getPublicKey()
|
||||||
|
GpgKeys.insert(GpgKey(userName = userName, gpgKeyId = key.getKeyID, title = title, publicKey = publicKey))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def deleteGpgPublicKey(userName: String, keyId: Int)(implicit s: Session): Unit =
|
||||||
|
GpgKeys.filter(_.byPrimaryKey(userName, keyId)).delete
|
||||||
|
}
|
||||||
@@ -39,7 +39,10 @@ trait HandleCommentService {
|
|||||||
))
|
))
|
||||||
case "reopen" if (issue.closed) =>
|
case "reopen" if (issue.closed) =>
|
||||||
false ->
|
false ->
|
||||||
(Some("reopen") -> Some(recordReopenIssueActivity _))
|
(Some("reopen") -> Some(
|
||||||
|
if (issue.isPullRequest) recordReopenPullRequestActivity _
|
||||||
|
else recordReopenIssueActivity _
|
||||||
|
))
|
||||||
}
|
}
|
||||||
.map {
|
.map {
|
||||||
case (closed, t) =>
|
case (closed, t) =>
|
||||||
@@ -80,16 +83,17 @@ trait HandleCommentService {
|
|||||||
|
|
||||||
// call web hooks
|
// call web hooks
|
||||||
action match {
|
action match {
|
||||||
case None => commentId foreach (callIssueCommentWebHook(repository, issue, _, loginAccount))
|
case None =>
|
||||||
|
commentId foreach (callIssueCommentWebHook(repository, issue, _, loginAccount, context.settings))
|
||||||
case Some(act) =>
|
case Some(act) =>
|
||||||
val webHookAction = act match {
|
val webHookAction = act match {
|
||||||
case "close" => "closed"
|
case "close" => "closed"
|
||||||
case "reopen" => "reopened"
|
case "reopen" => "reopened"
|
||||||
}
|
}
|
||||||
if (issue.isPullRequest)
|
if (issue.isPullRequest)
|
||||||
callPullRequestWebHook(webHookAction, repository, issue.issueId, loginAccount)
|
callPullRequestWebHook(webHookAction, repository, issue.issueId, loginAccount, context.settings)
|
||||||
else
|
else
|
||||||
callIssuesWebHook(webHookAction, repository, issue, loginAccount)
|
callIssuesWebHook(webHookAction, repository, issue, loginAccount, context.settings)
|
||||||
}
|
}
|
||||||
|
|
||||||
// call hooks
|
// call hooks
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ trait IssueCreationService {
|
|||||||
createReferComment(owner, name, issue, title + " " + body.getOrElse(""), loginAccount)
|
createReferComment(owner, name, issue, title + " " + body.getOrElse(""), loginAccount)
|
||||||
|
|
||||||
// call web hooks
|
// call web hooks
|
||||||
callIssuesWebHook("opened", repository, issue, loginAccount)
|
callIssuesWebHook("opened", repository, issue, loginAccount, context.settings)
|
||||||
|
|
||||||
// call hooks
|
// call hooks
|
||||||
PluginRegistry().getIssueHooks.foreach(_.created(issue, repository))
|
PluginRegistry().getIssueHooks.foreach(_.created(issue, repository))
|
||||||
|
|||||||
@@ -265,17 +265,19 @@ trait IssuesService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** for api
|
/** for api
|
||||||
* @return (issue, issueUser, commentCount)
|
* @return (issue, issueUser, assignedUser)
|
||||||
*/
|
*/
|
||||||
def searchIssueByApi(condition: IssueSearchCondition, offset: Int, limit: Int, repos: (String, String)*)(
|
def searchIssueByApi(condition: IssueSearchCondition, offset: Int, limit: Int, repos: (String, String)*)(
|
||||||
implicit s: Session
|
implicit s: Session
|
||||||
): List[(Issue, Account)] = {
|
): List[(Issue, Account, Option[Account])] = {
|
||||||
// get issues and comment count and labels
|
// get issues and comment count and labels
|
||||||
searchIssueQueryBase(condition, false, offset, limit, repos)
|
searchIssueQueryBase(condition, false, offset, limit, repos)
|
||||||
.join(Accounts)
|
.join(Accounts)
|
||||||
.on { case t1 ~ t2 ~ i ~ t3 => t3.userName === t1.openedUserName }
|
.on { case t1 ~ t2 ~ i ~ t3 => t3.userName === t1.openedUserName }
|
||||||
.sortBy { case t1 ~ t2 ~ i ~ t3 => i asc }
|
.joinLeft(Accounts)
|
||||||
.map { case t1 ~ t2 ~ i ~ t3 => (t1, t3) }
|
.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
|
.list
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -752,7 +754,7 @@ trait IssuesService {
|
|||||||
implicit s: Session
|
implicit s: Session
|
||||||
): Unit = {
|
): Unit = {
|
||||||
extractIssueId(message).foreach { issueId =>
|
extractIssueId(message).foreach { issueId =>
|
||||||
val content = fromIssue.issueId + ":" + fromIssue.title
|
val content = s"${fromIssue.issueId}:${fromIssue.title}"
|
||||||
if (getIssue(owner, repository, issueId).isDefined) {
|
if (getIssue(owner, repository, issueId).isDefined) {
|
||||||
// Not add if refer comment already exist.
|
// Not add if refer comment already exist.
|
||||||
if (!getComments(owner, repository, issueId.toInt).exists { x =>
|
if (!getComments(owner, repository, issueId.toInt).exists { x =>
|
||||||
@@ -908,12 +910,8 @@ object IssuesService {
|
|||||||
param(request, "groups").map(_.split(",").toSet).getOrElse(Set.empty)
|
param(request, "groups").map(_.split(",").toSet).getOrElse(Set.empty)
|
||||||
)
|
)
|
||||||
|
|
||||||
def page(request: HttpServletRequest) =
|
def page(request: HttpServletRequest) = {
|
||||||
try {
|
PaginationHelper.page(param(request, "page"))
|
||||||
val i = param(request, "page").getOrElse("1").toInt
|
|
||||||
if (i <= 0) 1 else i
|
|
||||||
} catch {
|
|
||||||
case e: NumberFormatException => 1
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,10 +7,8 @@ import gitbucket.core.plugin.PluginRegistry
|
|||||||
import gitbucket.core.service.RepositoryService.RepositoryInfo
|
import gitbucket.core.service.RepositoryService.RepositoryInfo
|
||||||
import gitbucket.core.util.Directory._
|
import gitbucket.core.util.Directory._
|
||||||
import gitbucket.core.util.{JGitUtil, LockUtil}
|
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.model.Profile.profile.blockingApi._
|
||||||
|
import gitbucket.core.service.SystemSettingsService.SystemSettings
|
||||||
import org.eclipse.jgit.merge.{MergeStrategy, Merger, RecursiveMerger}
|
import org.eclipse.jgit.merge.{MergeStrategy, Merger, RecursiveMerger}
|
||||||
import org.eclipse.jgit.api.Git
|
import org.eclipse.jgit.api.Git
|
||||||
import org.eclipse.jgit.transport.RefSpec
|
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.lib.{CommitBuilder, ObjectId, PersonIdent, Repository}
|
||||||
import org.eclipse.jgit.revwalk.{RevCommit, RevWalk}
|
import org.eclipse.jgit.revwalk.{RevCommit, RevWalk}
|
||||||
|
|
||||||
import scala.collection.JavaConverters._
|
import scala.jdk.CollectionConverters._
|
||||||
|
import scala.util.Using
|
||||||
|
|
||||||
trait MergeService {
|
trait MergeService {
|
||||||
self: AccountService
|
self: AccountService
|
||||||
@@ -35,7 +34,7 @@ trait MergeService {
|
|||||||
* Returns true if conflict will be caused.
|
* Returns true if conflict will be caused.
|
||||||
*/
|
*/
|
||||||
def checkConflict(userName: String, repositoryName: String, branch: String, issueId: Int): Option[String] = {
|
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()
|
new MergeCacheInfo(git, branch, issueId).checkConflict()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -52,7 +51,7 @@ trait MergeService {
|
|||||||
branch: String,
|
branch: String,
|
||||||
issueId: Int
|
issueId: Int
|
||||||
): Option[Option[String]] = {
|
): Option[Option[String]] = {
|
||||||
using(Git.open(getRepositoryDir(userName, repositoryName))) { git =>
|
Using.resource(Git.open(getRepositoryDir(userName, repositoryName))) { git =>
|
||||||
new MergeCacheInfo(git, branch, issueId).checkConflictCache()
|
new MergeCacheInfo(git, branch, issueId).checkConflictCache()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -99,7 +98,7 @@ trait MergeService {
|
|||||||
requestBranch: String,
|
requestBranch: String,
|
||||||
issueId: Int
|
issueId: Int
|
||||||
): Unit = {
|
): Unit = {
|
||||||
using(Git.open(getRepositoryDir(userName, repositoryName))) { git =>
|
Using.resource(Git.open(getRepositoryDir(userName, repositoryName))) { git =>
|
||||||
git.fetch
|
git.fetch
|
||||||
.setRemote(getRepositoryDir(requestUserName, requestRepositoryName).toURI.toString)
|
.setRemote(getRepositoryDir(requestUserName, requestRepositoryName).toURI.toString)
|
||||||
.setRefSpecs(new RefSpec(s"refs/heads/${requestBranch}:refs/pull/${issueId}/head"))
|
.setRefSpecs(new RefSpec(s"refs/heads/${requestBranch}:refs/pull/${issueId}/head"))
|
||||||
@@ -118,7 +117,7 @@ trait MergeService {
|
|||||||
remoteRepositoryName: String,
|
remoteRepositoryName: String,
|
||||||
remoteBranch: String
|
remoteBranch: String
|
||||||
): Either[String, (ObjectId, ObjectId, ObjectId)] = {
|
): 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 remoteRefName = s"refs/heads/${remoteBranch}"
|
||||||
val tmpRefName = s"refs/remote-temp/${remoteUserName}/${remoteRepositoryName}/${remoteBranch}"
|
val tmpRefName = s"refs/remote-temp/${remoteUserName}/${remoteRepositoryName}/${remoteBranch}"
|
||||||
val refSpec = new RefSpec(s"${remoteRefName}:${tmpRefName}").setForceUpdate(true)
|
val refSpec = new RefSpec(s"${remoteRefName}:${tmpRefName}").setForceUpdate(true)
|
||||||
@@ -169,7 +168,8 @@ trait MergeService {
|
|||||||
remoteBranch: String,
|
remoteBranch: String,
|
||||||
loginAccount: Account,
|
loginAccount: Account,
|
||||||
message: String,
|
message: String,
|
||||||
pullreq: Option[PullRequest]
|
pullreq: Option[PullRequest],
|
||||||
|
settings: SystemSettings
|
||||||
)(implicit s: Session, c: JsonFormat.Context): Option[ObjectId] = {
|
)(implicit s: Session, c: JsonFormat.Context): Option[ObjectId] = {
|
||||||
val localUserName = localRepository.owner
|
val localUserName = localRepository.owner
|
||||||
val localRepositoryName = localRepository.name
|
val localRepositoryName = localRepository.name
|
||||||
@@ -177,7 +177,7 @@ trait MergeService {
|
|||||||
val remoteRepositoryName = remoteRepository.name
|
val remoteRepositoryName = remoteRepository.name
|
||||||
tryMergeRemote(localUserName, localRepositoryName, localBranch, remoteUserName, remoteRepositoryName, remoteBranch).map {
|
tryMergeRemote(localUserName, localRepositoryName, localBranch, remoteUserName, remoteRepositoryName, remoteBranch).map {
|
||||||
case (newTreeId, oldBaseId, oldHeadId) =>
|
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 existIds = JGitUtil.getAllCommitIds(git).toSet
|
||||||
|
|
||||||
val committer = new PersonIdent(loginAccount.fullName, loginAccount.mailAddress)
|
val committer = new PersonIdent(loginAccount.fullName, loginAccount.mailAddress)
|
||||||
@@ -214,7 +214,7 @@ trait MergeService {
|
|||||||
closeIssuesFromMessage(commit.fullMessage, loginAccount.userName, localUserName, localRepositoryName)
|
closeIssuesFromMessage(commit.fullMessage, loginAccount.userName, localUserName, localRepositoryName)
|
||||||
.foreach { issueId =>
|
.foreach { issueId =>
|
||||||
getIssue(localRepository.owner, localRepository.name, issueId.toString).foreach { issue =>
|
getIssue(localRepository.owner, localRepository.name, issueId.toString).foreach { issue =>
|
||||||
callIssuesWebHook("closed", localRepository, issue, loginAccount)
|
callIssuesWebHook("closed", localRepository, issue, loginAccount, settings)
|
||||||
PluginRegistry().getIssueHooks
|
PluginRegistry().getIssueHooks
|
||||||
.foreach(
|
.foreach(
|
||||||
_.closedByCommitComment(issue, localRepository, commit.fullMessage, loginAccount)
|
_.closedByCommitComment(issue, localRepository, commit.fullMessage, loginAccount)
|
||||||
@@ -225,7 +225,7 @@ trait MergeService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pullreq.foreach { pullreq =>
|
pullreq.foreach { pullreq =>
|
||||||
callWebHookOf(localRepository.owner, localRepository.name, WebHook.Push) {
|
callWebHookOf(localRepository.owner, localRepository.name, WebHook.Push, settings) {
|
||||||
for {
|
for {
|
||||||
ownerAccount <- getAccountByUserName(localRepository.owner)
|
ownerAccount <- getAccountByUserName(localRepository.owner)
|
||||||
} yield {
|
} yield {
|
||||||
@@ -252,14 +252,17 @@ trait MergeService {
|
|||||||
issueId: Int,
|
issueId: Int,
|
||||||
loginAccount: Account,
|
loginAccount: Account,
|
||||||
message: String,
|
message: String,
|
||||||
strategy: String
|
strategy: String,
|
||||||
|
isDraft: Boolean,
|
||||||
|
settings: SystemSettings
|
||||||
)(implicit s: Session, c: JsonFormat.Context, context: Context): Either[String, ObjectId] = {
|
)(implicit s: Session, c: JsonFormat.Context, context: Context): Either[String, ObjectId] = {
|
||||||
|
if (!isDraft) {
|
||||||
if (repository.repository.options.mergeOptions.split(",").contains(strategy)) {
|
if (repository.repository.options.mergeOptions.split(",").contains(strategy)) {
|
||||||
LockUtil.lock(s"${repository.owner}/${repository.name}") {
|
LockUtil.lock(s"${repository.owner}/${repository.name}") {
|
||||||
getPullRequest(repository.owner, repository.name, issueId)
|
getPullRequest(repository.owner, repository.name, issueId)
|
||||||
.map {
|
.map {
|
||||||
case (issue, pullreq) =>
|
case (issue, pullreq) =>
|
||||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||||
// mark issue as merged and close.
|
// mark issue as merged and close.
|
||||||
val commentId =
|
val commentId =
|
||||||
createComment(repository.owner, repository.name, loginAccount.userName, issueId, message, "merge")
|
createComment(repository.owner, repository.name, loginAccount.userName, issueId, message, "merge")
|
||||||
@@ -278,11 +281,13 @@ trait MergeService {
|
|||||||
pullreq.commitIdTo
|
pullreq.commitIdTo
|
||||||
)
|
)
|
||||||
|
|
||||||
val revCommits = using(new RevWalk(git.getRepository)) { revWalk =>
|
val revCommits = Using
|
||||||
|
.resource(new RevWalk(git.getRepository)) { revWalk =>
|
||||||
commits.flatten.map { commit =>
|
commits.flatten.map { commit =>
|
||||||
revWalk.parseCommit(git.getRepository.resolve(commit.id))
|
revWalk.parseCommit(git.getRepository.resolve(commit.id))
|
||||||
}
|
}
|
||||||
}.reverse
|
}
|
||||||
|
.reverse
|
||||||
|
|
||||||
// merge git repository
|
// merge git repository
|
||||||
(strategy match {
|
(strategy match {
|
||||||
@@ -331,7 +336,7 @@ trait MergeService {
|
|||||||
repository.name
|
repository.name
|
||||||
).foreach { issueId =>
|
).foreach { issueId =>
|
||||||
getIssue(repository.owner, repository.name, issueId.toString).foreach { issue =>
|
getIssue(repository.owner, repository.name, issueId.toString).foreach { issue =>
|
||||||
callIssuesWebHook("closed", repository, issue, loginAccount)
|
callIssuesWebHook("closed", repository, issue, loginAccount, context.settings)
|
||||||
PluginRegistry().getIssueHooks
|
PluginRegistry().getIssueHooks
|
||||||
.foreach(_.closedByCommitComment(issue, repository, commit.fullMessage, loginAccount))
|
.foreach(_.closedByCommitComment(issue, repository, commit.fullMessage, loginAccount))
|
||||||
}
|
}
|
||||||
@@ -345,7 +350,7 @@ trait MergeService {
|
|||||||
repository.name
|
repository.name
|
||||||
).foreach { issueId =>
|
).foreach { issueId =>
|
||||||
getIssue(repository.owner, repository.name, issueId.toString).foreach { issue =>
|
getIssue(repository.owner, repository.name, issueId.toString).foreach { issue =>
|
||||||
callIssuesWebHook("closed", repository, issue, loginAccount)
|
callIssuesWebHook("closed", repository, issue, loginAccount, context.settings)
|
||||||
PluginRegistry().getIssueHooks
|
PluginRegistry().getIssueHooks
|
||||||
.foreach(_.closedByCommitComment(issue, repository, issueContent, loginAccount))
|
.foreach(_.closedByCommitComment(issue, repository, issueContent, loginAccount))
|
||||||
}
|
}
|
||||||
@@ -353,14 +358,23 @@ trait MergeService {
|
|||||||
closeIssuesFromMessage(message, loginAccount.userName, repository.owner, repository.name)
|
closeIssuesFromMessage(message, loginAccount.userName, repository.owner, repository.name)
|
||||||
.foreach { issueId =>
|
.foreach { issueId =>
|
||||||
getIssue(repository.owner, repository.name, issueId.toString).foreach { issue =>
|
getIssue(repository.owner, repository.name, issueId.toString).foreach { issue =>
|
||||||
callIssuesWebHook("closed", repository, issue, loginAccount)
|
callIssuesWebHook("closed", repository, issue, loginAccount, context.settings)
|
||||||
PluginRegistry().getIssueHooks
|
PluginRegistry().getIssueHooks
|
||||||
.foreach(_.closedByCommitComment(issue, repository, issueContent, loginAccount))
|
.foreach(_.closedByCommitComment(issue, repository, issueContent, loginAccount))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updatePullRequests(repository.owner, repository.name, pullreq.branch, loginAccount, "closed")
|
callPullRequestWebHook("closed", repository, issueId, context.loginAccount.get, context.settings)
|
||||||
|
|
||||||
|
updatePullRequests(
|
||||||
|
repository.owner,
|
||||||
|
repository.name,
|
||||||
|
pullreq.branch,
|
||||||
|
loginAccount,
|
||||||
|
"closed",
|
||||||
|
settings
|
||||||
|
)
|
||||||
|
|
||||||
// call hooks
|
// call hooks
|
||||||
PluginRegistry().getPullRequestHooks.foreach { h =>
|
PluginRegistry().getPullRequestHooks.foreach { h =>
|
||||||
@@ -378,7 +392,7 @@ trait MergeService {
|
|||||||
.getOrElse(Left("Pull request not found"))
|
.getOrElse(Left("Pull request not found"))
|
||||||
}
|
}
|
||||||
} else Left("Strategy not allowed")
|
} else Left("Strategy not allowed")
|
||||||
|
} else Left("Draft pull requests cannot be merged")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -400,7 +414,7 @@ object MergeService {
|
|||||||
mergeCommit.setCommitter(committer)
|
mergeCommit.setCommitter(committer)
|
||||||
mergeCommit.setMessage(message)
|
mergeCommit.setMessage(message)
|
||||||
// insertObject and got mergeCommit Object Id
|
// insertObject and got mergeCommit Object Id
|
||||||
using(repository.newObjectInserter) { inserter =>
|
Using.resource(repository.newObjectInserter) { inserter =>
|
||||||
val mergeCommitId = inserter.insert(mergeCommit)
|
val mergeCommitId = inserter.insert(mergeCommit)
|
||||||
inserter.flush()
|
inserter.flush()
|
||||||
mergeCommitId
|
mergeCommitId
|
||||||
@@ -468,7 +482,7 @@ object MergeService {
|
|||||||
} catch {
|
} catch {
|
||||||
case e: NoMergeBaseException => true
|
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
|
val committer = mergeTipCommit.getCommitterIdent
|
||||||
|
|
||||||
def _updateBranch(treeId: ObjectId, message: String, branchName: String): Unit = {
|
def _updateBranch(treeId: ObjectId, message: String, branchName: String): Unit = {
|
||||||
@@ -521,10 +535,10 @@ object MergeService {
|
|||||||
newCommit
|
newCommit
|
||||||
}
|
}
|
||||||
|
|
||||||
val mergeBaseTipCommit = using(new RevWalk(repository))(_.parseCommit(mergeBaseTip))
|
val mergeBaseTipCommit = Using.resource(new RevWalk(repository))(_.parseCommit(mergeBaseTip))
|
||||||
var previousId = mergeBaseTipCommit.getId
|
var previousId = mergeBaseTipCommit.getId
|
||||||
|
|
||||||
using(repository.newObjectInserter) { inserter =>
|
Using.resource(repository.newObjectInserter) { inserter =>
|
||||||
commits.foreach { commit =>
|
commits.foreach { commit =>
|
||||||
val nextCommit = _cloneCommit(commit, previousId, mergeBaseTipCommit.getId)
|
val nextCommit = _cloneCommit(commit, previousId, mergeBaseTipCommit.getId)
|
||||||
previousId = inserter.insert(nextCommit)
|
previousId = inserter.insert(nextCommit)
|
||||||
@@ -540,8 +554,9 @@ object MergeService {
|
|||||||
throw new RuntimeException("This pull request can't merge automatically.")
|
throw new RuntimeException("This pull request can't merge automatically.")
|
||||||
}
|
}
|
||||||
|
|
||||||
val mergeBaseTipCommit = using(new RevWalk(repository))(_.parseCommit(mergeBaseTip))
|
val mergeBaseTipCommit = Using.resource(new RevWalk(repository))(_.parseCommit(mergeBaseTip))
|
||||||
val mergeBranchHeadCommit = using(new RevWalk(repository))(_.parseCommit(repository.resolve(mergedBranchName)))
|
val mergeBranchHeadCommit =
|
||||||
|
Using.resource(new RevWalk(repository))(_.parseCommit(repository.resolve(mergedBranchName)))
|
||||||
|
|
||||||
// Create squash commit
|
// Create squash commit
|
||||||
val mergeCommit = new CommitBuilder()
|
val mergeCommit = new CommitBuilder()
|
||||||
@@ -552,7 +567,7 @@ object MergeService {
|
|||||||
mergeCommit.setMessage(message)
|
mergeCommit.setMessage(message)
|
||||||
|
|
||||||
// insertObject and got squash commit Object Id
|
// 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)
|
val newCommitId = inserter.insert(mergeCommit)
|
||||||
inserter.flush()
|
inserter.flush()
|
||||||
newCommitId
|
newCommitId
|
||||||
@@ -575,7 +590,7 @@ object MergeService {
|
|||||||
private def createMergeCommit(treeId: ObjectId, committer: PersonIdent, message: String) =
|
private def createMergeCommit(treeId: ObjectId, committer: PersonIdent, message: String) =
|
||||||
Util.createMergeCommit(repository, treeId, committer, message, Seq[ObjectId](mergeBaseTip, mergeTip))
|
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 gitbucket.core.model.Profile.profile.blockingApi._
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
|
|
||||||
import scala.collection.JavaConverters.{asScalaSet, mapAsJavaMap}
|
import scala.jdk.CollectionConverters._
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service class for the OpenID Connect authentication.
|
* Service class for the OpenID Connect authentication.
|
||||||
@@ -101,7 +101,7 @@ trait OpenIDConnectService {
|
|||||||
redirectURI: URI
|
redirectURI: URI
|
||||||
): Option[AuthenticationSuccessResponse] =
|
): Option[AuthenticationSuccessResponse] =
|
||||||
try {
|
try {
|
||||||
AuthenticationResponseParser.parse(redirectURI, mapAsJavaMap(params)) match {
|
AuthenticationResponseParser.parse(redirectURI, params.asJava) match {
|
||||||
case response: AuthenticationSuccessResponse =>
|
case response: AuthenticationSuccessResponse =>
|
||||||
if (response.getState == state) {
|
if (response.getState == state) {
|
||||||
Some(response)
|
Some(response)
|
||||||
@@ -207,5 +207,5 @@ object OpenIDConnectService {
|
|||||||
"RSA" -> Family.RSA,
|
"RSA" -> Family.RSA,
|
||||||
"ECDSA" -> Family.EC,
|
"ECDSA" -> Family.EC,
|
||||||
"EdDSA" -> Family.ED
|
"EdDSA" -> Family.ED
|
||||||
).toMap.map { case (name, family) => (name, asScalaSet(family).toSet) }
|
).toMap.map { case (name, family) => (name, family.asScala.toSet) }
|
||||||
}
|
}
|
||||||
|
|||||||
15
src/main/scala/gitbucket/core/service/PaginationHelper.scala
Normal file
15
src/main/scala/gitbucket/core/service/PaginationHelper.scala
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
package gitbucket.core.service
|
||||||
|
|
||||||
|
import scala.util.Try
|
||||||
|
|
||||||
|
object PaginationHelper {
|
||||||
|
|
||||||
|
def page(page: Option[String]) = {
|
||||||
|
|
||||||
|
page
|
||||||
|
.flatMap(pageStr => Try(pageStr.toInt).toOption)
|
||||||
|
.map(Math.max(1, _)) // remove negative pages
|
||||||
|
.getOrElse(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -6,7 +6,9 @@ import gitbucket.core.model.Profile.profile.blockingApi._
|
|||||||
import difflib.{Delta, DiffUtils}
|
import difflib.{Delta, DiffUtils}
|
||||||
import gitbucket.core.service.RepositoryService.RepositoryInfo
|
import gitbucket.core.service.RepositoryService.RepositoryInfo
|
||||||
import gitbucket.core.api.JsonFormat
|
import gitbucket.core.api.JsonFormat
|
||||||
import gitbucket.core.util.SyntaxSugars._
|
import gitbucket.core.controller.Context
|
||||||
|
import gitbucket.core.plugin.PluginRegistry
|
||||||
|
import gitbucket.core.service.SystemSettingsService.SystemSettings
|
||||||
import gitbucket.core.util.Directory._
|
import gitbucket.core.util.Directory._
|
||||||
import gitbucket.core.util.Implicits._
|
import gitbucket.core.util.Implicits._
|
||||||
import gitbucket.core.util.JGitUtil
|
import gitbucket.core.util.JGitUtil
|
||||||
@@ -17,10 +19,17 @@ import gitbucket.core.view.helpers
|
|||||||
import org.eclipse.jgit.api.Git
|
import org.eclipse.jgit.api.Git
|
||||||
import org.eclipse.jgit.lib.ObjectId
|
import org.eclipse.jgit.lib.ObjectId
|
||||||
|
|
||||||
import scala.collection.JavaConverters._
|
import scala.jdk.CollectionConverters._
|
||||||
|
import scala.util.Using
|
||||||
|
|
||||||
trait PullRequestService {
|
trait PullRequestService {
|
||||||
self: IssuesService with CommitsService with WebHookService with WebHookPullRequestService with RepositoryService =>
|
self: IssuesService
|
||||||
|
with CommitsService
|
||||||
|
with WebHookService
|
||||||
|
with WebHookPullRequestService
|
||||||
|
with RepositoryService
|
||||||
|
with MergeService
|
||||||
|
with ActivityService =>
|
||||||
import PullRequestService._
|
import PullRequestService._
|
||||||
|
|
||||||
def getPullRequest(owner: String, repository: String, issueId: Int)(
|
def getPullRequest(owner: String, repository: String, issueId: Int)(
|
||||||
@@ -40,6 +49,14 @@ trait PullRequestService {
|
|||||||
.map(pr => pr.commitIdTo -> pr.commitIdFrom)
|
.map(pr => pr.commitIdTo -> pr.commitIdFrom)
|
||||||
.update((commitIdTo, 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])(
|
def getPullRequestCountGroupByUser(closed: Boolean, owner: Option[String], repository: Option[String])(
|
||||||
implicit s: Session
|
implicit s: Session
|
||||||
): List[PullRequestCount] =
|
): List[PullRequestCount] =
|
||||||
@@ -81,28 +98,70 @@ trait PullRequestService {
|
|||||||
// .map { x => PullRequestCount(x._1, x._2) }
|
// .map { x => PullRequestCount(x._1, x._2) }
|
||||||
|
|
||||||
def createPullRequest(
|
def createPullRequest(
|
||||||
originUserName: String,
|
originRepository: RepositoryInfo,
|
||||||
originRepositoryName: String,
|
|
||||||
issueId: Int,
|
issueId: Int,
|
||||||
originBranch: String,
|
originBranch: String,
|
||||||
requestUserName: String,
|
requestUserName: String,
|
||||||
requestRepositoryName: String,
|
requestRepositoryName: String,
|
||||||
requestBranch: String,
|
requestBranch: String,
|
||||||
commitIdFrom: String,
|
commitIdFrom: String,
|
||||||
commitIdTo: String
|
commitIdTo: String,
|
||||||
)(implicit s: Session): Unit =
|
isDraft: Boolean,
|
||||||
|
loginAccount: Account,
|
||||||
|
settings: SystemSettings
|
||||||
|
)(implicit s: Session, context: Context): Unit = {
|
||||||
|
getIssue(originRepository.owner, originRepository.name, issueId.toString).foreach { baseIssue =>
|
||||||
PullRequests insert PullRequest(
|
PullRequests insert PullRequest(
|
||||||
originUserName,
|
originRepository.owner,
|
||||||
originRepositoryName,
|
originRepository.name,
|
||||||
issueId,
|
issueId,
|
||||||
originBranch,
|
originBranch,
|
||||||
requestUserName,
|
requestUserName,
|
||||||
requestRepositoryName,
|
requestRepositoryName,
|
||||||
requestBranch,
|
requestBranch,
|
||||||
commitIdFrom,
|
commitIdFrom,
|
||||||
commitIdTo
|
commitIdTo,
|
||||||
|
isDraft
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// fetch requested branch
|
||||||
|
fetchAsPullRequest(
|
||||||
|
originRepository.owner,
|
||||||
|
originRepository.name,
|
||||||
|
requestUserName,
|
||||||
|
requestRepositoryName,
|
||||||
|
requestBranch,
|
||||||
|
issueId
|
||||||
|
)
|
||||||
|
|
||||||
|
// record activity
|
||||||
|
recordPullRequestActivity(
|
||||||
|
originRepository.owner,
|
||||||
|
originRepository.name,
|
||||||
|
loginAccount.userName,
|
||||||
|
issueId,
|
||||||
|
baseIssue.title
|
||||||
|
)
|
||||||
|
|
||||||
|
// call web hook
|
||||||
|
callPullRequestWebHook("opened", originRepository, issueId, loginAccount, settings)
|
||||||
|
|
||||||
|
getIssue(originRepository.owner, originRepository.name, issueId.toString) foreach { issue =>
|
||||||
|
// extract references and create refer comment
|
||||||
|
createReferComment(
|
||||||
|
originRepository.owner,
|
||||||
|
originRepository.name,
|
||||||
|
issue,
|
||||||
|
baseIssue.title + " " + baseIssue.content,
|
||||||
|
loginAccount
|
||||||
|
)
|
||||||
|
|
||||||
|
// call hooks
|
||||||
|
PluginRegistry().getPullRequestHooks.foreach(_.created(issue, originRepository))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
def getPullRequestsByRequest(userName: String, repositoryName: String, branch: String, closed: Option[Boolean])(
|
def getPullRequestsByRequest(userName: String, repositoryName: String, branch: String, closed: Option[Boolean])(
|
||||||
implicit s: Session
|
implicit s: Session
|
||||||
): List[PullRequest] =
|
): List[PullRequest] =
|
||||||
@@ -169,7 +228,14 @@ trait PullRequestService {
|
|||||||
/**
|
/**
|
||||||
* Fetch pull request contents into refs/pull/${issueId}/head and update pull request table.
|
* 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,
|
implicit s: Session,
|
||||||
c: JsonFormat.Context
|
c: JsonFormat.Context
|
||||||
): Unit = {
|
): Unit = {
|
||||||
@@ -218,7 +284,8 @@ trait PullRequestService {
|
|||||||
action,
|
action,
|
||||||
getRepository(owner, repository).get,
|
getRepository(owner, repository).get,
|
||||||
pullreq.requestBranch,
|
pullreq.requestBranch,
|
||||||
loginAccount
|
loginAccount,
|
||||||
|
settings
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -330,7 +397,7 @@ trait PullRequestService {
|
|||||||
requestRepositoryName: String,
|
requestRepositoryName: String,
|
||||||
requestCommitId: String
|
requestCommitId: String
|
||||||
): (Seq[Seq[CommitInfo]], Seq[DiffInfo]) =
|
): (Seq[Seq[CommitInfo]], Seq[DiffInfo]) =
|
||||||
using(
|
Using.resources(
|
||||||
Git.open(getRepositoryDir(userName, repositoryName)),
|
Git.open(getRepositoryDir(userName, repositoryName)),
|
||||||
Git.open(getRepositoryDir(requestUserName, requestRepositoryName))
|
Git.open(getRepositoryDir(requestUserName, requestRepositoryName))
|
||||||
) { (oldGit, newGit) =>
|
) { (oldGit, newGit) =>
|
||||||
@@ -421,7 +488,7 @@ trait PullRequestService {
|
|||||||
originId: String,
|
originId: String,
|
||||||
forkedId: String
|
forkedId: String
|
||||||
): (Option[ObjectId], Option[ObjectId]) = {
|
): (Option[ObjectId], Option[ObjectId]) = {
|
||||||
using(
|
Using.resources(
|
||||||
Git.open(getRepositoryDir(originRepository.owner, originRepository.name)),
|
Git.open(getRepositoryDir(originRepository.owner, originRepository.name)),
|
||||||
Git.open(getRepositoryDir(forkedRepository.owner, forkedRepository.name))
|
Git.open(getRepositoryDir(forkedRepository.owner, forkedRepository.name))
|
||||||
) {
|
) {
|
||||||
@@ -487,7 +554,7 @@ object PullRequestService {
|
|||||||
lazy val commitStateSummary: (CommitState, String) = {
|
lazy val commitStateSummary: (CommitState, String) = {
|
||||||
val stateMap = statuses.groupBy(_.state)
|
val stateMap = statuses.groupBy(_.state)
|
||||||
val state = CommitState.combine(stateMap.keySet)
|
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
|
state -> summary
|
||||||
}
|
}
|
||||||
lazy val statusesAndRequired: List[(CommitStatus, Boolean)] = statuses.map { s =>
|
lazy val statusesAndRequired: List[(CommitStatus, Boolean)] = statuses.map { s =>
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
package gitbucket.core.service
|
package gitbucket.core.service
|
||||||
|
|
||||||
import gitbucket.core.controller.Context
|
import gitbucket.core.controller.Context
|
||||||
import gitbucket.core.model.{Account, ReleaseTag, ReleaseAsset}
|
import gitbucket.core.model.{Account, ReleaseAsset, ReleaseTag}
|
||||||
import gitbucket.core.model.Profile.profile.blockingApi._
|
import gitbucket.core.model.Profile.profile.blockingApi._
|
||||||
import gitbucket.core.model.Profile._
|
import gitbucket.core.model.Profile._
|
||||||
import gitbucket.core.model.Profile.dateColumnType
|
import gitbucket.core.model.Profile.dateColumnType
|
||||||
|
import gitbucket.core.util.JGitUtil
|
||||||
|
|
||||||
trait ReleaseService {
|
trait ReleaseService {
|
||||||
self: AccountService with RepositoryService =>
|
self: AccountService with RepositoryService =>
|
||||||
@@ -35,10 +36,9 @@ trait ReleaseService {
|
|||||||
ReleaseAssets.filter(x => x.byTag(owner, repository, tag)).list
|
ReleaseAssets.filter(x => x.byTag(owner, repository, tag)).list
|
||||||
}
|
}
|
||||||
|
|
||||||
def getReleaseAssetsMap(owner: String, repository: String)(
|
def getReleaseAssetsMap(owner: String, repository: String, releases: Seq[ReleaseTag])(
|
||||||
implicit s: Session
|
implicit s: Session
|
||||||
): Map[ReleaseTag, Seq[ReleaseAsset]] = {
|
): Map[ReleaseTag, Seq[ReleaseAsset]] = {
|
||||||
val releases = getReleases(owner, repository)
|
|
||||||
releases.map(rel => (rel -> getReleaseAssets(owner, repository, rel.tag))).toMap
|
releases.map(rel => (rel -> getReleaseAssets(owner, repository, rel.tag))).toMap
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,24 +73,22 @@ trait ReleaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def getReleases(owner: String, repository: String)(implicit s: Session): Seq[ReleaseTag] = {
|
def getReleases(owner: String, repository: String)(implicit s: Session): Seq[ReleaseTag] = {
|
||||||
ReleaseTags.filter(x => x.byRepository(owner, repository)).list
|
ReleaseTags.filter(x => x.byRepository(owner, repository)).sortBy(x => x.updatedDate).list
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def getReleases(owner: String, repository: String, tags: Seq[JGitUtil.TagInfo])(
|
||||||
|
implicit s: Session
|
||||||
|
): Seq[ReleaseTag] = {
|
||||||
|
ReleaseTags
|
||||||
|
.filter(x => x.byRepository(owner, repository))
|
||||||
|
.filter(x => x.tag inSetBind tags.map(_.name))
|
||||||
|
.sortBy(x => x.updatedDate)
|
||||||
|
.list
|
||||||
|
}
|
||||||
def getRelease(owner: String, repository: String, tag: String)(implicit s: Session): Option[ReleaseTag] = {
|
def getRelease(owner: String, repository: String, tag: String)(implicit s: Session): Option[ReleaseTag] = {
|
||||||
//Releases filter (_.byPrimaryKey(owner, repository, releaseId)) firstOption
|
ReleaseTags.filter(_.byTag(owner, repository, tag)).firstOption
|
||||||
ReleaseTags filter (_.byTag(owner, repository, tag)) firstOption
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// def getReleaseByTag(owner: String, repository: String, tag: String)(implicit s: Session): Option[Release] = {
|
|
||||||
// Releases filter (_.byTag(owner, repository, tag)) firstOption
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// def getRelease(owner: String, repository: String, releaseId: String)(implicit s: Session): Option[Release] = {
|
|
||||||
// if (isInteger(releaseId))
|
|
||||||
// getRelease(owner, repository, releaseId.toInt)
|
|
||||||
// else None
|
|
||||||
// }
|
|
||||||
|
|
||||||
def updateRelease(owner: String, repository: String, tag: String, title: String, content: Option[String])(
|
def updateRelease(owner: String, repository: String, tag: String, title: String, content: Option[String])(
|
||||||
implicit s: Session
|
implicit s: Session
|
||||||
): Int = {
|
): Int = {
|
||||||
@@ -107,3 +105,9 @@ trait ReleaseService {
|
|||||||
ReleaseTags filter (_.byPrimaryKey(owner, repository, tag)) delete
|
ReleaseTags filter (_.byPrimaryKey(owner, repository, tag)) delete
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object ReleaseService {
|
||||||
|
|
||||||
|
val ReleaseLimit = 10
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,16 +4,18 @@ import gitbucket.core.model.{Account, WebHook}
|
|||||||
import gitbucket.core.model.Profile._
|
import gitbucket.core.model.Profile._
|
||||||
import gitbucket.core.model.Profile.profile.blockingApi._
|
import gitbucket.core.model.Profile.profile.blockingApi._
|
||||||
import gitbucket.core.plugin.PluginRegistry
|
import gitbucket.core.plugin.PluginRegistry
|
||||||
|
import gitbucket.core.service.SystemSettingsService.SystemSettings
|
||||||
import gitbucket.core.service.WebHookService.WebHookPushPayload
|
import gitbucket.core.service.WebHookService.WebHookPushPayload
|
||||||
import gitbucket.core.util.Directory.getRepositoryDir
|
import gitbucket.core.util.Directory.getRepositoryDir
|
||||||
import gitbucket.core.util.JGitUtil.CommitInfo
|
import gitbucket.core.util.JGitUtil.CommitInfo
|
||||||
import gitbucket.core.util.{JGitUtil, LockUtil}
|
import gitbucket.core.util.{JGitUtil, LockUtil}
|
||||||
import gitbucket.core.util.SyntaxSugars.using
|
|
||||||
import org.eclipse.jgit.api.Git
|
import org.eclipse.jgit.api.Git
|
||||||
import org.eclipse.jgit.dircache.{DirCache, DirCacheBuilder}
|
import org.eclipse.jgit.dircache.{DirCache, DirCacheBuilder}
|
||||||
import org.eclipse.jgit.lib._
|
import org.eclipse.jgit.lib._
|
||||||
import org.eclipse.jgit.transport.{ReceiveCommand, ReceivePack}
|
import org.eclipse.jgit.transport.{ReceiveCommand, ReceivePack}
|
||||||
|
|
||||||
|
import scala.util.Using
|
||||||
|
|
||||||
trait RepositoryCommitFileService {
|
trait RepositoryCommitFileService {
|
||||||
self: AccountService with ActivityService with IssuesService with PullRequestService with WebHookPullRequestService =>
|
self: AccountService with ActivityService with IssuesService with PullRequestService with WebHookPullRequestService =>
|
||||||
import RepositoryCommitFileService._
|
import RepositoryCommitFileService._
|
||||||
@@ -24,12 +26,13 @@ trait RepositoryCommitFileService {
|
|||||||
branch: String,
|
branch: String,
|
||||||
path: String,
|
path: String,
|
||||||
message: String,
|
message: String,
|
||||||
loginAccount: Account
|
loginAccount: Account,
|
||||||
|
settings: SystemSettings
|
||||||
)(
|
)(
|
||||||
f: (Git, ObjectId, DirCacheBuilder, ObjectInserter) => Unit
|
f: (Git, ObjectId, DirCacheBuilder, ObjectInserter) => Unit
|
||||||
)(implicit s: Session, c: JsonFormat.Context) = {
|
)(implicit s: Session, c: JsonFormat.Context) = {
|
||||||
// prepend path to the filename
|
// prepend path to the filename
|
||||||
_commitFile(repository, branch, message, loginAccount)(f)
|
_commitFile(repository, branch, message, loginAccount, loginAccount.fullName, loginAccount.mailAddress, settings)(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
def commitFile(
|
def commitFile(
|
||||||
@@ -42,8 +45,39 @@ trait RepositoryCommitFileService {
|
|||||||
charset: String,
|
charset: String,
|
||||||
message: String,
|
message: String,
|
||||||
commit: String,
|
commit: String,
|
||||||
loginAccount: Account
|
loginAccount: Account,
|
||||||
)(implicit s: Session, c: JsonFormat.Context) = {
|
settings: SystemSettings
|
||||||
|
)(implicit s: Session, c: JsonFormat.Context): ObjectId = {
|
||||||
|
commitFile(
|
||||||
|
repository,
|
||||||
|
branch,
|
||||||
|
path,
|
||||||
|
newFileName,
|
||||||
|
oldFileName,
|
||||||
|
if (content.nonEmpty) { content.getBytes(charset) } else { Array.emptyByteArray },
|
||||||
|
message,
|
||||||
|
commit,
|
||||||
|
loginAccount,
|
||||||
|
loginAccount.fullName,
|
||||||
|
loginAccount.mailAddress,
|
||||||
|
settings
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
def commitFile(
|
||||||
|
repository: RepositoryService.RepositoryInfo,
|
||||||
|
branch: String,
|
||||||
|
path: String,
|
||||||
|
newFileName: Option[String],
|
||||||
|
oldFileName: Option[String],
|
||||||
|
content: Array[Byte],
|
||||||
|
message: String,
|
||||||
|
commit: String,
|
||||||
|
loginAccount: Account,
|
||||||
|
fullName: String,
|
||||||
|
mailAddress: String,
|
||||||
|
settings: SystemSettings
|
||||||
|
)(implicit s: Session, c: JsonFormat.Context): ObjectId = {
|
||||||
|
|
||||||
val newPath = newFileName.map { newFileName =>
|
val newPath = newFileName.map { newFileName =>
|
||||||
if (path.length == 0) newFileName else s"${path}/${newFileName}"
|
if (path.length == 0) newFileName else s"${path}/${newFileName}"
|
||||||
@@ -52,7 +86,7 @@ trait RepositoryCommitFileService {
|
|||||||
if (path.length == 0) oldFileName else s"${path}/${oldFileName}"
|
if (path.length == 0) oldFileName else s"${path}/${oldFileName}"
|
||||||
}
|
}
|
||||||
|
|
||||||
_commitFile(repository, branch, message, loginAccount) {
|
_commitFile(repository, branch, message, loginAccount, fullName, mailAddress, settings) {
|
||||||
case (git, headTip, builder, inserter) =>
|
case (git, headTip, builder, inserter) =>
|
||||||
if (headTip.getName == commit) {
|
if (headTip.getName == commit) {
|
||||||
val permission = JGitUtil
|
val permission = JGitUtil
|
||||||
@@ -70,7 +104,7 @@ trait RepositoryCommitFileService {
|
|||||||
newPath.foreach { newPath =>
|
newPath.foreach { newPath =>
|
||||||
builder.add(JGitUtil.createDirCacheEntry(newPath, permission.map { bits =>
|
builder.add(JGitUtil.createDirCacheEntry(newPath, permission.map { bits =>
|
||||||
FileMode.fromBits(bits)
|
FileMode.fromBits(bits)
|
||||||
} getOrElse FileMode.REGULAR_FILE, inserter.insert(Constants.OBJ_BLOB, content.getBytes(charset))))
|
} getOrElse FileMode.REGULAR_FILE, inserter.insert(Constants.OBJ_BLOB, content)))
|
||||||
}
|
}
|
||||||
builder.finish()
|
builder.finish()
|
||||||
}
|
}
|
||||||
@@ -81,13 +115,16 @@ trait RepositoryCommitFileService {
|
|||||||
repository: RepositoryService.RepositoryInfo,
|
repository: RepositoryService.RepositoryInfo,
|
||||||
branch: String,
|
branch: String,
|
||||||
message: String,
|
message: String,
|
||||||
loginAccount: Account
|
loginAccount: Account,
|
||||||
|
committerName: String,
|
||||||
|
committerMailAddress: String,
|
||||||
|
settings: SystemSettings
|
||||||
)(
|
)(
|
||||||
f: (Git, ObjectId, DirCacheBuilder, ObjectInserter) => Unit
|
f: (Git, ObjectId, DirCacheBuilder, ObjectInserter) => Unit
|
||||||
)(implicit s: Session, c: JsonFormat.Context) = {
|
)(implicit s: Session, c: JsonFormat.Context): ObjectId = {
|
||||||
|
|
||||||
LockUtil.lock(s"${repository.owner}/${repository.name}") {
|
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 builder = DirCache.newInCore.builder()
|
||||||
val inserter = git.getRepository.newObjectInserter()
|
val inserter = git.getRepository.newObjectInserter()
|
||||||
val headName = s"refs/heads/${branch}"
|
val headName = s"refs/heads/${branch}"
|
||||||
@@ -101,8 +138,8 @@ trait RepositoryCommitFileService {
|
|||||||
headTip,
|
headTip,
|
||||||
builder.getDirCache.writeTree(inserter),
|
builder.getDirCache.writeTree(inserter),
|
||||||
headName,
|
headName,
|
||||||
loginAccount.fullName,
|
committerName,
|
||||||
loginAccount.mailAddress,
|
committerMailAddress,
|
||||||
message
|
message
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -114,7 +151,7 @@ trait RepositoryCommitFileService {
|
|||||||
|
|
||||||
// call post commit hook
|
// call post commit hook
|
||||||
val error = PluginRegistry().getReceiveHooks.flatMap { hook =>
|
val error = PluginRegistry().getReceiveHooks.flatMap { hook =>
|
||||||
hook.preReceive(repository.owner, repository.name, receivePack, receiveCommand, loginAccount.userName)
|
hook.preReceive(repository.owner, repository.name, receivePack, receiveCommand, committerName)
|
||||||
}.headOption
|
}.headOption
|
||||||
|
|
||||||
error match {
|
error match {
|
||||||
@@ -131,11 +168,11 @@ trait RepositoryCommitFileService {
|
|||||||
val refUpdate = git.getRepository.updateRef(headName)
|
val refUpdate = git.getRepository.updateRef(headName)
|
||||||
refUpdate.setNewObjectId(commitId)
|
refUpdate.setNewObjectId(commitId)
|
||||||
refUpdate.setForceUpdate(false)
|
refUpdate.setForceUpdate(false)
|
||||||
refUpdate.setRefLogIdent(new PersonIdent(loginAccount.fullName, loginAccount.mailAddress))
|
refUpdate.setRefLogIdent(new PersonIdent(committerName, committerMailAddress))
|
||||||
refUpdate.update()
|
refUpdate.update()
|
||||||
|
|
||||||
// update pull request
|
// update pull request
|
||||||
updatePullRequests(repository.owner, repository.name, branch, loginAccount, "synchronize")
|
updatePullRequests(repository.owner, repository.name, branch, loginAccount, "synchronize", settings)
|
||||||
|
|
||||||
// record activity
|
// record activity
|
||||||
val commitInfo = new CommitInfo(JGitUtil.getRevCommitFromId(git, commitId))
|
val commitInfo = new CommitInfo(JGitUtil.getRevCommitFromId(git, commitId))
|
||||||
@@ -146,10 +183,9 @@ trait RepositoryCommitFileService {
|
|||||||
|
|
||||||
// close issue by commit message
|
// close issue by commit message
|
||||||
if (branch == repository.repository.defaultBranch) {
|
if (branch == repository.repository.defaultBranch) {
|
||||||
closeIssuesFromMessage(message, loginAccount.userName, repository.owner, repository.name).foreach {
|
closeIssuesFromMessage(message, committerName, repository.owner, repository.name).foreach { issueId =>
|
||||||
issueId =>
|
|
||||||
getIssue(repository.owner, repository.name, issueId.toString).foreach { issue =>
|
getIssue(repository.owner, repository.name, issueId.toString).foreach { issue =>
|
||||||
callIssuesWebHook("closed", repository, issue, loginAccount)
|
callIssuesWebHook("closed", repository, issue, loginAccount, settings)
|
||||||
PluginRegistry().getIssueHooks
|
PluginRegistry().getIssueHooks
|
||||||
.foreach(_.closedByCommitComment(issue, repository, message, loginAccount))
|
.foreach(_.closedByCommitComment(issue, repository, message, loginAccount))
|
||||||
}
|
}
|
||||||
@@ -158,11 +194,11 @@ trait RepositoryCommitFileService {
|
|||||||
|
|
||||||
// call post commit hook
|
// call post commit hook
|
||||||
PluginRegistry().getReceiveHooks.foreach { hook =>
|
PluginRegistry().getReceiveHooks.foreach { hook =>
|
||||||
hook.postReceive(repository.owner, repository.name, receivePack, receiveCommand, loginAccount.userName)
|
hook.postReceive(repository.owner, repository.name, receivePack, receiveCommand, committerName)
|
||||||
}
|
}
|
||||||
|
|
||||||
val commit = new JGitUtil.CommitInfo(JGitUtil.getRevCommitFromId(git, commitId))
|
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 =>
|
getAccountByUserName(repository.owner).map { ownerAccount =>
|
||||||
WebHookPushPayload(
|
WebHookPushPayload(
|
||||||
git,
|
git,
|
||||||
@@ -177,6 +213,7 @@ trait RepositoryCommitFileService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
commitId
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import java.nio.file.Files
|
|||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
|
||||||
import gitbucket.core.model.Profile.profile.blockingApi._
|
import gitbucket.core.model.Profile.profile.blockingApi._
|
||||||
import gitbucket.core.util.SyntaxSugars._
|
|
||||||
import gitbucket.core.util.Directory._
|
import gitbucket.core.util.Directory._
|
||||||
import gitbucket.core.util.{FileUtil, JGitUtil, LockUtil}
|
import gitbucket.core.util.{FileUtil, JGitUtil, LockUtil}
|
||||||
import gitbucket.core.model.{Account, Role}
|
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.ExecutionContext.Implicits.global
|
||||||
import scala.concurrent.Future
|
import scala.concurrent.Future
|
||||||
|
import scala.util.Using
|
||||||
|
|
||||||
object RepositoryCreationService {
|
object RepositoryCreationService {
|
||||||
|
|
||||||
@@ -107,7 +107,7 @@ trait RepositoryCreationService {
|
|||||||
JGitUtil.initRepository(gitdir)
|
JGitUtil.initRepository(gitdir)
|
||||||
|
|
||||||
if (initOption == "README" || initOption == "EMPTY_COMMIT") {
|
if (initOption == "README" || initOption == "EMPTY_COMMIT") {
|
||||||
using(Git.open(gitdir)) { git =>
|
Using.resource(Git.open(gitdir)) { git =>
|
||||||
val builder = DirCache.newInCore.builder()
|
val builder = DirCache.newInCore.builder()
|
||||||
val inserter = git.getRepository.newObjectInserter()
|
val inserter = git.getRepository.newObjectInserter()
|
||||||
val headId = git.getRepository.resolve(Constants.HEAD + "^{commit}")
|
val headId = git.getRepository.resolve(Constants.HEAD + "^{commit}")
|
||||||
@@ -148,7 +148,7 @@ trait RepositoryCreationService {
|
|||||||
|
|
||||||
copyRepositoryDir.foreach { dir =>
|
copyRepositoryDir.foreach { dir =>
|
||||||
try {
|
try {
|
||||||
using(Git.open(dir)) { git =>
|
Using.resource(Git.open(dir)) { git =>
|
||||||
git.push().setRemote(gitdir.toURI.toString).setPushAll().setPushTags().call()
|
git.push().setRemote(gitdir.toURI.toString).setPushAll().setPushTags().call()
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -4,12 +4,12 @@ import gitbucket.core.model.Issue
|
|||||||
import gitbucket.core.util._
|
import gitbucket.core.util._
|
||||||
import gitbucket.core.util.StringUtil
|
import gitbucket.core.util.StringUtil
|
||||||
import Directory._
|
import Directory._
|
||||||
import SyntaxSugars._
|
|
||||||
import org.eclipse.jgit.revwalk.RevWalk
|
import org.eclipse.jgit.revwalk.RevWalk
|
||||||
import org.eclipse.jgit.treewalk.TreeWalk
|
import org.eclipse.jgit.treewalk.TreeWalk
|
||||||
import org.eclipse.jgit.lib.FileMode
|
import org.eclipse.jgit.lib.FileMode
|
||||||
import org.eclipse.jgit.api.Git
|
import org.eclipse.jgit.api.Git
|
||||||
import gitbucket.core.model.Profile.profile.blockingApi._
|
import gitbucket.core.model.Profile.profile.blockingApi._
|
||||||
|
import scala.util.Using
|
||||||
|
|
||||||
trait RepositorySearchService { self: IssuesService =>
|
trait RepositorySearchService { self: IssuesService =>
|
||||||
import RepositorySearchService._
|
import RepositorySearchService._
|
||||||
@@ -37,12 +37,12 @@ trait RepositorySearchService { self: IssuesService =>
|
|||||||
}
|
}
|
||||||
|
|
||||||
def countFiles(owner: String, repository: String, query: String): Int =
|
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
|
if (JGitUtil.isEmpty(git)) 0 else searchRepositoryFiles(git, query).length
|
||||||
}
|
}
|
||||||
|
|
||||||
def searchFiles(owner: String, repository: String, query: String): List[FileSearchResult] =
|
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)) {
|
if (JGitUtil.isEmpty(git)) {
|
||||||
Nil
|
Nil
|
||||||
} else {
|
} else {
|
||||||
@@ -57,12 +57,12 @@ trait RepositorySearchService { self: IssuesService =>
|
|||||||
}
|
}
|
||||||
|
|
||||||
def countWikiPages(owner: String, repository: String, query: String): Int =
|
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
|
if (JGitUtil.isEmpty(git)) 0 else searchRepositoryFiles(git, query).length
|
||||||
}
|
}
|
||||||
|
|
||||||
def searchWikiPages(owner: String, repository: String, query: String): List[FileSearchResult] =
|
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)) {
|
if (JGitUtil.isEmpty(git)) {
|
||||||
Nil
|
Nil
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import org.eclipse.jgit.api.Git
|
|||||||
import org.eclipse.jgit.dircache.{DirCache, DirCacheBuilder}
|
import org.eclipse.jgit.dircache.{DirCache, DirCacheBuilder}
|
||||||
import org.eclipse.jgit.lib.{Repository => _, _}
|
import org.eclipse.jgit.lib.{Repository => _, _}
|
||||||
import org.eclipse.jgit.transport.{ReceiveCommand, ReceivePack}
|
import org.eclipse.jgit.transport.{ReceiveCommand, ReceivePack}
|
||||||
|
import scala.util.Using
|
||||||
|
|
||||||
trait RepositoryService {
|
trait RepositoryService {
|
||||||
self: AccountService =>
|
self: AccountService =>
|
||||||
@@ -458,6 +459,7 @@ trait RepositoryService {
|
|||||||
/**
|
/**
|
||||||
* Returns the list of visible repositories for the specified user.
|
* Returns the list of visible repositories for the specified user.
|
||||||
* If repositoryUserName is given then filters results by repository owner.
|
* If repositoryUserName is given then filters results by repository owner.
|
||||||
|
* This function is for plugin compatibility.
|
||||||
*
|
*
|
||||||
* @param loginAccount the logged in account
|
* @param loginAccount the logged in account
|
||||||
* @param repositoryUserName the repository owner (if None then returns all repositories which are visible for logged in user)
|
* @param repositoryUserName the repository owner (if None then returns all repositories which are visible for logged in user)
|
||||||
@@ -469,23 +471,42 @@ trait RepositoryService {
|
|||||||
loginAccount: Option[Account],
|
loginAccount: Option[Account],
|
||||||
repositoryUserName: Option[String] = None,
|
repositoryUserName: Option[String] = None,
|
||||||
withoutPhysicalInfo: Boolean = false
|
withoutPhysicalInfo: Boolean = false
|
||||||
|
)(implicit s: Session): List[RepositoryInfo] =
|
||||||
|
getVisibleRepositories(loginAccount, repositoryUserName, withoutPhysicalInfo, false)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the list of visible repositories for the specified user.
|
||||||
|
* If repositoryUserName is given then filters results by repository owner.
|
||||||
|
*
|
||||||
|
* @param loginAccount the logged in account
|
||||||
|
* @param repositoryUserName the repository owner (if None then returns all repositories which are visible for logged in user)
|
||||||
|
* @param withoutPhysicalInfo if true then the result does not include physical repository information such as commit count,
|
||||||
|
* branches and tags
|
||||||
|
* @param limit if true then result will include only repositories associated with the login account.
|
||||||
|
* @return the repository information which is sorted in descending order of lastActivityDate.
|
||||||
|
*/
|
||||||
|
def getVisibleRepositories(
|
||||||
|
loginAccount: Option[Account],
|
||||||
|
repositoryUserName: Option[String],
|
||||||
|
withoutPhysicalInfo: Boolean,
|
||||||
|
limit: Boolean
|
||||||
)(implicit s: Session): List[RepositoryInfo] = {
|
)(implicit s: Session): List[RepositoryInfo] = {
|
||||||
(loginAccount match {
|
(loginAccount match {
|
||||||
// for Administrators
|
// for Administrators
|
||||||
case Some(x) if (x.isAdmin) =>
|
case Some(x) if (x.isAdmin && !limit) =>
|
||||||
Repositories
|
Repositories
|
||||||
.join(Accounts)
|
.join(Accounts)
|
||||||
.on(_.userName === _.userName)
|
.on(_.userName === _.userName)
|
||||||
.filter { case (t1, t2) => t2.removed === false.bind }
|
.filter { case (t1, t2) => t2.removed === false.bind }
|
||||||
.map { case (t1, t2) => t1 }
|
.map { case (t1, t2) => t1 }
|
||||||
// for Normal Users
|
// for Normal Users
|
||||||
case Some(x) if (!x.isAdmin) =>
|
case Some(x) if (!x.isAdmin || limit) =>
|
||||||
Repositories
|
Repositories
|
||||||
.join(Accounts)
|
.join(Accounts)
|
||||||
.on(_.userName === _.userName)
|
.on(_.userName === _.userName)
|
||||||
.filter {
|
.filter {
|
||||||
case (t1, t2) =>
|
case (t1, t2) =>
|
||||||
(t2.removed === false.bind) && ((t1.isPrivate === false.bind) || (t1.userName === x.userName) ||
|
(t2.removed === false.bind) && ((t1.isPrivate === false.bind && !limit.bind) || (t1.userName === x.userName) ||
|
||||||
(t1.userName in GroupMembers.filter(_.userName === x.userName.bind).map(_.groupName)) ||
|
(t1.userName in GroupMembers.filter(_.userName === x.userName.bind).map(_.groupName)) ||
|
||||||
(Collaborators.filter { t3 =>
|
(Collaborators.filter { t3 =>
|
||||||
t3.byRepository(t1.userName, t1.repositoryName) &&
|
t3.byRepository(t1.userName, t1.repositoryName) &&
|
||||||
@@ -763,7 +784,7 @@ trait RepositoryService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get template file from project root. When didn't find, will lookup default folder.
|
// 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, "."))
|
choiceTemplate(JGitUtil.getFileList(git, repository.repository.defaultBranch, "."))
|
||||||
.orElse {
|
.orElse {
|
||||||
choiceTemplate(JGitUtil.getFileList(git, repository.repository.defaultBranch, ".gitbucket"))
|
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.ConfigUtil._
|
||||||
import gitbucket.core.util.Directory._
|
import gitbucket.core.util.Directory._
|
||||||
import gitbucket.core.util.SyntaxSugars._
|
import gitbucket.core.util.SyntaxSugars._
|
||||||
|
import scala.util.Using
|
||||||
|
|
||||||
trait SystemSettingsService {
|
trait SystemSettingsService {
|
||||||
|
|
||||||
@@ -20,9 +21,15 @@ trait SystemSettingsService {
|
|||||||
props.setProperty(AllowAccountRegistration, settings.allowAccountRegistration.toString)
|
props.setProperty(AllowAccountRegistration, settings.allowAccountRegistration.toString)
|
||||||
props.setProperty(AllowAnonymousAccess, settings.allowAnonymousAccess.toString)
|
props.setProperty(AllowAnonymousAccess, settings.allowAnonymousAccess.toString)
|
||||||
props.setProperty(IsCreateRepoOptionPublic, settings.isCreateRepoOptionPublic.toString)
|
props.setProperty(IsCreateRepoOptionPublic, settings.isCreateRepoOptionPublic.toString)
|
||||||
|
props.setProperty(RepositoryOperationCreate, settings.repositoryOperation.create.toString)
|
||||||
|
props.setProperty(RepositoryOperationDelete, settings.repositoryOperation.delete.toString)
|
||||||
|
props.setProperty(RepositoryOperationRename, settings.repositoryOperation.rename.toString)
|
||||||
|
props.setProperty(RepositoryOperationTransfer, settings.repositoryOperation.transfer.toString)
|
||||||
|
props.setProperty(RepositoryOperationFork, settings.repositoryOperation.fork.toString)
|
||||||
props.setProperty(Gravatar, settings.gravatar.toString)
|
props.setProperty(Gravatar, settings.gravatar.toString)
|
||||||
props.setProperty(Notification, settings.notification.toString)
|
props.setProperty(Notification, settings.notification.toString)
|
||||||
settings.activityLogLimit.foreach(x => props.setProperty(ActivityLogLimit, x.toString))
|
settings.activityLogLimit.foreach(x => props.setProperty(ActivityLogLimit, x.toString))
|
||||||
|
props.setProperty(LimitVisibleRepositories, settings.limitVisibleRepositories.toString)
|
||||||
props.setProperty(SshEnabled, settings.ssh.enabled.toString)
|
props.setProperty(SshEnabled, settings.ssh.enabled.toString)
|
||||||
settings.ssh.sshHost.foreach(x => props.setProperty(SshHost, x.trim))
|
settings.ssh.sshHost.foreach(x => props.setProperty(SshHost, x.trim))
|
||||||
settings.ssh.sshPort.foreach(x => props.setProperty(SshPort, x.toString))
|
settings.ssh.sshPort.foreach(x => props.setProperty(SshPort, x.toString))
|
||||||
@@ -68,20 +75,16 @@ trait SystemSettingsService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
props.setProperty(SkinName, settings.skinName.toString)
|
props.setProperty(SkinName, settings.skinName.toString)
|
||||||
|
settings.userDefinedCss.foreach(x => props.setProperty(UserDefinedCss, x))
|
||||||
props.setProperty(ShowMailAddress, settings.showMailAddress.toString)
|
props.setProperty(ShowMailAddress, settings.showMailAddress.toString)
|
||||||
props.setProperty(PluginNetworkInstall, settings.pluginNetworkInstall.toString)
|
props.setProperty(WebHookBlockPrivateAddress, settings.webHook.blockPrivateAddress.toString)
|
||||||
settings.pluginProxy.foreach { proxy =>
|
props.setProperty(WebHookWhitelist, settings.webHook.whitelist.mkString("\n"))
|
||||||
props.setProperty(PluginProxyHost, proxy.host)
|
props.setProperty(UploadMaxFileSize, settings.upload.maxFileSize.toString)
|
||||||
props.setProperty(PluginProxyPort, proxy.port.toString)
|
props.setProperty(UploadTimeout, settings.upload.timeout.toString)
|
||||||
proxy.user.foreach { user =>
|
props.setProperty(UploadLargeMaxFileSize, settings.upload.largeMaxFileSize.toString)
|
||||||
props.setProperty(PluginProxyUser, user)
|
props.setProperty(UploadLargeTimeout, settings.upload.largeTimeout.toString)
|
||||||
}
|
|
||||||
proxy.password.foreach { password =>
|
|
||||||
props.setProperty(PluginProxyPassword, password)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
using(new java.io.FileOutputStream(GitBucketConf)) { out =>
|
Using.resource(new java.io.FileOutputStream(GitBucketConf)) { out =>
|
||||||
props.store(out, null)
|
props.store(out, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -90,7 +93,7 @@ trait SystemSettingsService {
|
|||||||
def loadSystemSettings(): SystemSettings = {
|
def loadSystemSettings(): SystemSettings = {
|
||||||
defining(new java.util.Properties()) { props =>
|
defining(new java.util.Properties()) { props =>
|
||||||
if (GitBucketConf.exists) {
|
if (GitBucketConf.exists) {
|
||||||
using(new java.io.FileInputStream(GitBucketConf)) { in =>
|
Using.resource(new java.io.FileInputStream(GitBucketConf)) { in =>
|
||||||
props.load(in)
|
props.load(in)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -100,15 +103,27 @@ trait SystemSettingsService {
|
|||||||
getValue(props, AllowAccountRegistration, false),
|
getValue(props, AllowAccountRegistration, false),
|
||||||
getValue(props, AllowAnonymousAccess, true),
|
getValue(props, AllowAnonymousAccess, true),
|
||||||
getValue(props, IsCreateRepoOptionPublic, true),
|
getValue(props, IsCreateRepoOptionPublic, true),
|
||||||
|
RepositoryOperation(
|
||||||
|
create = getValue(props, RepositoryOperationCreate, true),
|
||||||
|
delete = getValue(props, RepositoryOperationDelete, true),
|
||||||
|
rename = getValue(props, RepositoryOperationRename, true),
|
||||||
|
transfer = getValue(props, RepositoryOperationTransfer, true),
|
||||||
|
fork = getValue(props, RepositoryOperationFork, true)
|
||||||
|
),
|
||||||
getValue(props, Gravatar, false),
|
getValue(props, Gravatar, false),
|
||||||
getValue(props, Notification, false),
|
getValue(props, Notification, false),
|
||||||
getOptionValue[Int](props, ActivityLogLimit, None),
|
getOptionValue[Int](props, ActivityLogLimit, None),
|
||||||
|
getValue(props, LimitVisibleRepositories, false),
|
||||||
Ssh(
|
Ssh(
|
||||||
getValue(props, SshEnabled, false),
|
getValue(props, SshEnabled, false),
|
||||||
getOptionValue[String](props, SshHost, None).map(_.trim),
|
getOptionValue[String](props, SshHost, None).map(_.trim),
|
||||||
getOptionValue(props, SshPort, Some(DefaultSshPort))
|
getOptionValue(props, SshPort, Some(DefaultSshPort))
|
||||||
),
|
),
|
||||||
getValue(props, UseSMTP, getValue(props, Notification, false)), // handle migration scenario from only notification to useSMTP
|
getValue(
|
||||||
|
props,
|
||||||
|
UseSMTP,
|
||||||
|
getValue(props, Notification, false)
|
||||||
|
), // handle migration scenario from only notification to useSMTP
|
||||||
if (getValue(props, UseSMTP, getValue(props, Notification, false))) {
|
if (getValue(props, UseSMTP, getValue(props, Notification, false))) {
|
||||||
Some(
|
Some(
|
||||||
Smtp(
|
Smtp(
|
||||||
@@ -156,19 +171,16 @@ trait SystemSettingsService {
|
|||||||
None
|
None
|
||||||
},
|
},
|
||||||
getValue(props, SkinName, "skin-blue"),
|
getValue(props, SkinName, "skin-blue"),
|
||||||
|
getOptionValue(props, UserDefinedCss, None),
|
||||||
getValue(props, ShowMailAddress, false),
|
getValue(props, ShowMailAddress, false),
|
||||||
getValue(props, PluginNetworkInstall, false),
|
WebHook(getValue(props, WebHookBlockPrivateAddress, false), getSeqValue(props, WebHookWhitelist, "")),
|
||||||
if (getValue(props, PluginProxyHost, "").nonEmpty) {
|
Upload(
|
||||||
Some(
|
getValue(props, UploadMaxFileSize, 3 * 1024 * 1024),
|
||||||
Proxy(
|
getValue(props, UploadTimeout, 3 * 10000),
|
||||||
getValue(props, PluginProxyHost, ""),
|
getValue(props, UploadLargeMaxFileSize, 3 * 1024 * 1024),
|
||||||
getValue(props, PluginProxyPort, 8080),
|
getValue(props, UploadLargeTimeout, 3 * 10000)
|
||||||
getOptionValue(props, PluginProxyUser, None),
|
|
||||||
getOptionValue(props, PluginProxyPassword, None)
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
} else None
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -185,9 +197,11 @@ object SystemSettingsService {
|
|||||||
allowAccountRegistration: Boolean,
|
allowAccountRegistration: Boolean,
|
||||||
allowAnonymousAccess: Boolean,
|
allowAnonymousAccess: Boolean,
|
||||||
isCreateRepoOptionPublic: Boolean,
|
isCreateRepoOptionPublic: Boolean,
|
||||||
|
repositoryOperation: RepositoryOperation,
|
||||||
gravatar: Boolean,
|
gravatar: Boolean,
|
||||||
notification: Boolean,
|
notification: Boolean,
|
||||||
activityLogLimit: Option[Int],
|
activityLogLimit: Option[Int],
|
||||||
|
limitVisibleRepositories: Boolean,
|
||||||
ssh: Ssh,
|
ssh: Ssh,
|
||||||
useSMTP: Boolean,
|
useSMTP: Boolean,
|
||||||
smtp: Option[Smtp],
|
smtp: Option[Smtp],
|
||||||
@@ -196,9 +210,10 @@ object SystemSettingsService {
|
|||||||
oidcAuthentication: Boolean,
|
oidcAuthentication: Boolean,
|
||||||
oidc: Option[OIDC],
|
oidc: Option[OIDC],
|
||||||
skinName: String,
|
skinName: String,
|
||||||
|
userDefinedCss: Option[String],
|
||||||
showMailAddress: Boolean,
|
showMailAddress: Boolean,
|
||||||
pluginNetworkInstall: Boolean,
|
webHook: WebHook,
|
||||||
pluginProxy: Option[Proxy]
|
upload: Upload
|
||||||
) {
|
) {
|
||||||
|
|
||||||
def baseUrl(request: HttpServletRequest): String =
|
def baseUrl(request: HttpServletRequest): String =
|
||||||
@@ -217,12 +232,21 @@ object SystemSettingsService {
|
|||||||
.fold(base)(_ + base.dropWhile(_ != ':'))
|
.fold(base)(_ + base.dropWhile(_ != ':'))
|
||||||
}
|
}
|
||||||
|
|
||||||
def sshAddress: Option[SshAddress] = ssh.sshHost.collect {
|
def sshAddress: Option[SshAddress] =
|
||||||
|
ssh.sshHost.collect {
|
||||||
case host if ssh.enabled =>
|
case host if ssh.enabled =>
|
||||||
SshAddress(host, ssh.sshPort.getOrElse(DefaultSshPort), "git")
|
SshAddress(host, ssh.sshPort.getOrElse(DefaultSshPort), "git")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case class RepositoryOperation(
|
||||||
|
create: Boolean,
|
||||||
|
delete: Boolean,
|
||||||
|
rename: Boolean,
|
||||||
|
transfer: Boolean,
|
||||||
|
fork: Boolean
|
||||||
|
)
|
||||||
|
|
||||||
case class Ssh(
|
case class Ssh(
|
||||||
enabled: Boolean,
|
enabled: Boolean,
|
||||||
sshHost: Option[String],
|
sshHost: Option[String],
|
||||||
@@ -270,12 +294,14 @@ object SystemSettingsService {
|
|||||||
host: String,
|
host: String,
|
||||||
port: Int,
|
port: Int,
|
||||||
user: Option[String],
|
user: Option[String],
|
||||||
password: Option[String],
|
password: Option[String]
|
||||||
)
|
)
|
||||||
|
|
||||||
case class SshAddress(host: String, port: Int, genericUser: String)
|
case class SshAddress(host: String, port: Int, genericUser: String)
|
||||||
|
|
||||||
case class Lfs(serverUrl: Option[String])
|
case class WebHook(blockPrivateAddress: Boolean, whitelist: Seq[String])
|
||||||
|
|
||||||
|
case class Upload(maxFileSize: Long, timeout: Long, largeMaxFileSize: Long, largeTimeout: Long)
|
||||||
|
|
||||||
val DefaultSshPort = 29418
|
val DefaultSshPort = 29418
|
||||||
val DefaultSmtpPort = 25
|
val DefaultSmtpPort = 25
|
||||||
@@ -286,9 +312,15 @@ object SystemSettingsService {
|
|||||||
private val AllowAccountRegistration = "allow_account_registration"
|
private val AllowAccountRegistration = "allow_account_registration"
|
||||||
private val AllowAnonymousAccess = "allow_anonymous_access"
|
private val AllowAnonymousAccess = "allow_anonymous_access"
|
||||||
private val IsCreateRepoOptionPublic = "is_create_repository_option_public"
|
private val IsCreateRepoOptionPublic = "is_create_repository_option_public"
|
||||||
|
private val RepositoryOperationCreate = "repository_operation_create"
|
||||||
|
private val RepositoryOperationDelete = "repository_operation_delete"
|
||||||
|
private val RepositoryOperationRename = "repository_operation_rename"
|
||||||
|
private val RepositoryOperationTransfer = "repository_operation_transfer"
|
||||||
|
private val RepositoryOperationFork = "repository_operation_fork"
|
||||||
private val Gravatar = "gravatar"
|
private val Gravatar = "gravatar"
|
||||||
private val Notification = "notification"
|
private val Notification = "notification"
|
||||||
private val ActivityLogLimit = "activity_log_limit"
|
private val ActivityLogLimit = "activity_log_limit"
|
||||||
|
private val LimitVisibleRepositories = "limitVisibleRepositories"
|
||||||
private val SshEnabled = "ssh"
|
private val SshEnabled = "ssh"
|
||||||
private val SshHost = "ssh.host"
|
private val SshHost = "ssh.host"
|
||||||
private val SshPort = "ssh.port"
|
private val SshPort = "ssh.port"
|
||||||
@@ -320,15 +352,17 @@ object SystemSettingsService {
|
|||||||
private val OidcClientSecret = "oidc.client_secret"
|
private val OidcClientSecret = "oidc.client_secret"
|
||||||
private val OidcJwsAlgorithm = "oidc.jws_algorithm"
|
private val OidcJwsAlgorithm = "oidc.jws_algorithm"
|
||||||
private val SkinName = "skinName"
|
private val SkinName = "skinName"
|
||||||
|
private val UserDefinedCss = "userDefinedCss"
|
||||||
private val ShowMailAddress = "showMailAddress"
|
private val ShowMailAddress = "showMailAddress"
|
||||||
private val PluginNetworkInstall = "plugin.networkInstall"
|
private val WebHookBlockPrivateAddress = "webhook.block_private_address"
|
||||||
private val PluginProxyHost = "plugin.proxy.host"
|
private val WebHookWhitelist = "webhook.whitelist"
|
||||||
private val PluginProxyPort = "plugin.proxy.port"
|
private val UploadMaxFileSize = "upload.maxFileSize"
|
||||||
private val PluginProxyUser = "plugin.proxy.user"
|
private val UploadTimeout = "upload.timeout"
|
||||||
private val PluginProxyPassword = "plugin.proxy.password"
|
private val UploadLargeMaxFileSize = "upload.largeMaxFileSize"
|
||||||
|
private val UploadLargeTimeout = "upload.largeTimeout"
|
||||||
|
|
||||||
private def getValue[A: ClassTag](props: java.util.Properties, key: String, default: A): A = {
|
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 =>
|
defining(props.getProperty(key)) { value =>
|
||||||
if (value == null || value.isEmpty) {
|
if (value == null || value.isEmpty) {
|
||||||
default
|
default
|
||||||
@@ -336,11 +370,21 @@ object SystemSettingsService {
|
|||||||
convertType(value).asInstanceOf[A]
|
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] = {
|
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 =>
|
defining(props.getProperty(key)) { value =>
|
||||||
if (value == null || value.isEmpty) {
|
if (value == null || value.isEmpty) {
|
||||||
default
|
default
|
||||||
@@ -348,7 +392,7 @@ object SystemSettingsService {
|
|||||||
Some(convertType(value)).asInstanceOf[Option[A]]
|
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
|
||||||
import fr.brouillard.oss.security.xhub.XHub.{XHubConverter, XHubDigest}
|
import fr.brouillard.oss.security.xhub.XHub.{XHubConverter, XHubDigest}
|
||||||
import gitbucket.core.api._
|
import gitbucket.core.api._
|
||||||
|
import gitbucket.core.controller.Context
|
||||||
import gitbucket.core.model.{
|
import gitbucket.core.model.{
|
||||||
Account,
|
Account,
|
||||||
|
AccountWebHook,
|
||||||
|
AccountWebHookEvent,
|
||||||
CommitComment,
|
CommitComment,
|
||||||
Issue,
|
Issue,
|
||||||
IssueComment,
|
IssueComment,
|
||||||
Label,
|
Label,
|
||||||
PullRequest,
|
PullRequest,
|
||||||
WebHook,
|
|
||||||
RepositoryWebHook,
|
RepositoryWebHook,
|
||||||
RepositoryWebHookEvent,
|
RepositoryWebHookEvent,
|
||||||
AccountWebHook,
|
WebHook
|
||||||
AccountWebHookEvent
|
|
||||||
}
|
}
|
||||||
import gitbucket.core.model.Profile._
|
import gitbucket.core.model.Profile._
|
||||||
import gitbucket.core.model.Profile.profile.blockingApi._
|
import gitbucket.core.model.Profile.profile.blockingApi._
|
||||||
import org.apache.http.client.utils.URLEncodedUtils
|
import org.apache.http.client.utils.URLEncodedUtils
|
||||||
import gitbucket.core.util.JGitUtil.CommitInfo
|
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 gitbucket.core.service.RepositoryService.RepositoryInfo
|
||||||
import org.apache.http.NameValuePair
|
import org.apache.http.NameValuePair
|
||||||
import org.apache.http.client.entity.UrlEncodedFormEntity
|
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.HttpRequest
|
||||||
import org.apache.http.HttpResponse
|
import org.apache.http.HttpResponse
|
||||||
import gitbucket.core.model.WebHookContentType
|
import gitbucket.core.model.WebHookContentType
|
||||||
|
import gitbucket.core.service.SystemSettingsService.SystemSettings
|
||||||
import org.apache.http.client.entity.EntityBuilder
|
import org.apache.http.client.entity.EntityBuilder
|
||||||
import org.apache.http.entity.ContentType
|
import org.apache.http.entity.ContentType
|
||||||
|
|
||||||
@@ -55,6 +58,7 @@ trait WebHookService {
|
|||||||
.map { case (w, t) => w -> t.event }
|
.map { case (w, t) => w -> t.event }
|
||||||
.list
|
.list
|
||||||
.groupBy(_._1)
|
.groupBy(_._1)
|
||||||
|
.view
|
||||||
.mapValues(_.map(_._2).toSet)
|
.mapValues(_.map(_._2).toSet)
|
||||||
.toList
|
.toList
|
||||||
.sortBy(_._1.url)
|
.sortBy(_._1.url)
|
||||||
@@ -87,6 +91,7 @@ trait WebHookService {
|
|||||||
.map { case (w, t) => w -> t.event }
|
.map { case (w, t) => w -> t.event }
|
||||||
.list
|
.list
|
||||||
.groupBy(_._1)
|
.groupBy(_._1)
|
||||||
|
.view
|
||||||
.mapValues(_.map(_._2).toSet)
|
.mapValues(_.map(_._2).toSet)
|
||||||
.headOption
|
.headOption
|
||||||
|
|
||||||
@@ -136,6 +141,7 @@ trait WebHookService {
|
|||||||
.map { case (w, t) => w -> t.event }
|
.map { case (w, t) => w -> t.event }
|
||||||
.list
|
.list
|
||||||
.groupBy(_._1)
|
.groupBy(_._1)
|
||||||
|
.view
|
||||||
.mapValues(_.map(_._2).toSet)
|
.mapValues(_.map(_._2).toSet)
|
||||||
.toList
|
.toList
|
||||||
.sortBy(_._1.url)
|
.sortBy(_._1.url)
|
||||||
@@ -164,6 +170,7 @@ trait WebHookService {
|
|||||||
.map { case (w, t) => w -> t.event }
|
.map { case (w, t) => w -> t.event }
|
||||||
.list
|
.list
|
||||||
.groupBy(_._1)
|
.groupBy(_._1)
|
||||||
|
.view
|
||||||
.mapValues(_.map(_._2).toSet)
|
.mapValues(_.map(_._2).toSet)
|
||||||
.headOption
|
.headOption
|
||||||
|
|
||||||
@@ -197,20 +204,28 @@ trait WebHookService {
|
|||||||
def deleteAccountWebHook(owner: String, url: String)(implicit s: Session): Unit =
|
def deleteAccountWebHook(owner: String, url: String)(implicit s: Session): Unit =
|
||||||
AccountWebHooks.filter(_.byPrimaryKey(owner, url)).delete
|
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]
|
makePayload: => Option[WebHookPayload]
|
||||||
)(implicit s: Session, c: JsonFormat.Context): Unit = {
|
)(implicit s: Session, c: JsonFormat.Context): Unit = {
|
||||||
val webHooks = getWebHooksByEvent(owner, repository, event)
|
val webHooks = getWebHooksByEvent(owner, repository, event)
|
||||||
if (webHooks.nonEmpty) {
|
if (webHooks.nonEmpty) {
|
||||||
makePayload.map(callWebHook(event, webHooks, _))
|
makePayload.map(callWebHook(event, webHooks, _, settings))
|
||||||
}
|
}
|
||||||
val accountWebHooks = getAccountWebHooksByEvent(owner, event)
|
val accountWebHooks = getAccountWebHooksByEvent(owner, event)
|
||||||
if (accountWebHooks.nonEmpty) {
|
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
|
implicit c: JsonFormat.Context
|
||||||
): List[(WebHook, String, Future[HttpRequest], Future[HttpResponse])] = {
|
): List[(WebHook, String, Future[HttpRequest], Future[HttpResponse])] = {
|
||||||
import org.apache.http.impl.client.HttpClientBuilder
|
import org.apache.http.impl.client.HttpClientBuilder
|
||||||
@@ -230,10 +245,13 @@ trait WebHookService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
if (!validateTargetAddress(settings, webHook.url)) {
|
||||||
|
throw new IllegalArgumentException(s"Illegal address: ${webHook.url}")
|
||||||
|
}
|
||||||
val httpClient = HttpClientBuilder.create.useSystemProperties.addInterceptorLast(itcp).build
|
val httpClient = HttpClientBuilder.create.useSystemProperties.addInterceptorLast(itcp).build
|
||||||
logger.debug(s"start web hook invocation for ${webHook.url}")
|
logger.debug(s"start web hook invocation for ${webHook.url}")
|
||||||
val httpPost = new HttpPost(webHook.url)
|
val httpPost = new HttpPost(webHook.url)
|
||||||
logger.info(s"Content-Type: ${webHook.ctype.ctype}")
|
logger.debug(s"Content-Type: ${webHook.ctype.ctype}")
|
||||||
httpPost.addHeader("Content-Type", webHook.ctype.ctype)
|
httpPost.addHeader("Content-Type", webHook.ctype.ctype)
|
||||||
httpPost.addHeader("X-Github-Event", event.name)
|
httpPost.addHeader("X-Github-Event", event.name)
|
||||||
httpPost.addHeader("X-Github-Delivery", java.util.UUID.randomUUID().toString)
|
httpPost.addHeader("X-Github-Delivery", java.util.UUID.randomUUID().toString)
|
||||||
@@ -298,7 +316,6 @@ trait WebHookService {
|
|||||||
} else {
|
} else {
|
||||||
Nil
|
Nil
|
||||||
}
|
}
|
||||||
// logger.debug("end callWebHook")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -311,10 +328,12 @@ trait WebHookPullRequestService extends WebHookService {
|
|||||||
action: String,
|
action: String,
|
||||||
repository: RepositoryService.RepositoryInfo,
|
repository: RepositoryService.RepositoryInfo,
|
||||||
issue: Issue,
|
issue: Issue,
|
||||||
sender: Account
|
sender: Account,
|
||||||
|
settings: SystemSettings
|
||||||
)(implicit s: Session, context: JsonFormat.Context): Unit = {
|
)(implicit s: Session, context: JsonFormat.Context): Unit = {
|
||||||
callWebHookOf(repository.owner, repository.name, WebHook.Issues) {
|
callWebHookOf(repository.owner, repository.name, WebHook.Issues, settings) {
|
||||||
val users = getAccountsByUserNames(Set(repository.owner, issue.openedUserName), Set(sender))
|
val users =
|
||||||
|
getAccountsByUserNames(Set(repository.owner, issue.openedUserName) ++ issue.assignedUserName, Set(sender))
|
||||||
for {
|
for {
|
||||||
repoOwner <- users.get(repository.owner)
|
repoOwner <- users.get(repository.owner)
|
||||||
issueUser <- users.get(issue.openedUserName)
|
issueUser <- users.get(issue.openedUserName)
|
||||||
@@ -327,6 +346,7 @@ trait WebHookPullRequestService extends WebHookService {
|
|||||||
issue,
|
issue,
|
||||||
RepositoryName(repository),
|
RepositoryName(repository),
|
||||||
ApiUser(issueUser),
|
ApiUser(issueUser),
|
||||||
|
issue.assignedUserName.flatMap(users.get(_)).map(ApiUser(_)),
|
||||||
getIssueLabels(repository.owner, repository.name, issue.issueId)
|
getIssueLabels(repository.owner, repository.name, issue.issueId)
|
||||||
.map(ApiLabel(_, RepositoryName(repository)))
|
.map(ApiLabel(_, RepositoryName(repository)))
|
||||||
),
|
),
|
||||||
@@ -340,10 +360,11 @@ trait WebHookPullRequestService extends WebHookService {
|
|||||||
action: String,
|
action: String,
|
||||||
repository: RepositoryService.RepositoryInfo,
|
repository: RepositoryService.RepositoryInfo,
|
||||||
issueId: Int,
|
issueId: Int,
|
||||||
sender: Account
|
sender: Account,
|
||||||
|
settings: SystemSettings
|
||||||
)(implicit s: Session, c: JsonFormat.Context): Unit = {
|
)(implicit s: Session, c: JsonFormat.Context): Unit = {
|
||||||
import WebHookService._
|
import WebHookService._
|
||||||
callWebHookOf(repository.owner, repository.name, WebHook.PullRequest) {
|
callWebHookOf(repository.owner, repository.name, WebHook.PullRequest, settings) {
|
||||||
for {
|
for {
|
||||||
(issue, pullRequest) <- getPullRequest(repository.owner, repository.name, issueId)
|
(issue, pullRequest) <- getPullRequest(repository.owner, repository.name, issueId)
|
||||||
users = getAccountsByUserNames(
|
users = getAccountsByUserNames(
|
||||||
@@ -396,13 +417,14 @@ trait WebHookPullRequestService extends WebHookService {
|
|||||||
if wht.event === WebHook.PullRequest.asInstanceOf[WebHook.Event].bind && wht.byRepositoryWebHook(wh)
|
if wht.event === WebHook.PullRequest.asInstanceOf[WebHook.Event].bind && wht.byRepositoryWebHook(wh)
|
||||||
} yield {
|
} yield {
|
||||||
((is, iu, pr, bu, ru), wh)
|
((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(
|
def callPullRequestWebHookByRequestBranch(
|
||||||
action: String,
|
action: String,
|
||||||
requestRepository: RepositoryService.RepositoryInfo,
|
requestRepository: RepositoryService.RepositoryInfo,
|
||||||
requestBranch: String,
|
requestBranch: String,
|
||||||
sender: Account
|
sender: Account,
|
||||||
|
settings: SystemSettings
|
||||||
)(implicit s: Session, c: JsonFormat.Context): Unit = {
|
)(implicit s: Session, c: JsonFormat.Context): Unit = {
|
||||||
import WebHookService._
|
import WebHookService._
|
||||||
for {
|
for {
|
||||||
@@ -433,7 +455,7 @@ trait WebHookPullRequestService extends WebHookService {
|
|||||||
mergedComment = getMergedComment(baseRepo.owner, baseRepo.name, issue.issueId)
|
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,
|
repository: RepositoryService.RepositoryInfo,
|
||||||
issue: Issue,
|
issue: Issue,
|
||||||
pullRequest: PullRequest,
|
pullRequest: PullRequest,
|
||||||
sender: Account
|
sender: Account,
|
||||||
|
settings: SystemSettings
|
||||||
)(implicit s: Session, c: JsonFormat.Context): Unit = {
|
)(implicit s: Session, c: JsonFormat.Context): Unit = {
|
||||||
import WebHookService._
|
import WebHookService._
|
||||||
callWebHookOf(repository.owner, repository.name, WebHook.PullRequestReviewComment) {
|
callWebHookOf(repository.owner, repository.name, WebHook.PullRequestReviewComment, settings) {
|
||||||
val users =
|
val users =
|
||||||
getAccountsByUserNames(Set(repository.owner, pullRequest.requestUserName, issue.openedUserName), Set(sender))
|
getAccountsByUserNames(Set(repository.owner, pullRequest.requestUserName, issue.openedUserName), Set(sender))
|
||||||
for {
|
for {
|
||||||
@@ -492,18 +515,20 @@ trait WebHookIssueCommentService extends WebHookPullRequestService {
|
|||||||
repository: RepositoryService.RepositoryInfo,
|
repository: RepositoryService.RepositoryInfo,
|
||||||
issue: Issue,
|
issue: Issue,
|
||||||
issueCommentId: Int,
|
issueCommentId: Int,
|
||||||
sender: Account
|
sender: Account,
|
||||||
|
settings: SystemSettings
|
||||||
)(implicit s: Session, c: JsonFormat.Context): Unit = {
|
)(implicit s: Session, c: JsonFormat.Context): Unit = {
|
||||||
callWebHookOf(repository.owner, repository.name, WebHook.IssueComment) {
|
callWebHookOf(repository.owner, repository.name, WebHook.IssueComment, settings) {
|
||||||
for {
|
for {
|
||||||
issueComment <- getComment(repository.owner, repository.name, issueCommentId.toString())
|
issueComment <- getComment(repository.owner, repository.name, issueCommentId.toString())
|
||||||
users = getAccountsByUserNames(
|
users = getAccountsByUserNames(
|
||||||
Set(issue.openedUserName, repository.owner, issueComment.commentedUserName),
|
Set(issue.openedUserName, repository.owner, issueComment.commentedUserName) ++ issue.assignedUserName,
|
||||||
Set(sender)
|
Set(sender)
|
||||||
)
|
)
|
||||||
issueUser <- users.get(issue.openedUserName)
|
issueUser <- users.get(issue.openedUserName)
|
||||||
repoOwner <- users.get(repository.owner)
|
repoOwner <- users.get(repository.owner)
|
||||||
commenter <- users.get(issueComment.commentedUserName)
|
commenter <- users.get(issueComment.commentedUserName)
|
||||||
|
assignedUser = issue.assignedUserName.flatMap(users.get(_))
|
||||||
labels = getIssueLabels(repository.owner, repository.name, issue.issueId)
|
labels = getIssueLabels(repository.owner, repository.name, issue.issueId)
|
||||||
} yield {
|
} yield {
|
||||||
WebHookIssueCommentPayload(
|
WebHookIssueCommentPayload(
|
||||||
@@ -513,6 +538,7 @@ trait WebHookIssueCommentService extends WebHookPullRequestService {
|
|||||||
commentUser = commenter,
|
commentUser = commenter,
|
||||||
repository = repository,
|
repository = repository,
|
||||||
repositoryUser = repoOwner,
|
repositoryUser = repoOwner,
|
||||||
|
assignedUser = assignedUser,
|
||||||
sender = sender,
|
sender = sender,
|
||||||
labels = labels
|
labels = labels
|
||||||
)
|
)
|
||||||
@@ -540,11 +566,8 @@ object WebHookService {
|
|||||||
object WebHookCreatePayload {
|
object WebHookCreatePayload {
|
||||||
|
|
||||||
def apply(
|
def apply(
|
||||||
git: Git,
|
|
||||||
sender: Account,
|
sender: Account,
|
||||||
refName: String,
|
|
||||||
repositoryInfo: RepositoryInfo,
|
repositoryInfo: RepositoryInfo,
|
||||||
commits: List[CommitInfo],
|
|
||||||
repositoryOwner: Account,
|
repositoryOwner: Account,
|
||||||
ref: String,
|
ref: String,
|
||||||
refType: String
|
refType: String
|
||||||
@@ -555,7 +578,7 @@ object WebHookService {
|
|||||||
ref_type = refType,
|
ref_type = refType,
|
||||||
description = repositoryInfo.repository.description.getOrElse(""),
|
description = repositoryInfo.repository.description.getOrElse(""),
|
||||||
master_branch = repositoryInfo.repository.defaultBranch,
|
master_branch = repositoryInfo.repository.defaultBranch,
|
||||||
repository = ApiRepository.forWebhookPayload(repositoryInfo, owner = ApiUser(repositoryOwner))
|
repository = ApiRepository(repositoryInfo, repositoryOwner)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -597,9 +620,9 @@ object WebHookService {
|
|||||||
before = ObjectId.toString(oldId),
|
before = ObjectId.toString(oldId),
|
||||||
after = ObjectId.toString(newId),
|
after = ObjectId.toString(newId),
|
||||||
commits = commits.map { commit =>
|
commits = commits.map { commit =>
|
||||||
ApiCommit.forWebhookPayload(git, RepositoryName(repositoryInfo), commit)
|
ApiCommit(git, RepositoryName(repositoryInfo), commit)
|
||||||
},
|
},
|
||||||
repository = ApiRepository.forWebhookPayload(repositoryInfo, owner = ApiUser(repositoryOwner))
|
repository = ApiRepository(repositoryInfo, repositoryOwner)
|
||||||
)
|
)
|
||||||
|
|
||||||
def createDummyPayload(sender: Account): WebHookPushPayload =
|
def createDummyPayload(sender: Account): WebHookPushPayload =
|
||||||
@@ -689,6 +712,7 @@ object WebHookService {
|
|||||||
commentUser: Account,
|
commentUser: Account,
|
||||||
repository: RepositoryInfo,
|
repository: RepositoryInfo,
|
||||||
repositoryUser: Account,
|
repositoryUser: Account,
|
||||||
|
assignedUser: Option[Account],
|
||||||
sender: Account,
|
sender: Account,
|
||||||
labels: List[Label]
|
labels: List[Label]
|
||||||
): WebHookIssueCommentPayload =
|
): WebHookIssueCommentPayload =
|
||||||
@@ -699,6 +723,7 @@ object WebHookService {
|
|||||||
issue,
|
issue,
|
||||||
RepositoryName(repository),
|
RepositoryName(repository),
|
||||||
ApiUser(issueUser),
|
ApiUser(issueUser),
|
||||||
|
assignedUser.map(ApiUser(_)),
|
||||||
labels.map(ApiLabel(_, RepositoryName(repository)))
|
labels.map(ApiLabel(_, RepositoryName(repository)))
|
||||||
),
|
),
|
||||||
comment =
|
comment =
|
||||||
|
|||||||
@@ -14,7 +14,9 @@ import org.eclipse.jgit.diff.{DiffEntry, DiffFormatter}
|
|||||||
import java.io.ByteArrayInputStream
|
import java.io.ByteArrayInputStream
|
||||||
import org.eclipse.jgit.patch._
|
import org.eclipse.jgit.patch._
|
||||||
import org.eclipse.jgit.api.errors.PatchFormatException
|
import org.eclipse.jgit.api.errors.PatchFormatException
|
||||||
import scala.collection.JavaConverters._
|
|
||||||
|
import scala.jdk.CollectionConverters._
|
||||||
|
import scala.util.Using
|
||||||
|
|
||||||
object WikiService {
|
object WikiService {
|
||||||
|
|
||||||
@@ -73,7 +75,7 @@ trait WikiService {
|
|||||||
* Returns the wiki page.
|
* Returns the wiki page.
|
||||||
*/
|
*/
|
||||||
def getWikiPage(owner: String, repository: String, pageName: String): Option[WikiPageInfo] = {
|
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)) {
|
if (!JGitUtil.isEmpty(git)) {
|
||||||
JGitUtil.getFileList(git, "master", ".").find(_.name == pageName + ".md").map { file =>
|
JGitUtil.getFileList(git, "master", ".").find(_.name == pageName + ".md").map { file =>
|
||||||
WikiPageInfo(
|
WikiPageInfo(
|
||||||
@@ -92,7 +94,7 @@ trait WikiService {
|
|||||||
* Returns the list of wiki page names.
|
* Returns the list of wiki page names.
|
||||||
*/
|
*/
|
||||||
def getWikiPageList(owner: String, repository: String): List[String] = {
|
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
|
JGitUtil
|
||||||
.getFileList(git, "master", ".")
|
.getFileList(git, "master", ".")
|
||||||
.filter(_.name.endsWith(".md"))
|
.filter(_.name.endsWith(".md"))
|
||||||
@@ -118,7 +120,7 @@ trait WikiService {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
LockUtil.lock(s"${owner}/${repository}/wiki") {
|
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 reader = git.getRepository.newObjectReader
|
||||||
val oldTreeIter = new CanonicalTreeParser
|
val oldTreeIter = new CanonicalTreeParser
|
||||||
oldTreeIter.reset(reader, git.getRepository.resolve(from + "^{tree}"))
|
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)
|
val formatter = new DiffFormatter(out)
|
||||||
formatter.setRepository(git.getRepository)
|
formatter.setRepository(git.getRepository)
|
||||||
formatter.format(diffs.asJava)
|
formatter.format(diffs.asJava)
|
||||||
@@ -237,7 +239,7 @@ trait WikiService {
|
|||||||
currentId: Option[String]
|
currentId: Option[String]
|
||||||
): Option[String] = {
|
): Option[String] = {
|
||||||
LockUtil.lock(s"${owner}/${repository}/wiki") {
|
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 builder = DirCache.newInCore.builder()
|
||||||
val inserter = git.getRepository.newObjectInserter()
|
val inserter = git.getRepository.newObjectInserter()
|
||||||
val headId = git.getRepository.resolve(Constants.HEAD + "^{commit}")
|
val headId = git.getRepository.resolve(Constants.HEAD + "^{commit}")
|
||||||
@@ -309,7 +311,7 @@ trait WikiService {
|
|||||||
message: String
|
message: String
|
||||||
): Unit = {
|
): Unit = {
|
||||||
LockUtil.lock(s"${owner}/${repository}/wiki") {
|
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 builder = DirCache.newInCore.builder()
|
||||||
val inserter = git.getRepository.newObjectInserter()
|
val inserter = git.getRepository.newObjectInserter()
|
||||||
val headId = git.getRepository.resolve(Constants.HEAD + "^{commit}")
|
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.SystemSettingsService.SystemSettings
|
||||||
import gitbucket.core.service.{AccessTokenService, AccountService, SystemSettingsService}
|
import gitbucket.core.service.{AccessTokenService, AccountService, SystemSettingsService}
|
||||||
import gitbucket.core.util.{AuthUtil, 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
|
||||||
|
|
||||||
class ApiAuthenticationFilter extends Filter with AccessTokenService with AccountService with SystemSettingsService {
|
class ApiAuthenticationFilter extends Filter with AccessTokenService with AccountService with SystemSettingsService {
|
||||||
|
|
||||||
@@ -20,15 +24,23 @@ class ApiAuthenticationFilter extends Filter with AccessTokenService with Accoun
|
|||||||
val response = res.asInstanceOf[HttpServletResponse]
|
val response = res.asInstanceOf[HttpServletResponse]
|
||||||
Option(request.getHeader("Authorization"))
|
Option(request.getHeader("Authorization"))
|
||||||
.map {
|
.map {
|
||||||
case auth if auth.startsWith("token ") =>
|
case auth if auth.toLowerCase().startsWith("token ") =>
|
||||||
AccessTokenService.getAccountByAccessToken(auth.substring(6).trim).toRight(())
|
AccessTokenService.getAccountByAccessToken(auth.substring(6).trim).toRight(())
|
||||||
case auth if auth.startsWith("Basic ") => doBasicAuth(auth, loadSystemSettings(), request).toRight(())
|
case auth if auth.startsWith("Basic ") => doBasicAuth(auth, loadSystemSettings(), request).toRight(())
|
||||||
case _ => Left(())
|
case _ => Left(())
|
||||||
}
|
}
|
||||||
|
.orElse {
|
||||||
|
Option(req.getParameter("access_token")).map(AccessTokenService.getAccountByAccessToken(_).toRight(()))
|
||||||
|
}
|
||||||
.orElse {
|
.orElse {
|
||||||
Option(request.getSession.getAttribute(Keys.Session.LoginAccount).asInstanceOf[Account]).map(Right(_))
|
Option(request.getSession.getAttribute(Keys.Session.LoginAccount).asInstanceOf[Account]).map(Right(_))
|
||||||
} match {
|
} match {
|
||||||
case Some(Right(account)) => request.setAttribute(Keys.Session.LoginAccount, account); chain.doFilter(req, res)
|
case Some(Right(account)) =>
|
||||||
|
request.setAttribute(Keys.Session.LoginAccount, account)
|
||||||
|
Database() withTransaction { implicit session =>
|
||||||
|
updateLastLoginDate(account.userName)
|
||||||
|
}
|
||||||
|
chain.doFilter(req, res)
|
||||||
case None => chain.doFilter(req, res)
|
case None => chain.doFilter(req, res)
|
||||||
case Some(Left(_)) => {
|
case Some(Left(_)) => {
|
||||||
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED)
|
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED)
|
||||||
@@ -41,8 +53,9 @@ class ApiAuthenticationFilter extends Filter with AccessTokenService with Accoun
|
|||||||
}
|
}
|
||||||
|
|
||||||
def doBasicAuth(auth: String, settings: SystemSettings, request: HttpServletRequest): Option[Account] = {
|
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)
|
val Array(username, password) = AuthUtil.decodeAuthHeader(auth).split(":", 2)
|
||||||
|
Database() withTransaction { implicit session =>
|
||||||
authenticate(settings, username, password)
|
authenticate(settings, username, password)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,32 @@ import org.scalatra.ScalatraFilter
|
|||||||
|
|
||||||
import scala.collection.mutable.ListBuffer
|
import scala.collection.mutable.ListBuffer
|
||||||
|
|
||||||
class CompositeScalatraFilter extends Filter {
|
abstract class ControllerFilter extends Filter {
|
||||||
|
|
||||||
|
def process(request: ServletRequest, response: ServletResponse, checkPath: String): Boolean
|
||||||
|
|
||||||
|
override def doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain): Unit = {
|
||||||
|
val contextPath = request.getServletContext.getContextPath
|
||||||
|
val requestPath = request.asInstanceOf[HttpServletRequest].getRequestURI.substring(contextPath.length)
|
||||||
|
val checkPath = if (requestPath.endsWith("/")) {
|
||||||
|
requestPath
|
||||||
|
} else {
|
||||||
|
requestPath + "/"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!checkPath.startsWith("/upload/") && !checkPath.startsWith("/git/") && !checkPath.startsWith("/git-lfs/") &&
|
||||||
|
!checkPath.startsWith("/assets/") && !checkPath.startsWith("/plugin-assets/")) {
|
||||||
|
val continue = process(request, response, checkPath)
|
||||||
|
if (!continue) {
|
||||||
|
return ()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
chain.doFilter(request, response)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CompositeScalatraFilter extends ControllerFilter {
|
||||||
|
|
||||||
private val filters = new ListBuffer[(ScalatraFilter, String)]()
|
private val filters = new ListBuffer[(ScalatraFilter, String)]()
|
||||||
|
|
||||||
@@ -29,17 +54,7 @@ class CompositeScalatraFilter extends Filter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override def doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain): Unit = {
|
override def process(request: ServletRequest, response: ServletResponse, checkPath: String): Boolean = {
|
||||||
val contextPath = request.getServletContext.getContextPath
|
|
||||||
val requestPath = request.asInstanceOf[HttpServletRequest].getRequestURI.substring(contextPath.length)
|
|
||||||
val checkPath = if (requestPath.endsWith("/")) {
|
|
||||||
requestPath
|
|
||||||
} else {
|
|
||||||
requestPath + "/"
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!checkPath.startsWith("/upload/") && !checkPath.startsWith("/git/") && !checkPath.startsWith("/git-lfs/") &&
|
|
||||||
!checkPath.startsWith("/plugin-assets/")) {
|
|
||||||
filters
|
filters
|
||||||
.filter {
|
.filter {
|
||||||
case (_, path) =>
|
case (_, path) =>
|
||||||
@@ -51,12 +66,11 @@ class CompositeScalatraFilter extends Filter {
|
|||||||
val mockChain = new MockFilterChain()
|
val mockChain = new MockFilterChain()
|
||||||
filter.doFilter(request, response, mockChain)
|
filter.doFilter(request, response, mockChain)
|
||||||
if (mockChain.continue == false) {
|
if (mockChain.continue == false) {
|
||||||
return ()
|
return false
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
chain.doFilter(request, response)
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,12 +4,16 @@ import javax.servlet._
|
|||||||
import javax.servlet.http._
|
import javax.servlet.http._
|
||||||
|
|
||||||
import gitbucket.core.model.Account
|
import gitbucket.core.model.Account
|
||||||
import gitbucket.core.model.Profile.profile.blockingApi._
|
|
||||||
import gitbucket.core.plugin.{GitRepositoryFilter, GitRepositoryRouting, PluginRegistry}
|
import gitbucket.core.plugin.{GitRepositoryFilter, GitRepositoryRouting, PluginRegistry}
|
||||||
import gitbucket.core.service.SystemSettingsService.SystemSettings
|
import gitbucket.core.service.SystemSettingsService.SystemSettings
|
||||||
import gitbucket.core.service.{AccessTokenService, AccountService, RepositoryService, SystemSettingsService}
|
import gitbucket.core.service.{AccessTokenService, AccountService, RepositoryService, SystemSettingsService}
|
||||||
import gitbucket.core.util.Implicits._
|
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
|
import org.slf4j.LoggerFactory
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -106,6 +110,7 @@ class GitAuthenticationFilter extends Filter with RepositoryService with Account
|
|||||||
if (isUpdating) {
|
if (isUpdating) {
|
||||||
if (hasDeveloperRole(repository.owner, repository.name, Some(account))) {
|
if (hasDeveloperRole(repository.owner, repository.name, Some(account))) {
|
||||||
request.setAttribute(Keys.Request.UserName, account.userName)
|
request.setAttribute(Keys.Request.UserName, account.userName)
|
||||||
|
request.setAttribute(Keys.Request.RepositoryLockKey, s"${repository.owner}/${repository.name}")
|
||||||
true
|
true
|
||||||
} else false
|
} else false
|
||||||
} else if (repository.repository.isPrivate) {
|
} else if (repository.repository.isPrivate) {
|
||||||
|
|||||||
@@ -8,7 +8,8 @@ import gitbucket.core.util.{FileUtil, StringUtil}
|
|||||||
import org.apache.commons.io.{FileUtils, IOUtils}
|
import org.apache.commons.io.{FileUtils, IOUtils}
|
||||||
import org.json4s.jackson.Serialization._
|
import org.json4s.jackson.Serialization._
|
||||||
import org.apache.http.HttpStatus
|
import org.apache.http.HttpStatus
|
||||||
import gitbucket.core.util.SyntaxSugars._
|
|
||||||
|
import scala.util.Using
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides GitLFS Transfer API
|
* Provides GitLFS Transfer API
|
||||||
@@ -28,8 +29,8 @@ class GitLfsTransferServlet extends HttpServlet {
|
|||||||
if (file.exists()) {
|
if (file.exists()) {
|
||||||
res.setStatus(HttpStatus.SC_OK)
|
res.setStatus(HttpStatus.SC_OK)
|
||||||
res.setContentType("application/octet-stream")
|
res.setContentType("application/octet-stream")
|
||||||
res.setContentLength(file.length.toInt)
|
res.setHeader("Content-Length", file.length.toString)
|
||||||
using(new FileInputStream(file), res.getOutputStream) { (in, out) =>
|
Using.resources(new FileInputStream(file), res.getOutputStream) { (in, out) =>
|
||||||
IOUtils.copy(in, out)
|
IOUtils.copy(in, out)
|
||||||
out.flush()
|
out.flush()
|
||||||
}
|
}
|
||||||
@@ -45,7 +46,7 @@ class GitLfsTransferServlet extends HttpServlet {
|
|||||||
} yield {
|
} yield {
|
||||||
val file = new File(FileUtil.getLfsFilePath(owner, repository, oid))
|
val file = new File(FileUtil.getLfsFilePath(owner, repository, oid))
|
||||||
FileUtils.forceMkdir(file.getParentFile)
|
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)
|
IOUtils.copy(in, out)
|
||||||
}
|
}
|
||||||
res.setStatus(HttpStatus.SC_OK)
|
res.setStatus(HttpStatus.SC_OK)
|
||||||
@@ -71,7 +72,7 @@ class GitLfsTransferServlet extends HttpServlet {
|
|||||||
|
|
||||||
private def sendError(res: HttpServletResponse, status: Int, message: String): Unit = {
|
private def sendError(res: HttpServletResponse, status: Int, message: String): Unit = {
|
||||||
res.setStatus(status)
|
res.setStatus(status)
|
||||||
using(res.getWriter()) { out =>
|
Using.resource(res.getWriter()) { out =>
|
||||||
out.write(write(GitLfs.Error(message)))
|
out.write(write(GitLfs.Error(message)))
|
||||||
out.flush()
|
out.flush()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,9 +4,10 @@ import java.io.File
|
|||||||
import java.util
|
import java.util
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
|
|
||||||
|
import scala.util.Using
|
||||||
|
|
||||||
import gitbucket.core.api
|
import gitbucket.core.api
|
||||||
import gitbucket.core.model.WebHook
|
import gitbucket.core.model.WebHook
|
||||||
import gitbucket.core.model.Profile.profile.blockingApi._
|
|
||||||
import gitbucket.core.plugin.{GitRepositoryRouting, PluginRegistry}
|
import gitbucket.core.plugin.{GitRepositoryRouting, PluginRegistry}
|
||||||
import gitbucket.core.service.IssuesService.IssueSearchCondition
|
import gitbucket.core.service.IssuesService.IssueSearchCondition
|
||||||
import gitbucket.core.service.WebHookService._
|
import gitbucket.core.service.WebHookService._
|
||||||
@@ -14,6 +15,11 @@ import gitbucket.core.service._
|
|||||||
import gitbucket.core.util.SyntaxSugars._
|
import gitbucket.core.util.SyntaxSugars._
|
||||||
import gitbucket.core.util.Implicits._
|
import gitbucket.core.util.Implicits._
|
||||||
import gitbucket.core.util._
|
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.api.Git
|
||||||
import org.eclipse.jgit.http.server.GitServlet
|
import org.eclipse.jgit.http.server.GitServlet
|
||||||
import org.eclipse.jgit.lib._
|
import org.eclipse.jgit.lib._
|
||||||
@@ -22,8 +28,8 @@ import org.eclipse.jgit.transport.resolver._
|
|||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import javax.servlet.ServletConfig
|
import javax.servlet.ServletConfig
|
||||||
import javax.servlet.http.{HttpServletRequest, HttpServletResponse}
|
import javax.servlet.http.{HttpServletRequest, HttpServletResponse}
|
||||||
|
|
||||||
import org.eclipse.jgit.diff.DiffEntry.ChangeType
|
import org.eclipse.jgit.diff.DiffEntry.ChangeType
|
||||||
|
import org.eclipse.jgit.internal.storage.file.FileRepository
|
||||||
import org.json4s.jackson.Serialization._
|
import org.json4s.jackson.Serialization._
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -41,7 +47,7 @@ class GitRepositoryServlet extends GitServlet with SystemSettingsService {
|
|||||||
setReceivePackFactory(new GitBucketReceivePackFactory())
|
setReceivePackFactory(new GitBucketReceivePackFactory())
|
||||||
|
|
||||||
val root: File = new File(Directory.RepositoryHome)
|
val root: File = new File(Directory.RepositoryHome)
|
||||||
setRepositoryResolver(new GitBucketRepositoryResolver(new FileResolver[HttpServletRequest](root, true)))
|
setRepositoryResolver(new GitBucketRepositoryResolver)
|
||||||
|
|
||||||
super.init(config)
|
super.init(config)
|
||||||
}
|
}
|
||||||
@@ -55,13 +61,26 @@ class GitRepositoryServlet extends GitServlet with SystemSettingsService {
|
|||||||
res.sendRedirect(baseUrl(req) + "/" + paths.dropRight(1).last + "/" + paths.last)
|
res.sendRedirect(baseUrl(req) + "/" + paths.dropRight(1).last + "/" + paths.last)
|
||||||
|
|
||||||
} else if (req.getMethod.toUpperCase == "POST" && req.getRequestURI.endsWith("/info/lfs/objects/batch")) {
|
} else if (req.getMethod.toUpperCase == "POST" && req.getRequestURI.endsWith("/info/lfs/objects/batch")) {
|
||||||
|
withLockRepository(req) {
|
||||||
serviceGitLfsBatchAPI(req, res)
|
serviceGitLfsBatchAPI(req, res)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// response for git client
|
// response for git client
|
||||||
|
withLockRepository(req) {
|
||||||
super.service(req, res)
|
super.service(req, res)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private def withLockRepository[T](req: HttpServletRequest)(f: => T): T = {
|
||||||
|
if (req.hasAttribute(Keys.Request.RepositoryLockKey)) {
|
||||||
|
LockUtil.lock(req.getAttribute(Keys.Request.RepositoryLockKey).asInstanceOf[String]) {
|
||||||
|
f
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides GitLFS Batch API
|
* Provides GitLFS Batch API
|
||||||
@@ -95,7 +114,7 @@ class GitRepositoryServlet extends GitServlet with SystemSettingsService {
|
|||||||
GitLfs.Action(
|
GitLfs.Action(
|
||||||
href = baseUrl + "/git-lfs/" + owner + "/" + repository + "/" + requestObject.oid,
|
href = baseUrl + "/git-lfs/" + owner + "/" + repository + "/" + requestObject.oid,
|
||||||
header =
|
header =
|
||||||
Map("Authorization" -> StringUtil.encodeBlowfish(timeout + " " + requestObject.oid)),
|
Map("Authorization" -> StringUtil.encodeBlowfish(s"$timeout ${requestObject.oid}")),
|
||||||
expires_at = new Date(timeout)
|
expires_at = new Date(timeout)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -116,7 +135,7 @@ class GitRepositoryServlet extends GitServlet with SystemSettingsService {
|
|||||||
GitLfs.Action(
|
GitLfs.Action(
|
||||||
href = baseUrl + "/git-lfs/" + owner + "/" + repository + "/" + requestObject.oid,
|
href = baseUrl + "/git-lfs/" + owner + "/" + repository + "/" + requestObject.oid,
|
||||||
header =
|
header =
|
||||||
Map("Authorization" -> StringUtil.encodeBlowfish(timeout + " " + requestObject.oid)),
|
Map("Authorization" -> StringUtil.encodeBlowfish(s"$timeout ${requestObject.oid}")),
|
||||||
expires_at = new Date(timeout)
|
expires_at = new Date(timeout)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -127,7 +146,7 @@ class GitRepositoryServlet extends GitServlet with SystemSettingsService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
res.setContentType("application/vnd.git-lfs+json")
|
res.setContentType("application/vnd.git-lfs+json")
|
||||||
using(res.getWriter) { out =>
|
Using.resource(res.getWriter) { out =>
|
||||||
out.print(write(batchResponse))
|
out.print(write(batchResponse))
|
||||||
out.flush()
|
out.flush()
|
||||||
}
|
}
|
||||||
@@ -138,10 +157,7 @@ class GitRepositoryServlet extends GitServlet with SystemSettingsService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class GitBucketRepositoryResolver(parent: FileResolver[HttpServletRequest])
|
class GitBucketRepositoryResolver extends RepositoryResolver[HttpServletRequest] {
|
||||||
extends RepositoryResolver[HttpServletRequest] {
|
|
||||||
|
|
||||||
private val resolver = new FileResolver[HttpServletRequest](new File(Directory.GitBucketHome), true)
|
|
||||||
|
|
||||||
override def open(req: HttpServletRequest, name: String): Repository = {
|
override def open(req: HttpServletRequest, name: String): Repository = {
|
||||||
// Rewrite repository path if routing is marched
|
// Rewrite repository path if routing is marched
|
||||||
@@ -150,10 +166,10 @@ class GitBucketRepositoryResolver(parent: FileResolver[HttpServletRequest])
|
|||||||
.map {
|
.map {
|
||||||
case GitRepositoryRouting(urlPattern, localPath, _) =>
|
case GitRepositoryRouting(urlPattern, localPath, _) =>
|
||||||
val path = urlPattern.r.replaceFirstIn(name, localPath)
|
val path = urlPattern.r.replaceFirstIn(name, localPath)
|
||||||
resolver.open(req, path)
|
new FileRepository(new File(Directory.GitBucketHome, path))
|
||||||
}
|
}
|
||||||
.getOrElse {
|
.getOrElse {
|
||||||
parent.open(req, name)
|
new FileRepository(new File(Directory.RepositoryHome, name))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -206,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])
|
class CommitLogHook(owner: String, repository: String, pusher: String, baseUrl: String, sshUrl: Option[String])
|
||||||
extends PostReceiveHook
|
extends PostReceiveHook
|
||||||
@@ -215,6 +231,7 @@ class CommitLogHook(owner: String, repository: String, pusher: String, baseUrl:
|
|||||||
with AccountService
|
with AccountService
|
||||||
with IssuesService
|
with IssuesService
|
||||||
with ActivityService
|
with ActivityService
|
||||||
|
with MergeService
|
||||||
with PullRequestService
|
with PullRequestService
|
||||||
with WebHookService
|
with WebHookService
|
||||||
with LabelsService
|
with LabelsService
|
||||||
@@ -222,7 +239,8 @@ class CommitLogHook(owner: String, repository: String, pusher: String, baseUrl:
|
|||||||
with MilestonesService
|
with MilestonesService
|
||||||
with WebHookPullRequestService
|
with WebHookPullRequestService
|
||||||
with WebHookPullRequestReviewCommentService
|
with WebHookPullRequestReviewCommentService
|
||||||
with CommitsService {
|
with CommitsService
|
||||||
|
with SystemSettingsService {
|
||||||
|
|
||||||
private val logger = LoggerFactory.getLogger(classOf[CommitLogHook])
|
private val logger = LoggerFactory.getLogger(classOf[CommitLogHook])
|
||||||
private var existIds: Seq[String] = Nil
|
private var existIds: Seq[String] = Nil
|
||||||
@@ -239,7 +257,7 @@ class CommitLogHook(owner: String, repository: String, pusher: String, baseUrl:
|
|||||||
command.setResult(ReceiveCommand.Result.REJECTED_OTHER_REASON, error)
|
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)
|
existIds = JGitUtil.getAllCommitIds(git)
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
@@ -252,9 +270,11 @@ class CommitLogHook(owner: String, repository: String, pusher: String, baseUrl:
|
|||||||
}
|
}
|
||||||
|
|
||||||
def onPostReceive(receivePack: ReceivePack, commands: java.util.Collection[ReceiveCommand]): Unit = {
|
def onPostReceive(receivePack: ReceivePack, commands: java.util.Collection[ReceiveCommand]): Unit = {
|
||||||
|
val settings = loadSystemSettings()
|
||||||
|
|
||||||
Database() withTransaction { implicit session =>
|
Database() withTransaction { implicit session =>
|
||||||
try {
|
try {
|
||||||
using(Git.open(Directory.getRepositoryDir(owner, repository))) { git =>
|
Using.resource(Git.open(Directory.getRepositoryDir(owner, repository))) { git =>
|
||||||
JGitUtil.removeCache(git)
|
JGitUtil.removeCache(git)
|
||||||
|
|
||||||
val pushedIds = scala.collection.mutable.Set[String]()
|
val pushedIds = scala.collection.mutable.Set[String]()
|
||||||
@@ -278,7 +298,7 @@ class CommitLogHook(owner: String, repository: String, pusher: String, baseUrl:
|
|||||||
if (JGitUtil.isEmpty(git) && commits.nonEmpty && branchName != repositoryInfo.repository.defaultBranch) {
|
if (JGitUtil.isEmpty(git) && commits.nonEmpty && branchName != repositoryInfo.repository.defaultBranch) {
|
||||||
saveRepositoryDefaultBranch(owner, repository, branchName)
|
saveRepositoryDefaultBranch(owner, repository, branchName)
|
||||||
// Change repository HEAD
|
// 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)
|
git.getRepository.updateRef(Constants.HEAD, true).link(Constants.R_HEADS + branchName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -300,7 +320,7 @@ class CommitLogHook(owner: String, repository: String, pusher: String, baseUrl:
|
|||||||
getAccountByUserName(pusher).foreach { pusherAccount =>
|
getAccountByUserName(pusher).foreach { pusherAccount =>
|
||||||
closeIssuesFromMessage(commit.fullMessage, pusher, owner, repository).foreach { issueId =>
|
closeIssuesFromMessage(commit.fullMessage, pusher, owner, repository).foreach { issueId =>
|
||||||
getIssue(owner, repository, issueId.toString).foreach { issue =>
|
getIssue(owner, repository, issueId.toString).foreach { issue =>
|
||||||
callIssuesWebHook("closed", repositoryInfo, issue, pusherAccount)
|
callIssuesWebHook("closed", repositoryInfo, issue, pusherAccount, settings)
|
||||||
PluginRegistry().getIssueHooks
|
PluginRegistry().getIssueHooks
|
||||||
.foreach(_.closedByCommitComment(issue, repositoryInfo, commit.fullMessage, pusherAccount))
|
.foreach(_.closedByCommitComment(issue, repositoryInfo, commit.fullMessage, pusherAccount))
|
||||||
}
|
}
|
||||||
@@ -320,7 +340,7 @@ class CommitLogHook(owner: String, repository: String, pusher: String, baseUrl:
|
|||||||
}.isDefined) {
|
}.isDefined) {
|
||||||
markMergeAndClosePullRequest(pusher, owner, repository, pull)
|
markMergeAndClosePullRequest(pusher, owner, repository, pull)
|
||||||
getAccountByUserName(pusher).foreach { pusherAccount =>
|
getAccountByUserName(pusher).foreach { pusherAccount =>
|
||||||
callPullRequestWebHook("closed", repositoryInfo, pull.issueId, pusherAccount)
|
callPullRequestWebHook("closed", repositoryInfo, pull.issueId, pusherAccount, settings)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -348,14 +368,14 @@ class CommitLogHook(owner: String, repository: String, pusher: String, baseUrl:
|
|||||||
case ReceiveCommand.Type.CREATE | ReceiveCommand.Type.UPDATE |
|
case ReceiveCommand.Type.CREATE | ReceiveCommand.Type.UPDATE |
|
||||||
ReceiveCommand.Type.UPDATE_NONFASTFORWARD =>
|
ReceiveCommand.Type.UPDATE_NONFASTFORWARD =>
|
||||||
getAccountByUserName(pusher).foreach { pusherAccount =>
|
getAccountByUserName(pusher).foreach { pusherAccount =>
|
||||||
updatePullRequests(owner, repository, branchName, pusherAccount, "synchronize")
|
updatePullRequests(owner, repository, branchName, pusherAccount, "synchronize", settings)
|
||||||
}
|
}
|
||||||
case _ =>
|
case _ =>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// call web hook
|
// call web hook
|
||||||
callWebHookOf(owner, repository, WebHook.Push) {
|
callWebHookOf(owner, repository, WebHook.Push, settings) {
|
||||||
for {
|
for {
|
||||||
pusherAccount <- getAccountByUserName(pusher)
|
pusherAccount <- getAccountByUserName(pusher)
|
||||||
ownerAccount <- getAccountByUserName(owner)
|
ownerAccount <- getAccountByUserName(owner)
|
||||||
@@ -373,18 +393,15 @@ class CommitLogHook(owner: String, repository: String, pusher: String, baseUrl:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (command.getType == ReceiveCommand.Type.CREATE) {
|
if (command.getType == ReceiveCommand.Type.CREATE) {
|
||||||
callWebHookOf(owner, repository, WebHook.Create) {
|
callWebHookOf(owner, repository, WebHook.Create, settings) {
|
||||||
for {
|
for {
|
||||||
pusherAccount <- getAccountByUserName(pusher)
|
pusherAccount <- getAccountByUserName(pusher)
|
||||||
ownerAccount <- getAccountByUserName(owner)
|
ownerAccount <- getAccountByUserName(owner)
|
||||||
} yield {
|
} yield {
|
||||||
val refType = if (refName(1) == "tags") "tag" else "branch"
|
val refType = if (refName(1) == "tags") "tag" else "branch"
|
||||||
WebHookCreatePayload(
|
WebHookCreatePayload(
|
||||||
git,
|
|
||||||
pusherAccount,
|
pusherAccount,
|
||||||
command.getRefName,
|
|
||||||
repositoryInfo,
|
repositoryInfo,
|
||||||
newCommits,
|
|
||||||
ownerAccount,
|
ownerAccount,
|
||||||
ref = branchName,
|
ref = branchName,
|
||||||
refType = refType
|
refType = refType
|
||||||
@@ -414,11 +431,14 @@ class WikiCommitHook(owner: String, repository: String, pusher: String, baseUrl:
|
|||||||
extends PostReceiveHook
|
extends PostReceiveHook
|
||||||
with WebHookService
|
with WebHookService
|
||||||
with AccountService
|
with AccountService
|
||||||
with RepositoryService {
|
with RepositoryService
|
||||||
|
with SystemSettingsService {
|
||||||
|
|
||||||
private val logger = LoggerFactory.getLogger(classOf[WikiCommitHook])
|
private val logger = LoggerFactory.getLogger(classOf[WikiCommitHook])
|
||||||
|
|
||||||
override def onPostReceive(receivePack: ReceivePack, commands: util.Collection[ReceiveCommand]): Unit = {
|
override def onPostReceive(receivePack: ReceivePack, commands: util.Collection[ReceiveCommand]): Unit = {
|
||||||
|
val settings = loadSystemSettings()
|
||||||
|
|
||||||
Database() withTransaction { implicit session =>
|
Database() withTransaction { implicit session =>
|
||||||
try {
|
try {
|
||||||
commands.asScala.headOption.foreach { command =>
|
commands.asScala.headOption.foreach { command =>
|
||||||
@@ -435,7 +455,7 @@ class WikiCommitHook(owner: String, repository: String, pusher: String, baseUrl:
|
|||||||
|
|
||||||
commitIds.foreach {
|
commitIds.foreach {
|
||||||
case (oldCommitId, newCommitId) =>
|
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 =>
|
JGitUtil.getCommitLog(git, oldCommitId, newCommitId).flatMap { commit =>
|
||||||
val diffs = JGitUtil.getDiffs(git, None, commit.id, false, false)
|
val diffs = JGitUtil.getDiffs(git, None, commit.id, false, false)
|
||||||
diffs.collect {
|
diffs.collect {
|
||||||
@@ -454,7 +474,7 @@ class WikiCommitHook(owner: String, repository: String, pusher: String, baseUrl:
|
|||||||
(commits.head._1, fileName, commits.last._3)
|
(commits.head._1, fileName, commits.last._3)
|
||||||
}
|
}
|
||||||
|
|
||||||
callWebHookOf(owner, repository, WebHook.Gollum) {
|
callWebHookOf(owner, repository, WebHook.Gollum, settings) {
|
||||||
for {
|
for {
|
||||||
pusherAccount <- getAccountByUserName(pusher)
|
pusherAccount <- getAccountByUserName(pusher)
|
||||||
repositoryUser <- getAccountByUserName(owner)
|
repositoryUser <- getAccountByUserName(owner)
|
||||||
|
|||||||
@@ -9,9 +9,12 @@ import gitbucket.core.plugin.PluginRegistry
|
|||||||
import gitbucket.core.service.{ActivityService, SystemSettingsService}
|
import gitbucket.core.service.{ActivityService, SystemSettingsService}
|
||||||
import gitbucket.core.util.DatabaseConfig
|
import gitbucket.core.util.DatabaseConfig
|
||||||
import gitbucket.core.util.Directory._
|
import gitbucket.core.util.Directory._
|
||||||
import gitbucket.core.util.SyntaxSugars._
|
|
||||||
import gitbucket.core.util.JDBCUtil._
|
import gitbucket.core.util.JDBCUtil._
|
||||||
import gitbucket.core.model.Profile.profile.blockingApi._
|
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.Solidbase
|
||||||
import io.github.gitbucket.solidbase.manager.JDBCVersionManager
|
import io.github.gitbucket.solidbase.manager.JDBCVersionManager
|
||||||
import javax.servlet.{ServletContextEvent, ServletContextListener}
|
import javax.servlet.{ServletContextEvent, ServletContextListener}
|
||||||
@@ -21,7 +24,8 @@ import org.slf4j.LoggerFactory
|
|||||||
import akka.actor.{Actor, ActorSystem, Props}
|
import akka.actor.{Actor, ActorSystem, Props}
|
||||||
import com.typesafe.akka.extension.quartz.QuartzSchedulerExtension
|
import com.typesafe.akka.extension.quartz.QuartzSchedulerExtension
|
||||||
|
|
||||||
import scala.collection.JavaConverters._
|
import scala.jdk.CollectionConverters._
|
||||||
|
import scala.util.Using
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize GitBucket system.
|
* Initialize GitBucket system.
|
||||||
@@ -84,7 +88,7 @@ class InitializeListener extends ServletContextListener with SystemSettingsServi
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Install bundled plugins
|
// Install bundled plugins
|
||||||
extractBundledPlugins(gitbucketVersion)
|
extractBundledPlugins()
|
||||||
|
|
||||||
// Load plugins
|
// Load plugins
|
||||||
logger.info("Initialize 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...")
|
logger.info("Extract bundled plugins...")
|
||||||
val cl = Thread.currentThread.getContextClassLoader
|
val cl = Thread.currentThread.getContextClassLoader
|
||||||
try {
|
try {
|
||||||
using(cl.getResourceAsStream("bundle-plugins.txt")) { pluginsFile =>
|
Using.resource(cl.getResourceAsStream("bundle-plugins.txt")) { pluginsFile =>
|
||||||
if (pluginsFile != null) {
|
if (pluginsFile != null) {
|
||||||
val plugins = IOUtils.readLines(pluginsFile, "UTF-8")
|
val plugins = IOUtils.readLines(pluginsFile, "UTF-8")
|
||||||
val gitbucketVersion = GitBucketCoreModule.getVersions.asScala.last.getVersion
|
val gitbucketVersion = GitBucketCoreModule.getVersions.asScala.last.getVersion
|
||||||
@@ -146,14 +150,14 @@ class InitializeListener extends ServletContextListener with SystemSettingsServi
|
|||||||
plugins.asScala.foreach { plugin =>
|
plugins.asScala.foreach { plugin =>
|
||||||
plugin.trim.split(":") match {
|
plugin.trim.split(":") match {
|
||||||
case Array(pluginId, pluginVersion) =>
|
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)
|
val in = cl.getResourceAsStream("plugins/" + fileName)
|
||||||
if (in != null) {
|
if (in != null) {
|
||||||
val file = new File(PluginHome, fileName)
|
val file = new File(PluginHome, fileName)
|
||||||
logger.info(s"Extract to ${file.getAbsolutePath}")
|
logger.info(s"Extract to ${file.getAbsolutePath}")
|
||||||
|
|
||||||
FileUtils.forceMkdirParent(file)
|
FileUtils.forceMkdirParent(file)
|
||||||
using(in, new FileOutputStream(file)) {
|
Using.resources(in, new FileOutputStream(file)) {
|
||||||
case (in, out) => IOUtils.copy(in, out)
|
case (in, out) => IOUtils.copy(in, out)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package gitbucket.core.servlet
|
package gitbucket.core.servlet
|
||||||
|
|
||||||
import javax.servlet.http.{HttpServlet, HttpServletRequest, HttpServletResponse}
|
import javax.servlet.http.{HttpServlet, HttpServletRequest, HttpServletResponse}
|
||||||
|
|
||||||
import gitbucket.core.plugin.PluginRegistry
|
import gitbucket.core.plugin.PluginRegistry
|
||||||
import gitbucket.core.util.FileUtil
|
import gitbucket.core.util.FileUtil
|
||||||
import org.apache.commons.io.IOUtils
|
import org.apache.commons.io.IOUtils
|
||||||
@@ -17,17 +16,22 @@ class PluginAssetsServlet extends HttpServlet {
|
|||||||
|
|
||||||
assetsMappings
|
assetsMappings
|
||||||
.find { case (prefix, _, _) => path.startsWith("/plugin-assets" + prefix) }
|
.find { case (prefix, _, _) => path.startsWith("/plugin-assets" + prefix) }
|
||||||
.flatMap {
|
.foreach {
|
||||||
case (prefix, resourcePath, classLoader) =>
|
case (prefix, resourcePath, classLoader) =>
|
||||||
|
val ifNoneMatch = req.getHeader("If-None-Match")
|
||||||
|
PluginRegistry.getPluginInfoFromClassLoader(classLoader).map { info =>
|
||||||
|
val etag = s""""${info.pluginJar.lastModified}"""" // ETag must wrapped with double quote
|
||||||
|
if (ifNoneMatch == etag) {
|
||||||
|
resp.setStatus(304)
|
||||||
|
} else {
|
||||||
val resourceName = path.substring(("/plugin-assets" + prefix).length)
|
val resourceName = path.substring(("/plugin-assets" + prefix).length)
|
||||||
Option(classLoader.getResourceAsStream(resourcePath.stripPrefix("/") + resourceName))
|
Option(classLoader.getResourceAsStream(resourcePath.stripPrefix("/") + resourceName))
|
||||||
}
|
|
||||||
.map { in =>
|
.map { in =>
|
||||||
try {
|
try {
|
||||||
val bytes = IOUtils.toByteArray(in)
|
val bytes = IOUtils.toByteArray(in)
|
||||||
resp.setContentLength(bytes.length)
|
resp.setContentLength(bytes.length)
|
||||||
resp.setContentType(FileUtil.getMimeType(path, bytes))
|
resp.setContentType(FileUtil.getMimeType(path, bytes))
|
||||||
resp.setHeader("Cache-Control", "max-age=3600")
|
resp.setHeader("ETag", etag)
|
||||||
resp.getOutputStream.write(bytes)
|
resp.getOutputStream.write(bytes)
|
||||||
} finally {
|
} finally {
|
||||||
in.close()
|
in.close()
|
||||||
@@ -37,5 +41,8 @@ class PluginAssetsServlet extends HttpServlet {
|
|||||||
resp.setStatus(404)
|
resp.setStatus(404)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import javax.servlet.http.HttpServletRequest
|
|||||||
import gitbucket.core.controller.ControllerBase
|
import gitbucket.core.controller.ControllerBase
|
||||||
import gitbucket.core.plugin.PluginRegistry
|
import gitbucket.core.plugin.PluginRegistry
|
||||||
|
|
||||||
class PluginControllerFilter extends Filter {
|
class PluginControllerFilter extends ControllerFilter {
|
||||||
|
|
||||||
private var filterConfig: FilterConfig = null
|
private var filterConfig: FilterConfig = null
|
||||||
|
|
||||||
@@ -21,16 +21,13 @@ class PluginControllerFilter extends Filter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override def doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain): Unit = {
|
override def process(request: ServletRequest, response: ServletResponse, checkPath: String): Boolean = {
|
||||||
val contextPath = request.getServletContext.getContextPath
|
|
||||||
val requestUri = request.asInstanceOf[HttpServletRequest].getRequestURI.substring(contextPath.length)
|
|
||||||
|
|
||||||
PluginRegistry()
|
PluginRegistry()
|
||||||
.getControllers()
|
.getControllers()
|
||||||
.filter {
|
.filter {
|
||||||
case (_, path) =>
|
case (_, path) =>
|
||||||
val start = path.replaceFirst("/\\*$", "/")
|
val start = path.replaceFirst("/\\*$", "/")
|
||||||
(requestUri + "/").startsWith(start)
|
checkPath.startsWith(start)
|
||||||
}
|
}
|
||||||
.foreach {
|
.foreach {
|
||||||
case (controller, _) =>
|
case (controller, _) =>
|
||||||
@@ -42,11 +39,11 @@ class PluginControllerFilter extends Filter {
|
|||||||
controller.doFilter(request, response, mockChain)
|
controller.doFilter(request, response, mockChain)
|
||||||
|
|
||||||
if (mockChain.continue == false) {
|
if (mockChain.continue == false) {
|
||||||
return ()
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
chain.doFilter(request, response)
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,13 +11,13 @@ import org.apache.sshd.server.session.ServerSession
|
|||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import java.io.{File, InputStream, OutputStream}
|
import java.io.{File, InputStream, OutputStream}
|
||||||
|
|
||||||
import SyntaxSugars._
|
|
||||||
import org.eclipse.jgit.api.Git
|
import org.eclipse.jgit.api.Git
|
||||||
import Directory._
|
import Directory._
|
||||||
import gitbucket.core.ssh.PublicKeyAuthenticator.AuthType
|
import gitbucket.core.ssh.PublicKeyAuthenticator.AuthType
|
||||||
import org.eclipse.jgit.transport.{ReceivePack, UploadPack}
|
import org.eclipse.jgit.transport.{ReceivePack, UploadPack}
|
||||||
import org.apache.sshd.server.shell.UnknownCommand
|
import org.apache.sshd.server.shell.UnknownCommand
|
||||||
import org.eclipse.jgit.errors.RepositoryNotFoundException
|
import org.eclipse.jgit.errors.RepositoryNotFoundException
|
||||||
|
import scala.util.Using
|
||||||
|
|
||||||
object GitCommand {
|
object GitCommand {
|
||||||
val DefaultCommandRegex = """\Agit-(upload|receive)-pack '/([a-zA-Z0-9\-_.]+)/([a-zA-Z0-9\-\+_.]+).git'\Z""".r
|
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) {
|
if (execute) {
|
||||||
using(Git.open(getRepositoryDir(owner, repoName))) { git =>
|
Using.resource(Git.open(getRepositoryDir(owner, repoName))) { git =>
|
||||||
val repository = git.getRepository
|
val repository = git.getRepository
|
||||||
val upload = new UploadPack(repository)
|
val upload = new UploadPack(repository)
|
||||||
upload.upload(in, out, err)
|
upload.upload(in, out, err)
|
||||||
@@ -177,7 +177,7 @@ class DefaultGitReceivePack(owner: String, repoName: String, baseUrl: String, ss
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (execute) {
|
if (execute) {
|
||||||
using(Git.open(getRepositoryDir(owner, repoName))) { git =>
|
Using.resource(Git.open(getRepositoryDir(owner, repoName))) { git =>
|
||||||
val repository = git.getRepository
|
val repository = git.getRepository
|
||||||
val receive = new ReceivePack(repository)
|
val receive = new ReceivePack(repository)
|
||||||
if (!repoName.endsWith(".wiki")) {
|
if (!repoName.endsWith(".wiki")) {
|
||||||
@@ -202,7 +202,7 @@ class PluginGitUploadPack(repoName: String, routing: GitRepositoryRouting)
|
|||||||
|
|
||||||
if (execute) {
|
if (execute) {
|
||||||
val path = routing.urlPattern.r.replaceFirstIn(repoName, routing.localPath)
|
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 repository = git.getRepository
|
||||||
val upload = new UploadPack(repository)
|
val upload = new UploadPack(repository)
|
||||||
upload.upload(in, out, err)
|
upload.upload(in, out, err)
|
||||||
@@ -222,7 +222,7 @@ class PluginGitReceivePack(repoName: String, routing: GitRepositoryRouting)
|
|||||||
|
|
||||||
if (execute) {
|
if (execute) {
|
||||||
val path = routing.urlPattern.r.replaceFirstIn(repoName, routing.localPath)
|
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 repository = git.getRepository
|
||||||
val receive = new ReceivePack(repository)
|
val receive = new ReceivePack(repository)
|
||||||
receive.receive(in, out, err)
|
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,9 +6,8 @@ import java.io.File
|
|||||||
import Directory._
|
import Directory._
|
||||||
import ConfigUtil._
|
import ConfigUtil._
|
||||||
import com.github.takezoe.slick.blocking.{BlockingH2Driver, BlockingJdbcProfile, BlockingMySQLDriver}
|
import com.github.takezoe.slick.blocking.{BlockingH2Driver, BlockingJdbcProfile, BlockingMySQLDriver}
|
||||||
import gitbucket.core.util.SyntaxSugars.defining
|
|
||||||
import liquibase.database.AbstractJdbcDatabase
|
import liquibase.database.AbstractJdbcDatabase
|
||||||
import liquibase.database.core.{H2Database, MySQLDatabase, PostgresDatabase}
|
import liquibase.database.core.{H2Database, MariaDBDatabase, MySQLDatabase, PostgresDatabase}
|
||||||
import org.apache.commons.io.FileUtils
|
import org.apache.commons.io.FileUtils
|
||||||
|
|
||||||
import scala.reflect.ClassTag
|
import scala.reflect.ClassTag
|
||||||
@@ -54,16 +53,14 @@ object DatabaseConfig {
|
|||||||
lazy val minimumIdle: Option[Int] = getOptionValue("db.minimumIdle", config.getInt)
|
lazy val minimumIdle: Option[Int] = getOptionValue("db.minimumIdle", config.getInt)
|
||||||
lazy val maximumPoolSize: Option[Int] = getOptionValue("db.maximumPoolSize", config.getInt)
|
lazy val maximumPoolSize: Option[Int] = getOptionValue("db.maximumPoolSize", config.getInt)
|
||||||
|
|
||||||
private def getValue[T](path: String, f: String => T): T = {
|
private def getValue[T: ClassTag](path: String, f: String => T): T = {
|
||||||
getSystemProperty(path).getOrElse(getEnvironmentVariable(path).getOrElse {
|
getConfigValue(path).getOrElse(f(path))
|
||||||
f(path)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private def getOptionValue[T](path: String, f: String => T): Option[T] = {
|
private def getOptionValue[T: ClassTag](path: String, f: String => T): Option[T] = {
|
||||||
getSystemProperty(path).orElse(getEnvironmentVariable(path).orElse {
|
getConfigValue(path).orElse {
|
||||||
if (config.hasPath(path)) Some(f(path)) else None
|
if (config.hasPath(path)) Some(f(path)) else None
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -81,6 +78,8 @@ object DatabaseType {
|
|||||||
H2
|
H2
|
||||||
} else if (url.startsWith("jdbc:mysql:")) {
|
} else if (url.startsWith("jdbc:mysql:")) {
|
||||||
MySQL
|
MySQL
|
||||||
|
} else if (url.startsWith("jdbc:mariadb:")) {
|
||||||
|
MariaDb
|
||||||
} else if (url.startsWith("jdbc:postgresql:")) {
|
} else if (url.startsWith("jdbc:postgresql:")) {
|
||||||
PostgreSQL
|
PostgreSQL
|
||||||
} else {
|
} else {
|
||||||
@@ -100,6 +99,12 @@ object DatabaseType {
|
|||||||
val liquiDriver = new MySQLDatabase()
|
val liquiDriver = new MySQLDatabase()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object MariaDb extends DatabaseType {
|
||||||
|
val jdbcDriver = "org.mariadb.jdbc.Driver"
|
||||||
|
val slickDriver = BlockingMySQLDriver
|
||||||
|
val liquiDriver = new MariaDBDatabase()
|
||||||
|
}
|
||||||
|
|
||||||
object PostgreSQL extends DatabaseType {
|
object PostgreSQL extends DatabaseType {
|
||||||
val jdbcDriver = "org.postgresql.Driver2"
|
val jdbcDriver = "org.postgresql.Driver2"
|
||||||
val slickDriver = BlockingPostgresDriver
|
val slickDriver = BlockingPostgresDriver
|
||||||
@@ -114,33 +119,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
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ object Directory {
|
|||||||
new File(getRepositoryFilesDir(owner, repository), "releases")
|
new File(getRepositoryFilesDir(owner, repository), "releases")
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Directory for files which are attached to issue.
|
* Directory for Git LFS files.
|
||||||
*/
|
*/
|
||||||
def getLfsDir(owner: String, repository: String): File =
|
def getLfsDir(owner: String, repository: String): File =
|
||||||
new File(getRepositoryFilesDir(owner, repository), "lfs")
|
new File(getRepositoryFilesDir(owner, repository), "lfs")
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user