Compare commits
230 Commits
fix/readme
...
options-bi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2a46834168 | ||
|
|
8596ea5519 | ||
|
|
608b1ce642 | ||
|
|
d97b9cf0c2 | ||
|
|
dd5464e1f2 | ||
|
|
c06db22b24 | ||
|
|
cd0ce9f3d8 | ||
|
|
70134323d1 | ||
|
|
8a28130540 | ||
|
|
e52b24ad5d | ||
|
|
947d2ee21b | ||
|
|
d07c955e2a | ||
|
|
edae0f134c | ||
|
|
db29855d2d | ||
|
|
e6cb9a7397 | ||
|
|
dc14d0c046 | ||
|
|
0118b275b6 | ||
|
|
c8f300b2cd | ||
|
|
db6a4e9fbf | ||
|
|
b21bf80dd4 | ||
|
|
0f05470cb8 | ||
|
|
d7ed78a919 | ||
|
|
e8e0539b45 | ||
|
|
e6cfccdd40 | ||
|
|
71bb6df75a | ||
|
|
bea9d55da6 | ||
|
|
d93429af8b | ||
|
|
7e09f80ee3 | ||
|
|
baf60bf603 | ||
|
|
4ff0db0246 | ||
|
|
79bd7648b0 | ||
|
|
b78d3f5865 | ||
|
|
5ab85372da | ||
|
|
1ae6ccb5f1 | ||
|
|
0a6d9295df | ||
|
|
6598745f07 | ||
|
|
590a79ff8a | ||
|
|
2343feadd4 | ||
|
|
bb3ed784bc | ||
|
|
31950cb262 | ||
|
|
2932042a6d | ||
|
|
646e02b521 | ||
|
|
bf0edcbdea | ||
|
|
4bd5730e02 | ||
|
|
a3fb627350 | ||
|
|
6dc6926abe | ||
|
|
6519718706 | ||
|
|
1e9730a779 | ||
|
|
e929ca2c41 | ||
|
|
ccad2cce32 | ||
|
|
349ecc6919 | ||
|
|
b6a95a8cb3 | ||
|
|
4680c349dd | ||
|
|
684d55e130 | ||
|
|
60e3e5b4e1 | ||
|
|
e93d394620 | ||
|
|
6dd2c3b2db | ||
|
|
42ec5ce740 | ||
|
|
dad806d3ea | ||
|
|
57dc9efaae | ||
|
|
f364522468 | ||
|
|
abf6c3a8e3 | ||
|
|
16cdbe1956 | ||
|
|
36a4663393 | ||
|
|
fd53028139 | ||
|
|
b9b22b4a0b | ||
|
|
8704f48e66 | ||
|
|
bad1bc6518 | ||
|
|
a5aae1c145 | ||
|
|
9963d61233 | ||
|
|
d7dea676fd | ||
|
|
4b0abdae9e | ||
|
|
972ce6b791 | ||
|
|
65d0426b91 | ||
|
|
8def53ffcc | ||
|
|
27d66855eb | ||
|
|
1d0f811399 | ||
|
|
fd3f16695e | ||
|
|
b3abc2775f | ||
|
|
91d6c715ea | ||
|
|
caac5fb99d | ||
|
|
c2044e5b39 | ||
|
|
9fc609ce17 | ||
|
|
7b6cc9244d | ||
|
|
9628d4fb44 | ||
|
|
3d2138812c | ||
|
|
575dc69e3b | ||
|
|
86aa8e413a | ||
|
|
9948f0daaa | ||
|
|
f215d78157 | ||
|
|
bf8d90c5cc | ||
|
|
21846d16e5 | ||
|
|
25b5722155 | ||
|
|
d0bef011ad | ||
|
|
ec87a75c00 | ||
|
|
e6da2cf2cb | ||
|
|
a4ece1f223 | ||
|
|
5efdccd1d8 | ||
|
|
7a92519bd7 | ||
|
|
9a984c0d49 | ||
|
|
fe3908d099 | ||
|
|
e23a9d22e5 | ||
|
|
93d527a0a4 | ||
|
|
8347a55cc2 | ||
|
|
bc59b8abc9 | ||
|
|
9aaf2a6d9a | ||
|
|
3ac72255fa | ||
|
|
c664ffd1db | ||
|
|
94da472717 | ||
|
|
5b998a6680 | ||
|
|
4cb21115dc | ||
|
|
d647d02c2f | ||
|
|
638dd24cec | ||
|
|
3228544c31 | ||
|
|
0b9cf10340 | ||
|
|
7bf7042013 | ||
|
|
ce8c9ef580 | ||
|
|
6cde041080 | ||
|
|
2bb1601d7c | ||
|
|
066f515a47 | ||
|
|
0a76d260fa | ||
|
|
65549863bc | ||
|
|
574e49c854 | ||
|
|
21b7d30174 | ||
|
|
e9c6053b86 | ||
|
|
177a4c7385 | ||
|
|
0accc935a3 | ||
|
|
5d4333eb0d | ||
|
|
32f8a38f6c | ||
|
|
3ae7955d15 | ||
|
|
755ed84740 | ||
|
|
5b17661c5d | ||
|
|
3e6f363471 | ||
|
|
2255a9af6a | ||
|
|
26ae2ff86d | ||
|
|
d39266228c | ||
|
|
7c5de1e393 | ||
|
|
a321ffbcce | ||
|
|
3c87c57d96 | ||
|
|
b47051e59b | ||
|
|
900a21008c | ||
|
|
c0ca6644ad | ||
|
|
081c2a9395 | ||
|
|
76604d8f90 | ||
|
|
33a2ac3830 | ||
|
|
faabc76fd6 | ||
|
|
bd5ea3e222 | ||
|
|
229ec927b9 | ||
|
|
304bbd3f25 | ||
|
|
2e565bc1c4 | ||
|
|
6a28909f40 | ||
|
|
d8e11a8eaa | ||
|
|
ece19f4a5e | ||
|
|
21e8deed89 | ||
|
|
450969c158 | ||
|
|
ba2e75a0ab | ||
|
|
fd090dc29b | ||
|
|
4c03974326 | ||
|
|
cb0b91cdc9 | ||
|
|
fd13b71fb2 | ||
|
|
cd7e661870 | ||
|
|
cc8c57458f | ||
|
|
b6b616b336 | ||
|
|
289f819f78 | ||
|
|
1c3044b873 | ||
|
|
ff0d1bd602 | ||
|
|
2ccdcda072 | ||
|
|
0a66c2a2d9 | ||
|
|
e512411863 | ||
|
|
03b6880089 | ||
|
|
8ba0ac976f | ||
|
|
1cfbfb3812 | ||
|
|
f0cfb1cb03 | ||
|
|
0581210a76 | ||
|
|
46ecb0a14d | ||
|
|
8aa960f129 | ||
|
|
3fba29c571 | ||
|
|
0a61d54a9c | ||
|
|
dd9d0f3732 | ||
|
|
170f2e98cc | ||
|
|
6e644726d0 | ||
|
|
4dd1eb57bd | ||
|
|
5301a5db3a | ||
|
|
ad3d6b7fff | ||
|
|
6ed7f269f1 | ||
|
|
fb3bb69ec6 | ||
|
|
3917ed45de | ||
|
|
3a3782bb7f | ||
|
|
cb1602840c | ||
|
|
c25063d834 | ||
|
|
2a449bd4b1 | ||
|
|
b2cce12980 | ||
|
|
f0df8e8dfa | ||
|
|
18dc4f1023 | ||
|
|
1d9576d5ea | ||
|
|
659bc2814c | ||
|
|
bd13c81684 | ||
|
|
7b75d93f3d | ||
|
|
fc3ed8a1de | ||
|
|
cf045b029c | ||
|
|
91953ae9b4 | ||
|
|
39b3fcad1d | ||
|
|
4faf9c213e | ||
|
|
d884312223 | ||
|
|
0834e492c0 | ||
|
|
871c964ef7 | ||
|
|
7596e41027 | ||
|
|
56a8cf523b | ||
|
|
d9ffe99972 | ||
|
|
07a0753420 | ||
|
|
6cf66117e7 | ||
|
|
a285c07d5e | ||
|
|
cd339263d9 | ||
|
|
592a4ec4d3 | ||
|
|
81f227eace | ||
|
|
5054020c1f | ||
|
|
904deb7d6a | ||
|
|
739f07c98e | ||
|
|
bd76e156bb | ||
|
|
3ef022b071 | ||
|
|
3dedc027ac | ||
|
|
54e6ed3431 | ||
|
|
b339858500 | ||
|
|
0baaa7728a | ||
|
|
30a37311f8 | ||
|
|
555d8b16cb | ||
|
|
900f233b3c | ||
|
|
ade6d4a20f | ||
|
|
9bf28a2799 | ||
|
|
a8c6698de8 |
@@ -1,19 +1,5 @@
|
||||
.git
|
||||
.git/**
|
||||
packager
|
||||
packager/**
|
||||
scripts
|
||||
scripts/**
|
||||
.github/
|
||||
.github/**
|
||||
config.codekit
|
||||
.dockerignore
|
||||
*.yml
|
||||
*.md
|
||||
.bra.toml
|
||||
.editorconfig
|
||||
.gitignore
|
||||
Dockerfile*
|
||||
vendor
|
||||
vendor/**
|
||||
gogs
|
||||
*
|
||||
!gitea
|
||||
!docker
|
||||
!public
|
||||
!templates
|
||||
|
||||
127
.drone.yml
Normal file
127
.drone.yml
Normal file
@@ -0,0 +1,127 @@
|
||||
workspace:
|
||||
base: /srv/app
|
||||
path: src/code.gitea.io/gitea
|
||||
|
||||
pipeline:
|
||||
test:
|
||||
image: webhippie/golang:edge
|
||||
pull: true
|
||||
environment:
|
||||
CGO_ENABLED: 1
|
||||
TAGS: sqlite
|
||||
GOPATH: /srv/app
|
||||
commands:
|
||||
- apk -U add openssh-client
|
||||
- make clean
|
||||
- make vet
|
||||
- make lint
|
||||
- make test
|
||||
- make build
|
||||
when:
|
||||
event: [ push, tag, pull_request ]
|
||||
|
||||
test-mysql:
|
||||
image: webhippie/golang:edge
|
||||
pull: true
|
||||
environment:
|
||||
CGO_ENABLED: 1
|
||||
TAGS: sqlite
|
||||
GOPATH: /srv/app
|
||||
commands:
|
||||
- make test-mysql
|
||||
when:
|
||||
event: [ push ]
|
||||
|
||||
test-pgsql:
|
||||
image: webhippie/golang:edge
|
||||
pull: true
|
||||
environment:
|
||||
CGO_ENABLED: 1
|
||||
TAGS: sqlite
|
||||
GOPATH: /srv/app
|
||||
commands:
|
||||
- make test-pgsql
|
||||
when:
|
||||
event: [ push ]
|
||||
|
||||
updater:
|
||||
image: karalabe/xgo-latest:latest
|
||||
pull: true
|
||||
environment:
|
||||
CGO_ENABLED: 1
|
||||
TAGS: sqlite
|
||||
GOPATH: /srv/app
|
||||
commands:
|
||||
- make release
|
||||
when:
|
||||
event: [ push, tag ]
|
||||
branch: [ master, refs/tags/* ]
|
||||
|
||||
coverage:
|
||||
image: plugins/coverage
|
||||
server: https://coverage.gitea.io
|
||||
when:
|
||||
event: [ push, tag, pull_request ]
|
||||
|
||||
docker:
|
||||
image: plugins/docker
|
||||
repo: gitea/gitea
|
||||
tags: [ '${TAG}' ]
|
||||
when:
|
||||
event: [ tag ]
|
||||
branch: [ refs/tags/* ]
|
||||
|
||||
docker:
|
||||
image: plugins/docker
|
||||
repo: gitea/gitea
|
||||
tags: [ 'latest' ]
|
||||
when:
|
||||
event: [ push ]
|
||||
branch: [ master ]
|
||||
|
||||
release:
|
||||
image: plugins/s3
|
||||
path_style: true
|
||||
strip_prefix: dist/release/
|
||||
source: dist/release/*
|
||||
target: /gitea/master
|
||||
when:
|
||||
event: [ push ]
|
||||
branch: [ master ]
|
||||
|
||||
release:
|
||||
image: plugins/s3
|
||||
path_style: true
|
||||
strip_prefix: dist/release/
|
||||
source: dist/release/*
|
||||
target: /gitea/$$TAG
|
||||
when:
|
||||
event: [ tag ]
|
||||
branch: [ refs/tags/* ]
|
||||
|
||||
github:
|
||||
image: plugins/github-release
|
||||
files:
|
||||
- dist/release/*
|
||||
when:
|
||||
event: [ tag ]
|
||||
branch: [ refs/tags/* ]
|
||||
|
||||
gitter:
|
||||
image: plugins/gitter
|
||||
|
||||
services:
|
||||
mysql:
|
||||
image: mysql:5.7
|
||||
environment:
|
||||
- MYSQL_DATABASE=test
|
||||
- MYSQL_ALLOW_EMPTY_PASSWORD=yes
|
||||
when:
|
||||
event: [ push ]
|
||||
|
||||
pgsql:
|
||||
image: postgres:9.5
|
||||
environment:
|
||||
- POSTGRES_DB=test
|
||||
when:
|
||||
event: [ push ]
|
||||
1
.drone.yml.sig
Normal file
1
.drone.yml.sig
Normal file
@@ -0,0 +1 @@
|
||||
eyJhbGciOiJIUzI1NiJ9.d29ya3NwYWNlOgogIGJhc2U6IC9zcnYvYXBwCiAgcGF0aDogc3JjL2NvZGUuZ2l0ZWEuaW8vZ2l0ZWEKCnBpcGVsaW5lOgogIHRlc3Q6CiAgICBpbWFnZTogd2ViaGlwcGllL2dvbGFuZzplZGdlCiAgICBwdWxsOiB0cnVlCiAgICBlbnZpcm9ubWVudDoKICAgICAgQ0dPX0VOQUJMRUQ6IDEKICAgICAgVEFHUzogc3FsaXRlCiAgICAgIEdPUEFUSDogL3Nydi9hcHAKICAgIGNvbW1hbmRzOgogICAgICAtIGFwayAtVSBhZGQgb3BlbnNzaC1jbGllbnQKICAgICAgLSBtYWtlIGNsZWFuCiAgICAgIC0gbWFrZSB2ZXQKICAgICAgLSBtYWtlIGxpbnQKICAgICAgLSBtYWtlIHRlc3QKICAgICAgLSBtYWtlIGJ1aWxkCiAgICB3aGVuOgogICAgICBldmVudDogWyBwdXNoLCB0YWcsIHB1bGxfcmVxdWVzdCBdCgogIHRlc3QtbXlzcWw6CiAgICBpbWFnZTogd2ViaGlwcGllL2dvbGFuZzplZGdlCiAgICBwdWxsOiB0cnVlCiAgICBlbnZpcm9ubWVudDoKICAgICAgQ0dPX0VOQUJMRUQ6IDEKICAgICAgVEFHUzogc3FsaXRlCiAgICAgIEdPUEFUSDogL3Nydi9hcHAKICAgIGNvbW1hbmRzOgogICAgICAtIG1ha2UgdGVzdC1teXNxbAogICAgd2hlbjoKICAgICAgZXZlbnQ6IFsgcHVzaCBdCgogIHRlc3QtcGdzcWw6CiAgICBpbWFnZTogd2ViaGlwcGllL2dvbGFuZzplZGdlCiAgICBwdWxsOiB0cnVlCiAgICBlbnZpcm9ubWVudDoKICAgICAgQ0dPX0VOQUJMRUQ6IDEKICAgICAgVEFHUzogc3FsaXRlCiAgICAgIEdPUEFUSDogL3Nydi9hcHAKICAgIGNvbW1hbmRzOgogICAgICAtIG1ha2UgdGVzdC1wZ3NxbAogICAgd2hlbjoKICAgICAgZXZlbnQ6IFsgcHVzaCBdCgogIHVwZGF0ZXI6CiAgICBpbWFnZToga2FyYWxhYmUveGdvLWxhdGVzdDpsYXRlc3QKICAgIHB1bGw6IHRydWUKICAgIGVudmlyb25tZW50OgogICAgICBDR09fRU5BQkxFRDogMQogICAgICBUQUdTOiBzcWxpdGUKICAgICAgR09QQVRIOiAvc3J2L2FwcAogICAgY29tbWFuZHM6CiAgICAgIC0gbWFrZSByZWxlYXNlCiAgICB3aGVuOgogICAgICBldmVudDogWyBwdXNoLCB0YWcgXQogICAgICBicmFuY2g6IFsgbWFzdGVyLCByZWZzL3RhZ3MvKiBdCgogIGNvdmVyYWdlOgogICAgaW1hZ2U6IHBsdWdpbnMvY292ZXJhZ2UKICAgIHNlcnZlcjogaHR0cHM6Ly9jb3ZlcmFnZS5naXRlYS5pbwogICAgd2hlbjoKICAgICAgZXZlbnQ6IFsgcHVzaCwgdGFnLCBwdWxsX3JlcXVlc3QgXQoKICBkb2NrZXI6CiAgICBpbWFnZTogcGx1Z2lucy9kb2NrZXIKICAgIHJlcG86IGdpdGVhL2dpdGVhCiAgICB0YWdzOiBbICcke1RBR30nIF0KICAgIHdoZW46CiAgICAgIGV2ZW50OiBbIHRhZyBdCiAgICAgIGJyYW5jaDogWyByZWZzL3RhZ3MvKiBdCgogIGRvY2tlcjoKICAgIGltYWdlOiBwbHVnaW5zL2RvY2tlcgogICAgcmVwbzogZ2l0ZWEvZ2l0ZWEKICAgIHRhZ3M6IFsgJ2xhdGVzdCcgXQogICAgd2hlbjoKICAgICAgZXZlbnQ6IFsgcHVzaCBdCiAgICAgIGJyYW5jaDogWyBtYXN0ZXIgXQoKICByZWxlYXNlOgogICAgaW1hZ2U6IHBsdWdpbnMvczMKICAgIHBhdGhfc3R5bGU6IHRydWUKICAgIHN0cmlwX3ByZWZpeDogZGlzdC9yZWxlYXNlLwogICAgc291cmNlOiBkaXN0L3JlbGVhc2UvKgogICAgdGFyZ2V0OiAvZ2l0ZWEvbWFzdGVyCiAgICB3aGVuOgogICAgICBldmVudDogWyBwdXNoIF0KICAgICAgYnJhbmNoOiBbIG1hc3RlciBdCgogIHJlbGVhc2U6CiAgICBpbWFnZTogcGx1Z2lucy9zMwogICAgcGF0aF9zdHlsZTogdHJ1ZQogICAgc3RyaXBfcHJlZml4OiBkaXN0L3JlbGVhc2UvCiAgICBzb3VyY2U6IGRpc3QvcmVsZWFzZS8qCiAgICB0YXJnZXQ6IC9naXRlYS8kJFRBRwogICAgd2hlbjoKICAgICAgZXZlbnQ6IFsgdGFnIF0KICAgICAgYnJhbmNoOiBbIHJlZnMvdGFncy8qIF0KCiAgZ2l0aHViOgogICAgaW1hZ2U6IHBsdWdpbnMvZ2l0aHViLXJlbGVhc2UKICAgIGZpbGVzOgogICAgICAtIGRpc3QvcmVsZWFzZS8qCiAgICB3aGVuOgogICAgICBldmVudDogWyB0YWcgXQogICAgICBicmFuY2g6IFsgcmVmcy90YWdzLyogXQoKICBnaXR0ZXI6CiAgICBpbWFnZTogcGx1Z2lucy9naXR0ZXIKCnNlcnZpY2VzOgogIG15c3FsOgogICAgaW1hZ2U6IG15c3FsOjUuNwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gTVlTUUxfREFUQUJBU0U9dGVzdAogICAgICAtIE1ZU1FMX0FMTE9XX0VNUFRZX1BBU1NXT1JEPXllcwogICAgd2hlbjoKICAgICAgZXZlbnQ6IFsgcHVzaCBdCgogIHBnc3FsOgogICAgaW1hZ2U6IHBvc3RncmVzOjkuNQogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gUE9TVEdSRVNfREI9dGVzdAogICAgd2hlbjoKICAgICAgZXZlbnQ6IFsgcHVzaCBdCg.LjU9u_DQ4fD2CytUu9RwwSimdcS3-KzR6nO4ff4edNU
|
||||
@@ -4,7 +4,6 @@ root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
@@ -12,17 +11,17 @@ trim_trailing_whitespace = true
|
||||
indent_style = tab
|
||||
indent_size = 8
|
||||
|
||||
[*.tmpl]
|
||||
indent_style = tab
|
||||
indent_size = 2
|
||||
|
||||
[Makefile]
|
||||
[*.{tmpl,html}]
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
|
||||
[*.{less,yml}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
indent_size = 4
|
||||
|
||||
[*.js]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
[Makefile]
|
||||
indent_style = tab
|
||||
|
||||
11
.gitattributes
vendored
11
.gitattributes
vendored
@@ -1,11 +0,0 @@
|
||||
public/conf/gitignore/* linguist-vendored
|
||||
public/conf/license/* linguist-vendored
|
||||
public/assets/* linguist-vendored
|
||||
public/plugins/* linguist-vendored
|
||||
public/plugins/* linguist-vendored
|
||||
public/css/themes/* linguist-vendored
|
||||
public/css/github.min.css linguist-vendored
|
||||
public/css/semantic-2.2.1.min.css linguist-vendored
|
||||
public/js/libs/* linguist-vendored
|
||||
public/js/jquery-1.11.3.min.js linguist-vendored
|
||||
public/js/semantic-2.2.1.min.js linguist-vendored
|
||||
24
.github/ISSUE_TEMPLATE.md
vendored
24
.github/ISSUE_TEMPLATE.md
vendored
@@ -1,24 +0,0 @@
|
||||
|
||||
1. Please speak English
|
||||
2. Please ask questions or config/deploy problems
|
||||
on our gitter channel: https://gitter.im/go-gitea/gitea
|
||||
Here are bugs and feature requests only.
|
||||
3. Please take a moment to search that an issue doesn't already exist.
|
||||
4. Please give all relevant information below for bug reports; incomplete
|
||||
details considered invalid report.
|
||||
|
||||
**You MUST delete above content including this line before posting;
|
||||
too lazy to take this action considered invalid report.**
|
||||
|
||||
- Gitea version (or commit ref):
|
||||
- Git version:
|
||||
- Operating system:
|
||||
- Database (use `[x]`):
|
||||
- [ ] PostgreSQL
|
||||
- [ ] MySQL
|
||||
- [ ] SQLite
|
||||
- Log gist:
|
||||
|
||||
## Description
|
||||
|
||||
...
|
||||
10
.github/PULL_REQUEST_TEMPLATE.md
vendored
10
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -1,10 +0,0 @@
|
||||
Please check the following:
|
||||
|
||||
1. Make sure you are targeting the `master` branch.
|
||||
2. Read contributing guidelines:
|
||||
https://github.com/go-gitea/gitea/blob/master/CONTRIBUTING.md
|
||||
3. Describe what your pull request does and which issue
|
||||
you're targeting (if any)
|
||||
|
||||
**You MUST delete above content including this line before posting;
|
||||
too lazy to take this action considered invalid pull request.**
|
||||
19
.github/issue_template.md
vendored
Normal file
19
.github/issue_template.md
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
1. Please speak English, this is the language everybody of us can speak and write.
|
||||
2. Please ask questions or config/deploy problems on our Gitter channel: https://gitter.im/go-gitea/gitea
|
||||
3. Please take a moment to search that an issue doesn't already exist.
|
||||
4. Please give all relevant information below for bug reports, incomplete details will be handled as an invalid report.
|
||||
|
||||
**You MUST delete the content above including this line before posting, otherwise your pull request will be invalid.**
|
||||
|
||||
- Gitea version (or commit ref):
|
||||
- Git version:
|
||||
- Operating system:
|
||||
- Database (use `[x]`):
|
||||
- [ ] PostgreSQL
|
||||
- [ ] MySQL
|
||||
- [ ] SQLite
|
||||
- Log gist:
|
||||
|
||||
## Description
|
||||
|
||||
...
|
||||
7
.github/pull_request_template.md
vendored
Normal file
7
.github/pull_request_template.md
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
Please check the following:
|
||||
|
||||
1. Make sure you are targeting the `master` branch, pull requests on release branches are only allowed for bug fixes.
|
||||
2. Read contributing guidelines: https://github.com/go-gitea/gitea/blob/master/CONTRIBUTING.md
|
||||
3. Describe what your pull request does and which issue you're targeting (if any)
|
||||
|
||||
**You MUST delete the content above including this line before posting, otherwise your pull request will be invalid.**
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -28,6 +28,9 @@ _testmain.go
|
||||
|
||||
coverage.out
|
||||
|
||||
/modules/options/bindata.go
|
||||
/modules/public/bindata.go
|
||||
|
||||
*.db
|
||||
*.log
|
||||
|
||||
|
||||
2
.mailmap
2
.mailmap
@@ -1,2 +0,0 @@
|
||||
Unknwon <u@gogs.io> <joe2010xtmf@163.com>
|
||||
Unknwon <u@gogs.io> 无闻 <u@gogs.io>
|
||||
36
.travis.yml
36
.travis.yml
@@ -1,36 +0,0 @@
|
||||
language: go
|
||||
|
||||
go_import_path: code.gitea.io/gitea
|
||||
|
||||
go:
|
||||
- 1.6
|
||||
- 1.7
|
||||
|
||||
env:
|
||||
TAGS: cert sqlite pam miniwinsvc
|
||||
|
||||
before_install:
|
||||
- sudo apt-get update -qq
|
||||
- sudo apt-get install -y libpam-dev
|
||||
|
||||
script:
|
||||
- make clean
|
||||
- make vet
|
||||
|
||||
# - make lint
|
||||
|
||||
- make test
|
||||
- make build
|
||||
|
||||
after_success:
|
||||
- bash <(curl -s https://codecov.io/bash)
|
||||
|
||||
notifications:
|
||||
webhooks:
|
||||
on_success: change
|
||||
on_failure: always
|
||||
on_start: never
|
||||
urls:
|
||||
- https://webhooks.gitter.im/e/ee6b822f3cf54c98e70c
|
||||
- https://webhooks.gitter.im/e/87428658ef177ce8a7e4
|
||||
- https://webhooks.gitter.im/e/a1d2b69804dfda72187e
|
||||
138
CONTRIBUTING.md
138
CONTRIBUTING.md
@@ -2,161 +2,85 @@
|
||||
|
||||
## Introduction
|
||||
|
||||
This document explains how to contribute changes to the Gitea
|
||||
project. It assumes you have followed the [installation
|
||||
instructions](https://github.com/go-gitea/docs/tree/master/en-US/installation)
|
||||
|
||||
Sensitive security-related issues should be reported to
|
||||
[security@gitea.io](mailto:security@gitea.io).
|
||||
This document explains how to contribute changes to the Gitea project. It assumes you have followed the [installation instructions](https://github.com/go-gitea/docs/tree/master/en-US/installation). Sensitive security-related issues should be reported to [security@gitea.io](mailto:security@gitea.io).
|
||||
|
||||
## Bug reports
|
||||
|
||||
Please search the issues on the issue tracker with a variety of keywords
|
||||
to ensure your bug is not already reported.
|
||||
Please search the issues on the issue tracker with a variety of keywords to ensure your bug is not already reported.
|
||||
|
||||
If unique, [open an issue](https://github.com/go-gitea/gitea/issues/new)
|
||||
and answer the questions so we can understand and reproduce the
|
||||
problematic behavior.
|
||||
If unique, [open an issue](https://github.com/go-gitea/gitea/issues/new) and answer the questions so we can understand and reproduce the problematic behavior.
|
||||
|
||||
The burden is on you to convince us that it is actually a bug
|
||||
in Gitea. This is easiest to do when you write clear, concise
|
||||
instructions so we can reproduce the behavior (even if it seems
|
||||
obvious). The more detailed and specific you are, the faster
|
||||
we will be able to help you. Check out [How to Report Bugs
|
||||
Effectively](http://www.chiark.greenend.org.uk/~sgtatham/bugs.html).
|
||||
The burden is on you to convince us that it is actually a bug in Gitea. This is easiest to do when you write clear, concise instructions so we can reproduce the behavior (even if it seems obvious). The more detailed and specific you are, the faster we will be able to help you. Check out [How to Report Bugs Effectively](http://www.chiark.greenend.org.uk/~sgtatham/bugs.html).
|
||||
|
||||
Please be kind, remember that Gitea comes at no cost to you, and you're
|
||||
getting free help.
|
||||
Please be kind, remember that Gitea comes at no cost to you, and you're getting free help.
|
||||
|
||||
## Discuss your design
|
||||
|
||||
The project welcomes submissions but please let everyone know what
|
||||
you're working on if you want to change or add something to the Gitea
|
||||
repositories.
|
||||
The project welcomes submissions but please let everyone know what you're working on if you want to change or add something to the Gitea repositories.
|
||||
|
||||
Before starting to write something new for the Gitea project, please
|
||||
[file an issue](https://github.com/go-gitea/gitea/issues/new).
|
||||
Significant changes must go through the [change proposal
|
||||
process](https://github.com/go-gitea/proposals) before they can be
|
||||
accepted.
|
||||
Before starting to write something new for the Gitea project, please [file an issue](https://github.com/go-gitea/gitea/issues/new). Significant changes must go through the [change proposal process](https://github.com/go-gitea/proposals) before they can be accepted.
|
||||
|
||||
This process gives everyone a chance to validate the design, helps
|
||||
prevent duplication of effort, and ensures that the idea fits inside
|
||||
the goals for the project and tools. It also checks that the design is
|
||||
sound before code is written; the code review tool is not the place for
|
||||
high-level discussions.
|
||||
This process gives everyone a chance to validate the design, helps prevent duplication of effort, and ensures that the idea fits inside the goals for the project and tools. It also checks that the design is sound before code is written; the code review tool is not the place for high-level discussions.
|
||||
|
||||
## Testing redux
|
||||
|
||||
Before sending code out for review, run all the tests for the whole
|
||||
tree to make sure the changes don't break other usage and keep the
|
||||
compatibility on upgrade:
|
||||
|
||||
After running for a while, the command should print
|
||||
|
||||
```
|
||||
ALL TESTS PASSED
|
||||
```
|
||||
Before sending code out for review, run all the tests for the whole tree to make sure the changes don't break other usage and keep the compatibility on upgrade. To make sure you are running the test suite exactly like we do you should install the CLI for [Drone CI](https://github.com/drone/drone), as we are using the server for continous testing, following [these instructions](http://readme.drone.io/0.5/install/cli/). After that you can simply call `drone exec` within your working directory and it will try to run the test suite locally.
|
||||
|
||||
## Code review
|
||||
|
||||
Changes to Gitea must be reviewed before they are accepted, no matter
|
||||
who makes the change even if an owners or a maintainer. We use github's
|
||||
pull request workflow to do that and use [lgtm](http://lgtm.co) to ensure
|
||||
every PR is reviewed by at least 2 maintainers.
|
||||
Changes to Gitea must be reviewed before they are accepted, no matter who makes the change even if it is an owner or a maintainer. We use GitHub's pull request workflow to do that and we also use [LGTM](http://lgtm.co) to ensure every PR is reviewed by at least 2 maintainers.
|
||||
|
||||
Please try to make your pull request easy to review for us. Please read the "[How to get faster PR reviews](https://github.com/kubernetes/kubernetes/blob/master/docs/devel/faster_reviews.md)" guide, it has lots of useful tips for any project you may want to contribute. Some of the key points:
|
||||
|
||||
* Make small pull requests. The smaller, the faster to review and the more likely it will be merged soon.
|
||||
* Don't make changes unrelated to your PR. Maybe there are typos on some comments, maybe refactoring would be welcome on a function... but if that is not related to your PR, please make *another* PR for that.
|
||||
* Split big pull requests in multiple small ones. An incremental change will be faster to review than a huge PR.
|
||||
|
||||
## Sign your work
|
||||
|
||||
The sign-off is a simple line at the end of the explanation for the
|
||||
patch. Your signature certifies that you wrote the patch or otherwise
|
||||
have the right to pass it on as an open-source patch. The rules are
|
||||
pretty simple: If you can certify [DCO](DCO), then you just add a line
|
||||
to every git commit message:
|
||||
The sign-off is a simple line at the end of the explanation for the patch. Your signature certifies that you wrote the patch or otherwise have the right to pass it on as an open-source patch. The rules are pretty simple: If you can certify [DCO](DCO), then you just add a line to every git commit message:
|
||||
|
||||
```
|
||||
Signed-off-by: Joe Smith <joe.smith@email.com>
|
||||
```
|
||||
|
||||
Please use your real name, we really dislike pseudonyms or anonymous
|
||||
contributions. We are in the opensource world without secrets. If you
|
||||
set your `user.name` and `user.email` git configs, you can sign your
|
||||
commit automatically with `git commit -s`.
|
||||
|
||||
## Contributors
|
||||
|
||||
Everyone who sent a PR to Gitea that gets accepted will
|
||||
be as a contributor. Please send a PR to add your name to
|
||||
[CONTRIBUTORS](CONTRIBUTORS). For the format, see the
|
||||
[CONTRIBUTORS](CONTRIBUTORS).
|
||||
Please use your real name, we really dislike pseudonyms or anonymous contributions. We are in the opensource world without secrets. If you set your `user.name` and `user.email` git configs, you can sign your commit automatically with `git commit -s`.
|
||||
|
||||
## Maintainers
|
||||
|
||||
To make sure every PR have been checked, we make a team maintainers. Any
|
||||
PR MUST be reviewed and by at least two maintainers before it can
|
||||
get merged. Maintainers should be a contributor of gitea(or gogs) and
|
||||
contributed at least 4 accepted PRs. And a contributor should apply as a
|
||||
maintainer in [gitter Gitea develop](https://gitter.im/go-gitea/develop).
|
||||
And the owners or the team maintainer could invite the contributor. A
|
||||
maintainer should spend some time on code reviews. If some maintainer
|
||||
have no time to do that, he should apply to leave maintainers team and
|
||||
we will give him an honor to be as a member of advisor team. Of course,
|
||||
if an advisor have time to code view, welcome it back to maintainers team.
|
||||
If some one have no time to code view and forget to leave the maintainers,
|
||||
the owners have the power to move him from maintainers team to advisors
|
||||
team.
|
||||
To make sure every PR is checked, we got team maintainers. Every PR **MUST** be reviewed by at least two maintainers (or owners) before it can get merged. A maintainer should be a contributor of Gitea (or Gogs) and contributed at least 4 accepted PRs. A contributor should apply as a maintainer in the [Gitter develop channel](https://gitter.im/go-gitea/develop). The owners or the team maintainers may invite the contributor. A maintainer should spend some time on code reviews. If a maintainer has no time to do that, they should apply to leave the maintainers team and we will give them the honor of being a member of the advisors team. Of course, if an advisor has time to code review, we will gladly welcome them back to maintainers team. If someone has no time to code review and forgets to leave the maintainers team, the owners have the power to move him from maintainers team to advisors team.
|
||||
|
||||
## Owners
|
||||
|
||||
Since Gitea is a pure community organization without any company
|
||||
support, to keep the development healthly We will elect the owners every
|
||||
year. Every time we will elect three owners. All the contributers could
|
||||
vote for three owners, one is the main owner, the other two are assistant
|
||||
owners. When the new owners have been elected, the old owners MUST move
|
||||
the power to the new owners. If some owner don't obey these rules,
|
||||
the other owners are allowed to revoke his owner status.
|
||||
Since Gitea is a pure community organization without any company support, to keep the development healthy we will elect the owners every year. Every time we will elect three owners. All the contributors may vote up to three people, one of which is the main owner, and the others are assistant owners. When the new owners have been elected, the old owners MUST move the power to the new ones. If an owner don't obey these rules, the others are allowed to revoke his owner status.
|
||||
|
||||
After the election, the new owners should say he agrees with these
|
||||
rules on the [CONTRIBUTING](CONTRIBUTING.md) on the [Gitter Gitea
|
||||
Channel](https://gitter.im/go-gitea/gitea). Below is the word to speak
|
||||
After the election, the new owners should say they agree with these rules on the [CONTRIBUTING](CONTRIBUTING.md) on the [Gitter main channel](https://gitter.im/go-gitea/gitea). Below are the words to speak:
|
||||
|
||||
```
|
||||
I'm glad to be an owner of Gitea,
|
||||
I agree with [CONTRIBUTING](CONTRIBUTING.md).
|
||||
I will spend part of my time on gitea
|
||||
and lead the development of gitea.
|
||||
I'm glad to be an owner of Gitea, I agree with [CONTRIBUTING](CONTRIBUTING.md). I will spend part of my time on Gitea and lead the development of Gitea.
|
||||
```
|
||||
|
||||
For a honor to the owners, this document will add the history owners
|
||||
below:
|
||||
To honor the past owners, here's the history of the owners and the time they served:
|
||||
|
||||
2016-11-04 ~ 2017-12-31
|
||||
|
||||
- lunny <xiaolunwen@gmail.com>
|
||||
- tboerger <thomas@webhippie.de>
|
||||
- bkcsoft <kim.carlbacker@gmail.com>
|
||||
* 2016-11-04 ~ 2017-12-31
|
||||
* [Lunny Xiao](https://github.com/lunny) <xiaolunwen@gmail.com>
|
||||
* [Thomas Boerger](https://github.com/tboerger) <thomas@webhippie.de>
|
||||
* [Kim Carlbäcker](https://github.com/bkcsoft) <kim.carlbacker@gmail.com>
|
||||
|
||||
## Versions
|
||||
|
||||
Gitea has one master as a tip branch and have many version branch
|
||||
such as v0.9. v0.9 is a release branch and we will tag v0.9.0 both for
|
||||
binary download. If v0.9.0 have some bugs, we will accept PR on v0.9
|
||||
and publish v0.9.1 and merge bug PR to master.
|
||||
Gitea has the `master` branch as a tip branch and has version branches such as `v0.9`. `v0.9` is a release branch and we will tag `v0.9.0` for binary download. If `v0.9.0` has bugs, we will accept pull requests on the `v0.9` branch and publish a `v0.9.1` tag, after bringing the bug fix also to the master branch.
|
||||
|
||||
Branch master is a tip version, so if you wish a production usage,
|
||||
please download the latest release tag version. All the branch will be
|
||||
protected via github, All the PRs to all the branches should be review
|
||||
by two maintainers and pass the automatic tests.
|
||||
Since the `master` branch is a tip version, if you wish to use Gitea in production, please download the latest release tag version. All the branches will be protected via GitHub, all the PRs to every branch must be reviewed by two maintainers and must pass the automatic tests.
|
||||
|
||||
## Copyright
|
||||
|
||||
Code that you contribute should use the standard copyright header:
|
||||
|
||||
```
|
||||
// Copyright 2016 - 2017 The Gitea Authors. All rights reserved.
|
||||
// Copyright 2016 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
```
|
||||
|
||||
Files in the repository are copyright the year they are added and the
|
||||
year they are last changed. If the copyright author is changed, just
|
||||
copy the head below the old one.
|
||||
Files in the repository contain copyright from the year they are added to the year they are last changed. If the copyright author is changed, just paste the header below the old one.
|
||||
|
||||
62
Dockerfile
62
Dockerfile
@@ -1,22 +1,44 @@
|
||||
FROM alpine:3.3
|
||||
MAINTAINER jp@roemer.im
|
||||
FROM alpine:3.4
|
||||
MAINTAINER Thomas Boerger <thomas@webhippie.de>
|
||||
|
||||
# Install system utils & Gogs runtime dependencies
|
||||
ADD https://github.com/tianon/gosu/releases/download/1.9/gosu-amd64 /usr/sbin/gosu
|
||||
RUN chmod +x /usr/sbin/gosu \
|
||||
&& apk --no-cache --no-progress add ca-certificates bash git linux-pam s6 curl openssh socat tzdata
|
||||
|
||||
ENV GITEA_CUSTOM /data/gogs
|
||||
|
||||
COPY . /app/gogs/
|
||||
WORKDIR /app/gogs/
|
||||
RUN ./docker/build.sh
|
||||
|
||||
# Configure LibC Name Service
|
||||
COPY docker/nsswitch.conf /etc/nsswitch.conf
|
||||
|
||||
# Configure Docker Container
|
||||
VOLUME ["/data"]
|
||||
EXPOSE 22 3000
|
||||
ENTRYPOINT ["docker/start.sh"]
|
||||
CMD ["/bin/s6-svscan", "/app/gogs/docker/s6/"]
|
||||
|
||||
RUN apk update && \
|
||||
apk add \
|
||||
su-exec \
|
||||
ca-certificates \
|
||||
sqlite \
|
||||
bash \
|
||||
git \
|
||||
linux-pam \
|
||||
s6 \
|
||||
curl \
|
||||
openssh \
|
||||
tzdata && \
|
||||
rm -rf \
|
||||
/var/cache/apk/* && \
|
||||
addgroup \
|
||||
-S -g 1000 \
|
||||
git && \
|
||||
adduser \
|
||||
-S -H -D \
|
||||
-h /data/git \
|
||||
-s /bin/bash \
|
||||
-u 1000 \
|
||||
-G git \
|
||||
git
|
||||
|
||||
ENV USER git
|
||||
ENV GITEA_CUSTOM /data/gitea
|
||||
ENV GODEBUG=netdns=go
|
||||
|
||||
VOLUME ["/data"]
|
||||
|
||||
ENTRYPOINT ["/usr/bin/entrypoint"]
|
||||
CMD ["/bin/s6-svscan", "/etc/s6"]
|
||||
|
||||
COPY docker /
|
||||
|
||||
COPY public /app/gitea/public
|
||||
COPY templates /app/gitea/templates
|
||||
COPY gitea /app/gitea/gitea
|
||||
|
||||
@@ -1,25 +1,44 @@
|
||||
FROM hypriot/rpi-alpine-scratch:v3.2
|
||||
MAINTAINER jp@roemer.im, raxetul@gmail.com
|
||||
FROM hypriot/rpi-alpine-scratch:v3.4
|
||||
MAINTAINER Thomas Boerger <thomas@webhippie.de>
|
||||
|
||||
# Install system utils & Gogs runtime dependencies
|
||||
ADD https://github.com/tianon/gosu/releases/download/1.9/gosu-armhf /usr/sbin/gosu
|
||||
RUN chmod +x /usr/sbin/gosu \
|
||||
&& echo "http://dl-4.alpinelinux.org/alpine/v3.3/main/" | tee /etc/apk/repositories \
|
||||
&& echo "http://dl-4.alpinelinux.org/alpine/v3.3/community/" | tee -a /etc/apk/repositories \
|
||||
&& apk -U --no-progress upgrade && rm -f /var/cache/apk/APKINDEX.* \
|
||||
&& apk --no-cache --no-progress add ca-certificates bash git linux-pam s6 curl openssh socat tzdata
|
||||
|
||||
ENV GITEA_CUSTOM /data/gogs
|
||||
|
||||
COPY . /app/gogs/
|
||||
WORKDIR /app/gogs/
|
||||
RUN ./docker/build.sh
|
||||
|
||||
# Configure LibC Name Service
|
||||
COPY docker/nsswitch.conf /etc/nsswitch.conf
|
||||
|
||||
# Configure Docker Container
|
||||
VOLUME ["/data"]
|
||||
EXPOSE 22 3000
|
||||
ENTRYPOINT ["docker/start.sh"]
|
||||
CMD ["/bin/s6-svscan", "/app/gogs/docker/s6/"]
|
||||
|
||||
RUN apk update && \
|
||||
apk add \
|
||||
su-exec \
|
||||
ca-certificates \
|
||||
sqlite \
|
||||
bash \
|
||||
git \
|
||||
linux-pam \
|
||||
s6 \
|
||||
curl \
|
||||
openssh \
|
||||
tzdata && \
|
||||
rm -rf \
|
||||
/var/cache/apk/* && \
|
||||
addgroup \
|
||||
-S -g 1000 \
|
||||
git && \
|
||||
adduser \
|
||||
-S -H -D \
|
||||
-h /data/git \
|
||||
-s /bin/bash \
|
||||
-u 1000 \
|
||||
-G git \
|
||||
git
|
||||
|
||||
ENV USER git
|
||||
ENV GITEA_CUSTOM /data/gitea
|
||||
ENV GODEBUG=netdns=go
|
||||
|
||||
VOLUME ["/data"]
|
||||
|
||||
ENTRYPOINT ["/usr/bin/entrypoint"]
|
||||
CMD ["/bin/s6-svscan", "/etc/s6"]
|
||||
|
||||
COPY docker /
|
||||
|
||||
COPY public /app/gitea/public
|
||||
COPY templates /app/gitea/templates
|
||||
COPY gitea /app/gitea/gitea
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
Andrey Nering <nobody@nobody.tld> (@andreynering)
|
||||
Alexey Makhov <amakhov@avito.ru> (@makhov)
|
||||
Andrey Nering <andrey.nering@gmail.com> (@andreynering)
|
||||
Kees de Vries <bouwko@gmail.com> (@Bwko)
|
||||
Kim Carlbäcker <kim.carlbacker@gmail.com> (@bkcsoft)
|
||||
LefsFlare <nobody@nobody.tld> (@LefsFlarey)
|
||||
Lunny Xiao <xiaolunwen@gmail.com> (@lunny)
|
||||
@@ -6,5 +8,5 @@ Matthias Loibl <mail@matthiasloibl.com> (@metalmatze)
|
||||
Rachid Zarouali <nobody@nobody.tld> (@xinity)
|
||||
Rémy Boulanouar <admin@dblk.org> (@DblK)
|
||||
Sandro Santilli <strk@kbt.io> (@strk)
|
||||
Thibault Meyer <nobody@nobody.tld> (@0xbaadf00d)
|
||||
Thibault Meyer <meyer.thibault@gmail.com> (@0xbaadf00d)
|
||||
Thomas Boerger <thomas@webhippie.de> (@tboerger)
|
||||
|
||||
93
Makefile
93
Makefile
@@ -1,13 +1,10 @@
|
||||
DIST := dist
|
||||
BIN := bin
|
||||
|
||||
EXECUTABLE := gitea
|
||||
IMPORT := code.gitea.io/gitea
|
||||
|
||||
SHA := $(shell git rev-parse --short HEAD)
|
||||
DATE := $(shell date -u '+%Y-%m-%d %I:%M:%S %Z')
|
||||
|
||||
BINDATA := $(shell find conf | sed 's/ /\\ /g')
|
||||
STYLESHEETS := $(wildcard public/less/index.less public/less/_*.less)
|
||||
JAVASCRIPTS :=
|
||||
|
||||
@@ -19,29 +16,23 @@ PACKAGES ?= $(shell go list ./... | grep -v /vendor/)
|
||||
|
||||
TAGS ?=
|
||||
|
||||
ifneq ($(TRAVIS_TAG),)
|
||||
VERSION ?= $(TRAVIS_TAG)
|
||||
ifneq ($(DRONE_TAG),)
|
||||
VERSION ?= $(DRONE_TAG)
|
||||
else
|
||||
ifneq ($(TRAVIS_BRANCH),)
|
||||
VERSION ?= $(TRAVIS_BRANCH)
|
||||
ifneq ($(DRONE_BRANCH),)
|
||||
VERSION ?= $(DRONE_BRANCH)
|
||||
else
|
||||
VERSION ?= master
|
||||
endif
|
||||
endif
|
||||
|
||||
.PHONY: all
|
||||
all: clean test build
|
||||
all: build
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
go clean -i ./...
|
||||
rm -rf $(BIN) $(DIST)
|
||||
|
||||
.PHONY: deps
|
||||
deps:
|
||||
@which go-bindata > /dev/null; if [ $$? -ne 0 ]; then \
|
||||
go get -u github.com/jteeuwen/go-bindata/...; \
|
||||
fi
|
||||
rm -rf $(EXECUTABLE) $(DIST)
|
||||
|
||||
.PHONY: fmt
|
||||
fmt:
|
||||
@@ -51,6 +42,20 @@ fmt:
|
||||
vet:
|
||||
go vet $(PACKAGES)
|
||||
|
||||
.PHONY: generate
|
||||
generate:
|
||||
@which go-bindata > /dev/null; if [ $$? -ne 0 ]; then \
|
||||
go get -u github.com/jteeuwen/go-bindata/...; \
|
||||
fi
|
||||
go generate $(PACKAGES)
|
||||
|
||||
.PHONY: errcheck
|
||||
errcheck:
|
||||
@which errcheck > /dev/null; if [ $$? -ne 0 ]; then \
|
||||
go get -u github.com/kisielk/errcheck; \
|
||||
fi
|
||||
errcheck $(PACKAGES)
|
||||
|
||||
.PHONY: lint
|
||||
lint:
|
||||
@which golint > /dev/null; if [ $$? -ne 0 ]; then \
|
||||
@@ -62,61 +67,53 @@ lint:
|
||||
test:
|
||||
for PKG in $(PACKAGES); do go test -cover -coverprofile $$GOPATH/src/$$PKG/coverage.out $$PKG || exit 1; done;
|
||||
|
||||
.PHONY: test-mysql
|
||||
test-mysql:
|
||||
@echo "Not integrated yet!"
|
||||
|
||||
.PHONY: test-pgsql
|
||||
test-pgsql:
|
||||
@echo "Not integrated yet!"
|
||||
|
||||
.PHONY: check
|
||||
check: test
|
||||
|
||||
.PHONY: install
|
||||
install: $(BIN)/$(EXECUTABLE)
|
||||
cp $< $(GOPATH)/bin/
|
||||
install: $(wildcard *.go)
|
||||
go install -v -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)'
|
||||
|
||||
.PHONY: build
|
||||
build: $(BIN)/$(EXECUTABLE)
|
||||
build: $(EXECUTABLE)
|
||||
|
||||
$(BIN)/$(EXECUTABLE): $(wildcard *.go)
|
||||
.PHONY: $(EXECUTABLE)
|
||||
$(EXECUTABLE): $(wildcard *.go)
|
||||
go build -v -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)' -o $@
|
||||
|
||||
.PHONY: release
|
||||
release: release-build release-copy release-check
|
||||
release: release-dirs release-build release-copy release-check
|
||||
|
||||
.PHONY: release-dirs
|
||||
release-dirs:
|
||||
mkdir -p $(DIST)/binaries $(DIST)/release
|
||||
|
||||
.PHONY: release-build
|
||||
release-build:
|
||||
@which xgo > /dev/null; if [ $$? -ne 0 ]; then \
|
||||
go get -u github.com/karalabe/xgo; \
|
||||
fi
|
||||
xgo -dest $(BIN) -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)' -targets '$(TARGETS)' -out $(EXECUTABLE)-$(VERSION) $(IMPORT)
|
||||
xgo -dest $(DIST)/binaries -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)' -targets '$(TARGETS)' -out $(EXECUTABLE)-$(VERSION) $(IMPORT)
|
||||
ifeq ($(CI),drone)
|
||||
mv /build/* $(DIST)/binaries
|
||||
endif
|
||||
|
||||
.PHONY: release-copy
|
||||
release-copy:
|
||||
mkdir -p $(DIST)/release
|
||||
$(foreach file,$(wildcard $(BIN)/$(EXECUTABLE)-*),cp $(file) $(DIST)/release/$(notdir $(file));)
|
||||
$(foreach file,$(wildcard $(DIST)/binaries/$(EXECUTABLE)-*),cp $(file) $(DIST)/release/$(notdir $(file));)
|
||||
|
||||
.PHONY: release-check
|
||||
release-check:
|
||||
cd $(DIST)/release; $(foreach file,$(wildcard $(DIST)/release/$(EXECUTABLE)-*),sha256sum $(notdir $(file)) > $(notdir $(file)).sha256;)
|
||||
|
||||
.PHONY: latest
|
||||
latest: release-build latest-copy latest-check
|
||||
|
||||
.PHONY: latest-copy
|
||||
latest-copy:
|
||||
mkdir -p $(DIST)/latest
|
||||
$(foreach file,$(wildcard $(BIN)/$(EXECUTABLE)-*),cp $(file) $(DIST)/latest/$(subst $(EXECUTABLE)-$(VERSION),$(EXECUTABLE)-latest,$(notdir $(file)));)
|
||||
|
||||
.PHONY: latest-check
|
||||
latest-check:
|
||||
cd $(DIST)/latest; $(foreach file,$(wildcard $(DIST)/latest/$(EXECUTABLE)-*),sha256sum $(notdir $(file)) > $(notdir $(file)).sha256;)
|
||||
|
||||
.PHONY: publish
|
||||
publish: release latest
|
||||
|
||||
.PHONY: bindata
|
||||
bindata: modules/bindata/bindata.go
|
||||
|
||||
.IGNORE: modules/bindata/bindata.go
|
||||
modules/bindata/bindata.go: $(BINDATA)
|
||||
go-bindata -o=$@ -ignore="\\.go|README.md|TRANSLATORS" -pkg=bindata conf/...
|
||||
go fmt $@
|
||||
|
||||
.PHONY: javascripts
|
||||
javascripts: public/js/index.js
|
||||
|
||||
@@ -131,5 +128,5 @@ stylesheets: public/css/index.css
|
||||
public/css/index.css: $(STYLESHEETS)
|
||||
lessc $< $@
|
||||
|
||||
.PHONY: generate
|
||||
generate: bindata javascripts stylesheets
|
||||
.PHONY: assets
|
||||
assets: javascripts stylesheets
|
||||
|
||||
29
README.md
29
README.md
@@ -1,13 +1,13 @@
|
||||
# Gitea - Git with a cup of tea
|
||||
|
||||
[](https://travis-ci.org/go-gitea/gitea)
|
||||
[](https://codecov.io/gh/go-gitea/gitea)
|
||||
[](https://goreportcard.com/report/github.com/go-gitea/gitea)
|
||||
[](https://godoc.org/github.com/go-gitea/gitea)
|
||||
[](http://microbadger.com/images/gitea/gitea "Get your own image badge on microbadger.com")
|
||||
[](http://drone.gitea.io/go-gitea/gitea)
|
||||
[](https://gitter.im/go-gitea/gitea?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
[](http://microbadger.com/images/gitea/gitea "Get your own image badge on microbadger.com")
|
||||
[](https://coverage.gitea.io/go-gitea/gitea)
|
||||
[](https://goreportcard.com/report/code.gitea.io/gitea)
|
||||
[](https://godoc.org/code.gitea.io/gitea)
|
||||
|
||||

|
||||
[](https://github.com/go-gitea/gitea)
|
||||
|
||||
##### Status
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
### Important Notes
|
||||
|
||||
1. **YOU MUST READ THE [Contributors Guide](https://github.com/go-gitea/gitea/CONTRIBUTING.md) BEFORE STARTING TO WORK ON A PULL REQUEST**.
|
||||
1. **YOU MUST READ THE [Contributors Guide](CONTRIBUTING.md) BEFORE STARTING TO WORK ON A PULL REQUEST**.
|
||||
2. If you think there are vulnerabilities in the project, please talk privately to **security@gitea.io**. Thanks!
|
||||
3. If you're interested in using APIs, we have experimental support with [documentation](https://godoc.org/github.com/go-gitea/go-sdk).
|
||||
|
||||
@@ -46,7 +46,7 @@ The goal of this project is to make the easiest, fastest, and most painless way
|
||||
- Mail service
|
||||
- Administration panel
|
||||
- Supports MySQL, PostgreSQL, SQLite3 and [TiDB](https://github.com/pingcap/tidb) (experimental)
|
||||
- Multi-language support ([19 languages](https://crowdin.com/project/gogs))
|
||||
- Multi-language support ([20 languages](https://crowdin.com/project/gogs))
|
||||
|
||||
## System Requirements
|
||||
|
||||
@@ -122,11 +122,16 @@ How to install Gitea:
|
||||
- Thanks [DigitalOcean](https://www.digitalocean.com) for hosting home and demo sites.
|
||||
- Thanks [KeyCDN](https://www.keycdn.com/) and [QiNiu](http://www.qiniu.com/) for providing CDN service.
|
||||
|
||||
## Contributors
|
||||
|
||||
- See [Maintainer](https://github.com/orgs/go-gitea/people)
|
||||
- See [Contributors](https://github.com/go-gitea/gitea/graphs/contributors) for full list of contributors.
|
||||
- See [Translators](conf/locale/TRANSLATORS) for public list of translators.
|
||||
## Contributing
|
||||
|
||||
Fork -> Patch -> Push -> Pull Request
|
||||
|
||||
## Authors
|
||||
|
||||
* [Maintainers](https://github.com/orgs/go-gitea/people)
|
||||
* [Contributors](https://github.com/go-gitea/gitea/graphs/contributors)
|
||||
* [Translators](conf/locale/TRANSLATORS)
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// +build cert
|
||||
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Copyright 2014 The Gogs Authors. All rights reserved.
|
||||
// Copyright 2016 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
// +build !cert
|
||||
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Copyright 2014 The Gogs Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
// CmdCert represents the available cert sub-command.
|
||||
var CmdCert = cli.Command{
|
||||
Name: "cert",
|
||||
Usage: "Generate self-signed certificate",
|
||||
Description: `Please use build tags "cert" to rebuild Gogs in order to have this ability`,
|
||||
Action: runCert,
|
||||
}
|
||||
|
||||
func runCert(*cli.Context) error {
|
||||
fmt.Println("Command cert not available, please use build tags 'cert' to rebuild.")
|
||||
os.Exit(1)
|
||||
|
||||
return nil
|
||||
}
|
||||
10
cmd/dump.go
10
cmd/dump.go
@@ -12,9 +12,9 @@ import (
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"github.com/Unknwon/cae/zip"
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"github.com/Unknwon/cae/zip"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
@@ -79,7 +79,6 @@ func runDump(ctx *cli.Context) error {
|
||||
log.Printf("Packing dump files...")
|
||||
z, err := zip.Create(fileName)
|
||||
if err != nil {
|
||||
os.Remove(fileName)
|
||||
log.Fatalf("Fail to create %s: %v", fileName, err)
|
||||
}
|
||||
|
||||
@@ -102,7 +101,7 @@ func runDump(ctx *cli.Context) error {
|
||||
}
|
||||
// FIXME: SSH key file.
|
||||
if err = z.Close(); err != nil {
|
||||
os.Remove(fileName)
|
||||
_ = os.Remove(fileName)
|
||||
log.Fatalf("Fail to save %s: %v", fileName, err)
|
||||
}
|
||||
|
||||
@@ -111,7 +110,10 @@ func runDump(ctx *cli.Context) error {
|
||||
}
|
||||
|
||||
log.Printf("Removing tmp work dir: %s", TmpWorkDir)
|
||||
os.RemoveAll(TmpWorkDir)
|
||||
|
||||
if err := os.RemoveAll(TmpWorkDir); err != nil {
|
||||
log.Fatalf("Fail to remove %s: %v", TmpWorkDir, err)
|
||||
}
|
||||
log.Printf("Finish dumping in file %s", fileName)
|
||||
|
||||
return nil
|
||||
|
||||
10
cmd/serve.go
10
cmd/serve.go
@@ -13,13 +13,13 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Unknwon/com"
|
||||
"code.gitea.io/git"
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/base"
|
||||
"code.gitea.io/gitea/modules/httplib"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"github.com/Unknwon/com"
|
||||
gouuid "github.com/satori/go.uuid"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
@@ -51,7 +51,9 @@ func setup(logPath string) {
|
||||
|
||||
if setting.UseSQLite3 || setting.UseTiDB {
|
||||
workDir, _ := setting.WorkDir()
|
||||
os.Chdir(workDir)
|
||||
if err := os.Chdir(workDir); err != nil {
|
||||
log.GitLogger.Fatal(4, "Fail to change directory %s: %v", workDir, err)
|
||||
}
|
||||
}
|
||||
|
||||
models.SetEngine()
|
||||
@@ -256,7 +258,11 @@ func runServ(c *cli.Context) error {
|
||||
}
|
||||
}
|
||||
|
||||
os.Setenv("GITEA_PUSHER_NAME", user.Name)
|
||||
|
||||
uuid := gouuid.NewV4().String()
|
||||
os.Setenv("GITEA_UUID", uuid)
|
||||
// Keep the old env variable name for backward compability
|
||||
os.Setenv("uuid", uuid)
|
||||
|
||||
// Special handle for Windows.
|
||||
|
||||
@@ -49,7 +49,7 @@ func runUpdate(c *cli.Context) error {
|
||||
}
|
||||
|
||||
task := models.UpdateTask{
|
||||
UUID: os.Getenv("uuid"),
|
||||
UUID: os.Getenv("GITEA_UUID"),
|
||||
RefName: args[0],
|
||||
OldCommitID: args[1],
|
||||
NewCommitID: args[2],
|
||||
|
||||
71
cmd/web.go
71
cmd/web.go
@@ -18,9 +18,10 @@ import (
|
||||
"code.gitea.io/git"
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/auth"
|
||||
"code.gitea.io/gitea/modules/bindata"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/options"
|
||||
"code.gitea.io/gitea/modules/public"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/template"
|
||||
"code.gitea.io/gitea/routers"
|
||||
@@ -123,11 +124,11 @@ func newMacaron() *macaron.Macaron {
|
||||
m.Use(gzip.Gziper())
|
||||
}
|
||||
if setting.Protocol == setting.FCGI {
|
||||
m.SetURLPrefix(setting.AppSubUrl)
|
||||
m.SetURLPrefix(setting.AppSubURL)
|
||||
}
|
||||
m.Use(macaron.Static(
|
||||
path.Join(setting.StaticRootPath, "public"),
|
||||
macaron.StaticOptions{
|
||||
m.Use(public.Static(
|
||||
&public.Options{
|
||||
Directory: path.Join(setting.StaticRootPath, "public"),
|
||||
SkipLogging: setting.DisableRouterLog,
|
||||
},
|
||||
))
|
||||
@@ -149,22 +150,29 @@ func newMacaron() *macaron.Macaron {
|
||||
models.InitMailRender(path.Join(setting.StaticRootPath, "templates/mail"),
|
||||
path.Join(setting.CustomPath, "templates/mail"), funcMap)
|
||||
|
||||
localeNames, err := bindata.AssetDir("conf/locale")
|
||||
localeNames, err := options.Dir("locale")
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(4, "Fail to list locale files: %v", err)
|
||||
}
|
||||
|
||||
localFiles := make(map[string][]byte)
|
||||
|
||||
for _, name := range localeNames {
|
||||
localFiles[name] = bindata.MustAsset("conf/locale/" + name)
|
||||
localFiles[name], err = options.Locale(name)
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(4, "Failed to load %s locale file. %v", name, err)
|
||||
}
|
||||
}
|
||||
|
||||
m.Use(i18n.I18n(i18n.Options{
|
||||
SubURL: setting.AppSubUrl,
|
||||
Files: localFiles,
|
||||
CustomDirectory: path.Join(setting.CustomPath, "conf/locale"),
|
||||
Langs: setting.Langs,
|
||||
Names: setting.Names,
|
||||
DefaultLang: "en-US",
|
||||
Redirect: true,
|
||||
SubURL: setting.AppSubURL,
|
||||
Files: localFiles,
|
||||
Langs: setting.Langs,
|
||||
Names: setting.Names,
|
||||
DefaultLang: "en-US",
|
||||
Redirect: true,
|
||||
}))
|
||||
m.Use(cache.Cacher(cache.Options{
|
||||
Adapter: setting.CacheAdapter,
|
||||
@@ -172,7 +180,7 @@ func newMacaron() *macaron.Macaron {
|
||||
Interval: setting.CacheInterval,
|
||||
}))
|
||||
m.Use(captcha.Captchaer(captcha.Options{
|
||||
SubURL: setting.AppSubUrl,
|
||||
SubURL: setting.AppSubURL,
|
||||
}))
|
||||
m.Use(session.Sessioner(setting.SessionConfig))
|
||||
m.Use(csrf.Csrfer(csrf.Options{
|
||||
@@ -180,11 +188,11 @@ func newMacaron() *macaron.Macaron {
|
||||
Cookie: setting.CSRFCookieName,
|
||||
SetCookie: true,
|
||||
Header: "X-Csrf-Token",
|
||||
CookiePath: setting.AppSubUrl,
|
||||
CookiePath: setting.AppSubURL,
|
||||
}))
|
||||
m.Use(toolbox.Toolboxer(m, toolbox.Options{
|
||||
HealthCheckFuncs: []*toolbox.HealthCheckFuncDesc{
|
||||
&toolbox.HealthCheckFuncDesc{
|
||||
{
|
||||
Desc: "Database connection",
|
||||
Func: models.Ping,
|
||||
},
|
||||
@@ -216,7 +224,7 @@ func runWeb(ctx *cli.Context) error {
|
||||
m.Get("/", ignSignIn, routers.Home)
|
||||
m.Group("/explore", func() {
|
||||
m.Get("", func(ctx *context.Context) {
|
||||
ctx.Redirect(setting.AppSubUrl + "/explore/repos")
|
||||
ctx.Redirect(setting.AppSubURL + "/explore/repos")
|
||||
})
|
||||
m.Get("/repos", routers.ExploreRepos)
|
||||
m.Get("/users", routers.ExploreUsers)
|
||||
@@ -280,7 +288,7 @@ func runWeb(ctx *cli.Context) error {
|
||||
|
||||
m.Group("/users", func() {
|
||||
m.Get("", admin.Users)
|
||||
m.Combo("/new").Get(admin.NewUser).Post(bindIgnErr(auth.AdminCrateUserForm{}), admin.NewUserPost)
|
||||
m.Combo("/new").Get(admin.NewUser).Post(bindIgnErr(auth.AdminCreateUserForm{}), admin.NewUserPost)
|
||||
m.Combo("/:userid").Get(admin.EditUser).Post(bindIgnErr(auth.AdminEditUserForm{}), admin.EditUserPost)
|
||||
m.Post("/:userid/delete", admin.DeleteUser)
|
||||
})
|
||||
@@ -336,11 +344,7 @@ func runWeb(ctx *cli.Context) error {
|
||||
}
|
||||
defer fr.Close()
|
||||
|
||||
ctx.Header().Set("Cache-Control", "public,max-age=86400")
|
||||
ctx.Header().Set("Content-Disposition", fmt.Sprintf(`inline; filename="%s"`, attach.Name))
|
||||
// Fix #312. Attachments with , in their name are not handled correctly by Google Chrome.
|
||||
// We must put the name in " manually.
|
||||
if err = repo.ServeData(ctx, "\""+attach.Name+"\"", fr); err != nil {
|
||||
if err = repo.ServeData(ctx, attach.Name, fr); err != nil {
|
||||
ctx.Handle(500, "ServeData", err)
|
||||
return
|
||||
}
|
||||
@@ -584,7 +588,7 @@ func runWeb(ctx *cli.Context) error {
|
||||
|
||||
m.Group("/pulls/:index", func() {
|
||||
m.Get("/commits", context.RepoRef(), repo.ViewPullCommits)
|
||||
m.Get("/files", context.RepoRef(), repo.SetEditorconfigIfExists, repo.ViewPullFiles)
|
||||
m.Get("/files", context.RepoRef(), repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.ViewPullFiles)
|
||||
m.Post("/merge", reqRepoWriter, repo.MergePullRequest)
|
||||
}, repo.MustAllowPulls)
|
||||
|
||||
@@ -592,12 +596,12 @@ func runWeb(ctx *cli.Context) error {
|
||||
m.Get("/src/*", repo.SetEditorconfigIfExists, repo.Home)
|
||||
m.Get("/raw/*", repo.SingleDownload)
|
||||
m.Get("/commits/*", repo.RefCommits)
|
||||
m.Get("/commit/:sha([a-f0-9]{7,40})$", repo.SetEditorconfigIfExists, repo.Diff)
|
||||
m.Get("/commit/:sha([a-f0-9]{7,40})$", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.Diff)
|
||||
m.Get("/forks", repo.Forks)
|
||||
}, context.RepoRef())
|
||||
m.Get("/commit/:sha([a-f0-9]{7,40})\\.:ext(patch|diff)", repo.RawDiff)
|
||||
|
||||
m.Get("/compare/:before([a-z0-9]{40})\\.\\.\\.:after([a-z0-9]{40})", repo.CompareDiff)
|
||||
m.Get("/compare/:before([a-z0-9]{40})\\.\\.\\.:after([a-z0-9]{40})", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.CompareDiff)
|
||||
}, ignSignIn, context.RepoAssignment(), repo.MustBeNotBare)
|
||||
m.Group("/:username/:reponame", func() {
|
||||
m.Get("/stars", repo.Stars)
|
||||
@@ -635,17 +639,17 @@ func runWeb(ctx *cli.Context) error {
|
||||
|
||||
// Flag for port number in case first time run conflict.
|
||||
if ctx.IsSet("port") {
|
||||
setting.AppUrl = strings.Replace(setting.AppUrl, setting.HTTPPort, ctx.String("port"), 1)
|
||||
setting.AppURL = strings.Replace(setting.AppURL, setting.HTTPPort, ctx.String("port"), 1)
|
||||
setting.HTTPPort = ctx.String("port")
|
||||
}
|
||||
|
||||
var listenAddr string
|
||||
if setting.Protocol == setting.UNIX_SOCKET {
|
||||
if setting.Protocol == setting.UnixSocket {
|
||||
listenAddr = fmt.Sprintf("%s", setting.HTTPAddr)
|
||||
} else {
|
||||
listenAddr = fmt.Sprintf("%s:%s", setting.HTTPAddr, setting.HTTPPort)
|
||||
}
|
||||
log.Info("Listen: %v://%s%s", setting.Protocol, listenAddr, setting.AppSubUrl)
|
||||
log.Info("Listen: %v://%s%s", setting.Protocol, listenAddr, setting.AppSubURL)
|
||||
|
||||
var err error
|
||||
switch setting.Protocol {
|
||||
@@ -656,9 +660,10 @@ func runWeb(ctx *cli.Context) error {
|
||||
err = server.ListenAndServeTLS(setting.CertFile, setting.KeyFile)
|
||||
case setting.FCGI:
|
||||
err = fcgi.Serve(nil, m)
|
||||
case setting.UNIX_SOCKET:
|
||||
os.Remove(listenAddr)
|
||||
|
||||
case setting.UnixSocket:
|
||||
if err := os.Remove(listenAddr); err != nil {
|
||||
log.Fatal(4, "Fail to remove unix socket directory %s: %v", listenAddr, err)
|
||||
}
|
||||
var listener *net.UnixListener
|
||||
listener, err = net.ListenUnix("unix", &net.UnixAddr{Name: listenAddr, Net: "unix"})
|
||||
if err != nil {
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
Execute following command in ROOT directory when anything is changed:
|
||||
|
||||
$ make bindata
|
||||
@@ -400,8 +400,8 @@ DEFAULT_INTERVAL = 8
|
||||
MAX_RESPONSE_ITEMS = 50
|
||||
|
||||
[i18n]
|
||||
LANGS = en-US,zh-CN,zh-HK,zh-TW,de-DE,fr-FR,nl-NL,lv-LV,ru-RU,ja-JP,es-ES,pt-BR,pl-PL,bg-BG,it-IT,fi-FI,tr-TR,cs-CZ,sr-SP
|
||||
NAMES = English,简体中文,繁體中文(香港),繁體中文(台湾),Deutsch,Français,Nederlands,Latviešu,Русский,日本語,Español,Português do Brasil,Polski,български,Italiano,Suomalainen,Türkçe,čeština,Српски
|
||||
LANGS = en-US,zh-CN,zh-HK,zh-TW,de-DE,fr-FR,nl-NL,lv-LV,ru-RU,ja-JP,es-ES,pt-BR,pl-PL,bg-BG,it-IT,fi-FI,tr-TR,cs-CZ,sr-SP,sv-SE
|
||||
NAMES = English,简体中文,繁體中文(香港),繁體中文(台湾),Deutsch,Français,Nederlands,Latviešu,Русский,日本語,Español,Português do Brasil,Polski,български,Italiano,Suomalainen,Türkçe,čeština,Српски,Svenska
|
||||
|
||||
; Used for datetimepicker
|
||||
[i18n.datelang]
|
||||
@@ -423,6 +423,8 @@ it-IT = it
|
||||
fi-FI = fi
|
||||
tr-TR = tr
|
||||
cs-CZ = cs-CZ
|
||||
sr-SP = sr
|
||||
sv-SE = sv
|
||||
|
||||
; Extension mapping to highlight class
|
||||
; e.g. .toml=ini
|
||||
|
||||
111
docker/README.md
111
docker/README.md
@@ -1,111 +0,0 @@
|
||||
# Docker for Gogs
|
||||
|
||||
Visit [Docker Hub](https://hub.docker.com/r/gogs/) see all available images and tags.
|
||||
|
||||
## Usage
|
||||
|
||||
To keep your data out of Docker container, we do a volume (`/var/gogs` -> `/data`) here, and you can change it based on your situation.
|
||||
|
||||
```
|
||||
# Pull image from Docker Hub.
|
||||
$ docker pull gogs/gogs
|
||||
|
||||
# Create local directory for volume.
|
||||
$ mkdir -p /var/gogs
|
||||
|
||||
# Use `docker run` for the first time.
|
||||
$ docker run --name=gogs -p 10022:22 -p 10080:3000 -v /var/gogs:/data gogs/gogs
|
||||
|
||||
# Use `docker start` if you have stopped it.
|
||||
$ docker start gogs
|
||||
```
|
||||
|
||||
Note: It is important to map the Gogs ssh service from the container to the host and set the appropriate SSH Port and URI settings when setting up Gogs for the first time. To access and clone Gogs Git repositories with the above configuration you would use: `git clone ssh://git@hostname:10022/username/myrepo.git` for example.
|
||||
|
||||
Files will be store in local path `/var/gogs` in my case.
|
||||
|
||||
Directory `/var/gogs` keeps Git repositories and Gogs data:
|
||||
|
||||
/var/gogs
|
||||
|-- git
|
||||
| |-- gogs-repositories
|
||||
|-- ssh
|
||||
| |-- # ssh public/private keys for Gogs
|
||||
|-- gogs
|
||||
|-- conf
|
||||
|-- data
|
||||
|-- log
|
||||
|
||||
### Volume with data container
|
||||
|
||||
If you're more comfortable with mounting data to a data container, the commands you execute at the first time will look like as follows:
|
||||
|
||||
```
|
||||
# Create data container
|
||||
docker run --name=gogs-data --entrypoint /bin/true gogs/gogs
|
||||
|
||||
# Use `docker run` for the first time.
|
||||
docker run --name=gogs --volumes-from gogs-data -p 10022:22 -p 10080:3000 gogs/gogs
|
||||
```
|
||||
|
||||
#### Using Docker 1.9 Volume command
|
||||
|
||||
```
|
||||
# Create docker volume.
|
||||
$ docker volume create --name gogs-data
|
||||
|
||||
# Use `docker run` for the first time.
|
||||
$ docker run --name=gogs -p 10022:22 -p 10080:3000 -v gogs-data:/data gogs/gogs
|
||||
```
|
||||
|
||||
## Settings
|
||||
|
||||
### Application
|
||||
|
||||
Most of settings are obvious and easy to understand, but there are some settings can be confusing by running Gogs inside Docker:
|
||||
|
||||
- **Repository Root Path**: keep it as default value `/home/git/gogs-repositories` because `start.sh` already made a symbolic link for you.
|
||||
- **Run User**: keep it as default value `git` because `start.sh` already setup a user with name `git`.
|
||||
- **Domain**: fill in with Docker container IP (e.g. `192.168.99.100`). But if you want to access your Gogs instance from a different physical machine, please fill in with the hostname or IP address of the Docker host machine.
|
||||
- **SSH Port**: Use the exposed port from Docker container. For example, your SSH server listens on `22` inside Docker, but you expose it by `10022:22`, then use `10022` for this value. **Builtin SSH server is not recommended inside Docker Container**
|
||||
- **HTTP Port**: Use port you want Gogs to listen on inside Docker container. For example, your Gogs listens on `3000` inside Docker, and you expose it by `10080:3000`, but you still use `3000` for this value.
|
||||
- **Application URL**: Use combination of **Domain** and **exposed HTTP Port** values (e.g. `http://192.168.99.100:10080/`).
|
||||
|
||||
Full documentation of application settings can be found [here](https://gogs.io/docs/advanced/configuration_cheat_sheet.html).
|
||||
|
||||
### Container options
|
||||
|
||||
This container have some options available via environment variables, these options are opt-in features that can help the administration of this container:
|
||||
|
||||
- **SOCAT_LINK**:
|
||||
- <u>Possible value:</u>
|
||||
`true`, `false`, `1`, `0`
|
||||
- <u>Default:</u>
|
||||
`true`
|
||||
- <u>Action:</u>
|
||||
Bind linked docker container to localhost socket using socat.
|
||||
Any exported port from a linked container will be binded to the matching port on localhost.
|
||||
- <u>Disclaimer:</u>
|
||||
As this option rely on the environment variable created by docker when a container is linked, this option should be deactivated in managed environment such as Rancher or Kubernetes (set to `0` or `false`)
|
||||
- **RUN_CROND**:
|
||||
- <u>Possible value:</u>
|
||||
`true`, `false`, `1`, `0`
|
||||
- <u>Default:</u>
|
||||
`false`
|
||||
- <u>Action:</u>
|
||||
Request crond to be run inside the container. Its default configuration will periodically run all scripts from `/etc/periodic/${period}` but custom crontabs can be added to `/var/spool/cron/crontabs/`.
|
||||
|
||||
## Upgrade
|
||||
|
||||
:exclamation::exclamation::exclamation:<span style="color: red">**Make sure you have volumed data to somewhere outside Docker container**</span>:exclamation::exclamation::exclamation:
|
||||
|
||||
Steps to upgrade Gogs with Docker:
|
||||
|
||||
- `docker pull gogs/gogs`
|
||||
- `docker stop gogs`
|
||||
- `docker rm gogs`
|
||||
- Finally, create container as the first time and don't forget to do same volume and port mapping.
|
||||
|
||||
## Known Issues
|
||||
|
||||
- The docker container can not currently be build on Raspberry 1 (armv6l) as our base image `alpine` does not have a `go` package available for this platform.
|
||||
@@ -1,36 +0,0 @@
|
||||
#!/bin/sh
|
||||
set -x
|
||||
set -e
|
||||
|
||||
# Set temp environment vars
|
||||
export GOPATH=/tmp/go
|
||||
export PATH=${PATH}:${GOPATH}/bin
|
||||
export GO15VENDOREXPERIMENT=1
|
||||
|
||||
# Install build deps
|
||||
apk --no-cache --no-progress add --virtual build-deps build-base linux-pam-dev go
|
||||
|
||||
# Install glide
|
||||
git clone -b 0.10.2 https://github.com/Masterminds/glide ${GOPATH}/src/github.com/Masterminds/glide
|
||||
cd ${GOPATH}/src/github.com/Masterminds/glide
|
||||
make build
|
||||
go install
|
||||
|
||||
|
||||
|
||||
# Build Gogs
|
||||
mkdir -p ${GOPATH}/src/github.com/gogits/
|
||||
ln -s /app/gogs/ ${GOPATH}/src/github.com/go-gitea/gitea
|
||||
cd ${GOPATH}/src/github.com/go-gitea/gitea
|
||||
glide install
|
||||
make build TAGS="sqlite cert pam"
|
||||
|
||||
# Cleanup GOPATH & vendoring dir
|
||||
rm -r $GOPATH /app/gogs/vendor
|
||||
|
||||
# Remove build deps
|
||||
apk --no-progress del build-deps
|
||||
|
||||
# Create git user for Gogs
|
||||
adduser -H -D -g 'Gogs Git User' git -h /data/git -s /bin/bash && passwd -u git
|
||||
echo "export GITEA_CUSTOM=${GITEA_CUSTOM}" >> /etc/profile
|
||||
@@ -13,4 +13,3 @@ ethers: db files
|
||||
rpc: db files
|
||||
|
||||
netgroup: nis
|
||||
|
||||
2
docker/etc/profile.d/gitea.sh
Executable file
2
docker/etc/profile.d/gitea.sh
Executable file
@@ -0,0 +1,2 @@
|
||||
#!/bin/bash
|
||||
export GITEA_CUSTOM=/data/gitea
|
||||
2
docker/etc/s6/.s6-svscan/finish
Executable file
2
docker/etc/s6/.s6-svscan/finish
Executable file
@@ -0,0 +1,2 @@
|
||||
#!/bin/bash
|
||||
exit 0
|
||||
2
docker/etc/s6/gitea/finish
Executable file
2
docker/etc/s6/gitea/finish
Executable file
@@ -0,0 +1,2 @@
|
||||
#!/bin/bash
|
||||
exit 0
|
||||
6
docker/etc/s6/gitea/run
Executable file
6
docker/etc/s6/gitea/run
Executable file
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
[[ -f ./setup ]] && source ./setup
|
||||
|
||||
pushd /app/gitea > /dev/null
|
||||
exec su-exec git /app/gitea/gitea web
|
||||
popd
|
||||
19
docker/etc/s6/gitea/setup
Executable file
19
docker/etc/s6/gitea/setup
Executable file
@@ -0,0 +1,19 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ ! -d /data/git/.ssh ]; then
|
||||
mkdir -p /data/git/.ssh
|
||||
chmod 700 /data/git/.ssh
|
||||
fi
|
||||
|
||||
if [ ! -f /data/git/.ssh/environment ]; then
|
||||
echo "GITEA_CUSTOM=/data/gitea" >| /data/git/.ssh/environment
|
||||
chmod 600 /data/git/.ssh/environment
|
||||
fi
|
||||
|
||||
if [ ! -f /data/gitea/conf/app.ini ]; then
|
||||
mkdir -p /data/gitea/conf
|
||||
cp /etc/templates/app.ini /data/gitea/conf/app.ini
|
||||
fi
|
||||
|
||||
chown -R git:git /data/gitea /app/gitea /data/git
|
||||
chmod 0755 /data/gitea /app/gitea /data/git
|
||||
2
docker/etc/s6/openssh/finish
Executable file
2
docker/etc/s6/openssh/finish
Executable file
@@ -0,0 +1,2 @@
|
||||
#!/bin/bash
|
||||
exit 0
|
||||
6
docker/etc/s6/openssh/run
Executable file
6
docker/etc/s6/openssh/run
Executable file
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
[[ -f ./setup ]] && source ./setup
|
||||
|
||||
pushd /root > /dev/null
|
||||
exec su-exec root /usr/sbin/sshd -E /var/log/sshd.log -D
|
||||
popd
|
||||
29
docker/etc/s6/openssh/setup
Executable file
29
docker/etc/s6/openssh/setup
Executable file
@@ -0,0 +1,29 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ ! -d /data/ssh ]; then
|
||||
mkdir -p /data/ssh
|
||||
fi
|
||||
|
||||
if [ ! -f /data/ssh/ssh_host_ed25519_key ]; then
|
||||
echo "Generating /data/ssh/ssh_host_ed25519_key..."
|
||||
ssh-keygen -t ed25519 -b 4096 -f /data/ssh/ssh_host_ed25519_key -N "" > /dev/null
|
||||
fi
|
||||
|
||||
if [ ! -f /data/ssh/ssh_host_rsa_key ]; then
|
||||
echo "Generating /data/ssh/ssh_host_rsa_key..."
|
||||
ssh-keygen -t rsa -b 2048 -f /data/ssh/ssh_host_rsa_key -N "" > /dev/null
|
||||
fi
|
||||
|
||||
if [ ! -f /data/ssh/ssh_host_dsa_key ]; then
|
||||
echo "Generating /data/ssh/ssh_host_dsa_key..."
|
||||
ssh-keygen -t dsa -f /data/ssh/ssh_host_dsa_key -N "" > /dev/null
|
||||
fi
|
||||
|
||||
if [ ! -f /data/ssh/ssh_host_ecdsa_key ]; then
|
||||
echo "Generating /data/ssh/ssh_host_ecdsa_key..."
|
||||
ssh-keygen -t ecdsa -b 256 -f /data/ssh/ssh_host_ecdsa_key -N "" > /dev/null
|
||||
fi
|
||||
|
||||
chown root:root /data/ssh/*
|
||||
chmod 0700 /data/ssh
|
||||
chmod 0600 /data/ssh/*
|
||||
2
docker/etc/s6/syslogd/finish
Executable file
2
docker/etc/s6/syslogd/finish
Executable file
@@ -0,0 +1,2 @@
|
||||
#!/bin/bash
|
||||
exit 0
|
||||
6
docker/etc/s6/syslogd/run
Executable file
6
docker/etc/s6/syslogd/run
Executable file
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
[[ -f ./setup ]] && source ./setup
|
||||
|
||||
pushd /root > /dev/null
|
||||
exec su-exec root /sbin/syslogd -nS -O-
|
||||
popd
|
||||
1
docker/etc/s6/syslogd/setup
Executable file
1
docker/etc/s6/syslogd/setup
Executable file
@@ -0,0 +1 @@
|
||||
#!/bin/bash
|
||||
@@ -1,16 +1,33 @@
|
||||
Port 22
|
||||
Protocol 2
|
||||
|
||||
AddressFamily any
|
||||
ListenAddress 0.0.0.0
|
||||
ListenAddress ::
|
||||
Protocol 2
|
||||
|
||||
LogLevel INFO
|
||||
|
||||
HostKey /data/ssh/ssh_host_ed25519_key
|
||||
HostKey /data/ssh/ssh_host_rsa_key
|
||||
HostKey /data/ssh/ssh_host_dsa_key
|
||||
HostKey /data/ssh/ssh_host_ecdsa_key
|
||||
HostKey /data/ssh/ssh_host_ed25519_key
|
||||
PermitRootLogin no
|
||||
AuthorizedKeysFile .ssh/authorized_keys
|
||||
PasswordAuthentication no
|
||||
UsePrivilegeSeparation no
|
||||
|
||||
AuthorizedKeysFile .ssh/authorized_keys
|
||||
|
||||
UseDNS no
|
||||
AllowAgentForwarding no
|
||||
AllowTcpForwarding no
|
||||
PrintMotd no
|
||||
PrintLastLog no
|
||||
|
||||
PermitUserEnvironment yes
|
||||
PermitRootLogin no
|
||||
ChallengeResponseAuthentication no
|
||||
PasswordAuthentication no
|
||||
PermitEmptyPasswords no
|
||||
|
||||
AllowUsers git
|
||||
|
||||
Banner none
|
||||
Subsystem sftp /usr/lib/ssh/sftp-server
|
||||
UsePrivilegeSeparation no
|
||||
24
docker/etc/templates/app.ini
Normal file
24
docker/etc/templates/app.ini
Normal file
@@ -0,0 +1,24 @@
|
||||
[repository]
|
||||
ROOT = /data/git/repositories
|
||||
|
||||
[repository.upload]
|
||||
TEMP_PATH = /data/gitea/uploads
|
||||
|
||||
[server]
|
||||
APP_DATA_PATH = /data/gitea
|
||||
|
||||
[database]
|
||||
HOST = mysql:3306
|
||||
PATH = /data/gitea/gitea.db
|
||||
|
||||
[session]
|
||||
PROVIDER_CONFIG = /data/gitea/sessions
|
||||
|
||||
[picture]
|
||||
AVATAR_UPLOAD_PATH = /data/gitea/avatars
|
||||
|
||||
[attachment]
|
||||
PATH = /data/gitea/attachments
|
||||
|
||||
[log]
|
||||
ROOT_PATH = /data/gitea/log
|
||||
@@ -1,5 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Cleanup SOCAT services and s6 event folder
|
||||
rm -rf $(find /app/gogs/docker/s6/ -name 'event')
|
||||
rm -rf /app/gogs/docker/s6/SOCAT_*
|
||||
@@ -1,9 +0,0 @@
|
||||
#!/bin/sh
|
||||
# Crontabs are located by default in /var/spool/cron/crontabs/
|
||||
# The default configuration is also calling all the scripts in /etc/periodic/${period}
|
||||
|
||||
if test -f ./setup; then
|
||||
source ./setup
|
||||
fi
|
||||
|
||||
exec gosu root /usr/sbin/crond -fS
|
||||
@@ -1,8 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
if test -f ./setup; then
|
||||
source ./setup
|
||||
fi
|
||||
|
||||
export USER=git
|
||||
exec gosu $USER /app/gogs/gogs web
|
||||
@@ -1,23 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
if ! test -d ~git/.ssh; then
|
||||
mkdir -p ~git/.ssh
|
||||
chmod 700 ~git/.ssh
|
||||
fi
|
||||
|
||||
if ! test -f ~git/.ssh/environment; then
|
||||
echo "GITEA_CUSTOM=${GITEA_CUSTOM}" > ~git/.ssh/environment
|
||||
chmod 600 ~git/.ssh/environment
|
||||
fi
|
||||
|
||||
cd /app/gogs
|
||||
|
||||
# Link volumed data with app data
|
||||
ln -sf /data/gogs/log ./log
|
||||
ln -sf /data/gogs/data ./data
|
||||
|
||||
# Backward Compatibility with Gogs Container v0.6.15
|
||||
ln -sf /data/git /home/git
|
||||
|
||||
chown -R git:git /data /app/gogs ~git/
|
||||
chmod 0755 /data /data/gogs ~git/
|
||||
@@ -1,7 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
if test -f ./setup; then
|
||||
source ./setup
|
||||
fi
|
||||
|
||||
exec gosu root /usr/sbin/sshd -D -f /app/gogs/docker/sshd_config
|
||||
@@ -1,23 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Check if host keys are present, else create them
|
||||
if ! test -f /data/ssh/ssh_host_rsa_key; then
|
||||
ssh-keygen -q -f /data/ssh/ssh_host_rsa_key -N '' -t rsa
|
||||
fi
|
||||
|
||||
if ! test -f /data/ssh/ssh_host_dsa_key; then
|
||||
ssh-keygen -q -f /data/ssh/ssh_host_dsa_key -N '' -t dsa
|
||||
fi
|
||||
|
||||
if ! test -f /data/ssh/ssh_host_ecdsa_key; then
|
||||
ssh-keygen -q -f /data/ssh/ssh_host_ecdsa_key -N '' -t ecdsa
|
||||
fi
|
||||
|
||||
if ! test -f /data/ssh/ssh_host_ed25519_key; then
|
||||
ssh-keygen -q -f /data/ssh/ssh_host_ed25519_key -N '' -t ed25519
|
||||
fi
|
||||
|
||||
# Set correct right to ssh keys
|
||||
chown -R root:root /data/ssh/*
|
||||
chmod 0700 /data/ssh
|
||||
chmod 0600 /data/ssh/*
|
||||
@@ -1,7 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
if test -f ./setup; then
|
||||
source ./setup
|
||||
fi
|
||||
|
||||
exec gosu root /sbin/syslogd -nS -O-
|
||||
@@ -1,65 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
create_socat_links() {
|
||||
# Bind linked docker container to localhost socket using socat
|
||||
USED_PORT="3000:22"
|
||||
while read NAME ADDR PORT; do
|
||||
if test -z "$NAME$ADDR$PORT"; then
|
||||
continue
|
||||
elif echo $USED_PORT | grep -E "(^|:)$PORT($|:)" > /dev/null; then
|
||||
echo "init:socat | Can't bind linked container ${NAME} to localhost, port ${PORT} already in use" 1>&2
|
||||
else
|
||||
SERV_FOLDER=/app/gogs/docker/s6/SOCAT_${NAME}_${PORT}
|
||||
mkdir -p ${SERV_FOLDER}
|
||||
CMD="socat -ls TCP4-LISTEN:${PORT},fork,reuseaddr TCP4:${ADDR}:${PORT}"
|
||||
echo -e "#!/bin/sh\nexec $CMD" > ${SERV_FOLDER}/run
|
||||
chmod +x ${SERV_FOLDER}/run
|
||||
USED_PORT="${USED_PORT}:${PORT}"
|
||||
echo "init:socat | Linked container ${NAME} will be binded to localhost on port ${PORT}" 1>&2
|
||||
fi
|
||||
done << EOT
|
||||
$(env | sed -En 's|(.*)_PORT_([0-9]+)_TCP=tcp://(.*):([0-9]+)|\1 \3 \4|p')
|
||||
EOT
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
# Cleanup SOCAT services and s6 event folder
|
||||
# On start and on shutdown in case container has been killed
|
||||
rm -rf $(find /app/gogs/docker/s6/ -name 'event')
|
||||
rm -rf /app/gogs/docker/s6/SOCAT_*
|
||||
}
|
||||
|
||||
create_volume_subfolder() {
|
||||
# Create VOLUME subfolder
|
||||
for f in /data/gogs/data /data/gogs/conf /data/gogs/log /data/git /data/ssh; do
|
||||
if ! test -d $f; then
|
||||
mkdir -p $f
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
cleanup
|
||||
create_volume_subfolder
|
||||
|
||||
LINK=$(echo "$SOCAT_LINK" | tr '[:upper:]' '[:lower:]')
|
||||
if [ "$LINK" = "false" -o "$LINK" = "0" ]; then
|
||||
echo "init:socat | Will not try to create socat links as requested" 1>&2
|
||||
else
|
||||
create_socat_links
|
||||
fi
|
||||
|
||||
CROND=$(echo "$RUN_CROND" | tr '[:upper:]' '[:lower:]')
|
||||
if [ "$CROND" = "true" -o "$CROND" = "1" ]; then
|
||||
echo "init:crond | Cron Daemon (crond) will be run as requested by s6" 1>&2
|
||||
rm -f /app/gogs/docker/s6/crond/down
|
||||
else
|
||||
# Tell s6 not to run the crond service
|
||||
touch /app/gogs/docker/s6/crond/down
|
||||
fi
|
||||
|
||||
# Exec CMD or S6 by default if nothing present
|
||||
if [ $# -gt 0 ];then
|
||||
exec "$@"
|
||||
else
|
||||
exec /bin/s6-svscan /app/gogs/docker/s6/
|
||||
fi
|
||||
11
docker/usr/bin/entrypoint
Executable file
11
docker/usr/bin/entrypoint
Executable file
@@ -0,0 +1,11 @@
|
||||
#!/bin/sh
|
||||
|
||||
for FOLDER in /data/gitea/conf /data/gitea/log /data/git /data/ssh; do
|
||||
mkdir -p ${FOLDER}
|
||||
done
|
||||
|
||||
if [ $# -gt 0 ]; then
|
||||
exec "$@"
|
||||
else
|
||||
exec /bin/s6-svscan /etc/s6
|
||||
fi
|
||||
9
main.go
9
main.go
@@ -7,10 +7,11 @@
|
||||
package main // import "code.gitea.io/gitea"
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"runtime"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
|
||||
"code.gitea.io/gitea/cmd"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"github.com/urfave/cli"
|
||||
@@ -38,5 +39,9 @@ func main() {
|
||||
cmd.CmdAdmin,
|
||||
}
|
||||
app.Flags = append(app.Flags, []cli.Flag{}...)
|
||||
log.Fatal(app.Run(os.Args))
|
||||
err := app.Run(os.Args)
|
||||
if err != nil {
|
||||
log.Fatal(4, "Fail to run app with %s: %v", os.Args, err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -10,14 +10,20 @@ import (
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
)
|
||||
|
||||
// AccessMode specifies the users access mode
|
||||
type AccessMode int
|
||||
|
||||
const (
|
||||
AccessModeNone AccessMode = iota // 0
|
||||
AccessModeRead // 1
|
||||
AccessModeWrite // 2
|
||||
AccessModeAdmin // 3
|
||||
AccessModeOwner // 4
|
||||
// AccessModeNone no access
|
||||
AccessModeNone AccessMode = iota // 0
|
||||
// AccessModeRead read access
|
||||
AccessModeRead // 1
|
||||
// AccessModeWrite write access
|
||||
AccessModeWrite // 2
|
||||
// AccessModeAdmin admin access
|
||||
AccessModeAdmin // 3
|
||||
// AccessModeOwner owner access
|
||||
AccessModeOwner // 4
|
||||
)
|
||||
|
||||
func (mode AccessMode) String() string {
|
||||
@@ -57,21 +63,21 @@ type Access struct {
|
||||
Mode AccessMode
|
||||
}
|
||||
|
||||
func accessLevel(e Engine, u *User, repo *Repository) (AccessMode, error) {
|
||||
func accessLevel(e Engine, user *User, repo *Repository) (AccessMode, error) {
|
||||
mode := AccessModeNone
|
||||
if !repo.IsPrivate {
|
||||
mode = AccessModeRead
|
||||
}
|
||||
|
||||
if u == nil {
|
||||
if user == nil {
|
||||
return mode, nil
|
||||
}
|
||||
|
||||
if u.ID == repo.OwnerID {
|
||||
if user.ID == repo.OwnerID {
|
||||
return AccessModeOwner, nil
|
||||
}
|
||||
|
||||
a := &Access{UserID: u.ID, RepoID: repo.ID}
|
||||
a := &Access{UserID: user.ID, RepoID: repo.ID}
|
||||
if has, err := e.Get(a); !has || err != nil {
|
||||
return mode, err
|
||||
}
|
||||
@@ -80,24 +86,24 @@ func accessLevel(e Engine, u *User, repo *Repository) (AccessMode, error) {
|
||||
|
||||
// AccessLevel returns the Access a user has to a repository. Will return NoneAccess if the
|
||||
// user does not have access. User can be nil!
|
||||
func AccessLevel(u *User, repo *Repository) (AccessMode, error) {
|
||||
return accessLevel(x, u, repo)
|
||||
func AccessLevel(user *User, repo *Repository) (AccessMode, error) {
|
||||
return accessLevel(x, user, repo)
|
||||
}
|
||||
|
||||
func hasAccess(e Engine, u *User, repo *Repository, testMode AccessMode) (bool, error) {
|
||||
mode, err := accessLevel(e, u, repo)
|
||||
func hasAccess(e Engine, user *User, repo *Repository, testMode AccessMode) (bool, error) {
|
||||
mode, err := accessLevel(e, user, repo)
|
||||
return testMode <= mode, err
|
||||
}
|
||||
|
||||
// HasAccess returns true if someone has the request access level. User can be nil!
|
||||
func HasAccess(u *User, repo *Repository, testMode AccessMode) (bool, error) {
|
||||
return hasAccess(x, u, repo, testMode)
|
||||
func HasAccess(user *User, repo *Repository, testMode AccessMode) (bool, error) {
|
||||
return hasAccess(x, user, repo, testMode)
|
||||
}
|
||||
|
||||
// GetRepositoryAccesses finds all repositories with their access mode where a user has access but does not own.
|
||||
func (u *User) GetRepositoryAccesses() (map[*Repository]AccessMode, error) {
|
||||
func (user *User) GetRepositoryAccesses() (map[*Repository]AccessMode, error) {
|
||||
accesses := make([]*Access, 0, 10)
|
||||
if err := x.Find(&accesses, &Access{UserID: u.ID}); err != nil {
|
||||
if err := x.Find(&accesses, &Access{UserID: user.ID}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -113,7 +119,7 @@ func (u *User) GetRepositoryAccesses() (map[*Repository]AccessMode, error) {
|
||||
}
|
||||
if err = repo.GetOwner(); err != nil {
|
||||
return nil, err
|
||||
} else if repo.OwnerID == u.ID {
|
||||
} else if repo.OwnerID == user.ID {
|
||||
continue
|
||||
}
|
||||
repos[repo] = access.Mode
|
||||
@@ -245,6 +251,6 @@ func (repo *Repository) recalculateAccesses(e Engine) error {
|
||||
}
|
||||
|
||||
// RecalculateAccesses recalculates all accesses for repository.
|
||||
func (r *Repository) RecalculateAccesses() error {
|
||||
return r.recalculateAccesses(x)
|
||||
func (repo *Repository) RecalculateAccesses() error {
|
||||
return repo.recalculateAccesses(x)
|
||||
}
|
||||
|
||||
106
models/action.go
106
models/action.go
@@ -24,8 +24,10 @@ import (
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
)
|
||||
|
||||
// ActionType represents the type of an action.
|
||||
type ActionType int
|
||||
|
||||
// Possible action types.
|
||||
const (
|
||||
ActionCreateRepo ActionType = iota + 1 // 1
|
||||
ActionRenameRepo // 2
|
||||
@@ -45,12 +47,13 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
// Same as Github. See https://help.github.com/articles/closing-issues-via-commit-messages
|
||||
IssueCloseKeywords = []string{"close", "closes", "closed", "fix", "fixes", "fixed", "resolve", "resolves", "resolved"}
|
||||
IssueReopenKeywords = []string{"reopen", "reopens", "reopened"}
|
||||
// Same as Github. See
|
||||
// https://help.github.com/articles/closing-issues-via-commit-messages
|
||||
issueCloseKeywords = []string{"close", "closes", "closed", "fix", "fixes", "fixed", "resolve", "resolves", "resolved"}
|
||||
issueReopenKeywords = []string{"reopen", "reopens", "reopened"}
|
||||
|
||||
IssueCloseKeywordsPat, IssueReopenKeywordsPat *regexp.Regexp
|
||||
IssueReferenceKeywordsPat *regexp.Regexp
|
||||
issueCloseKeywordsPat, issueReopenKeywordsPat *regexp.Regexp
|
||||
issueReferenceKeywordsPat *regexp.Regexp
|
||||
)
|
||||
|
||||
func assembleKeywordsPattern(words []string) string {
|
||||
@@ -58,13 +61,14 @@ func assembleKeywordsPattern(words []string) string {
|
||||
}
|
||||
|
||||
func init() {
|
||||
IssueCloseKeywordsPat = regexp.MustCompile(assembleKeywordsPattern(IssueCloseKeywords))
|
||||
IssueReopenKeywordsPat = regexp.MustCompile(assembleKeywordsPattern(IssueReopenKeywords))
|
||||
IssueReferenceKeywordsPat = regexp.MustCompile(`(?i)(?:)(^| )\S+`)
|
||||
issueCloseKeywordsPat = regexp.MustCompile(assembleKeywordsPattern(issueCloseKeywords))
|
||||
issueReopenKeywordsPat = regexp.MustCompile(assembleKeywordsPattern(issueReopenKeywords))
|
||||
issueReferenceKeywordsPat = regexp.MustCompile(`(?i)(?:)(^| )\S+`)
|
||||
}
|
||||
|
||||
// Action represents user operation type and other information to repository.,
|
||||
// it implemented interface base.Actioner so that can be used in template render.
|
||||
// Action represents user operation type and other information to
|
||||
// repository. It implemented interface base.Actioner so that can be
|
||||
// used in template render.
|
||||
type Action struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
UserID int64 // Receiver user id.
|
||||
@@ -82,10 +86,13 @@ type Action struct {
|
||||
CreatedUnix int64
|
||||
}
|
||||
|
||||
// BeforeInsert will be invoked by XORM before inserting a record
|
||||
// representing this object.
|
||||
func (a *Action) BeforeInsert() {
|
||||
a.CreatedUnix = time.Now().Unix()
|
||||
}
|
||||
|
||||
// AfterSet updates the webhook object upon setting a column.
|
||||
func (a *Action) AfterSet(colName string, _ xorm.Cell) {
|
||||
switch colName {
|
||||
case "created_unix":
|
||||
@@ -93,65 +100,87 @@ func (a *Action) AfterSet(colName string, _ xorm.Cell) {
|
||||
}
|
||||
}
|
||||
|
||||
// GetOpType gets the ActionType of this action.
|
||||
// TODO: change return type to ActionType ?
|
||||
func (a *Action) GetOpType() int {
|
||||
return int(a.OpType)
|
||||
}
|
||||
|
||||
// GetActUserName gets the action's user name.
|
||||
func (a *Action) GetActUserName() string {
|
||||
return a.ActUserName
|
||||
}
|
||||
|
||||
// ShortActUserName gets the action's user name trimmed to max 20
|
||||
// chars.
|
||||
func (a *Action) ShortActUserName() string {
|
||||
return base.EllipsisString(a.ActUserName, 20)
|
||||
}
|
||||
|
||||
// GetRepoUserName returns the name of the action repository owner.
|
||||
func (a *Action) GetRepoUserName() string {
|
||||
return a.RepoUserName
|
||||
}
|
||||
|
||||
// ShortRepoUserName returns the name of the action repository owner
|
||||
// trimmed to max 20 chars.
|
||||
func (a *Action) ShortRepoUserName() string {
|
||||
return base.EllipsisString(a.RepoUserName, 20)
|
||||
}
|
||||
|
||||
// GetRepoName returns the name of the action repository.
|
||||
func (a *Action) GetRepoName() string {
|
||||
return a.RepoName
|
||||
}
|
||||
|
||||
// ShortRepoName returns the name of the action repository
|
||||
// trimmed to max 33 chars.
|
||||
func (a *Action) ShortRepoName() string {
|
||||
return base.EllipsisString(a.RepoName, 33)
|
||||
}
|
||||
|
||||
// GetRepoPath returns the virtual path to the action repository.
|
||||
func (a *Action) GetRepoPath() string {
|
||||
return path.Join(a.RepoUserName, a.RepoName)
|
||||
}
|
||||
|
||||
// ShortRepoPath returns the virtual path to the action repository
|
||||
// trimed to max 20 + 1 + 33 chars.
|
||||
func (a *Action) ShortRepoPath() string {
|
||||
return path.Join(a.ShortRepoUserName(), a.ShortRepoName())
|
||||
}
|
||||
|
||||
// GetRepoLink returns relative link to action repository.
|
||||
func (a *Action) GetRepoLink() string {
|
||||
if len(setting.AppSubUrl) > 0 {
|
||||
return path.Join(setting.AppSubUrl, a.GetRepoPath())
|
||||
if len(setting.AppSubURL) > 0 {
|
||||
return path.Join(setting.AppSubURL, a.GetRepoPath())
|
||||
}
|
||||
return "/" + a.GetRepoPath()
|
||||
}
|
||||
|
||||
// GetBranch returns the action's repository branch.
|
||||
func (a *Action) GetBranch() string {
|
||||
return a.RefName
|
||||
}
|
||||
|
||||
// GetContent returns the action's content.
|
||||
func (a *Action) GetContent() string {
|
||||
return a.Content
|
||||
}
|
||||
|
||||
// GetCreate returns the action creation time.
|
||||
func (a *Action) GetCreate() time.Time {
|
||||
return a.Created
|
||||
}
|
||||
|
||||
// GetIssueInfos returns a list of issues associated with
|
||||
// the action.
|
||||
func (a *Action) GetIssueInfos() []string {
|
||||
return strings.SplitN(a.Content, "|", 2)
|
||||
}
|
||||
|
||||
// GetIssueTitle returns the title of first issue associated
|
||||
// with the action.
|
||||
func (a *Action) GetIssueTitle() string {
|
||||
index := com.StrTo(a.GetIssueInfos()[0]).MustInt64()
|
||||
issue, err := GetIssueByIndex(a.RepoID, index)
|
||||
@@ -162,6 +191,8 @@ func (a *Action) GetIssueTitle() string {
|
||||
return issue.Title
|
||||
}
|
||||
|
||||
// GetIssueContent returns the content of first issue associated with
|
||||
// this action.
|
||||
func (a *Action) GetIssueContent() string {
|
||||
index := com.StrTo(a.GetIssueInfos()[0]).MustInt64()
|
||||
issue, err := GetIssueByIndex(a.RepoID, index)
|
||||
@@ -221,6 +252,7 @@ func issueIndexTrimRight(c rune) bool {
|
||||
return !unicode.IsDigit(c)
|
||||
}
|
||||
|
||||
// PushCommit represents a commit in a push operation.
|
||||
type PushCommit struct {
|
||||
Sha1 string
|
||||
Message string
|
||||
@@ -231,6 +263,7 @@ type PushCommit struct {
|
||||
Timestamp time.Time
|
||||
}
|
||||
|
||||
// PushCommits represents list of commits in a push operation.
|
||||
type PushCommits struct {
|
||||
Len int
|
||||
Commits []*PushCommit
|
||||
@@ -239,13 +272,16 @@ type PushCommits struct {
|
||||
avatars map[string]string
|
||||
}
|
||||
|
||||
// NewPushCommits creates a new PushCommits object.
|
||||
func NewPushCommits() *PushCommits {
|
||||
return &PushCommits{
|
||||
avatars: make(map[string]string),
|
||||
}
|
||||
}
|
||||
|
||||
func (pc *PushCommits) ToApiPayloadCommits(repoLink string) []*api.PayloadCommit {
|
||||
// ToAPIPayloadCommits converts a PushCommits object to
|
||||
// api.PayloadCommit format.
|
||||
func (pc *PushCommits) ToAPIPayloadCommits(repoLink string) []*api.PayloadCommit {
|
||||
commits := make([]*api.PayloadCommit, len(pc.Commits))
|
||||
for i, commit := range pc.Commits {
|
||||
authorUsername := ""
|
||||
@@ -281,21 +317,21 @@ func (pc *PushCommits) ToApiPayloadCommits(repoLink string) []*api.PayloadCommit
|
||||
|
||||
// AvatarLink tries to match user in database with e-mail
|
||||
// in order to show custom avatar, and falls back to general avatar link.
|
||||
func (push *PushCommits) AvatarLink(email string) string {
|
||||
_, ok := push.avatars[email]
|
||||
func (pc *PushCommits) AvatarLink(email string) string {
|
||||
_, ok := pc.avatars[email]
|
||||
if !ok {
|
||||
u, err := GetUserByEmail(email)
|
||||
if err != nil {
|
||||
push.avatars[email] = base.AvatarLink(email)
|
||||
pc.avatars[email] = base.AvatarLink(email)
|
||||
if !IsErrUserNotExist(err) {
|
||||
log.Error(4, "GetUserByEmail: %v", err)
|
||||
}
|
||||
} else {
|
||||
push.avatars[email] = u.RelAvatarLink()
|
||||
pc.avatars[email] = u.RelAvatarLink()
|
||||
}
|
||||
}
|
||||
|
||||
return push.avatars[email]
|
||||
return pc.avatars[email]
|
||||
}
|
||||
|
||||
// UpdateIssuesCommit checks if issues are manipulated by commit message.
|
||||
@@ -305,7 +341,7 @@ func UpdateIssuesCommit(doer *User, repo *Repository, commits []*PushCommit) err
|
||||
c := commits[i]
|
||||
|
||||
refMarked := make(map[int64]bool)
|
||||
for _, ref := range IssueReferenceKeywordsPat.FindAllString(c.Message, -1) {
|
||||
for _, ref := range issueReferenceKeywordsPat.FindAllString(c.Message, -1) {
|
||||
ref = ref[strings.IndexByte(ref, byte(' '))+1:]
|
||||
ref = strings.TrimRightFunc(ref, issueIndexTrimRight)
|
||||
|
||||
@@ -343,7 +379,7 @@ func UpdateIssuesCommit(doer *User, repo *Repository, commits []*PushCommit) err
|
||||
|
||||
refMarked = make(map[int64]bool)
|
||||
// FIXME: can merge this one and next one to a common function.
|
||||
for _, ref := range IssueCloseKeywordsPat.FindAllString(c.Message, -1) {
|
||||
for _, ref := range issueCloseKeywordsPat.FindAllString(c.Message, -1) {
|
||||
ref = ref[strings.IndexByte(ref, byte(' '))+1:]
|
||||
ref = strings.TrimRightFunc(ref, issueIndexTrimRight)
|
||||
|
||||
@@ -383,7 +419,7 @@ func UpdateIssuesCommit(doer *User, repo *Repository, commits []*PushCommit) err
|
||||
}
|
||||
|
||||
// It is conflict to have close and reopen at same time, so refsMarkd doesn't need to reinit here.
|
||||
for _, ref := range IssueReopenKeywordsPat.FindAllString(c.Message, -1) {
|
||||
for _, ref := range issueReopenKeywordsPat.FindAllString(c.Message, -1) {
|
||||
ref = ref[strings.IndexByte(ref, byte(' '))+1:]
|
||||
ref = strings.TrimRightFunc(ref, issueIndexTrimRight)
|
||||
|
||||
@@ -425,6 +461,7 @@ func UpdateIssuesCommit(doer *User, repo *Repository, commits []*PushCommit) err
|
||||
return nil
|
||||
}
|
||||
|
||||
// CommitRepoActionOptions represent options of a new commit action.
|
||||
type CommitRepoActionOptions struct {
|
||||
PusherName string
|
||||
RepoOwnerID int64
|
||||
@@ -435,7 +472,8 @@ type CommitRepoActionOptions struct {
|
||||
Commits *PushCommits
|
||||
}
|
||||
|
||||
// CommitRepoAction adds new commit actio to the repository, and prepare corresponding webhooks.
|
||||
// CommitRepoAction adds new commit action to the repository, and prepare
|
||||
// corresponding webhooks.
|
||||
func CommitRepoAction(opts CommitRepoActionOptions) error {
|
||||
pusher, err := GetUserByName(opts.PusherName)
|
||||
if err != nil {
|
||||
@@ -502,14 +540,16 @@ func CommitRepoAction(opts CommitRepoActionOptions) error {
|
||||
|
||||
apiPusher := pusher.APIFormat()
|
||||
apiRepo := repo.APIFormat(nil)
|
||||
|
||||
var shaSum string
|
||||
switch opType {
|
||||
case ActionCommitRepo: // Push
|
||||
if err = PrepareWebhooks(repo, HookEventPush, &api.PushPayload{
|
||||
Ref: opts.RefFullName,
|
||||
Before: opts.OldCommitID,
|
||||
After: opts.NewCommitID,
|
||||
CompareURL: setting.AppUrl + opts.Commits.CompareURL,
|
||||
Commits: opts.Commits.ToApiPayloadCommits(repo.HTMLURL()),
|
||||
CompareURL: setting.AppURL + opts.Commits.CompareURL,
|
||||
Commits: opts.Commits.ToAPIPayloadCommits(repo.HTMLURL()),
|
||||
Repo: apiRepo,
|
||||
Pusher: apiPusher,
|
||||
Sender: apiPusher,
|
||||
@@ -518,8 +558,17 @@ func CommitRepoAction(opts CommitRepoActionOptions) error {
|
||||
}
|
||||
|
||||
if isNewBranch {
|
||||
gitRepo, err := git.OpenRepository(repo.RepoPath())
|
||||
if err != nil {
|
||||
log.Error(4, "OpenRepository[%s]: %v", repo.RepoPath(), err)
|
||||
}
|
||||
shaSum, err = gitRepo.GetBranchCommitID(opts.RefFullName)
|
||||
if err != nil {
|
||||
log.Error(4, "GetBranchCommitID[%s]: %v", opts.RefFullName, err)
|
||||
}
|
||||
return PrepareWebhooks(repo, HookEventCreate, &api.CreatePayload{
|
||||
Ref: refName,
|
||||
Sha: shaSum,
|
||||
RefType: "branch",
|
||||
Repo: apiRepo,
|
||||
Sender: apiPusher,
|
||||
@@ -527,8 +576,17 @@ func CommitRepoAction(opts CommitRepoActionOptions) error {
|
||||
}
|
||||
|
||||
case ActionPushTag: // Create
|
||||
gitRepo, err := git.OpenRepository(repo.RepoPath())
|
||||
if err != nil {
|
||||
log.Error(4, "OpenRepository[%s]: %v", repo.RepoPath(), err)
|
||||
}
|
||||
shaSum, err = gitRepo.GetTagCommitID(opts.RefFullName)
|
||||
if err != nil {
|
||||
log.Error(4, "GetTagCommitID[%s]: %v", opts.RefFullName, err)
|
||||
}
|
||||
return PrepareWebhooks(repo, HookEventCreate, &api.CreatePayload{
|
||||
Ref: refName,
|
||||
Sha: shaSum,
|
||||
RefType: "tag",
|
||||
Repo: apiRepo,
|
||||
Sender: apiPusher,
|
||||
|
||||
@@ -14,14 +14,15 @@ import (
|
||||
"github.com/Unknwon/com"
|
||||
"github.com/go-xorm/xorm"
|
||||
|
||||
"code.gitea.io/gitea/modules/base"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
)
|
||||
|
||||
//NoticeType describes the notice type
|
||||
type NoticeType int
|
||||
|
||||
const (
|
||||
//NoticeRepository type
|
||||
NoticeRepository NoticeType = iota + 1
|
||||
)
|
||||
|
||||
@@ -34,10 +35,12 @@ type Notice struct {
|
||||
CreatedUnix int64
|
||||
}
|
||||
|
||||
// BeforeInsert is invoked from XORM before inserting an object of this type.
|
||||
func (n *Notice) BeforeInsert() {
|
||||
n.CreatedUnix = time.Now().Unix()
|
||||
}
|
||||
|
||||
// AfterSet is invoked from XORM after setting the value of a field of this object.
|
||||
func (n *Notice) AfterSet(colName string, _ xorm.Cell) {
|
||||
switch colName {
|
||||
case "created_unix":
|
||||
@@ -131,7 +134,7 @@ func DeleteNoticesByIDs(ids []int64) error {
|
||||
return nil
|
||||
}
|
||||
_, err := x.
|
||||
Where("id IN (" + strings.Join(base.Int64sToStrings(ids), ",") + ")").
|
||||
In("id", ids).
|
||||
Delete(new(Notice))
|
||||
return err
|
||||
}
|
||||
|
||||
112
models/error.go
112
models/error.go
@@ -8,10 +8,12 @@ import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// ErrNameReserved represents a "reserved name" error.
|
||||
type ErrNameReserved struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
// IsErrNameReserved checks if an error is a ErrNameReserved.
|
||||
func IsErrNameReserved(err error) bool {
|
||||
_, ok := err.(ErrNameReserved)
|
||||
return ok
|
||||
@@ -21,10 +23,13 @@ func (err ErrNameReserved) Error() string {
|
||||
return fmt.Sprintf("name is reserved [name: %s]", err.Name)
|
||||
}
|
||||
|
||||
// ErrNamePatternNotAllowed represents a "pattern not allowed" error.
|
||||
type ErrNamePatternNotAllowed struct {
|
||||
Pattern string
|
||||
}
|
||||
|
||||
// IsErrNamePatternNotAllowed checks if an error is an
|
||||
// ErrNamePatternNotAllowed.
|
||||
func IsErrNamePatternNotAllowed(err error) bool {
|
||||
_, ok := err.(ErrNamePatternNotAllowed)
|
||||
return ok
|
||||
@@ -41,10 +46,12 @@ func (err ErrNamePatternNotAllowed) Error() string {
|
||||
// |______//____ >\___ >__|
|
||||
// \/ \/
|
||||
|
||||
// ErrUserAlreadyExist represents a "user already exists" error.
|
||||
type ErrUserAlreadyExist struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
// IsErrUserAlreadyExist checks if an error is a ErrUserAlreadyExists.
|
||||
func IsErrUserAlreadyExist(err error) bool {
|
||||
_, ok := err.(ErrUserAlreadyExist)
|
||||
return ok
|
||||
@@ -54,24 +61,29 @@ func (err ErrUserAlreadyExist) Error() string {
|
||||
return fmt.Sprintf("user already exists [name: %s]", err.Name)
|
||||
}
|
||||
|
||||
// ErrUserNotExist represents a "UserNotExist" kind of error.
|
||||
type ErrUserNotExist struct {
|
||||
UID int64
|
||||
Name string
|
||||
UID int64
|
||||
Name string
|
||||
KeyID int64
|
||||
}
|
||||
|
||||
// IsErrUserNotExist checks if an error is a ErrUserNotExist.
|
||||
func IsErrUserNotExist(err error) bool {
|
||||
_, ok := err.(ErrUserNotExist)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrUserNotExist) Error() string {
|
||||
return fmt.Sprintf("user does not exist [uid: %d, name: %s]", err.UID, err.Name)
|
||||
return fmt.Sprintf("user does not exist [uid: %d, name: %s, keyid: %d]", err.UID, err.Name, err.KeyID)
|
||||
}
|
||||
|
||||
// ErrEmailAlreadyUsed represents a "EmailAlreadyUsed" kind of error.
|
||||
type ErrEmailAlreadyUsed struct {
|
||||
Email string
|
||||
}
|
||||
|
||||
// IsErrEmailAlreadyUsed checks if an error is a ErrEmailAlreadyUsed.
|
||||
func IsErrEmailAlreadyUsed(err error) bool {
|
||||
_, ok := err.(ErrEmailAlreadyUsed)
|
||||
return ok
|
||||
@@ -81,10 +93,12 @@ func (err ErrEmailAlreadyUsed) Error() string {
|
||||
return fmt.Sprintf("e-mail has been used [email: %s]", err.Email)
|
||||
}
|
||||
|
||||
// ErrUserOwnRepos represents a "UserOwnRepos" kind of error.
|
||||
type ErrUserOwnRepos struct {
|
||||
UID int64
|
||||
}
|
||||
|
||||
// IsErrUserOwnRepos checks if an error is a ErrUserOwnRepos.
|
||||
func IsErrUserOwnRepos(err error) bool {
|
||||
_, ok := err.(ErrUserOwnRepos)
|
||||
return ok
|
||||
@@ -94,10 +108,12 @@ func (err ErrUserOwnRepos) Error() string {
|
||||
return fmt.Sprintf("user still has ownership of repositories [uid: %d]", err.UID)
|
||||
}
|
||||
|
||||
// ErrUserHasOrgs represents a "UserHasOrgs" kind of error.
|
||||
type ErrUserHasOrgs struct {
|
||||
UID int64
|
||||
}
|
||||
|
||||
// IsErrUserHasOrgs checks if an error is a ErrUserHasOrgs.
|
||||
func IsErrUserHasOrgs(err error) bool {
|
||||
_, ok := err.(ErrUserHasOrgs)
|
||||
return ok
|
||||
@@ -107,10 +123,12 @@ func (err ErrUserHasOrgs) Error() string {
|
||||
return fmt.Sprintf("user still has membership of organizations [uid: %d]", err.UID)
|
||||
}
|
||||
|
||||
// ErrReachLimitOfRepo represents a "ReachLimitOfRepo" kind of error.
|
||||
type ErrReachLimitOfRepo struct {
|
||||
Limit int
|
||||
}
|
||||
|
||||
// IsErrReachLimitOfRepo checks if an error is a ErrReachLimitOfRepo.
|
||||
func IsErrReachLimitOfRepo(err error) bool {
|
||||
_, ok := err.(ErrReachLimitOfRepo)
|
||||
return ok
|
||||
@@ -127,10 +145,12 @@ func (err ErrReachLimitOfRepo) Error() string {
|
||||
// \__/\ / |__|__|_ \__|
|
||||
// \/ \/
|
||||
|
||||
// ErrWikiAlreadyExist represents a "WikiAlreadyExist" kind of error.
|
||||
type ErrWikiAlreadyExist struct {
|
||||
Title string
|
||||
}
|
||||
|
||||
// IsErrWikiAlreadyExist checks if an error is a ErrWikiAlreadyExist.
|
||||
func IsErrWikiAlreadyExist(err error) bool {
|
||||
_, ok := err.(ErrWikiAlreadyExist)
|
||||
return ok
|
||||
@@ -147,10 +167,12 @@ func (err ErrWikiAlreadyExist) Error() string {
|
||||
// |____| |____/|___ /____/__|\___ > |____|__ \___ > ____|
|
||||
// \/ \/ \/ \/\/
|
||||
|
||||
// ErrKeyUnableVerify represents a "KeyUnableVerify" kind of error.
|
||||
type ErrKeyUnableVerify struct {
|
||||
Result string
|
||||
}
|
||||
|
||||
// IsErrKeyUnableVerify checks if an error is a ErrKeyUnableVerify.
|
||||
func IsErrKeyUnableVerify(err error) bool {
|
||||
_, ok := err.(ErrKeyUnableVerify)
|
||||
return ok
|
||||
@@ -160,10 +182,12 @@ func (err ErrKeyUnableVerify) Error() string {
|
||||
return fmt.Sprintf("Unable to verify key content [result: %s]", err.Result)
|
||||
}
|
||||
|
||||
// ErrKeyNotExist represents a "KeyNotExist" kind of error.
|
||||
type ErrKeyNotExist struct {
|
||||
ID int64
|
||||
}
|
||||
|
||||
// IsErrKeyNotExist checks if an error is a ErrKeyNotExist.
|
||||
func IsErrKeyNotExist(err error) bool {
|
||||
_, ok := err.(ErrKeyNotExist)
|
||||
return ok
|
||||
@@ -173,11 +197,13 @@ func (err ErrKeyNotExist) Error() string {
|
||||
return fmt.Sprintf("public key does not exist [id: %d]", err.ID)
|
||||
}
|
||||
|
||||
// ErrKeyAlreadyExist represents a "KeyAlreadyExist" kind of error.
|
||||
type ErrKeyAlreadyExist struct {
|
||||
OwnerID int64
|
||||
Content string
|
||||
}
|
||||
|
||||
// IsErrKeyAlreadyExist checks if an error is a ErrKeyAlreadyExist.
|
||||
func IsErrKeyAlreadyExist(err error) bool {
|
||||
_, ok := err.(ErrKeyAlreadyExist)
|
||||
return ok
|
||||
@@ -187,11 +213,13 @@ func (err ErrKeyAlreadyExist) Error() string {
|
||||
return fmt.Sprintf("public key already exists [owner_id: %d, content: %s]", err.OwnerID, err.Content)
|
||||
}
|
||||
|
||||
// ErrKeyNameAlreadyUsed represents a "KeyNameAlreadyUsed" kind of error.
|
||||
type ErrKeyNameAlreadyUsed struct {
|
||||
OwnerID int64
|
||||
Name string
|
||||
}
|
||||
|
||||
// IsErrKeyNameAlreadyUsed checks if an error is a ErrKeyNameAlreadyUsed.
|
||||
func IsErrKeyNameAlreadyUsed(err error) bool {
|
||||
_, ok := err.(ErrKeyNameAlreadyUsed)
|
||||
return ok
|
||||
@@ -201,12 +229,14 @@ func (err ErrKeyNameAlreadyUsed) Error() string {
|
||||
return fmt.Sprintf("public key already exists [owner_id: %d, name: %s]", err.OwnerID, err.Name)
|
||||
}
|
||||
|
||||
// ErrKeyAccessDenied represents a "KeyAccessDenied" kind of error.
|
||||
type ErrKeyAccessDenied struct {
|
||||
UserID int64
|
||||
KeyID int64
|
||||
Note string
|
||||
}
|
||||
|
||||
// IsErrKeyAccessDenied checks if an error is a ErrKeyAccessDenied.
|
||||
func IsErrKeyAccessDenied(err error) bool {
|
||||
_, ok := err.(ErrKeyAccessDenied)
|
||||
return ok
|
||||
@@ -217,12 +247,14 @@ func (err ErrKeyAccessDenied) Error() string {
|
||||
err.UserID, err.KeyID, err.Note)
|
||||
}
|
||||
|
||||
// ErrDeployKeyNotExist represents a "DeployKeyNotExist" kind of error.
|
||||
type ErrDeployKeyNotExist struct {
|
||||
ID int64
|
||||
KeyID int64
|
||||
RepoID int64
|
||||
}
|
||||
|
||||
// IsErrDeployKeyNotExist checks if an error is a ErrDeployKeyNotExist.
|
||||
func IsErrDeployKeyNotExist(err error) bool {
|
||||
_, ok := err.(ErrDeployKeyNotExist)
|
||||
return ok
|
||||
@@ -232,11 +264,13 @@ func (err ErrDeployKeyNotExist) Error() string {
|
||||
return fmt.Sprintf("Deploy key does not exist [id: %d, key_id: %d, repo_id: %d]", err.ID, err.KeyID, err.RepoID)
|
||||
}
|
||||
|
||||
// ErrDeployKeyAlreadyExist represents a "DeployKeyAlreadyExist" kind of error.
|
||||
type ErrDeployKeyAlreadyExist struct {
|
||||
KeyID int64
|
||||
RepoID int64
|
||||
}
|
||||
|
||||
// IsErrDeployKeyAlreadyExist checks if an error is a ErrDeployKeyAlreadyExist.
|
||||
func IsErrDeployKeyAlreadyExist(err error) bool {
|
||||
_, ok := err.(ErrDeployKeyAlreadyExist)
|
||||
return ok
|
||||
@@ -246,11 +280,13 @@ func (err ErrDeployKeyAlreadyExist) Error() string {
|
||||
return fmt.Sprintf("public key already exists [key_id: %d, repo_id: %d]", err.KeyID, err.RepoID)
|
||||
}
|
||||
|
||||
// ErrDeployKeyNameAlreadyUsed represents a "DeployKeyNameAlreadyUsed" kind of error.
|
||||
type ErrDeployKeyNameAlreadyUsed struct {
|
||||
RepoID int64
|
||||
Name string
|
||||
}
|
||||
|
||||
// IsErrDeployKeyNameAlreadyUsed checks if an error is a ErrDeployKeyNameAlreadyUsed.
|
||||
func IsErrDeployKeyNameAlreadyUsed(err error) bool {
|
||||
_, ok := err.(ErrDeployKeyNameAlreadyUsed)
|
||||
return ok
|
||||
@@ -267,10 +303,12 @@ func (err ErrDeployKeyNameAlreadyUsed) Error() string {
|
||||
// \____|__ /\___ >___ >___ >____ >____ > |____| \____/|__|_ \\___ >___| /
|
||||
// \/ \/ \/ \/ \/ \/ \/ \/ \/
|
||||
|
||||
// ErrAccessTokenNotExist represents a "AccessTokenNotExist" kind of error.
|
||||
type ErrAccessTokenNotExist struct {
|
||||
SHA string
|
||||
}
|
||||
|
||||
// IsErrAccessTokenNotExist checks if an error is a ErrAccessTokenNotExist.
|
||||
func IsErrAccessTokenNotExist(err error) bool {
|
||||
_, ok := err.(ErrAccessTokenNotExist)
|
||||
return ok
|
||||
@@ -280,9 +318,11 @@ func (err ErrAccessTokenNotExist) Error() string {
|
||||
return fmt.Sprintf("access token does not exist [sha: %s]", err.SHA)
|
||||
}
|
||||
|
||||
// ErrAccessTokenEmpty represents a "AccessTokenEmpty" kind of error.
|
||||
type ErrAccessTokenEmpty struct {
|
||||
}
|
||||
|
||||
// IsErrAccessTokenEmpty checks if an error is a ErrAccessTokenEmpty.
|
||||
func IsErrAccessTokenEmpty(err error) bool {
|
||||
_, ok := err.(ErrAccessTokenEmpty)
|
||||
return ok
|
||||
@@ -299,10 +339,12 @@ func (err ErrAccessTokenEmpty) Error() string {
|
||||
// \_______ /__| \___ (____ /___| /__/_____ \(____ /__| |__|\____/|___| /
|
||||
// \/ /_____/ \/ \/ \/ \/ \/
|
||||
|
||||
// ErrLastOrgOwner represents a "LastOrgOwner" kind of error.
|
||||
type ErrLastOrgOwner struct {
|
||||
UID int64
|
||||
}
|
||||
|
||||
// IsErrLastOrgOwner checks if an error is a ErrLastOrgOwner.
|
||||
func IsErrLastOrgOwner(err error) bool {
|
||||
_, ok := err.(ErrLastOrgOwner)
|
||||
return ok
|
||||
@@ -319,12 +361,14 @@ func (err ErrLastOrgOwner) Error() string {
|
||||
// |____|_ /\___ > __/ \____/____ >__||__| \____/|__| / ____|
|
||||
// \/ \/|__| \/ \/
|
||||
|
||||
// ErrRepoNotExist represents a "RepoNotExist" kind of error.
|
||||
type ErrRepoNotExist struct {
|
||||
ID int64
|
||||
UID int64
|
||||
Name string
|
||||
}
|
||||
|
||||
// IsErrRepoNotExist checks if an error is a ErrRepoNotExist.
|
||||
func IsErrRepoNotExist(err error) bool {
|
||||
_, ok := err.(ErrRepoNotExist)
|
||||
return ok
|
||||
@@ -334,11 +378,13 @@ func (err ErrRepoNotExist) Error() string {
|
||||
return fmt.Sprintf("repository does not exist [id: %d, uid: %d, name: %s]", err.ID, err.UID, err.Name)
|
||||
}
|
||||
|
||||
// ErrRepoAlreadyExist represents a "RepoAlreadyExist" kind of error.
|
||||
type ErrRepoAlreadyExist struct {
|
||||
Uname string
|
||||
Name string
|
||||
}
|
||||
|
||||
// IsErrRepoAlreadyExist checks if an error is a ErrRepoAlreadyExist.
|
||||
func IsErrRepoAlreadyExist(err error) bool {
|
||||
_, ok := err.(ErrRepoAlreadyExist)
|
||||
return ok
|
||||
@@ -348,12 +394,14 @@ func (err ErrRepoAlreadyExist) Error() string {
|
||||
return fmt.Sprintf("repository already exists [uname: %s, name: %s]", err.Uname, err.Name)
|
||||
}
|
||||
|
||||
// ErrInvalidCloneAddr represents a "InvalidCloneAddr" kind of error.
|
||||
type ErrInvalidCloneAddr struct {
|
||||
IsURLError bool
|
||||
IsInvalidPath bool
|
||||
IsPermissionDenied bool
|
||||
}
|
||||
|
||||
// IsErrInvalidCloneAddr checks if an error is a ErrInvalidCloneAddr.
|
||||
func IsErrInvalidCloneAddr(err error) bool {
|
||||
_, ok := err.(ErrInvalidCloneAddr)
|
||||
return ok
|
||||
@@ -364,10 +412,12 @@ func (err ErrInvalidCloneAddr) Error() string {
|
||||
err.IsURLError, err.IsInvalidPath, err.IsPermissionDenied)
|
||||
}
|
||||
|
||||
// ErrUpdateTaskNotExist represents a "UpdateTaskNotExist" kind of error.
|
||||
type ErrUpdateTaskNotExist struct {
|
||||
UUID string
|
||||
}
|
||||
|
||||
// IsErrUpdateTaskNotExist checks if an error is a ErrUpdateTaskNotExist.
|
||||
func IsErrUpdateTaskNotExist(err error) bool {
|
||||
_, ok := err.(ErrUpdateTaskNotExist)
|
||||
return ok
|
||||
@@ -377,10 +427,12 @@ func (err ErrUpdateTaskNotExist) Error() string {
|
||||
return fmt.Sprintf("update task does not exist [uuid: %s]", err.UUID)
|
||||
}
|
||||
|
||||
// ErrReleaseAlreadyExist represents a "ReleaseAlreadyExist" kind of error.
|
||||
type ErrReleaseAlreadyExist struct {
|
||||
TagName string
|
||||
}
|
||||
|
||||
// IsErrReleaseAlreadyExist checks if an error is a ErrReleaseAlreadyExist.
|
||||
func IsErrReleaseAlreadyExist(err error) bool {
|
||||
_, ok := err.(ErrReleaseAlreadyExist)
|
||||
return ok
|
||||
@@ -390,11 +442,13 @@ func (err ErrReleaseAlreadyExist) Error() string {
|
||||
return fmt.Sprintf("release tag already exist [tag_name: %s]", err.TagName)
|
||||
}
|
||||
|
||||
// ErrReleaseNotExist represents a "ReleaseNotExist" kind of error.
|
||||
type ErrReleaseNotExist struct {
|
||||
ID int64
|
||||
TagName string
|
||||
}
|
||||
|
||||
// IsErrReleaseNotExist checks if an error is a ErrReleaseNotExist.
|
||||
func IsErrReleaseNotExist(err error) bool {
|
||||
_, ok := err.(ErrReleaseNotExist)
|
||||
return ok
|
||||
@@ -404,10 +458,12 @@ func (err ErrReleaseNotExist) Error() string {
|
||||
return fmt.Sprintf("release tag does not exist [id: %d, tag_name: %s]", err.ID, err.TagName)
|
||||
}
|
||||
|
||||
// ErrInvalidTagName represents a "InvalidTagName" kind of error.
|
||||
type ErrInvalidTagName struct {
|
||||
TagName string
|
||||
}
|
||||
|
||||
// IsErrInvalidTagName checks if an error is a ErrInvalidTagName.
|
||||
func IsErrInvalidTagName(err error) bool {
|
||||
_, ok := err.(ErrInvalidTagName)
|
||||
return ok
|
||||
@@ -417,10 +473,12 @@ func (err ErrInvalidTagName) Error() string {
|
||||
return fmt.Sprintf("release tag name is not valid [tag_name: %s]", err.TagName)
|
||||
}
|
||||
|
||||
// ErrRepoFileAlreadyExist represents a "RepoFileAlreadyExist" kind of error.
|
||||
type ErrRepoFileAlreadyExist struct {
|
||||
FileName string
|
||||
}
|
||||
|
||||
// IsErrRepoFileAlreadyExist checks if an error is a ErrRepoFileAlreadyExist.
|
||||
func IsErrRepoFileAlreadyExist(err error) bool {
|
||||
_, ok := err.(ErrRepoFileAlreadyExist)
|
||||
return ok
|
||||
@@ -437,10 +495,12 @@ func (err ErrRepoFileAlreadyExist) Error() string {
|
||||
// |______ / |__| (____ /___| /\___ >___| /
|
||||
// \/ \/ \/ \/ \/
|
||||
|
||||
// ErrBranchNotExist represents a "BranchNotExist" kind of error.
|
||||
type ErrBranchNotExist struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
// IsErrBranchNotExist checks if an error is a ErrBranchNotExist.
|
||||
func IsErrBranchNotExist(err error) bool {
|
||||
_, ok := err.(ErrBranchNotExist)
|
||||
return ok
|
||||
@@ -457,10 +517,12 @@ func (err ErrBranchNotExist) Error() string {
|
||||
// \__/\ / \___ >___ /___| /\____/ \____/|__|_ \
|
||||
// \/ \/ \/ \/ \/
|
||||
|
||||
// ErrWebhookNotExist represents a "WebhookNotExist" kind of error.
|
||||
type ErrWebhookNotExist struct {
|
||||
ID int64
|
||||
}
|
||||
|
||||
// IsErrWebhookNotExist checks if an error is a ErrWebhookNotExist.
|
||||
func IsErrWebhookNotExist(err error) bool {
|
||||
_, ok := err.(ErrWebhookNotExist)
|
||||
return ok
|
||||
@@ -477,12 +539,14 @@ func (err ErrWebhookNotExist) Error() string {
|
||||
// |___/____ >____ >____/ \___ >
|
||||
// \/ \/ \/
|
||||
|
||||
// ErrIssueNotExist represents a "IssueNotExist" kind of error.
|
||||
type ErrIssueNotExist struct {
|
||||
ID int64
|
||||
RepoID int64
|
||||
Index int64
|
||||
}
|
||||
|
||||
// IsErrIssueNotExist checks if an error is a ErrIssueNotExist.
|
||||
func IsErrIssueNotExist(err error) bool {
|
||||
_, ok := err.(ErrIssueNotExist)
|
||||
return ok
|
||||
@@ -499,6 +563,7 @@ func (err ErrIssueNotExist) Error() string {
|
||||
// |____| |____/|____/____/____|_ /\___ >__ |____/ \___ >____ > |__|
|
||||
// \/ \/ |__| \/ \/
|
||||
|
||||
// ErrPullRequestNotExist represents a "PullRequestNotExist" kind of error.
|
||||
type ErrPullRequestNotExist struct {
|
||||
ID int64
|
||||
IssueID int64
|
||||
@@ -508,6 +573,7 @@ type ErrPullRequestNotExist struct {
|
||||
BaseBranch string
|
||||
}
|
||||
|
||||
// IsErrPullRequestNotExist checks if an error is a ErrPullRequestNotExist.
|
||||
func IsErrPullRequestNotExist(err error) bool {
|
||||
_, ok := err.(ErrPullRequestNotExist)
|
||||
return ok
|
||||
@@ -518,6 +584,28 @@ func (err ErrPullRequestNotExist) Error() string {
|
||||
err.ID, err.IssueID, err.HeadRepoID, err.BaseRepoID, err.HeadBarcnh, err.BaseBranch)
|
||||
}
|
||||
|
||||
// ErrPullRequestAlreadyExists represents a "PullRequestAlreadyExists"-error
|
||||
type ErrPullRequestAlreadyExists struct {
|
||||
ID int64
|
||||
IssueID int64
|
||||
HeadRepoID int64
|
||||
BaseRepoID int64
|
||||
HeadBranch string
|
||||
BaseBranch string
|
||||
}
|
||||
|
||||
// IsErrPullRequestAlreadyExists checks if an error is a ErrPullRequestAlreadyExists.
|
||||
func IsErrPullRequestAlreadyExists(err error) bool {
|
||||
_, ok := err.(ErrPullRequestAlreadyExists)
|
||||
return ok
|
||||
}
|
||||
|
||||
// Error does pretty-printing :D
|
||||
func (err ErrPullRequestAlreadyExists) Error() string {
|
||||
return fmt.Sprintf("pull request already exists for these targets [id: %d, issue_id: %d, head_repo_id: %d, base_repo_id: %d, head_branch: %s, base_branch: %s]",
|
||||
err.ID, err.IssueID, err.HeadRepoID, err.BaseRepoID, err.HeadBranch, err.BaseBranch)
|
||||
}
|
||||
|
||||
// _________ __
|
||||
// \_ ___ \ ____ _____ _____ ____ _____/ |_
|
||||
// / \ \/ / _ \ / \ / \_/ __ \ / \ __\
|
||||
@@ -525,11 +613,13 @@ func (err ErrPullRequestNotExist) Error() string {
|
||||
// \______ /\____/|__|_| /__|_| /\___ >___| /__|
|
||||
// \/ \/ \/ \/ \/
|
||||
|
||||
// ErrCommentNotExist represents a "CommentNotExist" kind of error.
|
||||
type ErrCommentNotExist struct {
|
||||
ID int64
|
||||
IssueID int64
|
||||
}
|
||||
|
||||
// IsErrCommentNotExist checks if an error is a ErrCommentNotExist.
|
||||
func IsErrCommentNotExist(err error) bool {
|
||||
_, ok := err.(ErrCommentNotExist)
|
||||
return ok
|
||||
@@ -546,11 +636,13 @@ func (err ErrCommentNotExist) Error() string {
|
||||
// |_______ (____ /___ /\___ >____/
|
||||
// \/ \/ \/ \/
|
||||
|
||||
// ErrLabelNotExist represents a "LabelNotExist" kind of error.
|
||||
type ErrLabelNotExist struct {
|
||||
LabelID int64
|
||||
RepoID int64
|
||||
}
|
||||
|
||||
// IsErrLabelNotExist checks if an error is a ErrLabelNotExist.
|
||||
func IsErrLabelNotExist(err error) bool {
|
||||
_, ok := err.(ErrLabelNotExist)
|
||||
return ok
|
||||
@@ -567,11 +659,13 @@ func (err ErrLabelNotExist) Error() string {
|
||||
// \____|__ /__|____/\___ >____ > |__| \____/|___| /\___ >
|
||||
// \/ \/ \/ \/ \/
|
||||
|
||||
// ErrMilestoneNotExist represents a "MilestoneNotExist" kind of error.
|
||||
type ErrMilestoneNotExist struct {
|
||||
ID int64
|
||||
RepoID int64
|
||||
}
|
||||
|
||||
// IsErrMilestoneNotExist checks if an error is a ErrMilestoneNotExist.
|
||||
func IsErrMilestoneNotExist(err error) bool {
|
||||
_, ok := err.(ErrMilestoneNotExist)
|
||||
return ok
|
||||
@@ -588,11 +682,13 @@ func (err ErrMilestoneNotExist) Error() string {
|
||||
// \____|__ /__| |__| (____ /\___ >___| /__|_| /\___ >___| /__|
|
||||
// \/ \/ \/ \/ \/ \/ \/
|
||||
|
||||
// ErrAttachmentNotExist represents a "AttachmentNotExist" kind of error.
|
||||
type ErrAttachmentNotExist struct {
|
||||
ID int64
|
||||
UUID string
|
||||
}
|
||||
|
||||
// IsErrAttachmentNotExist checks if an error is a ErrAttachmentNotExist.
|
||||
func IsErrAttachmentNotExist(err error) bool {
|
||||
_, ok := err.(ErrAttachmentNotExist)
|
||||
return ok
|
||||
@@ -609,10 +705,12 @@ func (err ErrAttachmentNotExist) Error() string {
|
||||
// |_______ \____/\___ /|__|___| / /_______ /\____/|____/ |__| \___ >___ >
|
||||
// \/ /_____/ \/ \/ \/ \/
|
||||
|
||||
// ErrLoginSourceNotExist represents a "LoginSourceNotExist" kind of error.
|
||||
type ErrLoginSourceNotExist struct {
|
||||
ID int64
|
||||
}
|
||||
|
||||
// IsErrLoginSourceNotExist checks if an error is a ErrLoginSourceNotExist.
|
||||
func IsErrLoginSourceNotExist(err error) bool {
|
||||
_, ok := err.(ErrLoginSourceNotExist)
|
||||
return ok
|
||||
@@ -622,10 +720,12 @@ func (err ErrLoginSourceNotExist) Error() string {
|
||||
return fmt.Sprintf("login source does not exist [id: %d]", err.ID)
|
||||
}
|
||||
|
||||
// ErrLoginSourceAlreadyExist represents a "LoginSourceAlreadyExist" kind of error.
|
||||
type ErrLoginSourceAlreadyExist struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
// IsErrLoginSourceAlreadyExist checks if an error is a ErrLoginSourceAlreadyExist.
|
||||
func IsErrLoginSourceAlreadyExist(err error) bool {
|
||||
_, ok := err.(ErrLoginSourceAlreadyExist)
|
||||
return ok
|
||||
@@ -635,10 +735,12 @@ func (err ErrLoginSourceAlreadyExist) Error() string {
|
||||
return fmt.Sprintf("login source already exists [name: %s]", err.Name)
|
||||
}
|
||||
|
||||
// ErrLoginSourceInUse represents a "LoginSourceInUse" kind of error.
|
||||
type ErrLoginSourceInUse struct {
|
||||
ID int64
|
||||
}
|
||||
|
||||
// IsErrLoginSourceInUse checks if an error is a ErrLoginSourceInUse.
|
||||
func IsErrLoginSourceInUse(err error) bool {
|
||||
_, ok := err.(ErrLoginSourceInUse)
|
||||
return ok
|
||||
@@ -655,11 +757,13 @@ func (err ErrLoginSourceInUse) Error() string {
|
||||
// |____| \___ >____ /__|_| /
|
||||
// \/ \/ \/
|
||||
|
||||
// ErrTeamAlreadyExist represents a "TeamAlreadyExist" kind of error.
|
||||
type ErrTeamAlreadyExist struct {
|
||||
OrgID int64
|
||||
Name string
|
||||
}
|
||||
|
||||
// IsErrTeamAlreadyExist checks if an error is a ErrTeamAlreadyExist.
|
||||
func IsErrTeamAlreadyExist(err error) bool {
|
||||
_, ok := err.(ErrTeamAlreadyExist)
|
||||
return ok
|
||||
@@ -677,11 +781,13 @@ func (err ErrTeamAlreadyExist) Error() string {
|
||||
// |__| \/ \/
|
||||
//
|
||||
|
||||
// ErrUploadNotExist represents a "UploadNotExist" kind of error.
|
||||
type ErrUploadNotExist struct {
|
||||
ID int64
|
||||
UUID string
|
||||
}
|
||||
|
||||
// IsErrUploadNotExist checks if an error is a ErrUploadNotExist.
|
||||
func IsErrUploadNotExist(err error) bool {
|
||||
_, ok := err.(ErrAttachmentNotExist)
|
||||
return ok
|
||||
|
||||
@@ -28,8 +28,10 @@ import (
|
||||
"golang.org/x/text/transform"
|
||||
)
|
||||
|
||||
// DiffLineType represents the type of a DiffLine.
|
||||
type DiffLineType uint8
|
||||
|
||||
// DiffLineType possible values.
|
||||
const (
|
||||
DiffLinePlain DiffLineType = iota + 1
|
||||
DiffLineAdd
|
||||
@@ -37,8 +39,10 @@ const (
|
||||
DiffLineSection
|
||||
)
|
||||
|
||||
// DiffFileType represents the type of a DiffFile.
|
||||
type DiffFileType uint8
|
||||
|
||||
// DiffFileType possible values.
|
||||
const (
|
||||
DiffFileAdd DiffFileType = iota + 1
|
||||
DiffFileChange
|
||||
@@ -46,6 +50,7 @@ const (
|
||||
DiffFileRename
|
||||
)
|
||||
|
||||
// DiffLine represents a line difference in a DiffSection.
|
||||
type DiffLine struct {
|
||||
LeftIdx int
|
||||
RightIdx int
|
||||
@@ -53,10 +58,12 @@ type DiffLine struct {
|
||||
Content string
|
||||
}
|
||||
|
||||
// GetType returns the type of a DiffLine.
|
||||
func (d *DiffLine) GetType() int {
|
||||
return int(d.Type)
|
||||
}
|
||||
|
||||
// DiffSection represents a section of a DiffFile.
|
||||
type DiffSection struct {
|
||||
Name string
|
||||
Lines []*DiffLine
|
||||
@@ -97,7 +104,7 @@ func diffToHTML(diffs []diffmatchpatch.Diff, lineType DiffLineType) template.HTM
|
||||
return template.HTML(buf.Bytes())
|
||||
}
|
||||
|
||||
// get an specific line by type (add or del) and file line number
|
||||
// GetLine gets a specific line by type (add or del) and file line number
|
||||
func (diffSection *DiffSection) GetLine(lineType DiffLineType, idx int) *DiffLine {
|
||||
var (
|
||||
difference = 0
|
||||
@@ -146,7 +153,7 @@ func init() {
|
||||
diffMatchPatch.DiffEditCost = 100
|
||||
}
|
||||
|
||||
// computes inline diff for the given line
|
||||
// GetComputedInlineDiffFor computes inline diff for the given line.
|
||||
func (diffSection *DiffSection) GetComputedInlineDiffFor(diffLine *DiffLine) template.HTML {
|
||||
if setting.Git.DisableDiffHighlight {
|
||||
return template.HTML(html.EscapeString(diffLine.Content[1:]))
|
||||
@@ -183,6 +190,7 @@ func (diffSection *DiffSection) GetComputedInlineDiffFor(diffLine *DiffLine) tem
|
||||
return diffToHTML(diffRecord, diffLine.Type)
|
||||
}
|
||||
|
||||
// DiffFile represents a file diff.
|
||||
type DiffFile struct {
|
||||
Name string
|
||||
OldName string
|
||||
@@ -198,26 +206,32 @@ type DiffFile struct {
|
||||
IsIncomplete bool
|
||||
}
|
||||
|
||||
// GetType returns type of diff file.
|
||||
func (diffFile *DiffFile) GetType() int {
|
||||
return int(diffFile.Type)
|
||||
}
|
||||
|
||||
// GetHighlightClass returns highlight class for a filename.
|
||||
func (diffFile *DiffFile) GetHighlightClass() string {
|
||||
return highlight.FileNameToHighlightClass(diffFile.Name)
|
||||
}
|
||||
|
||||
// Diff represents a difference between two git trees.
|
||||
type Diff struct {
|
||||
TotalAddition, TotalDeletion int
|
||||
Files []*DiffFile
|
||||
IsIncomplete bool
|
||||
}
|
||||
|
||||
// NumFiles returns number of files changes in a diff.
|
||||
func (diff *Diff) NumFiles() int {
|
||||
return len(diff.Files)
|
||||
}
|
||||
|
||||
const DIFF_HEAD = "diff --git "
|
||||
const cmdDiffHead = "diff --git "
|
||||
|
||||
// ParsePatch builds a Diff object from a io.Reader and some
|
||||
// parameters.
|
||||
// TODO: move this function to gogits/git-module
|
||||
func ParsePatch(maxLines, maxLineCharacteres, maxFiles int, reader io.Reader) (*Diff, error) {
|
||||
var (
|
||||
@@ -307,19 +321,19 @@ func ParsePatch(maxLines, maxLineCharacteres, maxFiles int, reader io.Reader) (*
|
||||
}
|
||||
|
||||
// Get new file.
|
||||
if strings.HasPrefix(line, DIFF_HEAD) {
|
||||
if strings.HasPrefix(line, cmdDiffHead) {
|
||||
middle := -1
|
||||
|
||||
// Note: In case file name is surrounded by double quotes (it happens only in git-shell).
|
||||
// e.g. diff --git "a/xxx" "b/xxx"
|
||||
hasQuote := line[len(DIFF_HEAD)] == '"'
|
||||
hasQuote := line[len(cmdDiffHead)] == '"'
|
||||
if hasQuote {
|
||||
middle = strings.Index(line, ` "b/`)
|
||||
} else {
|
||||
middle = strings.Index(line, " b/")
|
||||
}
|
||||
|
||||
beg := len(DIFF_HEAD)
|
||||
beg := len(cmdDiffHead)
|
||||
a := line[beg+2 : middle]
|
||||
b := line[middle+3:]
|
||||
if hasQuote {
|
||||
@@ -405,6 +419,9 @@ func ParsePatch(maxLines, maxLineCharacteres, maxFiles int, reader io.Reader) (*
|
||||
return diff, nil
|
||||
}
|
||||
|
||||
// GetDiffRange builds a Diff between two commits of a repository.
|
||||
// passing the empty string as beforeCommitID returns a diff from the
|
||||
// parent commit.
|
||||
func GetDiffRange(repoPath, beforeCommitID, afterCommitID string, maxLines, maxLineCharacteres, maxFiles int) (*Diff, error) {
|
||||
gitRepo, err := git.OpenRepository(repoPath)
|
||||
if err != nil {
|
||||
@@ -456,8 +473,10 @@ func GetDiffRange(repoPath, beforeCommitID, afterCommitID string, maxLines, maxL
|
||||
return diff, nil
|
||||
}
|
||||
|
||||
// RawDiffType type of a raw diff.
|
||||
type RawDiffType string
|
||||
|
||||
// RawDiffType possible values.
|
||||
const (
|
||||
RawDiffNormal RawDiffType = "diff"
|
||||
RawDiffPatch RawDiffType = "patch"
|
||||
@@ -509,6 +528,7 @@ func GetRawDiff(repoPath, commitID string, diffType RawDiffType, writer io.Write
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetDiffCommit builds a Diff representing the given commitID.
|
||||
func GetDiffCommit(repoPath, commitID string, maxLines, maxLineCharacteres, maxFiles int) (*Diff, error) {
|
||||
return GetDiffRange(repoPath, "", commitID, maxLines, maxLineCharacteres, maxFiles)
|
||||
}
|
||||
|
||||
163
models/issue.go
163
models/issue.go
@@ -25,7 +25,7 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
ErrMissingIssueNumber = errors.New("No issue number specified")
|
||||
errMissingIssueNumber = errors.New("No issue number specified")
|
||||
)
|
||||
|
||||
// Issue represents an issue or pull request of repository.
|
||||
@@ -62,16 +62,20 @@ type Issue struct {
|
||||
Comments []*Comment `xorm:"-"`
|
||||
}
|
||||
|
||||
// BeforeInsert is invoked from XORM before inserting an object of this type.
|
||||
func (issue *Issue) BeforeInsert() {
|
||||
issue.CreatedUnix = time.Now().Unix()
|
||||
issue.UpdatedUnix = issue.CreatedUnix
|
||||
}
|
||||
|
||||
// BeforeUpdate is invoked from XORM before updating this object.
|
||||
func (issue *Issue) BeforeUpdate() {
|
||||
issue.UpdatedUnix = time.Now().Unix()
|
||||
issue.DeadlineUnix = issue.Deadline.Unix()
|
||||
}
|
||||
|
||||
// AfterSet is invoked from XORM after setting the value of a field of
|
||||
// this object.
|
||||
func (issue *Issue) AfterSet(colName string, _ xorm.Cell) {
|
||||
switch colName {
|
||||
case "deadline_unix":
|
||||
@@ -150,10 +154,12 @@ func (issue *Issue) loadAttributes(e Engine) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// LoadAttributes loads the attribute of this issue.
|
||||
func (issue *Issue) LoadAttributes() error {
|
||||
return issue.loadAttributes(x)
|
||||
}
|
||||
|
||||
// HTMLURL returns the absolute URL to this issue.
|
||||
func (issue *Issue) HTMLURL() string {
|
||||
var path string
|
||||
if issue.IsPull {
|
||||
@@ -164,15 +170,31 @@ func (issue *Issue) HTMLURL() string {
|
||||
return fmt.Sprintf("%s/%s/%d", issue.Repo.HTMLURL(), path, issue.Index)
|
||||
}
|
||||
|
||||
// State returns string representation of issue status.
|
||||
func (i *Issue) State() api.StateType {
|
||||
if i.IsClosed {
|
||||
return api.STATE_CLOSED
|
||||
// DiffURL returns the absolute URL to this diff
|
||||
func (issue *Issue) DiffURL() string {
|
||||
if issue.IsPull {
|
||||
return fmt.Sprintf("%s/pulls/%d.diff", issue.Repo.HTMLURL(), issue.Index)
|
||||
}
|
||||
return api.STATE_OPEN
|
||||
return ""
|
||||
}
|
||||
|
||||
// This method assumes some fields assigned with values:
|
||||
// PatchURL returns the absolute URL to this patch
|
||||
func (issue *Issue) PatchURL() string {
|
||||
if issue.IsPull {
|
||||
return fmt.Sprintf("%s/pulls/%d.patch", issue.Repo.HTMLURL(), issue.Index)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// State returns string representation of issue status.
|
||||
func (issue *Issue) State() api.StateType {
|
||||
if issue.IsClosed {
|
||||
return api.StateClosed
|
||||
}
|
||||
return api.StateOpen
|
||||
}
|
||||
|
||||
// APIFormat assumes some fields assigned with values:
|
||||
// Required - Poster, Labels,
|
||||
// Optional - Milestone, Assignee, PullRequest
|
||||
func (issue *Issue) APIFormat() *api.Issue {
|
||||
@@ -213,22 +235,22 @@ func (issue *Issue) APIFormat() *api.Issue {
|
||||
}
|
||||
|
||||
// HashTag returns unique hash tag for issue.
|
||||
func (i *Issue) HashTag() string {
|
||||
return "issue-" + com.ToStr(i.ID)
|
||||
func (issue *Issue) HashTag() string {
|
||||
return "issue-" + com.ToStr(issue.ID)
|
||||
}
|
||||
|
||||
// IsPoster returns true if given user by ID is the poster.
|
||||
func (i *Issue) IsPoster(uid int64) bool {
|
||||
return i.PosterID == uid
|
||||
func (issue *Issue) IsPoster(uid int64) bool {
|
||||
return issue.PosterID == uid
|
||||
}
|
||||
|
||||
func (i *Issue) hasLabel(e Engine, labelID int64) bool {
|
||||
return hasIssueLabel(e, i.ID, labelID)
|
||||
func (issue *Issue) hasLabel(e Engine, labelID int64) bool {
|
||||
return hasIssueLabel(e, issue.ID, labelID)
|
||||
}
|
||||
|
||||
// HasLabel returns true if issue has been labeled by given ID.
|
||||
func (i *Issue) HasLabel(labelID int64) bool {
|
||||
return i.hasLabel(x, labelID)
|
||||
func (issue *Issue) HasLabel(labelID int64) bool {
|
||||
return issue.hasLabel(x, labelID)
|
||||
}
|
||||
|
||||
func (issue *Issue) sendLabelUpdatedWebhook(doer *User) {
|
||||
@@ -254,8 +276,8 @@ func (issue *Issue) sendLabelUpdatedWebhook(doer *User) {
|
||||
}
|
||||
}
|
||||
|
||||
func (i *Issue) addLabel(e *xorm.Session, label *Label) error {
|
||||
return newIssueLabel(e, i, label)
|
||||
func (issue *Issue) addLabel(e *xorm.Session, label *Label) error {
|
||||
return newIssueLabel(e, issue, label)
|
||||
}
|
||||
|
||||
// AddLabel adds a new label to the issue.
|
||||
@@ -322,6 +344,8 @@ func (issue *Issue) clearLabels(e *xorm.Session) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ClearLabels removes all issue labels as the given user.
|
||||
// Triggers appropriate WebHooks, if any.
|
||||
func (issue *Issue) ClearLabels(doer *User) (err error) {
|
||||
sess := x.NewSession()
|
||||
defer sessionRelease(sess)
|
||||
@@ -361,6 +385,7 @@ func (issue *Issue) ClearLabels(doer *User) (err error) {
|
||||
}
|
||||
|
||||
// ReplaceLabels removes all current labels and add new labels to the issue.
|
||||
// Triggers appropriate WebHooks, if any.
|
||||
func (issue *Issue) ReplaceLabels(labels []*Label) (err error) {
|
||||
sess := x.NewSession()
|
||||
defer sessionRelease(sess)
|
||||
@@ -377,12 +402,13 @@ func (issue *Issue) ReplaceLabels(labels []*Label) (err error) {
|
||||
return sess.Commit()
|
||||
}
|
||||
|
||||
func (i *Issue) GetAssignee() (err error) {
|
||||
if i.AssigneeID == 0 || i.Assignee != nil {
|
||||
// GetAssignee sets the Assignee attribute of this issue.
|
||||
func (issue *Issue) GetAssignee() (err error) {
|
||||
if issue.AssigneeID == 0 || issue.Assignee != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
i.Assignee, err = GetUserByID(i.AssigneeID)
|
||||
issue.Assignee, err = GetUserByID(issue.AssigneeID)
|
||||
if IsErrUserNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
@@ -390,8 +416,8 @@ func (i *Issue) GetAssignee() (err error) {
|
||||
}
|
||||
|
||||
// ReadBy sets issue to be read by given user.
|
||||
func (i *Issue) ReadBy(uid int64) error {
|
||||
return UpdateIssueUserByRead(uid, i.ID)
|
||||
func (issue *Issue) ReadBy(uid int64) error {
|
||||
return UpdateIssueUserByRead(uid, issue.ID)
|
||||
}
|
||||
|
||||
func updateIssueCols(e Engine, issue *Issue, cols ...string) error {
|
||||
@@ -404,41 +430,41 @@ func UpdateIssueCols(issue *Issue, cols ...string) error {
|
||||
return updateIssueCols(x, issue, cols...)
|
||||
}
|
||||
|
||||
func (i *Issue) changeStatus(e *xorm.Session, doer *User, repo *Repository, isClosed bool) (err error) {
|
||||
func (issue *Issue) changeStatus(e *xorm.Session, doer *User, repo *Repository, isClosed bool) (err error) {
|
||||
// Nothing should be performed if current status is same as target status
|
||||
if i.IsClosed == isClosed {
|
||||
if issue.IsClosed == isClosed {
|
||||
return nil
|
||||
}
|
||||
i.IsClosed = isClosed
|
||||
issue.IsClosed = isClosed
|
||||
|
||||
if err = updateIssueCols(e, i, "is_closed"); err != nil {
|
||||
if err = updateIssueCols(e, issue, "is_closed"); err != nil {
|
||||
return err
|
||||
} else if err = updateIssueUsersByStatus(e, i.ID, isClosed); err != nil {
|
||||
} else if err = updateIssueUsersByStatus(e, issue.ID, isClosed); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Update issue count of labels
|
||||
if err = i.getLabels(e); err != nil {
|
||||
if err = issue.getLabels(e); err != nil {
|
||||
return err
|
||||
}
|
||||
for idx := range i.Labels {
|
||||
if i.IsClosed {
|
||||
i.Labels[idx].NumClosedIssues++
|
||||
for idx := range issue.Labels {
|
||||
if issue.IsClosed {
|
||||
issue.Labels[idx].NumClosedIssues++
|
||||
} else {
|
||||
i.Labels[idx].NumClosedIssues--
|
||||
issue.Labels[idx].NumClosedIssues--
|
||||
}
|
||||
if err = updateLabel(e, i.Labels[idx]); err != nil {
|
||||
if err = updateLabel(e, issue.Labels[idx]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Update issue count of milestone
|
||||
if err = changeMilestoneIssueStats(e, i); err != nil {
|
||||
if err = changeMilestoneIssueStats(e, issue); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// New action comment
|
||||
if _, err = createStatusComment(e, doer, repo, i); err != nil {
|
||||
if _, err = createStatusComment(e, doer, repo, issue); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -473,7 +499,7 @@ func (issue *Issue) ChangeStatus(doer *User, repo *Repository, isClosed bool) (e
|
||||
if isClosed {
|
||||
apiPullRequest.Action = api.HookIssueClosed
|
||||
} else {
|
||||
apiPullRequest.Action = api.HookIssueReopened
|
||||
apiPullRequest.Action = api.HookIssueReOpened
|
||||
}
|
||||
err = PrepareWebhooks(repo, HookEventPullRequest, apiPullRequest)
|
||||
}
|
||||
@@ -486,6 +512,7 @@ func (issue *Issue) ChangeStatus(doer *User, repo *Repository, isClosed bool) (e
|
||||
return nil
|
||||
}
|
||||
|
||||
// ChangeTitle changes the title of this issue, as the given user.
|
||||
func (issue *Issue) ChangeTitle(doer *User, title string) (err error) {
|
||||
oldTitle := issue.Title
|
||||
issue.Title = title
|
||||
@@ -517,6 +544,7 @@ func (issue *Issue) ChangeTitle(doer *User, title string) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ChangeContent changes issue content, as the given user.
|
||||
func (issue *Issue) ChangeContent(doer *User, content string) (err error) {
|
||||
oldContent := issue.Content
|
||||
issue.Content = content
|
||||
@@ -548,6 +576,7 @@ func (issue *Issue) ChangeContent(doer *User, content string) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ChangeAssignee changes the Asssignee field of this issue.
|
||||
func (issue *Issue) ChangeAssignee(doer *User, assigneeID int64) (err error) {
|
||||
issue.AssigneeID = assigneeID
|
||||
if err = UpdateIssueUserByAssignee(issue); err != nil {
|
||||
@@ -586,6 +615,7 @@ func (issue *Issue) ChangeAssignee(doer *User, assigneeID int64) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewIssueOptions represents the options of a new issue.
|
||||
type NewIssueOptions struct {
|
||||
Repo *Repository
|
||||
Issue *Issue
|
||||
@@ -650,7 +680,7 @@ func newIssue(e *xorm.Session, opts NewIssueOptions) (err error) {
|
||||
}
|
||||
|
||||
if len(opts.LableIDs) > 0 {
|
||||
// During the session, SQLite3 dirver cannot handle retrieve objects after update something.
|
||||
// During the session, SQLite3 driver cannot handle retrieve objects after update something.
|
||||
// So we have to get all needed labels first.
|
||||
labels := make([]*Label, 0, len(opts.LableIDs))
|
||||
if err = e.In("id", opts.LableIDs).Find(&labels); err != nil {
|
||||
@@ -735,7 +765,7 @@ func NewIssue(repo *Repository, issue *Issue, labelIDs []int64, uuids []string)
|
||||
func GetIssueByRef(ref string) (*Issue, error) {
|
||||
n := strings.IndexByte(ref, byte('#'))
|
||||
if n == -1 {
|
||||
return nil, ErrMissingIssueNumber
|
||||
return nil, errMissingIssueNumber
|
||||
}
|
||||
|
||||
index, err := com.StrTo(ref[n+1:]).Int64()
|
||||
@@ -756,7 +786,7 @@ func GetIssueByRef(ref string) (*Issue, error) {
|
||||
return issue, issue.LoadAttributes()
|
||||
}
|
||||
|
||||
// GetIssueByIndex returns raw issue without loading attributes by index in a repository.
|
||||
// GetRawIssueByIndex returns raw issue without loading attributes by index in a repository.
|
||||
func GetRawIssueByIndex(repoID, index int64) (*Issue, error) {
|
||||
issue := &Issue{
|
||||
RepoID: repoID,
|
||||
@@ -796,6 +826,7 @@ func GetIssueByID(id int64) (*Issue, error) {
|
||||
return getIssueByID(x, id)
|
||||
}
|
||||
|
||||
// IssuesOptions represents options of an issue.
|
||||
type IssuesOptions struct {
|
||||
UserID int64
|
||||
AssigneeID int64
|
||||
@@ -820,20 +851,12 @@ func Issues(opts *IssuesOptions) ([]*Issue, error) {
|
||||
sess := x.Limit(setting.UI.IssuePagingNum, (opts.Page-1)*setting.UI.IssuePagingNum)
|
||||
|
||||
if opts.RepoID > 0 {
|
||||
sess.
|
||||
Where("issue.repo_id=?", opts.RepoID).
|
||||
And("issue.is_closed=?", opts.IsClosed)
|
||||
} else if opts.RepoIDs != nil {
|
||||
sess.And("issue.repo_id=?", opts.RepoID)
|
||||
} else if len(opts.RepoIDs) > 0 {
|
||||
// In case repository IDs are provided but actually no repository has issue.
|
||||
if len(opts.RepoIDs) == 0 {
|
||||
return make([]*Issue, 0), nil
|
||||
}
|
||||
sess.
|
||||
In("issue.repo_id", base.Int64sToStrings(opts.RepoIDs)).
|
||||
And("issue.is_closed=?", opts.IsClosed)
|
||||
} else {
|
||||
sess.Where("issue.is_closed=?", opts.IsClosed)
|
||||
sess.In("issue.repo_id", opts.RepoIDs)
|
||||
}
|
||||
sess.And("issue.is_closed=?", opts.IsClosed)
|
||||
|
||||
if opts.AssigneeID > 0 {
|
||||
sess.And("issue.assignee_id=?", opts.AssigneeID)
|
||||
@@ -975,9 +998,9 @@ func NewIssueUsers(repo *Repository, issue *Issue) (err error) {
|
||||
}
|
||||
|
||||
// PairsContains returns true when pairs list contains given issue.
|
||||
func PairsContains(ius []*IssueUser, issueId, uid int64) int {
|
||||
func PairsContains(ius []*IssueUser, issueID, uid int64) int {
|
||||
for i := range ius {
|
||||
if ius[i].IssueID == issueId &&
|
||||
if ius[i].IssueID == issueID &&
|
||||
ius[i].UID == uid {
|
||||
return i
|
||||
}
|
||||
@@ -1100,6 +1123,7 @@ func parseCountResult(results []map[string][]byte) int64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// IssueStatsOptions contains parameters accepted by GetIssueStats.
|
||||
type IssueStatsOptions struct {
|
||||
RepoID int64
|
||||
UserID int64
|
||||
@@ -1185,9 +1209,9 @@ func GetUserIssueStats(repoID, uid int64, repoIDs []int64, filterMode int, isPul
|
||||
Where("issue.is_closed = ?", isClosed).
|
||||
And("issue.is_pull = ?", isPull)
|
||||
|
||||
if repoID > 0 || len(repoIDs) == 0 {
|
||||
if repoID > 0 {
|
||||
sess.And("repo_id = ?", repoID)
|
||||
} else {
|
||||
} else if len(repoIDs) > 0 {
|
||||
sess.In("repo_id", repoIDs)
|
||||
}
|
||||
|
||||
@@ -1358,10 +1382,12 @@ type Milestone struct {
|
||||
ClosedDateUnix int64
|
||||
}
|
||||
|
||||
// BeforeInsert is invoked from XORM before inserting an object of this type.
|
||||
func (m *Milestone) BeforeInsert() {
|
||||
m.DeadlineUnix = m.Deadline.Unix()
|
||||
}
|
||||
|
||||
// BeforeUpdate is invoked from XORM before updating this object.
|
||||
func (m *Milestone) BeforeUpdate() {
|
||||
if m.NumIssues > 0 {
|
||||
m.Completeness = m.NumClosedIssues * 100 / m.NumIssues
|
||||
@@ -1373,6 +1399,8 @@ func (m *Milestone) BeforeUpdate() {
|
||||
m.ClosedDateUnix = m.ClosedDate.Unix()
|
||||
}
|
||||
|
||||
// AfterSet is invoked from XORM after setting the value of a field of
|
||||
// this object.
|
||||
func (m *Milestone) AfterSet(colName string, _ xorm.Cell) {
|
||||
switch colName {
|
||||
case "num_closed_issues":
|
||||
@@ -1397,11 +1425,12 @@ func (m *Milestone) AfterSet(colName string, _ xorm.Cell) {
|
||||
// State returns string representation of milestone status.
|
||||
func (m *Milestone) State() api.StateType {
|
||||
if m.IsClosed {
|
||||
return api.STATE_CLOSED
|
||||
return api.StateClosed
|
||||
}
|
||||
return api.STATE_OPEN
|
||||
return api.StateOpen
|
||||
}
|
||||
|
||||
// APIFormat returns this Milestone in API format.
|
||||
func (m *Milestone) APIFormat() *api.Milestone {
|
||||
apiMilestone := &api.Milestone{
|
||||
ID: m.ID,
|
||||
@@ -1452,7 +1481,7 @@ func getMilestoneByRepoID(e Engine, repoID, id int64) (*Milestone, error) {
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// GetWebhookByRepoID returns the milestone in a repository.
|
||||
// GetMilestoneByRepoID returns the milestone in a repository.
|
||||
func GetMilestoneByRepoID(repoID, id int64) (*Milestone, error) {
|
||||
return getMilestoneByRepoID(x, repoID, id)
|
||||
}
|
||||
@@ -1684,10 +1713,13 @@ type Attachment struct {
|
||||
CreatedUnix int64
|
||||
}
|
||||
|
||||
// BeforeInsert is invoked from XORM before inserting an object of this type.
|
||||
func (a *Attachment) BeforeInsert() {
|
||||
a.CreatedUnix = time.Now().Unix()
|
||||
}
|
||||
|
||||
// AfterSet is invoked from XORM after setting the value of a field of
|
||||
// this object.
|
||||
func (a *Attachment) AfterSet(colName string, _ xorm.Cell) {
|
||||
switch colName {
|
||||
case "created_unix":
|
||||
@@ -1695,14 +1727,15 @@ func (a *Attachment) AfterSet(colName string, _ xorm.Cell) {
|
||||
}
|
||||
}
|
||||
|
||||
// AttachmentLocalPath returns where attachment is stored in local file system based on given UUID.
|
||||
// AttachmentLocalPath returns where attachment is stored in local file
|
||||
// system based on given UUID.
|
||||
func AttachmentLocalPath(uuid string) string {
|
||||
return path.Join(setting.AttachmentPath, uuid[0:1], uuid[1:2], uuid)
|
||||
}
|
||||
|
||||
// LocalPath returns where attachment is stored in local file system.
|
||||
func (attach *Attachment) LocalPath() string {
|
||||
return AttachmentLocalPath(attach.UUID)
|
||||
func (a *Attachment) LocalPath() string {
|
||||
return AttachmentLocalPath(a.UUID)
|
||||
}
|
||||
|
||||
// NewAttachment creates a new attachment object.
|
||||
@@ -1802,8 +1835,8 @@ func DeleteAttachments(attachments []*Attachment, remove bool) (int, error) {
|
||||
}
|
||||
|
||||
// DeleteAttachmentsByIssue deletes all attachments associated with the given issue.
|
||||
func DeleteAttachmentsByIssue(issueId int64, remove bool) (int, error) {
|
||||
attachments, err := GetAttachmentsByIssueID(issueId)
|
||||
func DeleteAttachmentsByIssue(issueID int64, remove bool) (int, error) {
|
||||
attachments, err := GetAttachmentsByIssueID(issueID)
|
||||
|
||||
if err != nil {
|
||||
return 0, err
|
||||
@@ -1813,8 +1846,8 @@ func DeleteAttachmentsByIssue(issueId int64, remove bool) (int, error) {
|
||||
}
|
||||
|
||||
// DeleteAttachmentsByComment deletes all attachments associated with the given comment.
|
||||
func DeleteAttachmentsByComment(commentId int64, remove bool) (int, error) {
|
||||
attachments, err := GetAttachmentsByCommentID(commentId)
|
||||
func DeleteAttachmentsByComment(commentID int64, remove bool) (int, error) {
|
||||
attachments, err := GetAttachmentsByCommentID(commentID)
|
||||
|
||||
if err != nil {
|
||||
return 0, err
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
// CommentType defines whether a comment is just a simple comment, an action (like close) or a reference.
|
||||
type CommentType int
|
||||
|
||||
// Enumerate all the comment types
|
||||
const (
|
||||
// Plain comment, can be associated with a commit (CommitID > 0) and a line (LineNum > 0)
|
||||
CommentTypeComment CommentType = iota
|
||||
@@ -37,8 +38,10 @@ const (
|
||||
CommentTypePullRef
|
||||
)
|
||||
|
||||
// CommentTag defines comment tag type
|
||||
type CommentTag int
|
||||
|
||||
// Enumerate all the comment tag types
|
||||
const (
|
||||
CommentTagNone CommentTag = iota
|
||||
CommentTagPoster
|
||||
@@ -72,15 +75,19 @@ type Comment struct {
|
||||
ShowTag CommentTag `xorm:"-"`
|
||||
}
|
||||
|
||||
// BeforeInsert will be invoked by XORM before inserting a record
|
||||
// representing this object.
|
||||
func (c *Comment) BeforeInsert() {
|
||||
c.CreatedUnix = time.Now().Unix()
|
||||
c.UpdatedUnix = c.CreatedUnix
|
||||
}
|
||||
|
||||
// BeforeUpdate is invoked from XORM before updating this object.
|
||||
func (c *Comment) BeforeUpdate() {
|
||||
c.UpdatedUnix = time.Now().Unix()
|
||||
}
|
||||
|
||||
// AfterSet is invoked from XORM after setting the value of a field of this object.
|
||||
func (c *Comment) AfterSet(colName string, _ xorm.Cell) {
|
||||
var err error
|
||||
switch colName {
|
||||
@@ -107,6 +114,7 @@ func (c *Comment) AfterSet(colName string, _ xorm.Cell) {
|
||||
}
|
||||
}
|
||||
|
||||
// AfterDelete is invoked from XORM after the object is deleted.
|
||||
func (c *Comment) AfterDelete() {
|
||||
_, err := DeleteAttachmentsByComment(c.ID, true)
|
||||
|
||||
@@ -115,6 +123,7 @@ func (c *Comment) AfterDelete() {
|
||||
}
|
||||
}
|
||||
|
||||
// APIFormat converts a Comment to the api.Comment format
|
||||
func (c *Comment) APIFormat() *api.Comment {
|
||||
return &api.Comment{
|
||||
ID: c.ID,
|
||||
@@ -137,21 +146,21 @@ func (c *Comment) EventTag() string {
|
||||
|
||||
// MailParticipants sends new comment emails to repository watchers
|
||||
// and mentioned people.
|
||||
func (cmt *Comment) MailParticipants(opType ActionType, issue *Issue) (err error) {
|
||||
mentions := markdown.FindAllMentions(cmt.Content)
|
||||
if err = UpdateIssueMentions(cmt.IssueID, mentions); err != nil {
|
||||
return fmt.Errorf("UpdateIssueMentions [%d]: %v", cmt.IssueID, err)
|
||||
func (c *Comment) MailParticipants(opType ActionType, issue *Issue) (err error) {
|
||||
mentions := markdown.FindAllMentions(c.Content)
|
||||
if err = UpdateIssueMentions(c.IssueID, mentions); err != nil {
|
||||
return fmt.Errorf("UpdateIssueMentions [%d]: %v", c.IssueID, err)
|
||||
}
|
||||
|
||||
switch opType {
|
||||
case ActionCommentIssue:
|
||||
issue.Content = cmt.Content
|
||||
issue.Content = c.Content
|
||||
case ActionCloseIssue:
|
||||
issue.Content = fmt.Sprintf("Closed #%d", issue.Index)
|
||||
case ActionReopenIssue:
|
||||
issue.Content = fmt.Sprintf("Reopened #%d", issue.Index)
|
||||
}
|
||||
if err = mailIssueCommentToParticipants(issue, cmt.Poster, mentions); err != nil {
|
||||
if err = mailIssueCommentToParticipants(issue, c.Poster, mentions); err != nil {
|
||||
log.Error(4, "mailIssueCommentToParticipants: %v", err)
|
||||
}
|
||||
|
||||
@@ -272,6 +281,7 @@ func createStatusComment(e *xorm.Session, doer *User, repo *Repository, issue *I
|
||||
})
|
||||
}
|
||||
|
||||
// CreateCommentOptions defines options for creating comment
|
||||
type CreateCommentOptions struct {
|
||||
Type CommentType
|
||||
Doer *User
|
||||
@@ -374,7 +384,7 @@ func GetCommentsByIssueID(issueID int64) ([]*Comment, error) {
|
||||
return getCommentsByIssueID(x, issueID)
|
||||
}
|
||||
|
||||
// GetCommentsByIssueID returns a list of comments of an issue since a given time point.
|
||||
// GetCommentsByIssueIDSince returns a list of comments of an issue since a given time point.
|
||||
func GetCommentsByIssueIDSince(issueID, since int64) ([]*Comment, error) {
|
||||
return getCommentsByIssueIDSince(x, issueID, since)
|
||||
}
|
||||
|
||||
@@ -14,8 +14,6 @@ import (
|
||||
"github.com/go-xorm/xorm"
|
||||
|
||||
api "code.gitea.io/sdk/gitea"
|
||||
|
||||
"code.gitea.io/gitea/modules/base"
|
||||
)
|
||||
|
||||
var labelColorPattern = regexp.MustCompile("#([a-fA-F0-9]{6})")
|
||||
@@ -64,11 +62,12 @@ type Label struct {
|
||||
IsChecked bool `xorm:"-"`
|
||||
}
|
||||
|
||||
// APIFormat converts a Label to the api.Label format
|
||||
func (label *Label) APIFormat() *api.Label {
|
||||
return &api.Label{
|
||||
ID: label.ID,
|
||||
Name: label.Name,
|
||||
Color: label.Color,
|
||||
Color: strings.TrimLeft(label.Color, "#"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,9 +78,9 @@ func (label *Label) CalOpenIssues() {
|
||||
|
||||
// ForegroundColor calculates the text color for labels based
|
||||
// on their background color.
|
||||
func (l *Label) ForegroundColor() template.CSS {
|
||||
if strings.HasPrefix(l.Color, "#") {
|
||||
if color, err := strconv.ParseUint(l.Color[1:], 16, 64); err == nil {
|
||||
func (label *Label) ForegroundColor() template.CSS {
|
||||
if strings.HasPrefix(label.Color, "#") {
|
||||
if color, err := strconv.ParseUint(label.Color[1:], 16, 64); err == nil {
|
||||
r := float32(0xFF & (color >> 16))
|
||||
g := float32(0xFF & (color >> 8))
|
||||
b := float32(0xFF & color)
|
||||
@@ -103,6 +102,27 @@ func NewLabels(labels ...*Label) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// getLabelInRepoByName returns a label by Name in given repository.
|
||||
// If pass repoID as 0, then ORM will ignore limitation of repository
|
||||
// and can return arbitrary label with any valid ID.
|
||||
func getLabelInRepoByName(e Engine, repoID int64, labelName string) (*Label, error) {
|
||||
if len(labelName) <= 0 {
|
||||
return nil, ErrLabelNotExist{0, repoID}
|
||||
}
|
||||
|
||||
l := &Label{
|
||||
Name: labelName,
|
||||
RepoID: repoID,
|
||||
}
|
||||
has, err := x.Get(l)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !has {
|
||||
return nil, ErrLabelNotExist{0, l.RepoID}
|
||||
}
|
||||
return l, nil
|
||||
}
|
||||
|
||||
// getLabelInRepoByID returns a label by ID in given repository.
|
||||
// If pass repoID as 0, then ORM will ignore limitation of repository
|
||||
// and can return arbitrary label with any valid ID.
|
||||
@@ -129,6 +149,11 @@ func GetLabelByID(id int64) (*Label, error) {
|
||||
return getLabelInRepoByID(x, 0, id)
|
||||
}
|
||||
|
||||
// GetLabelInRepoByName returns a label by name in given repository.
|
||||
func GetLabelInRepoByName(repoID int64, labelName string) (*Label, error) {
|
||||
return getLabelInRepoByName(x, repoID, labelName)
|
||||
}
|
||||
|
||||
// GetLabelInRepoByID returns a label by ID in given repository.
|
||||
func GetLabelInRepoByID(repoID, labelID int64) (*Label, error) {
|
||||
return getLabelInRepoByID(x, repoID, labelID)
|
||||
@@ -140,7 +165,7 @@ func GetLabelsInRepoByIDs(repoID int64, labelIDs []int64) ([]*Label, error) {
|
||||
labels := make([]*Label, 0, len(labelIDs))
|
||||
return labels, x.
|
||||
Where("repo_id = ?", repoID).
|
||||
In("id", base.Int64sToStrings(labelIDs)).
|
||||
In("id", labelIDs).
|
||||
Asc("name").
|
||||
Find(&labels)
|
||||
}
|
||||
@@ -170,7 +195,7 @@ func getLabelsByIssueID(e Engine, issueID int64) ([]*Label, error) {
|
||||
labels := make([]*Label, 0, len(labelIDs))
|
||||
return labels, e.
|
||||
Where("id > 0").
|
||||
In("id", base.Int64sToStrings(labelIDs)).
|
||||
In("id", labelIDs).
|
||||
Asc("name").
|
||||
Find(&labels)
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
)
|
||||
|
||||
func (issue *Issue) MailSubject() string {
|
||||
func (issue *Issue) mailSubject() string {
|
||||
return fmt.Sprintf("[%s] %s (#%d)", issue.Repo.Name, issue.Title, issue.Index)
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
)
|
||||
|
||||
// LoginType represents an login type.
|
||||
type LoginType int
|
||||
|
||||
// Note: new type must append to the end of list to maintain compatibility.
|
||||
@@ -36,6 +37,7 @@ const (
|
||||
LoginDLDAP // 5
|
||||
)
|
||||
|
||||
// LoginNames contains the name of LoginType values.
|
||||
var LoginNames = map[LoginType]string{
|
||||
LoginLDAP: "LDAP (via BindDN)",
|
||||
LoginDLDAP: "LDAP (simple auth)", // Via direct bind
|
||||
@@ -43,6 +45,7 @@ var LoginNames = map[LoginType]string{
|
||||
LoginPAM: "PAM",
|
||||
}
|
||||
|
||||
// SecurityProtocolNames contains the name of SecurityProtocol values.
|
||||
var SecurityProtocolNames = map[ldap.SecurityProtocol]string{
|
||||
ldap.SecurityProtocolUnencrypted: "Unencrypted",
|
||||
ldap.SecurityProtocolLDAPS: "LDAPS",
|
||||
@@ -56,22 +59,28 @@ var (
|
||||
_ core.Conversion = &PAMConfig{}
|
||||
)
|
||||
|
||||
// LDAPConfig holds configuration for LDAP login source.
|
||||
type LDAPConfig struct {
|
||||
*ldap.Source
|
||||
}
|
||||
|
||||
// FromDB fills up a LDAPConfig from serialized format.
|
||||
func (cfg *LDAPConfig) FromDB(bs []byte) error {
|
||||
return json.Unmarshal(bs, &cfg)
|
||||
}
|
||||
|
||||
// ToDB exports a LDAPConfig to a serialized format.
|
||||
func (cfg *LDAPConfig) ToDB() ([]byte, error) {
|
||||
return json.Marshal(cfg)
|
||||
}
|
||||
|
||||
// SecurityProtocolName returns the name of configured security
|
||||
// protocol.
|
||||
func (cfg *LDAPConfig) SecurityProtocolName() string {
|
||||
return SecurityProtocolNames[cfg.SecurityProtocol]
|
||||
}
|
||||
|
||||
// SMTPConfig holds configuration for the SMTP login source.
|
||||
type SMTPConfig struct {
|
||||
Auth string
|
||||
Host string
|
||||
@@ -81,22 +90,27 @@ type SMTPConfig struct {
|
||||
SkipVerify bool
|
||||
}
|
||||
|
||||
// FromDB fills up an SMTPConfig from serialized format.
|
||||
func (cfg *SMTPConfig) FromDB(bs []byte) error {
|
||||
return json.Unmarshal(bs, cfg)
|
||||
}
|
||||
|
||||
// ToDB exports an SMTPConfig to a serialized format.
|
||||
func (cfg *SMTPConfig) ToDB() ([]byte, error) {
|
||||
return json.Marshal(cfg)
|
||||
}
|
||||
|
||||
// PAMConfig holds configuration for the PAM login source.
|
||||
type PAMConfig struct {
|
||||
ServiceName string // pam service (e.g. system-auth)
|
||||
}
|
||||
|
||||
// FromDB fills up a PAMConfig from serialized format.
|
||||
func (cfg *PAMConfig) FromDB(bs []byte) error {
|
||||
return json.Unmarshal(bs, &cfg)
|
||||
}
|
||||
|
||||
// ToDB exports a PAMConfig to a serialized format.
|
||||
func (cfg *PAMConfig) ToDB() ([]byte, error) {
|
||||
return json.Marshal(cfg)
|
||||
}
|
||||
@@ -115,13 +129,15 @@ type LoginSource struct {
|
||||
UpdatedUnix int64
|
||||
}
|
||||
|
||||
func (s *LoginSource) BeforeInsert() {
|
||||
s.CreatedUnix = time.Now().Unix()
|
||||
s.UpdatedUnix = s.CreatedUnix
|
||||
// BeforeInsert is invoked from XORM before inserting an object of this type.
|
||||
func (source *LoginSource) BeforeInsert() {
|
||||
source.CreatedUnix = time.Now().Unix()
|
||||
source.UpdatedUnix = source.CreatedUnix
|
||||
}
|
||||
|
||||
func (s *LoginSource) BeforeUpdate() {
|
||||
s.UpdatedUnix = time.Now().Unix()
|
||||
// BeforeUpdate is invoked from XORM before updating this object.
|
||||
func (source *LoginSource) BeforeUpdate() {
|
||||
source.UpdatedUnix = time.Now().Unix()
|
||||
}
|
||||
|
||||
// Cell2Int64 converts a xorm.Cell type to int64,
|
||||
@@ -135,6 +151,7 @@ func Cell2Int64(val xorm.Cell) int64 {
|
||||
return (*val).(int64)
|
||||
}
|
||||
|
||||
// BeforeSet is invoked from XORM before setting the value of a field of this object.
|
||||
func (source *LoginSource) BeforeSet(colName string, val xorm.Cell) {
|
||||
switch colName {
|
||||
case "type":
|
||||
@@ -151,41 +168,49 @@ func (source *LoginSource) BeforeSet(colName string, val xorm.Cell) {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *LoginSource) AfterSet(colName string, _ xorm.Cell) {
|
||||
// AfterSet is invoked from XORM after setting the value of a field of this object.
|
||||
func (source *LoginSource) AfterSet(colName string, _ xorm.Cell) {
|
||||
switch colName {
|
||||
case "created_unix":
|
||||
s.Created = time.Unix(s.CreatedUnix, 0).Local()
|
||||
source.Created = time.Unix(source.CreatedUnix, 0).Local()
|
||||
case "updated_unix":
|
||||
s.Updated = time.Unix(s.UpdatedUnix, 0).Local()
|
||||
source.Updated = time.Unix(source.UpdatedUnix, 0).Local()
|
||||
}
|
||||
}
|
||||
|
||||
// TypeName return name of this login source type.
|
||||
func (source *LoginSource) TypeName() string {
|
||||
return LoginNames[source.Type]
|
||||
}
|
||||
|
||||
// IsLDAP returns true of this source is of the LDAP type.
|
||||
func (source *LoginSource) IsLDAP() bool {
|
||||
return source.Type == LoginLDAP
|
||||
}
|
||||
|
||||
// IsDLDAP returns true of this source is of the DLDAP type.
|
||||
func (source *LoginSource) IsDLDAP() bool {
|
||||
return source.Type == LoginDLDAP
|
||||
}
|
||||
|
||||
// IsSMTP returns true of this source is of the SMTP type.
|
||||
func (source *LoginSource) IsSMTP() bool {
|
||||
return source.Type == LoginSMTP
|
||||
}
|
||||
|
||||
// IsPAM returns true of this source is of the PAM type.
|
||||
func (source *LoginSource) IsPAM() bool {
|
||||
return source.Type == LoginPAM
|
||||
}
|
||||
|
||||
// HasTLS returns true of this source supports TLS.
|
||||
func (source *LoginSource) HasTLS() bool {
|
||||
return ((source.IsLDAP() || source.IsDLDAP()) &&
|
||||
source.LDAP().SecurityProtocol > ldap.SecurityProtocolUnencrypted) ||
|
||||
source.IsSMTP()
|
||||
}
|
||||
|
||||
// UseTLS returns true of this source is configured to use TLS.
|
||||
func (source *LoginSource) UseTLS() bool {
|
||||
switch source.Type {
|
||||
case LoginLDAP, LoginDLDAP:
|
||||
@@ -197,6 +222,8 @@ func (source *LoginSource) UseTLS() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// SkipVerify returns true if this source is configured to skip SSL
|
||||
// verification.
|
||||
func (source *LoginSource) SkipVerify() bool {
|
||||
switch source.Type {
|
||||
case LoginLDAP, LoginDLDAP:
|
||||
@@ -208,17 +235,23 @@ func (source *LoginSource) SkipVerify() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// LDAP returns LDAPConfig for this source, if of LDAP type.
|
||||
func (source *LoginSource) LDAP() *LDAPConfig {
|
||||
return source.Cfg.(*LDAPConfig)
|
||||
}
|
||||
|
||||
// SMTP returns SMTPConfig for this source, if of SMTP type.
|
||||
func (source *LoginSource) SMTP() *SMTPConfig {
|
||||
return source.Cfg.(*SMTPConfig)
|
||||
}
|
||||
|
||||
// PAM returns PAMConfig for this source, if of PAM type.
|
||||
func (source *LoginSource) PAM() *PAMConfig {
|
||||
return source.Cfg.(*PAMConfig)
|
||||
}
|
||||
|
||||
// CreateLoginSource inserts a LoginSource in the DB if not already
|
||||
// existing with the given name.
|
||||
func CreateLoginSource(source *LoginSource) error {
|
||||
has, err := x.Get(&LoginSource{Name: source.Name})
|
||||
if err != nil {
|
||||
@@ -231,6 +264,7 @@ func CreateLoginSource(source *LoginSource) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// LoginSources returns a slice of all login sources found in DB.
|
||||
func LoginSources() ([]*LoginSource, error) {
|
||||
auths := make([]*LoginSource, 0, 5)
|
||||
return auths, x.Find(&auths)
|
||||
@@ -248,11 +282,13 @@ func GetLoginSourceByID(id int64) (*LoginSource, error) {
|
||||
return source, nil
|
||||
}
|
||||
|
||||
// UpdateSource updates a LoginSource record in DB.
|
||||
func UpdateSource(source *LoginSource) error {
|
||||
_, err := x.Id(source.ID).AllCols().Update(source)
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteSource deletes a LoginSource record in DB.
|
||||
func DeleteSource(source *LoginSource) error {
|
||||
count, err := x.Count(&User{LoginSource: source.ID})
|
||||
if err != nil {
|
||||
@@ -292,11 +328,11 @@ func composeFullName(firstname, surname, username string) string {
|
||||
|
||||
// LoginViaLDAP queries if login/password is valid against the LDAP directory pool,
|
||||
// and create a local user if success when enabled.
|
||||
func LoginViaLDAP(user *User, login, passowrd string, source *LoginSource, autoRegister bool) (*User, error) {
|
||||
username, fn, sn, mail, isAdmin, succeed := source.Cfg.(*LDAPConfig).SearchEntry(login, passowrd, source.Type == LoginDLDAP)
|
||||
func LoginViaLDAP(user *User, login, password string, source *LoginSource, autoRegister bool) (*User, error) {
|
||||
username, fn, sn, mail, isAdmin, succeed := source.Cfg.(*LDAPConfig).SearchEntry(login, password, source.Type == LoginDLDAP)
|
||||
if !succeed {
|
||||
// User not in LDAP, do nothing
|
||||
return nil, ErrUserNotExist{0, login}
|
||||
return nil, ErrUserNotExist{0, login, 0}
|
||||
}
|
||||
|
||||
if !autoRegister {
|
||||
@@ -357,13 +393,16 @@ func (auth *smtpLoginAuth) Next(fromServer []byte, more bool) ([]byte, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// SMTP authentication type names.
|
||||
const (
|
||||
SMTPPlain = "PLAIN"
|
||||
SMTPLogin = "LOGIN"
|
||||
)
|
||||
|
||||
// SMTPAuths contains available SMTP authentication type names.
|
||||
var SMTPAuths = []string{SMTPPlain, SMTPLogin}
|
||||
|
||||
// SMTPAuth performs an SMTP authentication.
|
||||
func SMTPAuth(a smtp.Auth, cfg *SMTPConfig) error {
|
||||
c, err := smtp.Dial(fmt.Sprintf("%s:%d", cfg.Host, cfg.Port))
|
||||
if err != nil {
|
||||
@@ -404,9 +443,9 @@ func LoginViaSMTP(user *User, login, password string, sourceID int64, cfg *SMTPC
|
||||
if len(cfg.AllowedDomains) > 0 {
|
||||
idx := strings.Index(login, "@")
|
||||
if idx == -1 {
|
||||
return nil, ErrUserNotExist{0, login}
|
||||
return nil, ErrUserNotExist{0, login, 0}
|
||||
} else if !com.IsSliceContainsStr(strings.Split(cfg.AllowedDomains, ","), login[idx+1:]) {
|
||||
return nil, ErrUserNotExist{0, login}
|
||||
return nil, ErrUserNotExist{0, login, 0}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -425,7 +464,7 @@ func LoginViaSMTP(user *User, login, password string, sourceID int64, cfg *SMTPC
|
||||
tperr, ok := err.(*textproto.Error)
|
||||
if (ok && tperr.Code == 535) ||
|
||||
strings.Contains(err.Error(), "Username and Password not accepted") {
|
||||
return nil, ErrUserNotExist{0, login}
|
||||
return nil, ErrUserNotExist{0, login, 0}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
@@ -463,9 +502,9 @@ func LoginViaSMTP(user *User, login, password string, sourceID int64, cfg *SMTPC
|
||||
// LoginViaPAM queries if login/password is valid against the PAM,
|
||||
// and create a local user if success when enabled.
|
||||
func LoginViaPAM(user *User, login, password string, sourceID int64, cfg *PAMConfig, autoRegister bool) (*User, error) {
|
||||
if err := pam.PAMAuth(cfg.ServiceName, login, password); err != nil {
|
||||
if err := pam.Auth(cfg.ServiceName, login, password); err != nil {
|
||||
if strings.Contains(err.Error(), "Authentication failure") {
|
||||
return nil, ErrUserNotExist{0, login}
|
||||
return nil, ErrUserNotExist{0, login, 0}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
@@ -487,6 +526,7 @@ func LoginViaPAM(user *User, login, password string, sourceID int64, cfg *PAMCon
|
||||
return user, CreateUser(user)
|
||||
}
|
||||
|
||||
// ExternalUserLogin attempts a login using external source types.
|
||||
func ExternalUserLogin(user *User, login, password string, source *LoginSource, autoRegister bool) (*User, error) {
|
||||
if !source.IsActived {
|
||||
return nil, ErrLoginSourceNotActived
|
||||
@@ -505,7 +545,7 @@ func ExternalUserLogin(user *User, login, password string, source *LoginSource,
|
||||
}
|
||||
|
||||
// UserSignIn validates user name and password.
|
||||
func UserSignIn(username, passowrd string) (*User, error) {
|
||||
func UserSignIn(username, password string) (*User, error) {
|
||||
var user *User
|
||||
if strings.Contains(username, "@") {
|
||||
user = &User{Email: strings.ToLower(username)}
|
||||
@@ -521,11 +561,11 @@ func UserSignIn(username, passowrd string) (*User, error) {
|
||||
if hasUser {
|
||||
switch user.LoginType {
|
||||
case LoginNoType, LoginPlain:
|
||||
if user.ValidatePassword(passowrd) {
|
||||
if user.ValidatePassword(password) {
|
||||
return user, nil
|
||||
}
|
||||
|
||||
return nil, ErrUserNotExist{user.ID, user.Name}
|
||||
return nil, ErrUserNotExist{user.ID, user.Name, 0}
|
||||
|
||||
default:
|
||||
var source LoginSource
|
||||
@@ -536,7 +576,7 @@ func UserSignIn(username, passowrd string) (*User, error) {
|
||||
return nil, ErrLoginSourceNotExist{user.LoginSource}
|
||||
}
|
||||
|
||||
return ExternalUserLogin(user, user.LoginName, passowrd, &source, false)
|
||||
return ExternalUserLogin(user, user.LoginName, password, &source, false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -546,7 +586,7 @@ func UserSignIn(username, passowrd string) (*User, error) {
|
||||
}
|
||||
|
||||
for _, source := range sources {
|
||||
authUser, err := ExternalUserLogin(nil, username, passowrd, source, true)
|
||||
authUser, err := ExternalUserLogin(nil, username, password, source, true)
|
||||
if err == nil {
|
||||
return authUser, nil
|
||||
}
|
||||
@@ -554,5 +594,5 @@ func UserSignIn(username, passowrd string) (*User, error) {
|
||||
log.Warn("Failed to login '%s' via '%s': %v", username, source.Name, err)
|
||||
}
|
||||
|
||||
return nil, ErrUserNotExist{user.ID, user.Name}
|
||||
return nil, ErrUserNotExist{user.ID, user.Name, 0}
|
||||
}
|
||||
|
||||
@@ -20,23 +20,24 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
MailAuthActivate base.TplName = "auth/activate"
|
||||
MailAuthActivateEmail base.TplName = "auth/activate_email"
|
||||
MailAuthResetPassword base.TplName = "auth/reset_passwd"
|
||||
MailAuthRegisterNotify base.TplName = "auth/register_notify"
|
||||
mailAuthActivate base.TplName = "auth/activate"
|
||||
mailAuthActivateEmail base.TplName = "auth/activate_email"
|
||||
mailAuthResetPassword base.TplName = "auth/reset_passwd"
|
||||
mailAuthRegisterNotify base.TplName = "auth/register_notify"
|
||||
|
||||
MailIssueComment base.TplName = "issue/comment"
|
||||
MailIssueMention base.TplName = "issue/mention"
|
||||
mailIssueComment base.TplName = "issue/comment"
|
||||
mailIssueMention base.TplName = "issue/mention"
|
||||
|
||||
MailNotifyCollaborator base.TplName = "notify/collaborator"
|
||||
mailNotifyCollaborator base.TplName = "notify/collaborator"
|
||||
)
|
||||
|
||||
type MailRender interface {
|
||||
type mailRenderInterface interface {
|
||||
HTMLString(string, interface{}, ...macaron.HTMLOptions) (string, error)
|
||||
}
|
||||
|
||||
var mailRender MailRender
|
||||
var mailRender mailRenderInterface
|
||||
|
||||
// InitMailRender initializes the macaron mail renderer
|
||||
func InitMailRender(dir, appendDir string, funcMap []template.FuncMap) {
|
||||
opt := &macaron.RenderOptions{
|
||||
Directory: dir,
|
||||
@@ -53,10 +54,12 @@ func InitMailRender(dir, appendDir string, funcMap []template.FuncMap) {
|
||||
}
|
||||
}
|
||||
|
||||
// SendTestMail sends a test mail
|
||||
func SendTestMail(email string) error {
|
||||
return gomail.Send(&mailer.Sender{}, mailer.NewMessage([]string{email}, "Gogs Test Email!", "Gogs Test Email!").Message)
|
||||
}
|
||||
|
||||
// SendUserMail sends a mail to the user
|
||||
func SendUserMail(c *macaron.Context, u *User, tpl base.TplName, code, subject, info string) {
|
||||
data := map[string]interface{}{
|
||||
"Username": u.DisplayName(),
|
||||
@@ -76,15 +79,17 @@ func SendUserMail(c *macaron.Context, u *User, tpl base.TplName, code, subject,
|
||||
mailer.SendAsync(msg)
|
||||
}
|
||||
|
||||
// SendActivateAccountMail sends an activation mail to the user
|
||||
func SendActivateAccountMail(c *macaron.Context, u *User) {
|
||||
SendUserMail(c, u, MailAuthActivate, u.GenerateActivateCode(), c.Tr("mail.activate_account"), "activate account")
|
||||
SendUserMail(c, u, mailAuthActivate, u.GenerateActivateCode(), c.Tr("mail.activate_account"), "activate account")
|
||||
}
|
||||
|
||||
// SendResetPasswordMail sends a password reset mail to the user
|
||||
func SendResetPasswordMail(c *macaron.Context, u *User) {
|
||||
SendUserMail(c, u, MailAuthResetPassword, u.GenerateActivateCode(), c.Tr("mail.reset_password"), "reset password")
|
||||
SendUserMail(c, u, mailAuthResetPassword, u.GenerateActivateCode(), c.Tr("mail.reset_password"), "reset password")
|
||||
}
|
||||
|
||||
// SendActivateAccountMail sends confirmation email.
|
||||
// SendActivateEmailMail sends confirmation email.
|
||||
func SendActivateEmailMail(c *macaron.Context, u *User, email *EmailAddress) {
|
||||
data := map[string]interface{}{
|
||||
"Username": u.DisplayName(),
|
||||
@@ -92,7 +97,7 @@ func SendActivateEmailMail(c *macaron.Context, u *User, email *EmailAddress) {
|
||||
"Code": u.GenerateEmailActivateCode(email.Email),
|
||||
"Email": email.Email,
|
||||
}
|
||||
body, err := mailRender.HTMLString(string(MailAuthActivateEmail), data)
|
||||
body, err := mailRender.HTMLString(string(mailAuthActivateEmail), data)
|
||||
if err != nil {
|
||||
log.Error(3, "HTMLString: %v", err)
|
||||
return
|
||||
@@ -109,7 +114,7 @@ func SendRegisterNotifyMail(c *macaron.Context, u *User) {
|
||||
data := map[string]interface{}{
|
||||
"Username": u.DisplayName(),
|
||||
}
|
||||
body, err := mailRender.HTMLString(string(MailAuthRegisterNotify), data)
|
||||
body, err := mailRender.HTMLString(string(mailAuthRegisterNotify), data)
|
||||
if err != nil {
|
||||
log.Error(3, "HTMLString: %v", err)
|
||||
return
|
||||
@@ -131,7 +136,7 @@ func SendCollaboratorMail(u, doer *User, repo *Repository) {
|
||||
"RepoName": repoName,
|
||||
"Link": repo.HTMLURL(),
|
||||
}
|
||||
body, err := mailRender.HTMLString(string(MailNotifyCollaborator), data)
|
||||
body, err := mailRender.HTMLString(string(mailNotifyCollaborator), data)
|
||||
if err != nil {
|
||||
log.Error(3, "HTMLString: %v", err)
|
||||
return
|
||||
@@ -152,7 +157,7 @@ func composeTplData(subject, body, link string) map[string]interface{} {
|
||||
}
|
||||
|
||||
func composeIssueMessage(issue *Issue, doer *User, tplName base.TplName, tos []string, info string) *mailer.Message {
|
||||
subject := issue.MailSubject()
|
||||
subject := issue.mailSubject()
|
||||
body := string(markdown.RenderSpecialLink([]byte(issue.Content), issue.Repo.HTMLURL(), issue.Repo.ComposeMetas()))
|
||||
data := composeTplData(subject, body, issue.HTMLURL())
|
||||
data["Doer"] = doer
|
||||
@@ -171,7 +176,7 @@ func SendIssueCommentMail(issue *Issue, doer *User, tos []string) {
|
||||
return
|
||||
}
|
||||
|
||||
mailer.SendAsync(composeIssueMessage(issue, doer, MailIssueComment, tos, "issue comment"))
|
||||
mailer.SendAsync(composeIssueMessage(issue, doer, mailIssueComment, tos, "issue comment"))
|
||||
}
|
||||
|
||||
// SendIssueMentionMail composes and sends issue mention emails to target receivers.
|
||||
@@ -179,5 +184,5 @@ func SendIssueMentionMail(issue *Issue, doer *User, tos []string) {
|
||||
if len(tos) == 0 {
|
||||
return
|
||||
}
|
||||
mailer.SendAsync(composeIssueMessage(issue, doer, MailIssueMention, tos, "issue mention"))
|
||||
mailer.SendAsync(composeIssueMessage(issue, doer, mailIssueMention, tos, "issue mention"))
|
||||
}
|
||||
|
||||
@@ -25,8 +25,9 @@ import (
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
)
|
||||
|
||||
const _MIN_DB_VER = 4
|
||||
const minDBVersion = 4
|
||||
|
||||
// Migration describes on migration from lower version to high version
|
||||
type Migration interface {
|
||||
Description() string
|
||||
Migrate(*xorm.Engine) error
|
||||
@@ -37,19 +38,22 @@ type migration struct {
|
||||
migrate func(*xorm.Engine) error
|
||||
}
|
||||
|
||||
// NewMigration creates a new migration
|
||||
func NewMigration(desc string, fn func(*xorm.Engine) error) Migration {
|
||||
return &migration{desc, fn}
|
||||
}
|
||||
|
||||
// Description returns the migration's description
|
||||
func (m *migration) Description() string {
|
||||
return m.description
|
||||
}
|
||||
|
||||
// Migrate executes the migration
|
||||
func (m *migration) Migrate(x *xorm.Engine) error {
|
||||
return m.migrate(x)
|
||||
}
|
||||
|
||||
// The version table. Should have only one row with id==1
|
||||
// Version describes the version table. Should have only one row with id==1
|
||||
type Version struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
Version int64
|
||||
@@ -57,11 +61,11 @@ type Version struct {
|
||||
|
||||
// This is a sequence of migrations. Add new migrations to the bottom of the list.
|
||||
// If you want to "retire" a migration, remove it from the top of the list and
|
||||
// update _MIN_VER_DB accordingly
|
||||
// update minDBVersion accordingly
|
||||
var migrations = []Migration{
|
||||
// v0 -> v4: before 0.6.0 -> 0.7.33
|
||||
NewMigration("fix locale file load panic", fixLocaleFileLoadPanic), // V4 -> V5:v0.6.0
|
||||
NewMigration("trim action compare URL prefix", trimCommitActionAppUrlPrefix), // V5 -> V6:v0.6.3
|
||||
NewMigration("trim action compare URL prefix", trimCommitActionAppURLPrefix), // V5 -> V6:v0.6.3
|
||||
NewMigration("generate issue-label from issue", issueToIssueLabel), // V6 -> V7:v0.6.4
|
||||
NewMigration("refactor attachment table", attachmentRefactor), // V7 -> V8:v0.6.4
|
||||
NewMigration("rename pull request fields", renamePullRequestFields), // V8 -> V9:v0.6.16
|
||||
@@ -72,6 +76,8 @@ var migrations = []Migration{
|
||||
|
||||
// v13 -> v14:v0.9.87
|
||||
NewMigration("set comment updated with created", setCommentUpdatedWithCreated),
|
||||
|
||||
NewMigration("create user column diff view style", createUserColumnDiffViewStyle),
|
||||
}
|
||||
|
||||
// Migrate database to current version
|
||||
@@ -87,7 +93,7 @@ func Migrate(x *xorm.Engine) error {
|
||||
} else if !has {
|
||||
// If the version record does not exist we think
|
||||
// it is a fresh installation and we can skip all migrations.
|
||||
currentVersion.Version = int64(_MIN_DB_VER + len(migrations))
|
||||
currentVersion.Version = int64(minDBVersion + len(migrations))
|
||||
|
||||
if _, err = x.InsertOne(currentVersion); err != nil {
|
||||
return fmt.Errorf("insert: %v", err)
|
||||
@@ -95,19 +101,19 @@ func Migrate(x *xorm.Engine) error {
|
||||
}
|
||||
|
||||
v := currentVersion.Version
|
||||
if _MIN_DB_VER > v {
|
||||
log.Fatal(4, `Gogs no longer supports auto-migration from your previously installed version.
|
||||
if minDBVersion > v {
|
||||
log.Fatal(4, `Gogs no longer supports auto-migration from your previously installed version.
|
||||
Please try to upgrade to a lower version (>= v0.6.0) first, then upgrade to current version.`)
|
||||
return nil
|
||||
}
|
||||
|
||||
if int(v-_MIN_DB_VER) > len(migrations) {
|
||||
if int(v-minDBVersion) > len(migrations) {
|
||||
// User downgraded Gogs.
|
||||
currentVersion.Version = int64(len(migrations) + _MIN_DB_VER)
|
||||
currentVersion.Version = int64(len(migrations) + minDBVersion)
|
||||
_, err = x.Id(1).Update(currentVersion)
|
||||
return err
|
||||
}
|
||||
for i, m := range migrations[v-_MIN_DB_VER:] {
|
||||
for i, m := range migrations[v-minDBVersion:] {
|
||||
log.Info("Migration: %s", m.Description())
|
||||
if err = m.Migrate(x); err != nil {
|
||||
return fmt.Errorf("do migrate: %v", err)
|
||||
@@ -142,7 +148,7 @@ func fixLocaleFileLoadPanic(_ *xorm.Engine) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func trimCommitActionAppUrlPrefix(x *xorm.Engine) error {
|
||||
func trimCommitActionAppURLPrefix(x *xorm.Engine) error {
|
||||
type PushCommit struct {
|
||||
Sha1 string
|
||||
Message string
|
||||
@@ -153,7 +159,7 @@ func trimCommitActionAppUrlPrefix(x *xorm.Engine) error {
|
||||
type PushCommits struct {
|
||||
Len int
|
||||
Commits []*PushCommit
|
||||
CompareUrl string
|
||||
CompareURL string `json:"CompareUrl"`
|
||||
}
|
||||
|
||||
type Action struct {
|
||||
@@ -184,11 +190,11 @@ func trimCommitActionAppUrlPrefix(x *xorm.Engine) error {
|
||||
return fmt.Errorf("unmarshal action content[%d]: %v", actID, err)
|
||||
}
|
||||
|
||||
infos := strings.Split(pushCommits.CompareUrl, "/")
|
||||
infos := strings.Split(pushCommits.CompareURL, "/")
|
||||
if len(infos) <= 4 {
|
||||
continue
|
||||
}
|
||||
pushCommits.CompareUrl = strings.Join(infos[len(infos)-4:], "/")
|
||||
pushCommits.CompareURL = strings.Join(infos[len(infos)-4:], "/")
|
||||
|
||||
p, err := json.Marshal(pushCommits)
|
||||
if err != nil {
|
||||
@@ -461,27 +467,34 @@ func generateOrgRandsAndSalt(x *xorm.Engine) (err error) {
|
||||
return sess.Commit()
|
||||
}
|
||||
|
||||
// TAction defines the struct for migrating table action
|
||||
type TAction struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
CreatedUnix int64
|
||||
}
|
||||
|
||||
// TableName will be invoked by XORM to customrize the table name
|
||||
func (t *TAction) TableName() string { return "action" }
|
||||
|
||||
// TNotice defines the struct for migrating table notice
|
||||
type TNotice struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
CreatedUnix int64
|
||||
}
|
||||
|
||||
// TableName will be invoked by XORM to customrize the table name
|
||||
func (t *TNotice) TableName() string { return "notice" }
|
||||
|
||||
// TComment defines the struct for migrating table comment
|
||||
type TComment struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
CreatedUnix int64
|
||||
}
|
||||
|
||||
// TableName will be invoked by XORM to customrize the table name
|
||||
func (t *TComment) TableName() string { return "comment" }
|
||||
|
||||
// TIssue defines the struct for migrating table issue
|
||||
type TIssue struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
DeadlineUnix int64
|
||||
@@ -489,99 +502,124 @@ type TIssue struct {
|
||||
UpdatedUnix int64
|
||||
}
|
||||
|
||||
// TableName will be invoked by XORM to customrize the table name
|
||||
func (t *TIssue) TableName() string { return "issue" }
|
||||
|
||||
// TMilestone defines the struct for migrating table milestone
|
||||
type TMilestone struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
DeadlineUnix int64
|
||||
ClosedDateUnix int64
|
||||
}
|
||||
|
||||
// TableName will be invoked by XORM to customrize the table name
|
||||
func (t *TMilestone) TableName() string { return "milestone" }
|
||||
|
||||
// TAttachment defines the struct for migrating table attachment
|
||||
type TAttachment struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
CreatedUnix int64
|
||||
}
|
||||
|
||||
// TableName will be invoked by XORM to customrize the table name
|
||||
func (t *TAttachment) TableName() string { return "attachment" }
|
||||
|
||||
// TLoginSource defines the struct for migrating table login_source
|
||||
type TLoginSource struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
CreatedUnix int64
|
||||
UpdatedUnix int64
|
||||
}
|
||||
|
||||
// TableName will be invoked by XORM to customrize the table name
|
||||
func (t *TLoginSource) TableName() string { return "login_source" }
|
||||
|
||||
// TPull defines the struct for migrating table pull_request
|
||||
type TPull struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
MergedUnix int64
|
||||
}
|
||||
|
||||
// TableName will be invoked by XORM to customrize the table name
|
||||
func (t *TPull) TableName() string { return "pull_request" }
|
||||
|
||||
// TRelease defines the struct for migrating table release
|
||||
type TRelease struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
CreatedUnix int64
|
||||
}
|
||||
|
||||
// TableName will be invoked by XORM to customrize the table name
|
||||
func (t *TRelease) TableName() string { return "release" }
|
||||
|
||||
// TRepo defines the struct for migrating table repository
|
||||
type TRepo struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
CreatedUnix int64
|
||||
UpdatedUnix int64
|
||||
}
|
||||
|
||||
// TableName will be invoked by XORM to customrize the table name
|
||||
func (t *TRepo) TableName() string { return "repository" }
|
||||
|
||||
// TMirror defines the struct for migrating table mirror
|
||||
type TMirror struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
UpdatedUnix int64
|
||||
NextUpdateUnix int64
|
||||
}
|
||||
|
||||
// TableName will be invoked by XORM to customrize the table name
|
||||
func (t *TMirror) TableName() string { return "mirror" }
|
||||
|
||||
// TPublicKey defines the struct for migrating table public_key
|
||||
type TPublicKey struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
CreatedUnix int64
|
||||
UpdatedUnix int64
|
||||
}
|
||||
|
||||
// TableName will be invoked by XORM to customrize the table name
|
||||
func (t *TPublicKey) TableName() string { return "public_key" }
|
||||
|
||||
// TDeployKey defines the struct for migrating table deploy_key
|
||||
type TDeployKey struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
CreatedUnix int64
|
||||
UpdatedUnix int64
|
||||
}
|
||||
|
||||
// TableName will be invoked by XORM to customrize the table name
|
||||
func (t *TDeployKey) TableName() string { return "deploy_key" }
|
||||
|
||||
// TAccessToken defines the struct for migrating table access_token
|
||||
type TAccessToken struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
CreatedUnix int64
|
||||
UpdatedUnix int64
|
||||
}
|
||||
|
||||
// TableName will be invoked by XORM to customrize the table name
|
||||
func (t *TAccessToken) TableName() string { return "access_token" }
|
||||
|
||||
// TUser defines the struct for migrating table user
|
||||
type TUser struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
CreatedUnix int64
|
||||
UpdatedUnix int64
|
||||
}
|
||||
|
||||
// TableName will be invoked by XORM to customrize the table name
|
||||
func (t *TUser) TableName() string { return "user" }
|
||||
|
||||
// TWebhook defines the struct for migrating table webhook
|
||||
type TWebhook struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
CreatedUnix int64
|
||||
UpdatedUnix int64
|
||||
}
|
||||
|
||||
// TableName will be invoked by XORM to customrize the table name
|
||||
func (t *TWebhook) TableName() string { return "webhook" }
|
||||
|
||||
func convertDateToUnix(x *xorm.Engine) (err error) {
|
||||
|
||||
@@ -22,3 +22,17 @@ func setCommentUpdatedWithCreated(x *xorm.Engine) (err error) {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UserV14 describes the added fields for migrating from v13 -> v14
|
||||
type UserV14 struct {
|
||||
DiffViewStyle string `xorm:"NOT NULL DEFAULT ''"`
|
||||
}
|
||||
|
||||
// TableName will be invoked by XORM to customrize the table name
|
||||
func (*UserV14) TableName() string {
|
||||
return "user"
|
||||
}
|
||||
|
||||
func createUserColumnDiffViewStyle(x *xorm.Engine) error {
|
||||
return x.Sync2(new(UserV14))
|
||||
}
|
||||
|
||||
@@ -13,9 +13,12 @@ import (
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
// Needed for the MySQL driver
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/go-xorm/core"
|
||||
"github.com/go-xorm/xorm"
|
||||
|
||||
// Needed for the Postgresql driver
|
||||
_ "github.com/lib/pq"
|
||||
|
||||
"code.gitea.io/gitea/models/migrations"
|
||||
@@ -45,16 +48,22 @@ func sessionRelease(sess *xorm.Session) {
|
||||
}
|
||||
|
||||
var (
|
||||
x *xorm.Engine
|
||||
tables []interface{}
|
||||
x *xorm.Engine
|
||||
tables []interface{}
|
||||
|
||||
// HasEngine specifies if we have a xorm.Engine
|
||||
HasEngine bool
|
||||
|
||||
// DbCfg holds the database settings
|
||||
DbCfg struct {
|
||||
Type, Host, Name, User, Passwd, Path, SSLMode string
|
||||
}
|
||||
|
||||
// EnableSQLite3 use SQLite3
|
||||
EnableSQLite3 bool
|
||||
EnableTiDB bool
|
||||
|
||||
// EnableTiDB enable TiDB
|
||||
EnableTiDB bool
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -69,12 +78,13 @@ func init() {
|
||||
new(Team), new(OrgUser), new(TeamUser), new(TeamRepo),
|
||||
new(Notice), new(EmailAddress))
|
||||
|
||||
gonicNames := []string{"SSL"}
|
||||
gonicNames := []string{"SSL", "UID"}
|
||||
for _, name := range gonicNames {
|
||||
core.LintGonicMapper[name] = true
|
||||
}
|
||||
}
|
||||
|
||||
// LoadConfigs loads the database settings
|
||||
func LoadConfigs() {
|
||||
sec := setting.Cfg.Section("database")
|
||||
DbCfg.Type = sec.Key("DB_TYPE").String()
|
||||
@@ -115,7 +125,7 @@ func parsePostgreSQLHostPort(info string) (string, string) {
|
||||
|
||||
func getEngine() (*xorm.Engine, error) {
|
||||
connStr := ""
|
||||
var Param string = "?"
|
||||
var Param = "?"
|
||||
if strings.Contains(DbCfg.Name, Param) {
|
||||
Param = "&"
|
||||
}
|
||||
@@ -139,7 +149,7 @@ func getEngine() (*xorm.Engine, error) {
|
||||
}
|
||||
case "sqlite3":
|
||||
if !EnableSQLite3 {
|
||||
return nil, errors.New("This binary version does not build support for SQLite3.")
|
||||
return nil, errors.New("this binary version does not build support for SQLite3")
|
||||
}
|
||||
if err := os.MkdirAll(path.Dir(DbCfg.Path), os.ModePerm); err != nil {
|
||||
return nil, fmt.Errorf("Fail to create directories: %v", err)
|
||||
@@ -147,7 +157,7 @@ func getEngine() (*xorm.Engine, error) {
|
||||
connStr = "file:" + DbCfg.Path + "?cache=shared&mode=rwc"
|
||||
case "tidb":
|
||||
if !EnableTiDB {
|
||||
return nil, errors.New("This binary version does not build support for TiDB.")
|
||||
return nil, errors.New("this binary version does not build support for TiDB")
|
||||
}
|
||||
if err := os.MkdirAll(path.Dir(DbCfg.Path), os.ModePerm); err != nil {
|
||||
return nil, fmt.Errorf("Fail to create directories: %v", err)
|
||||
@@ -159,6 +169,7 @@ func getEngine() (*xorm.Engine, error) {
|
||||
return xorm.NewEngine(DbCfg.Type, connStr)
|
||||
}
|
||||
|
||||
// NewTestEngine sets a new test xorm.Engine
|
||||
func NewTestEngine(x *xorm.Engine) (err error) {
|
||||
x, err = getEngine()
|
||||
if err != nil {
|
||||
@@ -169,6 +180,7 @@ func NewTestEngine(x *xorm.Engine) (err error) {
|
||||
return x.StoreEngine("InnoDB").Sync2(tables...)
|
||||
}
|
||||
|
||||
// SetEngine sets the xorm.Engine
|
||||
func SetEngine() (err error) {
|
||||
x, err = getEngine()
|
||||
if err != nil {
|
||||
@@ -180,7 +192,10 @@ func SetEngine() (err error) {
|
||||
// WARNING: for serv command, MUST remove the output to os.stdout,
|
||||
// so use log file to instead print to stdout.
|
||||
logPath := path.Join(setting.LogRootPath, "xorm.log")
|
||||
os.MkdirAll(path.Dir(logPath), os.ModePerm)
|
||||
|
||||
if err := os.MkdirAll(path.Dir(logPath), os.ModePerm); err != nil {
|
||||
return fmt.Errorf("Fail to create dir %s: %v", logPath, err)
|
||||
}
|
||||
|
||||
f, err := os.Create(logPath)
|
||||
if err != nil {
|
||||
@@ -191,22 +206,28 @@ func SetEngine() (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewEngine initializes a new xorm.Engine
|
||||
func NewEngine() (err error) {
|
||||
if err = SetEngine(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = x.Ping(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = migrations.Migrate(x); err != nil {
|
||||
return fmt.Errorf("migrate: %v", err)
|
||||
}
|
||||
|
||||
if err = x.StoreEngine("InnoDB").Sync2(tables...); err != nil {
|
||||
return fmt.Errorf("sync database struct error: %v\n", err)
|
||||
return fmt.Errorf("sync database struct error: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Statistic contains the database statistics
|
||||
type Statistic struct {
|
||||
Counter struct {
|
||||
User, Org, PublicKey,
|
||||
@@ -218,6 +239,7 @@ type Statistic struct {
|
||||
}
|
||||
}
|
||||
|
||||
// GetStatistic returns the database statistics
|
||||
func GetStatistic() (stats Statistic) {
|
||||
stats.Counter.User = CountUsers()
|
||||
stats.Counter.Org = CountOrganizations()
|
||||
@@ -244,6 +266,7 @@ func GetStatistic() (stats Statistic) {
|
||||
return
|
||||
}
|
||||
|
||||
// Ping tests if database is alive
|
||||
func Ping() error {
|
||||
return x.Ping()
|
||||
}
|
||||
|
||||
@@ -10,13 +10,14 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/go-xorm/builder"
|
||||
"github.com/go-xorm/xorm"
|
||||
|
||||
"code.gitea.io/gitea/modules/base"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrOrgNotExist = errors.New("Organization does not exist")
|
||||
// ErrOrgNotExist organization does not exist
|
||||
ErrOrgNotExist = errors.New("Organization does not exist")
|
||||
// ErrTeamNotExist team does not exist
|
||||
ErrTeamNotExist = errors.New("Team does not exist")
|
||||
)
|
||||
|
||||
@@ -40,7 +41,7 @@ func (org *User) GetTeam(name string) (*Team, error) {
|
||||
}
|
||||
|
||||
func (org *User) getOwnerTeam(e Engine) (*Team, error) {
|
||||
return org.getTeam(e, OWNER_TEAM)
|
||||
return org.getTeam(e, ownerTeamName)
|
||||
}
|
||||
|
||||
// GetOwnerTeam returns owner team of organization.
|
||||
@@ -51,7 +52,7 @@ func (org *User) GetOwnerTeam() (*Team, error) {
|
||||
func (org *User) getTeams(e Engine) error {
|
||||
return e.
|
||||
Where("org_id=?", org.ID).
|
||||
OrderBy("CASE WHEN name LIKE '" + OWNER_TEAM + "' THEN '' ELSE name END").
|
||||
OrderBy("CASE WHEN name LIKE '" + ownerTeamName + "' THEN '' ELSE name END").
|
||||
Find(&org.Teams)
|
||||
}
|
||||
|
||||
@@ -69,7 +70,7 @@ func (org *User) GetMembers() error {
|
||||
|
||||
var ids = make([]int64, len(ous))
|
||||
for i, ou := range ous {
|
||||
ids[i] = ou.Uid
|
||||
ids[i] = ou.UID
|
||||
}
|
||||
org.Members, err = GetUsersByIDs(ids)
|
||||
return err
|
||||
@@ -128,7 +129,7 @@ func CreateOrganization(org, owner *User) (err error) {
|
||||
|
||||
// Add initial creator to organization and owner team.
|
||||
if _, err = sess.Insert(&OrgUser{
|
||||
Uid: owner.ID,
|
||||
UID: owner.ID,
|
||||
OrgID: org.ID,
|
||||
IsOwner: true,
|
||||
NumTeams: 1,
|
||||
@@ -139,8 +140,8 @@ func CreateOrganization(org, owner *User) (err error) {
|
||||
// Create default owner team.
|
||||
t := &Team{
|
||||
OrgID: org.ID,
|
||||
LowerName: strings.ToLower(OWNER_TEAM),
|
||||
Name: OWNER_TEAM,
|
||||
LowerName: strings.ToLower(ownerTeamName),
|
||||
Name: ownerTeamName,
|
||||
Authorize: AccessModeOwner,
|
||||
NumMembers: 1,
|
||||
}
|
||||
@@ -149,7 +150,7 @@ func CreateOrganization(org, owner *User) (err error) {
|
||||
}
|
||||
|
||||
if _, err = sess.Insert(&TeamUser{
|
||||
Uid: owner.ID,
|
||||
UID: owner.ID,
|
||||
OrgID: org.ID,
|
||||
TeamID: t.ID,
|
||||
}); err != nil {
|
||||
@@ -236,7 +237,7 @@ func DeleteOrganization(org *User) (err error) {
|
||||
// OrgUser represents an organization-user relation.
|
||||
type OrgUser struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
Uid int64 `xorm:"INDEX UNIQUE(s)"`
|
||||
UID int64 `xorm:"INDEX UNIQUE(s)"`
|
||||
OrgID int64 `xorm:"INDEX UNIQUE(s)"`
|
||||
IsPublic bool
|
||||
IsOwner bool
|
||||
@@ -244,29 +245,29 @@ type OrgUser struct {
|
||||
}
|
||||
|
||||
// IsOrganizationOwner returns true if given user is in the owner team.
|
||||
func IsOrganizationOwner(orgId, uid int64) bool {
|
||||
func IsOrganizationOwner(orgID, uid int64) bool {
|
||||
has, _ := x.
|
||||
Where("is_owner=?", true).
|
||||
And("uid=?", uid).
|
||||
And("org_id=?", orgId).
|
||||
And("org_id=?", orgID).
|
||||
Get(new(OrgUser))
|
||||
return has
|
||||
}
|
||||
|
||||
// IsOrganizationMember returns true if given user is member of organization.
|
||||
func IsOrganizationMember(orgId, uid int64) bool {
|
||||
func IsOrganizationMember(orgID, uid int64) bool {
|
||||
has, _ := x.
|
||||
Where("uid=?", uid).
|
||||
And("org_id=?", orgId).
|
||||
And("org_id=?", orgID).
|
||||
Get(new(OrgUser))
|
||||
return has
|
||||
}
|
||||
|
||||
// IsPublicMembership returns true if given user public his/her membership.
|
||||
func IsPublicMembership(orgId, uid int64) bool {
|
||||
func IsPublicMembership(orgID, uid int64) bool {
|
||||
has, _ := x.
|
||||
Where("uid=?", uid).
|
||||
And("org_id=?", orgId).
|
||||
And("org_id=?", orgID).
|
||||
And("is_public=?", true).
|
||||
Get(new(OrgUser))
|
||||
return has
|
||||
@@ -312,7 +313,7 @@ func GetOwnedOrgsByUserID(userID int64) ([]*User, error) {
|
||||
return getOwnedOrgsByUserID(sess, userID)
|
||||
}
|
||||
|
||||
// GetOwnedOrganizationsByUserIDDesc returns a list of organizations are owned by
|
||||
// GetOwnedOrgsByUserIDDesc returns a list of organizations are owned by
|
||||
// given user ID, ordered descending by the given condition.
|
||||
func GetOwnedOrgsByUserIDDesc(userID int64, desc string) ([]*User, error) {
|
||||
sess := x.NewSession()
|
||||
@@ -323,8 +324,8 @@ func GetOwnedOrgsByUserIDDesc(userID int64, desc string) ([]*User, error) {
|
||||
func GetOrgUsersByUserID(uid int64, all bool) ([]*OrgUser, error) {
|
||||
ous := make([]*OrgUser, 0, 10)
|
||||
sess := x.
|
||||
Join("LEFT", "user", `"org_user".org_id="user".id`).
|
||||
Where(`"org_user".uid=?`, uid)
|
||||
Join("LEFT", "user", "`org_user`.org_id=`user`.id").
|
||||
Where("`org_user`.uid=?", uid)
|
||||
if !all {
|
||||
// Only show public organizations
|
||||
sess.And("is_public=?", true)
|
||||
@@ -375,7 +376,7 @@ func AddOrgUser(orgID, uid int64) error {
|
||||
}
|
||||
|
||||
ou := &OrgUser{
|
||||
Uid: uid,
|
||||
UID: uid,
|
||||
OrgID: orgID,
|
||||
}
|
||||
|
||||
@@ -499,7 +500,7 @@ func (org *User) getUserTeams(e Engine, userID int64, cols ...string) ([]*Team,
|
||||
Find(&teams)
|
||||
}
|
||||
|
||||
// GetUserTeamIDs returns of all team IDs of the organization that user is memeber of.
|
||||
// GetUserTeamIDs returns of all team IDs of the organization that user is member of.
|
||||
func (org *User) GetUserTeamIDs(userID int64) ([]int64, error) {
|
||||
teams, err := org.getUserTeams(x, userID, "team.id")
|
||||
if err != nil {
|
||||
@@ -513,7 +514,7 @@ func (org *User) GetUserTeamIDs(userID int64) ([]int64, error) {
|
||||
return teamIDs, nil
|
||||
}
|
||||
|
||||
// GetTeams returns all teams that belong to organization,
|
||||
// GetUserTeams returns all teams that belong to user,
|
||||
// and that the user has joined.
|
||||
func (org *User) GetUserTeams(userID int64) ([]*Team, error) {
|
||||
return org.getUserTeams(x, userID)
|
||||
@@ -540,7 +541,7 @@ func (org *User) GetUserRepositories(userID int64, page, pageSize int) ([]*Repos
|
||||
Select("`repository`.*").
|
||||
Join("INNER", "team_repo", "`team_repo`.repo_id=`repository`.id").
|
||||
Where("(`repository`.owner_id=? AND `repository`.is_private=?)", org.ID, false).
|
||||
Or("team_repo.team_id IN (?)", strings.Join(base.Int64sToStrings(teamIDs), ",")).
|
||||
Or(builder.In("team_repo.team_id", teamIDs)).
|
||||
GroupBy("`repository`.id").
|
||||
OrderBy("updated_unix DESC").
|
||||
Limit(pageSize, (page-1)*pageSize).
|
||||
@@ -551,7 +552,7 @@ func (org *User) GetUserRepositories(userID int64, page, pageSize int) ([]*Repos
|
||||
repoCount, err := x.
|
||||
Join("INNER", "team_repo", "`team_repo`.repo_id=`repository`.id").
|
||||
Where("(`repository`.owner_id=? AND `repository`.is_private=?)", org.ID, false).
|
||||
Or("team_repo.team_id IN (?)", strings.Join(base.Int64sToStrings(teamIDs), ",")).
|
||||
Or(builder.In("team_repo.team_id", teamIDs)).
|
||||
GroupBy("`repository`.id").
|
||||
Count(&Repository{})
|
||||
if err != nil {
|
||||
@@ -561,7 +562,7 @@ func (org *User) GetUserRepositories(userID int64, page, pageSize int) ([]*Repos
|
||||
return repos, repoCount, nil
|
||||
}
|
||||
|
||||
// GetUserRepositories returns mirror repositories of the organization
|
||||
// GetUserMirrorRepositories returns mirror repositories of the user
|
||||
// that the user with the given userID has access to.
|
||||
func (org *User) GetUserMirrorRepositories(userID int64) ([]*Repository, error) {
|
||||
teamIDs, err := org.GetUserTeamIDs(userID)
|
||||
@@ -577,7 +578,7 @@ func (org *User) GetUserMirrorRepositories(userID int64) ([]*Repository, error)
|
||||
Select("`repository`.*").
|
||||
Join("INNER", "team_repo", "`team_repo`.repo_id=`repository`.id AND `repository`.is_mirror=?", true).
|
||||
Where("(`repository`.owner_id=? AND `repository`.is_private=?)", org.ID, false).
|
||||
Or("team_repo.team_id IN (?)", strings.Join(base.Int64sToStrings(teamIDs), ",")).
|
||||
Or(builder.In("team_repo.team_id", teamIDs)).
|
||||
GroupBy("`repository`.id").
|
||||
OrderBy("updated_unix DESC").
|
||||
Find(&repos)
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
const OWNER_TEAM = "Owners"
|
||||
const ownerTeamName = "Owners"
|
||||
|
||||
// Team represents a organization team.
|
||||
type Team struct {
|
||||
@@ -28,12 +28,12 @@ type Team struct {
|
||||
|
||||
// IsOwnerTeam returns true if team is owner team.
|
||||
func (t *Team) IsOwnerTeam() bool {
|
||||
return t.Name == OWNER_TEAM
|
||||
return t.Name == ownerTeamName
|
||||
}
|
||||
|
||||
// IsTeamMember returns true if given user is a member of team.
|
||||
func (t *Team) IsMember(uid int64) bool {
|
||||
return IsTeamMember(t.OrgID, t.ID, uid)
|
||||
// IsMember returns true if given user is a member of team.
|
||||
func (t *Team) IsMember(userID int64) bool {
|
||||
return IsTeamMember(t.OrgID, t.ID, userID)
|
||||
}
|
||||
|
||||
func (t *Team) getRepositories(e Engine) (err error) {
|
||||
@@ -72,13 +72,13 @@ func (t *Team) GetMembers() (err error) {
|
||||
|
||||
// AddMember adds new membership of the team to the organization,
|
||||
// the user will have membership to the organization automatically when needed.
|
||||
func (t *Team) AddMember(uid int64) error {
|
||||
return AddTeamMember(t.OrgID, t.ID, uid)
|
||||
func (t *Team) AddMember(userID int64) error {
|
||||
return AddTeamMember(t.OrgID, t.ID, userID)
|
||||
}
|
||||
|
||||
// RemoveMember removes member from team of organization.
|
||||
func (t *Team) RemoveMember(uid int64) error {
|
||||
return RemoveTeamMember(t.OrgID, t.ID, uid)
|
||||
func (t *Team) RemoveMember(userID int64) error {
|
||||
return RemoveTeamMember(t.OrgID, t.ID, userID)
|
||||
}
|
||||
|
||||
func (t *Team) hasRepository(e Engine, repoID int64) bool {
|
||||
@@ -196,6 +196,7 @@ func (t *Team) RemoveRepository(repoID int64) error {
|
||||
return sess.Commit()
|
||||
}
|
||||
|
||||
// IsUsableTeamName tests if a name could be as team name
|
||||
func IsUsableTeamName(name string) (err error) {
|
||||
var reservedTeamNames = []string{"new"}
|
||||
|
||||
@@ -256,9 +257,9 @@ func NewTeam(t *Team) (err error) {
|
||||
return sess.Commit()
|
||||
}
|
||||
|
||||
func getTeam(e Engine, orgId int64, name string) (*Team, error) {
|
||||
func getTeam(e Engine, orgID int64, name string) (*Team, error) {
|
||||
t := &Team{
|
||||
OrgID: orgId,
|
||||
OrgID: orgID,
|
||||
LowerName: strings.ToLower(name),
|
||||
}
|
||||
has, err := e.Get(t)
|
||||
@@ -271,13 +272,13 @@ func getTeam(e Engine, orgId int64, name string) (*Team, error) {
|
||||
}
|
||||
|
||||
// GetTeam returns team by given team name and organization.
|
||||
func GetTeam(orgId int64, name string) (*Team, error) {
|
||||
return getTeam(x, orgId, name)
|
||||
func GetTeam(orgID int64, name string) (*Team, error) {
|
||||
return getTeam(x, orgID, name)
|
||||
}
|
||||
|
||||
func getTeamByID(e Engine, teamId int64) (*Team, error) {
|
||||
func getTeamByID(e Engine, teamID int64) (*Team, error) {
|
||||
t := new(Team)
|
||||
has, err := e.Id(teamId).Get(t)
|
||||
has, err := e.Id(teamID).Get(t)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !has {
|
||||
@@ -287,8 +288,8 @@ func getTeamByID(e Engine, teamId int64) (*Team, error) {
|
||||
}
|
||||
|
||||
// GetTeamByID returns team by given ID.
|
||||
func GetTeamByID(teamId int64) (*Team, error) {
|
||||
return getTeamByID(x, teamId)
|
||||
func GetTeamByID(teamID int64) (*Team, error) {
|
||||
return getTeamByID(x, teamID)
|
||||
}
|
||||
|
||||
// UpdateTeam updates information of team.
|
||||
@@ -397,21 +398,21 @@ type TeamUser struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
OrgID int64 `xorm:"INDEX"`
|
||||
TeamID int64 `xorm:"UNIQUE(s)"`
|
||||
Uid int64 `xorm:"UNIQUE(s)"`
|
||||
UID int64 `xorm:"UNIQUE(s)"`
|
||||
}
|
||||
|
||||
func isTeamMember(e Engine, orgID, teamID, uid int64) bool {
|
||||
func isTeamMember(e Engine, orgID, teamID, userID int64) bool {
|
||||
has, _ := e.
|
||||
Where("org_id=?", orgID).
|
||||
And("team_id=?", teamID).
|
||||
And("uid=?", uid).
|
||||
And("uid=?", userID).
|
||||
Get(new(TeamUser))
|
||||
return has
|
||||
}
|
||||
|
||||
// IsTeamMember returns true if given user is a member of team.
|
||||
func IsTeamMember(orgID, teamID, uid int64) bool {
|
||||
return isTeamMember(x, orgID, teamID, uid)
|
||||
func IsTeamMember(orgID, teamID, userID int64) bool {
|
||||
return isTeamMember(x, orgID, teamID, userID)
|
||||
}
|
||||
|
||||
func getTeamMembers(e Engine, teamID int64) (_ []*User, err error) {
|
||||
@@ -424,8 +425,8 @@ func getTeamMembers(e Engine, teamID int64) (_ []*User, err error) {
|
||||
members := make([]*User, 0, len(teamUsers))
|
||||
for i := range teamUsers {
|
||||
member := new(User)
|
||||
if _, err = e.Id(teamUsers[i].Uid).Get(member); err != nil {
|
||||
return nil, fmt.Errorf("get user '%d': %v", teamUsers[i].Uid, err)
|
||||
if _, err = e.Id(teamUsers[i].UID).Get(member); err != nil {
|
||||
return nil, fmt.Errorf("get user '%d': %v", teamUsers[i].UID, err)
|
||||
}
|
||||
members = append(members, member)
|
||||
}
|
||||
@@ -437,11 +438,11 @@ func GetTeamMembers(teamID int64) ([]*User, error) {
|
||||
return getTeamMembers(x, teamID)
|
||||
}
|
||||
|
||||
func getUserTeams(e Engine, orgId, uid int64) ([]*Team, error) {
|
||||
func getUserTeams(e Engine, orgID, userID int64) ([]*Team, error) {
|
||||
tus := make([]*TeamUser, 0, 5)
|
||||
if err := e.
|
||||
Where("uid=?", uid).
|
||||
And("org_id=?", orgId).
|
||||
Where("uid=?", userID).
|
||||
And("org_id=?", orgID).
|
||||
Find(&tus); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -461,18 +462,18 @@ func getUserTeams(e Engine, orgId, uid int64) ([]*Team, error) {
|
||||
}
|
||||
|
||||
// GetUserTeams returns all teams that user belongs to in given organization.
|
||||
func GetUserTeams(orgId, uid int64) ([]*Team, error) {
|
||||
return getUserTeams(x, orgId, uid)
|
||||
func GetUserTeams(orgID, userID int64) ([]*Team, error) {
|
||||
return getUserTeams(x, orgID, userID)
|
||||
}
|
||||
|
||||
// AddTeamMember adds new membership of given team to given organization,
|
||||
// the user will have membership to given organization automatically when needed.
|
||||
func AddTeamMember(orgID, teamID, uid int64) error {
|
||||
if IsTeamMember(orgID, teamID, uid) {
|
||||
func AddTeamMember(orgID, teamID, userID int64) error {
|
||||
if IsTeamMember(orgID, teamID, userID) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := AddOrgUser(orgID, uid); err != nil {
|
||||
if err := AddOrgUser(orgID, userID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -494,7 +495,7 @@ func AddTeamMember(orgID, teamID, uid int64) error {
|
||||
}
|
||||
|
||||
tu := &TeamUser{
|
||||
Uid: uid,
|
||||
UID: userID,
|
||||
OrgID: orgID,
|
||||
TeamID: teamID,
|
||||
}
|
||||
@@ -514,7 +515,7 @@ func AddTeamMember(orgID, teamID, uid int64) error {
|
||||
// We make sure it exists before.
|
||||
ou := new(OrgUser)
|
||||
if _, err = sess.
|
||||
Where("uid = ?", uid).
|
||||
Where("uid = ?", userID).
|
||||
And("org_id = ?", orgID).
|
||||
Get(ou); err != nil {
|
||||
return err
|
||||
@@ -530,8 +531,8 @@ func AddTeamMember(orgID, teamID, uid int64) error {
|
||||
return sess.Commit()
|
||||
}
|
||||
|
||||
func removeTeamMember(e Engine, orgID, teamID, uid int64) error {
|
||||
if !isTeamMember(e, orgID, teamID, uid) {
|
||||
func removeTeamMember(e Engine, orgID, teamID, userID int64) error {
|
||||
if !isTeamMember(e, orgID, teamID, userID) {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -543,7 +544,7 @@ func removeTeamMember(e Engine, orgID, teamID, uid int64) error {
|
||||
|
||||
// Check if the user to delete is the last member in owner team.
|
||||
if t.IsOwnerTeam() && t.NumMembers == 1 {
|
||||
return ErrLastOrgOwner{UID: uid}
|
||||
return ErrLastOrgOwner{UID: userID}
|
||||
}
|
||||
|
||||
t.NumMembers--
|
||||
@@ -559,7 +560,7 @@ func removeTeamMember(e Engine, orgID, teamID, uid int64) error {
|
||||
}
|
||||
|
||||
tu := &TeamUser{
|
||||
Uid: uid,
|
||||
UID: userID,
|
||||
OrgID: orgID,
|
||||
TeamID: teamID,
|
||||
}
|
||||
@@ -582,7 +583,7 @@ func removeTeamMember(e Engine, orgID, teamID, uid int64) error {
|
||||
// This must exist.
|
||||
ou := new(OrgUser)
|
||||
_, err = e.
|
||||
Where("uid = ?", uid).
|
||||
Where("uid = ?", userID).
|
||||
And("org_id = ?", org.ID).
|
||||
Get(ou)
|
||||
if err != nil {
|
||||
@@ -602,13 +603,13 @@ func removeTeamMember(e Engine, orgID, teamID, uid int64) error {
|
||||
}
|
||||
|
||||
// RemoveTeamMember removes member from given team of given organization.
|
||||
func RemoveTeamMember(orgID, teamID, uid int64) error {
|
||||
func RemoveTeamMember(orgID, teamID, userID int64) error {
|
||||
sess := x.NewSession()
|
||||
defer sessionRelease(sess)
|
||||
if err := sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := removeTeamMember(sess, orgID, teamID, uid); err != nil {
|
||||
if err := removeTeamMember(sess, orgID, teamID, userID); err != nil {
|
||||
return err
|
||||
}
|
||||
return sess.Commit()
|
||||
|
||||
166
models/pull.go
166
models/pull.go
@@ -21,17 +21,21 @@ import (
|
||||
"github.com/go-xorm/xorm"
|
||||
)
|
||||
|
||||
var PullRequestQueue = sync.NewUniqueQueue(setting.Repository.PullRequestQueueLength)
|
||||
var pullRequestQueue = sync.NewUniqueQueue(setting.Repository.PullRequestQueueLength)
|
||||
|
||||
// PullRequestType defines pull request type
|
||||
type PullRequestType int
|
||||
|
||||
// Enumerate all the pull request types
|
||||
const (
|
||||
PullRequestGitea PullRequestType = iota
|
||||
PullRequestGit
|
||||
)
|
||||
|
||||
// PullRequestStatus defines pull request status
|
||||
type PullRequestStatus int
|
||||
|
||||
// Enumerate all the pull request status
|
||||
const (
|
||||
PullRequestStatusConflict PullRequestStatus = iota
|
||||
PullRequestStatusChecking
|
||||
@@ -65,10 +69,12 @@ type PullRequest struct {
|
||||
MergedUnix int64
|
||||
}
|
||||
|
||||
// BeforeUpdate is invoked from XORM before updating an object of this type.
|
||||
func (pr *PullRequest) BeforeUpdate() {
|
||||
pr.MergedUnix = pr.Merged.Unix()
|
||||
}
|
||||
|
||||
// AfterSet is invoked from XORM after setting the value of a field of this object.
|
||||
// Note: don't try to get Issue because will end up recursive querying.
|
||||
func (pr *PullRequest) AfterSet(colName string, _ xorm.Cell) {
|
||||
switch colName {
|
||||
@@ -96,10 +102,12 @@ func (pr *PullRequest) loadAttributes(e Engine) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// LoadAttributes loads pull request attributes from database
|
||||
func (pr *PullRequest) LoadAttributes() error {
|
||||
return pr.loadAttributes(x)
|
||||
}
|
||||
|
||||
// LoadIssue loads issue information from database
|
||||
func (pr *PullRequest) LoadIssue() (err error) {
|
||||
if pr.Issue != nil {
|
||||
return nil
|
||||
@@ -109,11 +117,58 @@ func (pr *PullRequest) LoadIssue() (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
// This method assumes following fields have been assigned with valid values:
|
||||
// APIFormat assumes following fields have been assigned with valid values:
|
||||
// Required - Issue
|
||||
// Optional - Merger
|
||||
func (pr *PullRequest) APIFormat() *api.PullRequest {
|
||||
var (
|
||||
baseBranch *Branch
|
||||
headBranch *Branch
|
||||
baseCommit *git.Commit
|
||||
headCommit *git.Commit
|
||||
err error
|
||||
)
|
||||
apiIssue := pr.Issue.APIFormat()
|
||||
if pr.BaseRepo == nil {
|
||||
pr.BaseRepo, err = GetRepositoryByID(pr.BaseRepoID)
|
||||
if err != nil {
|
||||
log.Error(log.ERROR, "GetRepositoryById[%d]: %v", pr.ID, err)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if pr.HeadRepo == nil {
|
||||
pr.HeadRepo, err = GetRepositoryByID(pr.HeadRepoID)
|
||||
if err != nil {
|
||||
log.Error(log.ERROR, "GetRepositoryById[%d]: %v", pr.ID, err)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if baseBranch, err = pr.BaseRepo.GetBranch(pr.BaseBranch); err != nil {
|
||||
return nil
|
||||
}
|
||||
if baseCommit, err = baseBranch.GetCommit(); err != nil {
|
||||
return nil
|
||||
}
|
||||
if headBranch, err = pr.HeadRepo.GetBranch(pr.HeadBranch); err != nil {
|
||||
return nil
|
||||
}
|
||||
if headCommit, err = headBranch.GetCommit(); err != nil {
|
||||
return nil
|
||||
}
|
||||
apiBaseBranchInfo := &api.PRBranchInfo{
|
||||
Name: pr.BaseBranch,
|
||||
Ref: pr.BaseBranch,
|
||||
Sha: baseCommit.ID.String(),
|
||||
RepoID: pr.BaseRepoID,
|
||||
Repository: pr.BaseRepo.APIFormat(nil),
|
||||
}
|
||||
apiHeadBranchInfo := &api.PRBranchInfo{
|
||||
Name: pr.HeadBranch,
|
||||
Ref: pr.HeadBranch,
|
||||
Sha: headCommit.ID.String(),
|
||||
RepoID: pr.HeadRepoID,
|
||||
Repository: pr.HeadRepo.APIFormat(nil),
|
||||
}
|
||||
apiPullRequest := &api.PullRequest{
|
||||
ID: pr.ID,
|
||||
Index: pr.Index,
|
||||
@@ -126,12 +181,17 @@ func (pr *PullRequest) APIFormat() *api.PullRequest {
|
||||
State: apiIssue.State,
|
||||
Comments: apiIssue.Comments,
|
||||
HTMLURL: pr.Issue.HTMLURL(),
|
||||
DiffURL: pr.Issue.DiffURL(),
|
||||
PatchURL: pr.Issue.PatchURL(),
|
||||
HasMerged: pr.HasMerged,
|
||||
Base: apiBaseBranchInfo,
|
||||
Head: apiHeadBranchInfo,
|
||||
MergeBase: pr.MergeBase,
|
||||
}
|
||||
|
||||
if pr.Status != PullRequestStatusChecking {
|
||||
mergeable := pr.Status != PullRequestStatusConflict
|
||||
apiPullRequest.Mergeable = &mergeable
|
||||
apiPullRequest.Mergeable = mergeable
|
||||
}
|
||||
if pr.HasMerged {
|
||||
apiPullRequest.Merged = &pr.Merged
|
||||
@@ -150,10 +210,12 @@ func (pr *PullRequest) getHeadRepo(e Engine) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetHeadRepo loads the head repository
|
||||
func (pr *PullRequest) GetHeadRepo() error {
|
||||
return pr.getHeadRepo(x)
|
||||
}
|
||||
|
||||
// GetBaseRepo loads the target repository
|
||||
func (pr *PullRequest) GetBaseRepo() (err error) {
|
||||
if pr.BaseRepo != nil {
|
||||
return nil
|
||||
@@ -208,7 +270,11 @@ func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository) (err error
|
||||
|
||||
// Clone base repo.
|
||||
tmpBasePath := path.Join(setting.AppDataPath, "tmp/repos", com.ToStr(time.Now().Nanosecond())+".git")
|
||||
os.MkdirAll(path.Dir(tmpBasePath), os.ModePerm)
|
||||
|
||||
if err := os.MkdirAll(path.Dir(tmpBasePath), os.ModePerm); err != nil {
|
||||
return fmt.Errorf("Fail to create dir %s: %v", tmpBasePath, err)
|
||||
}
|
||||
|
||||
defer os.RemoveAll(path.Dir(tmpBasePath))
|
||||
|
||||
var stderr string
|
||||
@@ -317,8 +383,8 @@ func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository) (err error
|
||||
Ref: git.BRANCH_PREFIX + pr.BaseBranch,
|
||||
Before: pr.MergeBase,
|
||||
After: pr.MergedCommitID,
|
||||
CompareURL: setting.AppUrl + pr.BaseRepo.ComposeCompareURL(pr.MergeBase, pr.MergedCommitID),
|
||||
Commits: ListToPushCommits(l).ToApiPayloadCommits(pr.BaseRepo.HTMLURL()),
|
||||
CompareURL: setting.AppURL + pr.BaseRepo.ComposeCompareURL(pr.MergeBase, pr.MergedCommitID),
|
||||
Commits: ListToPushCommits(l).ToAPIPayloadCommits(pr.BaseRepo.HTMLURL()),
|
||||
Repo: pr.BaseRepo.APIFormat(nil),
|
||||
Pusher: pr.HeadRepo.MustOwner().APIFormat(),
|
||||
Sender: doer.APIFormat(),
|
||||
@@ -329,7 +395,7 @@ func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository) (err error
|
||||
return nil
|
||||
}
|
||||
|
||||
// patchConflicts is a list of conflit description from Git.
|
||||
// patchConflicts is a list of conflict description from Git.
|
||||
var patchConflicts = []string{
|
||||
"patch does not apply",
|
||||
"already exists in working directory",
|
||||
@@ -337,7 +403,7 @@ var patchConflicts = []string{
|
||||
"error:",
|
||||
}
|
||||
|
||||
// testPatch checks if patch can be merged to base repository without conflit.
|
||||
// testPatch checks if patch can be merged to base repository without conflict.
|
||||
// FIXME: make a mechanism to clean up stable local copies.
|
||||
func (pr *PullRequest) testPatch() (err error) {
|
||||
if pr.BaseRepo == nil {
|
||||
@@ -374,7 +440,7 @@ func (pr *PullRequest) testPatch() (err error) {
|
||||
if err != nil {
|
||||
for i := range patchConflicts {
|
||||
if strings.Contains(stderr, patchConflicts[i]) {
|
||||
log.Trace("PullRequest[%d].testPatch (apply): has conflit", pr.ID)
|
||||
log.Trace("PullRequest[%d].testPatch (apply): has conflict", pr.ID)
|
||||
fmt.Println(stderr)
|
||||
pr.Status = PullRequestStatusConflict
|
||||
return nil
|
||||
@@ -458,6 +524,46 @@ func NewPullRequest(repo *Repository, pull *Issue, labelIDs []int64, uuids []str
|
||||
return nil
|
||||
}
|
||||
|
||||
// PullRequestsOptions holds the options for PRs
|
||||
type PullRequestsOptions struct {
|
||||
Page int
|
||||
State string
|
||||
SortType string
|
||||
Labels []string
|
||||
MilestoneID int64
|
||||
}
|
||||
|
||||
func listPullRequestStatement(baseRepoID int64, opts *PullRequestsOptions) *xorm.Session {
|
||||
sess := x.Where("pull_request.base_repo_id=?", baseRepoID)
|
||||
|
||||
sess.Join("INNER", "issue", "pull_request.issue_id = issue.id")
|
||||
switch opts.State {
|
||||
case "closed", "open":
|
||||
sess.And("issue.is_closed=?", opts.State == "closed")
|
||||
}
|
||||
|
||||
return sess
|
||||
}
|
||||
|
||||
// PullRequests returns all pull requests for a base Repo by the given conditions
|
||||
func PullRequests(baseRepoID int64, opts *PullRequestsOptions) ([]*PullRequest, int64, error) {
|
||||
if opts.Page <= 0 {
|
||||
opts.Page = 1
|
||||
}
|
||||
|
||||
countSession := listPullRequestStatement(baseRepoID, opts)
|
||||
maxResults, err := countSession.Count(new(PullRequest))
|
||||
if err != nil {
|
||||
log.Error(4, "Count PRs", err)
|
||||
return nil, maxResults, err
|
||||
}
|
||||
|
||||
prs := make([]*PullRequest, 0, ItemsPerPage)
|
||||
findSession := listPullRequestStatement(baseRepoID, opts)
|
||||
findSession.Limit(ItemsPerPage, (opts.Page-1)*ItemsPerPage)
|
||||
return prs, maxResults, findSession.Find(&prs)
|
||||
}
|
||||
|
||||
// GetUnmergedPullRequest returnss a pull request that is open and has not been merged
|
||||
// by given head/base and repo/branch.
|
||||
func GetUnmergedPullRequest(headRepoID, baseRepoID int64, headBranch, baseBranch string) (*PullRequest, error) {
|
||||
@@ -498,6 +604,30 @@ func GetUnmergedPullRequestsByBaseInfo(repoID int64, branch string) ([]*PullRequ
|
||||
Find(&prs)
|
||||
}
|
||||
|
||||
// GetPullRequestByIndex returns a pull request by the given index
|
||||
func GetPullRequestByIndex(repoID int64, index int64) (*PullRequest, error) {
|
||||
pr := &PullRequest{
|
||||
BaseRepoID: repoID,
|
||||
Index: index,
|
||||
}
|
||||
|
||||
has, err := x.Get(pr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !has {
|
||||
return nil, ErrPullRequestNotExist{0, repoID, index, 0, "", ""}
|
||||
}
|
||||
|
||||
if err = pr.LoadAttributes(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = pr.LoadIssue(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return pr, nil
|
||||
}
|
||||
|
||||
func getPullRequestByID(e Engine, id int64) (*PullRequest, error) {
|
||||
pr := new(PullRequest)
|
||||
has, err := e.Id(id).Get(pr)
|
||||
@@ -538,7 +668,7 @@ func (pr *PullRequest) Update() error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Update updates specific fields of pull request.
|
||||
// UpdateCols updates specific fields of pull request.
|
||||
func (pr *PullRequest) UpdateCols(cols ...string) error {
|
||||
_, err := x.Id(pr.ID).Cols(cols...).Update(pr)
|
||||
return err
|
||||
@@ -612,7 +742,9 @@ func (pr *PullRequest) PushToBaseRepo() (err error) {
|
||||
headFile := fmt.Sprintf("refs/pull/%d/head", pr.Index)
|
||||
|
||||
// Remove head in case there is a conflict.
|
||||
os.Remove(path.Join(pr.BaseRepo.RepoPath(), headFile))
|
||||
file := path.Join(pr.BaseRepo.RepoPath(), headFile)
|
||||
|
||||
_ = os.Remove(file)
|
||||
|
||||
if err = git.Push(headRepoPath, tmpRemoteName, fmt.Sprintf("%s:%s", pr.HeadBranch, headFile)); err != nil {
|
||||
return fmt.Errorf("Push: %v", err)
|
||||
@@ -623,7 +755,7 @@ func (pr *PullRequest) PushToBaseRepo() (err error) {
|
||||
|
||||
// AddToTaskQueue adds itself to pull request test task queue.
|
||||
func (pr *PullRequest) AddToTaskQueue() {
|
||||
go PullRequestQueue.AddFunc(pr.ID, func() {
|
||||
go pullRequestQueue.AddFunc(pr.ID, func() {
|
||||
pr.Status = PullRequestStatusChecking
|
||||
if err := pr.UpdateCols("status"); err != nil {
|
||||
log.Error(5, "AddToTaskQueue.UpdateCols[%d].(add to queue): %v", pr.ID, err)
|
||||
@@ -631,6 +763,7 @@ func (pr *PullRequest) AddToTaskQueue() {
|
||||
})
|
||||
}
|
||||
|
||||
// PullRequestList defines a list of pull requests
|
||||
type PullRequestList []*PullRequest
|
||||
|
||||
func (prs PullRequestList) loadAttributes(e Engine) error {
|
||||
@@ -661,6 +794,7 @@ func (prs PullRequestList) loadAttributes(e Engine) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// LoadAttributes load all the prs attributes
|
||||
func (prs PullRequestList) LoadAttributes() error {
|
||||
return prs.loadAttributes(x)
|
||||
}
|
||||
@@ -730,6 +864,7 @@ func AddTestPullRequestTask(doer *User, repoID int64, branch string, isSync bool
|
||||
}
|
||||
}
|
||||
|
||||
// ChangeUsernameInPullRequests changes the name of head_user_name
|
||||
func ChangeUsernameInPullRequests(oldUserName, newUserName string) error {
|
||||
pr := PullRequest{
|
||||
HeadUserName: strings.ToLower(newUserName),
|
||||
@@ -750,7 +885,7 @@ func (pr *PullRequest) checkAndUpdateStatus() {
|
||||
}
|
||||
|
||||
// Make sure there is no waiting test to process before levaing the checking status.
|
||||
if !PullRequestQueue.Exist(pr.ID) {
|
||||
if !pullRequestQueue.Exist(pr.ID) {
|
||||
if err := pr.UpdateCols("status"); err != nil {
|
||||
log.Error(4, "Update[%d]: %v", pr.ID, err)
|
||||
}
|
||||
@@ -786,9 +921,9 @@ func TestPullRequests() {
|
||||
}
|
||||
|
||||
// Start listening on new test requests.
|
||||
for prID := range PullRequestQueue.Queue() {
|
||||
for prID := range pullRequestQueue.Queue() {
|
||||
log.Trace("TestPullRequests[%v]: processing test task", prID)
|
||||
PullRequestQueue.Remove(prID)
|
||||
pullRequestQueue.Remove(prID)
|
||||
|
||||
pr, err := GetPullRequestByID(com.StrTo(prID).MustInt64())
|
||||
if err != nil {
|
||||
@@ -803,6 +938,7 @@ func TestPullRequests() {
|
||||
}
|
||||
}
|
||||
|
||||
// InitTestPullRequests runs the task to test all the checking status pull requests
|
||||
func InitTestPullRequests() {
|
||||
go TestPullRequests()
|
||||
}
|
||||
|
||||
@@ -38,12 +38,14 @@ type Release struct {
|
||||
CreatedUnix int64
|
||||
}
|
||||
|
||||
// BeforeInsert is invoked from XORM before inserting an object of this type.
|
||||
func (r *Release) BeforeInsert() {
|
||||
if r.CreatedUnix == 0 {
|
||||
r.CreatedUnix = time.Now().Unix()
|
||||
}
|
||||
}
|
||||
|
||||
// AfterSet is invoked from XORM after setting the value of a field of this object.
|
||||
func (r *Release) AfterSet(colName string, _ xorm.Cell) {
|
||||
switch colName {
|
||||
case "created_unix":
|
||||
@@ -151,15 +153,15 @@ func GetReleasesByRepoID(repoID int64, page, pageSize int) (rels []*Release, err
|
||||
return rels, err
|
||||
}
|
||||
|
||||
type ReleaseSorter struct {
|
||||
type releaseSorter struct {
|
||||
rels []*Release
|
||||
}
|
||||
|
||||
func (rs *ReleaseSorter) Len() int {
|
||||
func (rs *releaseSorter) Len() int {
|
||||
return len(rs.rels)
|
||||
}
|
||||
|
||||
func (rs *ReleaseSorter) Less(i, j int) bool {
|
||||
func (rs *releaseSorter) Less(i, j int) bool {
|
||||
diffNum := rs.rels[i].NumCommits - rs.rels[j].NumCommits
|
||||
if diffNum != 0 {
|
||||
return diffNum > 0
|
||||
@@ -167,13 +169,13 @@ func (rs *ReleaseSorter) Less(i, j int) bool {
|
||||
return rs.rels[i].Created.After(rs.rels[j].Created)
|
||||
}
|
||||
|
||||
func (rs *ReleaseSorter) Swap(i, j int) {
|
||||
func (rs *releaseSorter) Swap(i, j int) {
|
||||
rs.rels[i], rs.rels[j] = rs.rels[j], rs.rels[i]
|
||||
}
|
||||
|
||||
// SortReleases sorts releases by number of commits and created time.
|
||||
func SortReleases(rels []*Release) {
|
||||
sorter := &ReleaseSorter{rels: rels}
|
||||
sorter := &releaseSorter{rels: rels}
|
||||
sort.Sort(sorter)
|
||||
}
|
||||
|
||||
|
||||
159
models/repo.go
159
models/repo.go
@@ -20,9 +20,9 @@ import (
|
||||
"time"
|
||||
|
||||
"code.gitea.io/git"
|
||||
"code.gitea.io/gitea/modules/bindata"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/markdown"
|
||||
"code.gitea.io/gitea/modules/options"
|
||||
"code.gitea.io/gitea/modules/process"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/sync"
|
||||
@@ -41,30 +41,50 @@ const (
|
||||
var repoWorkingPool = sync.NewExclusivePool()
|
||||
|
||||
var (
|
||||
ErrRepoFileNotExist = errors.New("Repository file does not exist")
|
||||
// ErrRepoFileNotExist repository file does not exist error
|
||||
ErrRepoFileNotExist = errors.New("Repository file does not exist")
|
||||
|
||||
// ErrRepoFileNotLoaded repository file not loaded error
|
||||
ErrRepoFileNotLoaded = errors.New("Repository file not loaded")
|
||||
ErrMirrorNotExist = errors.New("Mirror does not exist")
|
||||
ErrInvalidReference = errors.New("Invalid reference specified")
|
||||
ErrNameEmpty = errors.New("Name is empty")
|
||||
|
||||
// ErrMirrorNotExist mirror does not exist error
|
||||
ErrMirrorNotExist = errors.New("Mirror does not exist")
|
||||
|
||||
// ErrInvalidReference invalid reference specified error
|
||||
ErrInvalidReference = errors.New("Invalid reference specified")
|
||||
|
||||
// ErrNameEmpty name is empty error
|
||||
ErrNameEmpty = errors.New("Name is empty")
|
||||
)
|
||||
|
||||
var (
|
||||
Gitignores, Licenses, Readmes, LabelTemplates []string
|
||||
// Gitignores contains the gitiginore files
|
||||
Gitignores []string
|
||||
|
||||
// Maximum items per page in forks, watchers and stars of a repo
|
||||
// Licenses contains the license files
|
||||
Licenses []string
|
||||
|
||||
// Readmes contains the readme files
|
||||
Readmes []string
|
||||
|
||||
// LabelTemplates contains the label template files
|
||||
LabelTemplates []string
|
||||
|
||||
// ItemsPerPage maximum items per page in forks, watchers and stars of a repo
|
||||
ItemsPerPage = 40
|
||||
)
|
||||
|
||||
// LoadRepoConfig loads the repository config
|
||||
func LoadRepoConfig() {
|
||||
// Load .gitignore and license files and readme templates.
|
||||
types := []string{"gitignore", "license", "readme", "label"}
|
||||
typeFiles := make([][]string, 4)
|
||||
for i, t := range types {
|
||||
files, err := bindata.AssetDir("conf/" + t)
|
||||
files, err := options.Dir(t)
|
||||
if err != nil {
|
||||
log.Fatal(4, "Fail to get %s files: %v", t, err)
|
||||
}
|
||||
customPath := path.Join(setting.CustomPath, "conf", t)
|
||||
customPath := path.Join(setting.CustomPath, "options", t)
|
||||
if com.IsDir(customPath) {
|
||||
customFiles, err := com.StatDir(customPath)
|
||||
if err != nil {
|
||||
@@ -104,6 +124,7 @@ func LoadRepoConfig() {
|
||||
Licenses = sortedLicenses
|
||||
}
|
||||
|
||||
// NewRepoContext creates a new repository context
|
||||
func NewRepoContext() {
|
||||
zip.Verbose = false
|
||||
|
||||
@@ -200,15 +221,18 @@ type Repository struct {
|
||||
UpdatedUnix int64
|
||||
}
|
||||
|
||||
// BeforeInsert is invoked from XORM before inserting an object of this type.
|
||||
func (repo *Repository) BeforeInsert() {
|
||||
repo.CreatedUnix = time.Now().Unix()
|
||||
repo.UpdatedUnix = repo.CreatedUnix
|
||||
}
|
||||
|
||||
// BeforeUpdate is invoked from XORM before updating this object.
|
||||
func (repo *Repository) BeforeUpdate() {
|
||||
repo.UpdatedUnix = time.Now().Unix()
|
||||
}
|
||||
|
||||
// AfterSet is invoked from XORM after setting the value of a field of this object.
|
||||
func (repo *Repository) AfterSet(colName string, _ xorm.Cell) {
|
||||
switch colName {
|
||||
case "default_branch":
|
||||
@@ -224,7 +248,7 @@ func (repo *Repository) AfterSet(colName string, _ xorm.Cell) {
|
||||
repo.NumOpenMilestones = repo.NumMilestones - repo.NumClosedMilestones
|
||||
case "external_tracker_style":
|
||||
if len(repo.ExternalTrackerStyle) == 0 {
|
||||
repo.ExternalTrackerStyle = markdown.ISSUE_NAME_STYLE_NUMERIC
|
||||
repo.ExternalTrackerStyle = markdown.IssueNameStyleNumeric
|
||||
}
|
||||
case "created_unix":
|
||||
repo.Created = time.Unix(repo.CreatedUnix, 0).Local()
|
||||
@@ -241,14 +265,17 @@ func (repo *Repository) MustOwner() *User {
|
||||
return repo.mustOwner(x)
|
||||
}
|
||||
|
||||
// FullName returns the repository full name
|
||||
func (repo *Repository) FullName() string {
|
||||
return repo.MustOwner().Name + "/" + repo.Name
|
||||
}
|
||||
|
||||
// HTMLURL returns the repository HTML URL
|
||||
func (repo *Repository) HTMLURL() string {
|
||||
return setting.AppUrl + repo.FullName()
|
||||
return setting.AppURL + repo.FullName()
|
||||
}
|
||||
|
||||
// APIFormat converts a Repository to api.Repository
|
||||
// Arguments that are allowed to be nil: permission
|
||||
func (repo *Repository) APIFormat(permission *api.Permission) *api.Repository {
|
||||
cloneLink := repo.CloneLink()
|
||||
@@ -284,6 +311,7 @@ func (repo *Repository) getOwner(e Engine) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
// GetOwner returns the repository owner
|
||||
func (repo *Repository) GetOwner() error {
|
||||
return repo.getOwner(x)
|
||||
}
|
||||
@@ -310,10 +338,10 @@ func (repo *Repository) ComposeMetas() map[string]string {
|
||||
"repo": repo.Name,
|
||||
}
|
||||
switch repo.ExternalTrackerStyle {
|
||||
case markdown.ISSUE_NAME_STYLE_ALPHANUMERIC:
|
||||
repo.ExternalMetas["style"] = markdown.ISSUE_NAME_STYLE_ALPHANUMERIC
|
||||
case markdown.IssueNameStyleAlphanumeric:
|
||||
repo.ExternalMetas["style"] = markdown.IssueNameStyleAlphanumeric
|
||||
default:
|
||||
repo.ExternalMetas["style"] = markdown.ISSUE_NAME_STYLE_NUMERIC
|
||||
repo.ExternalMetas["style"] = markdown.IssueNameStyleNumeric
|
||||
}
|
||||
|
||||
}
|
||||
@@ -381,11 +409,13 @@ func (repo *Repository) IssueStats(uid int64, filterMode int, isPull bool) (int6
|
||||
return GetRepoIssueStats(repo.ID, uid, filterMode, isPull)
|
||||
}
|
||||
|
||||
// GetMirror sets the repository mirror, returns an error upon failure
|
||||
func (repo *Repository) GetMirror() (err error) {
|
||||
repo.Mirror, err = GetMirrorByRepoID(repo.ID)
|
||||
return err
|
||||
}
|
||||
|
||||
// GetBaseRepo returns the base repository
|
||||
func (repo *Repository) GetBaseRepo() (err error) {
|
||||
if !repo.IsFork {
|
||||
return nil
|
||||
@@ -399,31 +429,38 @@ func (repo *Repository) repoPath(e Engine) string {
|
||||
return RepoPath(repo.mustOwner(e).Name, repo.Name)
|
||||
}
|
||||
|
||||
// RepoPath returns the repository path
|
||||
func (repo *Repository) RepoPath() string {
|
||||
return repo.repoPath(x)
|
||||
}
|
||||
|
||||
// GitConfigPath returns the repository git config path
|
||||
func (repo *Repository) GitConfigPath() string {
|
||||
return filepath.Join(repo.RepoPath(), "config")
|
||||
}
|
||||
|
||||
// RelLink returns the repository relative link
|
||||
func (repo *Repository) RelLink() string {
|
||||
return "/" + repo.FullName()
|
||||
}
|
||||
|
||||
// Link returns the repository link
|
||||
func (repo *Repository) Link() string {
|
||||
return setting.AppSubUrl + "/" + repo.FullName()
|
||||
return setting.AppSubURL + "/" + repo.FullName()
|
||||
}
|
||||
|
||||
// ComposeCompareURL returns the repository comparison URL
|
||||
func (repo *Repository) ComposeCompareURL(oldCommitID, newCommitID string) string {
|
||||
return fmt.Sprintf("%s/%s/compare/%s...%s", repo.MustOwner().Name, repo.Name, oldCommitID, newCommitID)
|
||||
}
|
||||
|
||||
// HasAccess returns true when user has access to this repository
|
||||
func (repo *Repository) HasAccess(u *User) bool {
|
||||
has, _ := HasAccess(u, repo, AccessModeRead)
|
||||
return has
|
||||
}
|
||||
|
||||
// IsOwnedBy returns true when user owns this repository
|
||||
func (repo *Repository) IsOwnedBy(userID int64) bool {
|
||||
return repo.OwnerID == userID
|
||||
}
|
||||
@@ -438,7 +475,7 @@ func (repo *Repository) CanEnablePulls() bool {
|
||||
return !repo.IsMirror
|
||||
}
|
||||
|
||||
// AllowPulls returns true if repository meets the requirements of accepting pulls and has them enabled.
|
||||
// AllowsPulls returns true if repository meets the requirements of accepting pulls and has them enabled.
|
||||
func (repo *Repository) AllowsPulls() bool {
|
||||
return repo.CanEnablePulls() && repo.EnablePulls
|
||||
}
|
||||
@@ -448,6 +485,7 @@ func (repo *Repository) CanEnableEditor() bool {
|
||||
return !repo.IsMirror
|
||||
}
|
||||
|
||||
// NextIssueIndex returns the next issue index
|
||||
// FIXME: should have a mutex to prevent producing same index for two issues that are created
|
||||
// closely enough.
|
||||
func (repo *Repository) NextIssueIndex() int64 {
|
||||
@@ -455,22 +493,23 @@ func (repo *Repository) NextIssueIndex() int64 {
|
||||
}
|
||||
|
||||
var (
|
||||
DescPattern = regexp.MustCompile(`https?://\S+`)
|
||||
descPattern = regexp.MustCompile(`https?://\S+`)
|
||||
)
|
||||
|
||||
// DescriptionHtml does special handles to description and return HTML string.
|
||||
func (repo *Repository) DescriptionHtml() template.HTML {
|
||||
// DescriptionHTML does special handles to description and return HTML string.
|
||||
func (repo *Repository) DescriptionHTML() template.HTML {
|
||||
sanitize := func(s string) string {
|
||||
return fmt.Sprintf(`<a href="%[1]s" target="_blank">%[1]s</a>`, s)
|
||||
return fmt.Sprintf(`<a href="%[1]s" target="_blank" rel="noopener">%[1]s</a>`, s)
|
||||
}
|
||||
return template.HTML(DescPattern.ReplaceAllStringFunc(markdown.Sanitizer.Sanitize(repo.Description), sanitize))
|
||||
return template.HTML(descPattern.ReplaceAllStringFunc(markdown.Sanitizer.Sanitize(repo.Description), sanitize))
|
||||
}
|
||||
|
||||
// LocalCopyPath returns the local repository copy path
|
||||
func (repo *Repository) LocalCopyPath() string {
|
||||
return path.Join(setting.AppDataPath, "tmp/local-rpeo", com.ToStr(repo.ID))
|
||||
}
|
||||
|
||||
// UpdateLocalCopy pulls latest changes of given branch from repoPath to localPath.
|
||||
// UpdateLocalCopyBranch pulls latest changes of given branch from repoPath to localPath.
|
||||
// It creates a new clone if local copy does not exist.
|
||||
// This function checks out target branch by default, it is safe to assume subsequent
|
||||
// operations are operating against target branch when caller has confidence for no race condition.
|
||||
@@ -519,8 +558,12 @@ func (repo *Repository) SavePatch(index int64, patch []byte) error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("PatchPath: %v", err)
|
||||
}
|
||||
dir := filepath.Dir(patchPath)
|
||||
|
||||
if err := os.MkdirAll(dir, os.ModePerm); err != nil {
|
||||
return fmt.Errorf("Fail to create dir %s: %v", dir, err)
|
||||
}
|
||||
|
||||
os.MkdirAll(filepath.Dir(patchPath), os.ModePerm)
|
||||
if err = ioutil.WriteFile(patchPath, patch, 0644); err != nil {
|
||||
return fmt.Errorf("WriteFile: %v", err)
|
||||
}
|
||||
@@ -550,7 +593,7 @@ type CloneLink struct {
|
||||
|
||||
// ComposeHTTPSCloneURL returns HTTPS clone URL based on given owner and repository name.
|
||||
func ComposeHTTPSCloneURL(owner, repo string) string {
|
||||
return fmt.Sprintf("%s%s/%s.git", setting.AppUrl, owner, repo)
|
||||
return fmt.Sprintf("%s%s/%s.git", setting.AppURL, owner, repo)
|
||||
}
|
||||
|
||||
func (repo *Repository) cloneLink(isWiki bool) *CloneLink {
|
||||
@@ -575,6 +618,7 @@ func (repo *Repository) CloneLink() (cl *CloneLink) {
|
||||
return repo.cloneLink(false)
|
||||
}
|
||||
|
||||
// MigrateRepoOptions contains the repository migrate options
|
||||
type MigrateRepoOptions struct {
|
||||
Name string
|
||||
Description string
|
||||
@@ -629,7 +673,10 @@ func MigrateRepository(u *User, opts MigrateRepoOptions) (*Repository, error) {
|
||||
|
||||
migrateTimeout := time.Duration(setting.Git.Timeout.Migrate) * time.Second
|
||||
|
||||
os.RemoveAll(repoPath)
|
||||
if err := os.RemoveAll(repoPath); err != nil {
|
||||
return repo, fmt.Errorf("Fail to remove %s: %v", repoPath, err)
|
||||
}
|
||||
|
||||
if err = git.Clone(opts.RemoteAddr, repoPath, git.CloneRepoOptions{
|
||||
Mirror: true,
|
||||
Quiet: true,
|
||||
@@ -640,7 +687,11 @@ func MigrateRepository(u *User, opts MigrateRepoOptions) (*Repository, error) {
|
||||
|
||||
wikiRemotePath := wikiRemoteURL(opts.RemoteAddr)
|
||||
if len(wikiRemotePath) > 0 {
|
||||
os.RemoveAll(wikiPath)
|
||||
|
||||
if err := os.RemoveAll(wikiPath); err != nil {
|
||||
return repo, fmt.Errorf("Fail to remove %s: %v", wikiPath, err)
|
||||
}
|
||||
|
||||
if err = git.Clone(wikiRemotePath, wikiPath, git.CloneRepoOptions{
|
||||
Mirror: true,
|
||||
Quiet: true,
|
||||
@@ -711,7 +762,7 @@ func createUpdateHook(repoPath string) error {
|
||||
fmt.Sprintf(tplUpdateHook, setting.ScriptType, "\""+setting.AppPath+"\"", setting.CustomConf))
|
||||
}
|
||||
|
||||
// Finish migrating repository and/or wiki with things that don't need to be done for mirrors.
|
||||
// CleanUpMigrateInfo finishes migrating repository and/or wiki with things that don't need to be done for mirrors.
|
||||
func CleanUpMigrateInfo(repo *Repository) (*Repository, error) {
|
||||
repoPath := repo.RepoPath()
|
||||
if err := createUpdateHook(repoPath); err != nil {
|
||||
@@ -759,6 +810,7 @@ func initRepoCommit(tmpPath string, sig *git.Signature) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateRepoOptions contains the create repository options
|
||||
type CreateRepoOptions struct {
|
||||
Name string
|
||||
Description string
|
||||
@@ -771,14 +823,25 @@ type CreateRepoOptions struct {
|
||||
}
|
||||
|
||||
func getRepoInitFile(tp, name string) ([]byte, error) {
|
||||
relPath := path.Join("conf", tp, strings.TrimLeft(name, "./"))
|
||||
cleanedName := strings.TrimLeft(name, "./")
|
||||
relPath := path.Join("options", tp, cleanedName)
|
||||
|
||||
// Use custom file when available.
|
||||
customPath := path.Join(setting.CustomPath, relPath)
|
||||
if com.IsFile(customPath) {
|
||||
return ioutil.ReadFile(customPath)
|
||||
}
|
||||
return bindata.Asset(relPath)
|
||||
|
||||
switch tp {
|
||||
case "readme":
|
||||
return options.Readme(cleanedName)
|
||||
case "gitignore":
|
||||
return options.Gitignore(cleanedName)
|
||||
case "license":
|
||||
return options.License(cleanedName)
|
||||
default:
|
||||
return []byte{}, fmt.Errorf("Invalid init file type")
|
||||
}
|
||||
}
|
||||
|
||||
func prepareRepoCommit(repo *Repository, tmpDir, repoPath string, opts CreateRepoOptions) error {
|
||||
@@ -861,7 +924,11 @@ func initRepository(e Engine, repoPath string, u *User, repo *Repository, opts C
|
||||
|
||||
// Initialize repository according to user's choice.
|
||||
if opts.AutoInit {
|
||||
os.MkdirAll(tmpDir, os.ModePerm)
|
||||
|
||||
if err := os.MkdirAll(tmpDir, os.ModePerm); err != nil {
|
||||
return fmt.Errorf("Fail to create dir %s: %v", tmpDir, err)
|
||||
}
|
||||
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
if err = prepareRepoCommit(repo, tmpDir, repoPath, opts); err != nil {
|
||||
@@ -897,6 +964,7 @@ var (
|
||||
reservedRepoPatterns = []string{"*.git", "*.wiki"}
|
||||
)
|
||||
|
||||
// IsUsableRepoName returns true when repository is usable
|
||||
func IsUsableRepoName(name string) error {
|
||||
return isUsableName(reservedRepoNames, reservedRepoPatterns, name)
|
||||
}
|
||||
@@ -1030,6 +1098,7 @@ func CountUserRepositories(userID int64, private bool) int64 {
|
||||
return countRepositories(userID, private)
|
||||
}
|
||||
|
||||
// Repositories returns all repositories
|
||||
func Repositories(page, pageSize int) (_ []*Repository, err error) {
|
||||
repos := make([]*Repository, 0, pageSize)
|
||||
return repos, x.Limit(pageSize, (page-1)*pageSize).Asc("id").Find(&repos)
|
||||
@@ -1155,7 +1224,12 @@ func TransferOwnership(doer *User, newOwnerName string, repo *Repository) error
|
||||
}
|
||||
|
||||
// Rename remote repository to new path and delete local copy.
|
||||
os.MkdirAll(UserPath(newOwner.Name), os.ModePerm)
|
||||
dir := UserPath(newOwner.Name)
|
||||
|
||||
if err := os.MkdirAll(dir, os.ModePerm); err != nil {
|
||||
return fmt.Errorf("Fail to create dir %s: %v", dir, err)
|
||||
}
|
||||
|
||||
if err = os.Rename(RepoPath(owner.Name, repo.Name), RepoPath(newOwner.Name, repo.Name)); err != nil {
|
||||
return fmt.Errorf("rename repository directory: %v", err)
|
||||
}
|
||||
@@ -1275,6 +1349,7 @@ func updateRepository(e Engine, repo *Repository, visibilityChanged bool) (err e
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateRepository updates a repository
|
||||
func UpdateRepository(repo *Repository, visibilityChanged bool) (err error) {
|
||||
sess := x.NewSession()
|
||||
defer sessionRelease(sess)
|
||||
@@ -1474,7 +1549,7 @@ func GetUserRepositories(userID int64, private bool, page, pageSize int) ([]*Rep
|
||||
return repos, sess.Find(&repos)
|
||||
}
|
||||
|
||||
// GetUserRepositories returns a list of mirror repositories of given user.
|
||||
// GetUserMirrorRepositories returns a list of mirror repositories of given user.
|
||||
func GetUserMirrorRepositories(userID int64) ([]*Repository, error) {
|
||||
repos := make([]*Repository, 0, 10)
|
||||
return repos, x.
|
||||
@@ -1502,6 +1577,7 @@ func GetRepositoryCount(u *User) (int64, error) {
|
||||
return getRepositoryCount(x, u)
|
||||
}
|
||||
|
||||
// SearchRepoOptions holds the search options
|
||||
type SearchRepoOptions struct {
|
||||
Keyword string
|
||||
OwnerID int64
|
||||
@@ -1670,6 +1746,7 @@ func GitFsck() {
|
||||
}
|
||||
}
|
||||
|
||||
// GitGcRepos calls 'git gc' to remove unnecessary files and optimize the local repository
|
||||
func GitGcRepos() error {
|
||||
args := append([]string{"gc"}, setting.Git.GCArgs...)
|
||||
return x.
|
||||
@@ -1712,6 +1789,7 @@ func repoStatsCheck(checker *repoChecker) {
|
||||
}
|
||||
}
|
||||
|
||||
// CheckRepoStats checks the repository stats
|
||||
func CheckRepoStats() {
|
||||
if taskStatusTable.IsRunning(checkRepos) {
|
||||
return
|
||||
@@ -1806,6 +1884,7 @@ func CheckRepoStats() {
|
||||
// ***** END: Repository.NumForks *****
|
||||
}
|
||||
|
||||
// RepositoryList contains a list of repositories
|
||||
type RepositoryList []*Repository
|
||||
|
||||
func (repos RepositoryList) loadAttributes(e Engine) error {
|
||||
@@ -1838,10 +1917,12 @@ func (repos RepositoryList) loadAttributes(e Engine) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// LoadAttributes loads the attributes for the given RepositoryList
|
||||
func (repos RepositoryList) LoadAttributes() error {
|
||||
return repos.loadAttributes(x)
|
||||
}
|
||||
|
||||
// MirrorRepositoryList contains the mirror repositories
|
||||
type MirrorRepositoryList []*Repository
|
||||
|
||||
func (repos MirrorRepositoryList) loadAttributes(e Engine) error {
|
||||
@@ -1876,6 +1957,7 @@ func (repos MirrorRepositoryList) loadAttributes(e Engine) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// LoadAttributes loads the attributes for the given MirrorRepositoryList
|
||||
func (repos MirrorRepositoryList) LoadAttributes() error {
|
||||
return repos.loadAttributes(x)
|
||||
}
|
||||
@@ -1925,7 +2007,7 @@ func watchRepo(e Engine, userID, repoID int64, watch bool) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
// Watch or unwatch repository.
|
||||
// WatchRepo watch or unwatch repository.
|
||||
func WatchRepo(userID, repoID int64, watch bool) (err error) {
|
||||
return watchRepo(x, userID, repoID, watch)
|
||||
}
|
||||
@@ -1940,7 +2022,7 @@ func GetWatchers(repoID int64) ([]*Watch, error) {
|
||||
return getWatchers(x, repoID)
|
||||
}
|
||||
|
||||
// Repository.GetWatchers returns range of users watching given repository.
|
||||
// GetWatchers returns range of users watching given repository.
|
||||
func (repo *Repository) GetWatchers(page int) ([]*User, error) {
|
||||
users := make([]*User, 0, ItemsPerPage)
|
||||
sess := x.
|
||||
@@ -1993,13 +2075,14 @@ func NotifyWatchers(act *Action) error {
|
||||
// /_______ /|__| (____ /__|
|
||||
// \/ \/
|
||||
|
||||
// Star contains the star information
|
||||
type Star struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
UID int64 `xorm:"UNIQUE(s)"`
|
||||
RepoID int64 `xorm:"UNIQUE(s)"`
|
||||
}
|
||||
|
||||
// Star or unstar repository.
|
||||
// StarRepo star or unstar repository.
|
||||
func StarRepo(userID, repoID int64, star bool) (err error) {
|
||||
if star {
|
||||
if IsStaring(userID, repoID) {
|
||||
@@ -2031,6 +2114,7 @@ func IsStaring(userID, repoID int64) bool {
|
||||
return has
|
||||
}
|
||||
|
||||
// GetStargazers returns the users who gave stars to this repository
|
||||
func (repo *Repository) GetStargazers(page int) ([]*User, error) {
|
||||
users := make([]*User, 0, ItemsPerPage)
|
||||
sess := x.
|
||||
@@ -2060,6 +2144,7 @@ func HasForkedRepo(ownerID, repoID int64) (*Repository, bool) {
|
||||
return repo, has
|
||||
}
|
||||
|
||||
// ForkRepository forks a repository
|
||||
func ForkRepository(u *User, oldRepo *Repository, name, desc string) (_ *Repository, err error) {
|
||||
repo := &Repository{
|
||||
OwnerID: u.ID,
|
||||
@@ -2099,7 +2184,7 @@ func ForkRepository(u *User, oldRepo *Repository, name, desc string) (_ *Reposit
|
||||
repoPath, fmt.Sprintf("ForkRepository(git update-server-info): %s", repoPath),
|
||||
"git", "update-server-info")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("git update-server-info: %v", err)
|
||||
return nil, fmt.Errorf("git update-server-info: %v", stderr)
|
||||
}
|
||||
|
||||
if err = createUpdateHook(repoPath); err != nil {
|
||||
@@ -2109,6 +2194,7 @@ func ForkRepository(u *User, oldRepo *Repository, name, desc string) (_ *Reposit
|
||||
return repo, sess.Commit()
|
||||
}
|
||||
|
||||
// GetForks returns all the forks of the repository
|
||||
func (repo *Repository) GetForks() ([]*Repository, error) {
|
||||
forks := make([]*Repository, 0, repo.NumForks)
|
||||
return forks, x.Find(&forks, &Repository{ForkID: repo.ID})
|
||||
@@ -2122,6 +2208,7 @@ func (repo *Repository) GetForks() ([]*Repository, error) {
|
||||
// \/ \/ \/ \/ \/
|
||||
//
|
||||
|
||||
// CreateNewBranch creates a new repository branch
|
||||
func (repo *Repository) CreateNewBranch(doer *User, oldBranchName, branchName string) (err error) {
|
||||
repoWorkingPool.CheckIn(com.ToStr(repo.ID))
|
||||
defer repoWorkingPool.CheckOut(com.ToStr(repo.ID))
|
||||
|
||||
@@ -8,11 +8,13 @@ import (
|
||||
"code.gitea.io/git"
|
||||
)
|
||||
|
||||
// Branch holds the branch information
|
||||
type Branch struct {
|
||||
Path string
|
||||
Name string
|
||||
}
|
||||
|
||||
// GetBranchesByPath returns a branch by it's path
|
||||
func GetBranchesByPath(path string) ([]*Branch, error) {
|
||||
gitRepo, err := git.OpenRepository(path)
|
||||
if err != nil {
|
||||
@@ -34,24 +36,27 @@ func GetBranchesByPath(path string) ([]*Branch, error) {
|
||||
return branches, nil
|
||||
}
|
||||
|
||||
func (repo *Repository) GetBranch(br string) (*Branch, error) {
|
||||
if !git.IsBranchExist(repo.RepoPath(), br) {
|
||||
return nil, &ErrBranchNotExist{br}
|
||||
// GetBranch returns a branch by it's name
|
||||
func (repo *Repository) GetBranch(branch string) (*Branch, error) {
|
||||
if !git.IsBranchExist(repo.RepoPath(), branch) {
|
||||
return nil, &ErrBranchNotExist{branch}
|
||||
}
|
||||
return &Branch{
|
||||
Path: repo.RepoPath(),
|
||||
Name: br,
|
||||
Name: branch,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetBranches returns all the branches of a repository
|
||||
func (repo *Repository) GetBranches() ([]*Branch, error) {
|
||||
return GetBranchesByPath(repo.RepoPath())
|
||||
}
|
||||
|
||||
func (br *Branch) GetCommit() (*git.Commit, error) {
|
||||
gitRepo, err := git.OpenRepository(br.Path)
|
||||
// GetCommit returns all the commits of a branch
|
||||
func (branch *Branch) GetCommit() (*git.Commit, error) {
|
||||
gitRepo, err := git.OpenRepository(branch.Path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return gitRepo.GetBranchCommit(br.Name)
|
||||
return gitRepo.GetBranchCommit(branch.Name)
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ type Collaboration struct {
|
||||
Mode AccessMode `xorm:"DEFAULT 2 NOT NULL"`
|
||||
}
|
||||
|
||||
// ModeI18nKey returns the collaboration mode I18n Key
|
||||
func (c *Collaboration) ModeI18nKey() string {
|
||||
switch c.Mode {
|
||||
case AccessModeRead:
|
||||
@@ -67,7 +68,7 @@ func (repo *Repository) AddCollaborator(u *User) error {
|
||||
}
|
||||
|
||||
func (repo *Repository) getCollaborations(e Engine) ([]*Collaboration, error) {
|
||||
collaborations := make([]*Collaboration, 0)
|
||||
var collaborations []*Collaboration
|
||||
return collaborations, e.Find(&collaborations, &Collaboration{RepoID: repo.ID})
|
||||
}
|
||||
|
||||
|
||||
@@ -50,6 +50,7 @@ func discardLocalRepoBranchChanges(localPath, branch string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// DiscardLocalRepoBranchChanges discards the local repository branch changes
|
||||
func (repo *Repository) DiscardLocalRepoBranchChanges(branch string) error {
|
||||
return discardLocalRepoBranchChanges(repo.LocalCopyPath(), branch)
|
||||
}
|
||||
@@ -66,10 +67,12 @@ func checkoutNewBranch(repoPath, localPath, oldBranch, newBranch string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// CheckoutNewBranch checks out a new branch
|
||||
func (repo *Repository) CheckoutNewBranch(oldBranch, newBranch string) error {
|
||||
return checkoutNewBranch(repo.RepoPath(), repo.LocalCopyPath(), oldBranch, newBranch)
|
||||
}
|
||||
|
||||
// UpdateRepoFileOptions holds the repository file update options
|
||||
type UpdateRepoFileOptions struct {
|
||||
LastCommitID string
|
||||
OldBranch string
|
||||
@@ -101,7 +104,11 @@ func (repo *Repository) UpdateRepoFile(doer *User, opts UpdateRepoFileOptions) (
|
||||
localPath := repo.LocalCopyPath()
|
||||
oldFilePath := path.Join(localPath, opts.OldTreeName)
|
||||
filePath := path.Join(localPath, opts.NewTreeName)
|
||||
os.MkdirAll(path.Dir(filePath), os.ModePerm)
|
||||
dir := path.Dir(filePath)
|
||||
|
||||
if err := os.MkdirAll(dir, os.ModePerm); err != nil {
|
||||
return fmt.Errorf("Fail to create dir %s: %v", dir, err)
|
||||
}
|
||||
|
||||
// If it's meant to be a new file, make sure it doesn't exist.
|
||||
if opts.IsNewFile {
|
||||
@@ -182,7 +189,12 @@ func (repo *Repository) GetDiffPreview(branch, treePath, content string) (diff *
|
||||
|
||||
localPath := repo.LocalCopyPath()
|
||||
filePath := path.Join(localPath, treePath)
|
||||
os.MkdirAll(filepath.Dir(filePath), os.ModePerm)
|
||||
dir := filepath.Dir(filePath)
|
||||
|
||||
if err := os.MkdirAll(dir, os.ModePerm); err != nil {
|
||||
return nil, fmt.Errorf("Fail to create dir %s: %v", dir, err)
|
||||
}
|
||||
|
||||
if err = ioutil.WriteFile(filePath, []byte(content), 0666); err != nil {
|
||||
return nil, fmt.Errorf("WriteFile: %v", err)
|
||||
}
|
||||
@@ -223,6 +235,7 @@ func (repo *Repository) GetDiffPreview(branch, treePath, content string) (diff *
|
||||
// \/ \/ \/ \/ \/ \/
|
||||
//
|
||||
|
||||
// DeleteRepoFileOptions holds the repository delete file options
|
||||
type DeleteRepoFileOptions struct {
|
||||
LastCommitID string
|
||||
OldBranch string
|
||||
@@ -231,6 +244,7 @@ type DeleteRepoFileOptions struct {
|
||||
Message string
|
||||
}
|
||||
|
||||
// DeleteRepoFile deletes a repository file
|
||||
func (repo *Repository) DeleteRepoFile(doer *User, opts DeleteRepoFileOptions) (err error) {
|
||||
repoWorkingPool.CheckIn(com.ToStr(repo.ID))
|
||||
defer repoWorkingPool.CheckOut(com.ToStr(repo.ID))
|
||||
@@ -351,6 +365,7 @@ func NewUpload(name string, buf []byte, file multipart.File) (_ *Upload, err err
|
||||
return upload, nil
|
||||
}
|
||||
|
||||
// GetUploadByUUID returns the Upload by UUID
|
||||
func GetUploadByUUID(uuid string) (*Upload, error) {
|
||||
upload := &Upload{UUID: uuid}
|
||||
has, err := x.Get(upload)
|
||||
@@ -362,6 +377,7 @@ func GetUploadByUUID(uuid string) (*Upload, error) {
|
||||
return upload, nil
|
||||
}
|
||||
|
||||
// GetUploadsByUUIDs returns multiple uploads by UUIDS
|
||||
func GetUploadsByUUIDs(uuids []string) ([]*Upload, error) {
|
||||
if len(uuids) == 0 {
|
||||
return []*Upload{}, nil
|
||||
@@ -372,6 +388,7 @@ func GetUploadsByUUIDs(uuids []string) ([]*Upload, error) {
|
||||
return uploads, x.In("uuid", uuids).Find(&uploads)
|
||||
}
|
||||
|
||||
// DeleteUploads deletes multiple uploads
|
||||
func DeleteUploads(uploads ...*Upload) (err error) {
|
||||
if len(uploads) == 0 {
|
||||
return nil
|
||||
@@ -407,10 +424,12 @@ func DeleteUploads(uploads ...*Upload) (err error) {
|
||||
return sess.Commit()
|
||||
}
|
||||
|
||||
// DeleteUpload delete a upload
|
||||
func DeleteUpload(u *Upload) error {
|
||||
return DeleteUploads(u)
|
||||
}
|
||||
|
||||
// DeleteUploadByUUID deletes a upload by UUID
|
||||
func DeleteUploadByUUID(uuid string) error {
|
||||
upload, err := GetUploadByUUID(uuid)
|
||||
if err != nil {
|
||||
@@ -427,6 +446,7 @@ func DeleteUploadByUUID(uuid string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// UploadRepoFileOptions contains the uploaded repository file options
|
||||
type UploadRepoFileOptions struct {
|
||||
LastCommitID string
|
||||
OldBranch string
|
||||
@@ -436,6 +456,7 @@ type UploadRepoFileOptions struct {
|
||||
Files []string // In UUID format.
|
||||
}
|
||||
|
||||
// UploadRepoFiles uploads files to a repository
|
||||
func (repo *Repository) UploadRepoFiles(doer *User, opts UploadRepoFileOptions) (err error) {
|
||||
if len(opts.Files) == 0 {
|
||||
return nil
|
||||
@@ -463,7 +484,10 @@ func (repo *Repository) UploadRepoFiles(doer *User, opts UploadRepoFileOptions)
|
||||
|
||||
localPath := repo.LocalCopyPath()
|
||||
dirPath := path.Join(localPath, opts.TreePath)
|
||||
os.MkdirAll(dirPath, os.ModePerm)
|
||||
|
||||
if err := os.MkdirAll(dirPath, os.ModePerm); err != nil {
|
||||
return fmt.Errorf("Fail to create dir %s: %v", dirPath, err)
|
||||
}
|
||||
|
||||
// Copy uploaded files into repository.
|
||||
for _, upload := range uploads {
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/sync"
|
||||
)
|
||||
|
||||
// MirrorQueue holds an UniqueQueue object of the mirror
|
||||
var MirrorQueue = sync.NewUniqueQueue(setting.Repository.MirrorQueueLength)
|
||||
|
||||
// Mirror represents mirror information of a repository.
|
||||
@@ -37,16 +38,19 @@ type Mirror struct {
|
||||
address string `xorm:"-"`
|
||||
}
|
||||
|
||||
// BeforeInsert will be invoked by XORM before inserting a record
|
||||
func (m *Mirror) BeforeInsert() {
|
||||
m.UpdatedUnix = time.Now().Unix()
|
||||
m.NextUpdateUnix = m.NextUpdate.Unix()
|
||||
}
|
||||
|
||||
// BeforeUpdate is invoked from XORM before updating this object.
|
||||
func (m *Mirror) BeforeUpdate() {
|
||||
m.UpdatedUnix = time.Now().Unix()
|
||||
m.NextUpdateUnix = m.NextUpdate.Unix()
|
||||
}
|
||||
|
||||
// AfterSet is invoked from XORM after setting the value of a field of this object.
|
||||
func (m *Mirror) AfterSet(colName string, _ xorm.Cell) {
|
||||
var err error
|
||||
switch colName {
|
||||
@@ -180,10 +184,12 @@ func updateMirror(e Engine, m *Mirror) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// UpdateMirror updates the mirror
|
||||
func UpdateMirror(m *Mirror) error {
|
||||
return updateMirror(x, m)
|
||||
}
|
||||
|
||||
// DeleteMirrorByRepoID deletes a mirror by repoID
|
||||
func DeleteMirrorByRepoID(repoID int64) error {
|
||||
_, err := x.Delete(&Mirror{RepoID: repoID})
|
||||
return err
|
||||
@@ -241,6 +247,7 @@ func SyncMirrors() {
|
||||
}
|
||||
}
|
||||
|
||||
// InitSyncMirrors initializes a go routine to sync the mirros
|
||||
func InitSyncMirrors() {
|
||||
go SyncMirrors()
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ func TestRepo(t *testing.T) {
|
||||
Convey("It should be nil even if other settings are present", func() {
|
||||
repo.EnableExternalTracker = false
|
||||
repo.ExternalTrackerFormat = "http://someurl.com/{user}/{repo}/{issue}"
|
||||
repo.ExternalTrackerStyle = markdown.ISSUE_NAME_STYLE_NUMERIC
|
||||
repo.ExternalTrackerStyle = markdown.IssueNameStyleNumeric
|
||||
So(repo.ComposeMetas(), ShouldEqual, map[string]string(nil))
|
||||
})
|
||||
})
|
||||
@@ -33,17 +33,17 @@ func TestRepo(t *testing.T) {
|
||||
repo.EnableExternalTracker = true
|
||||
Convey("It should default to numeric issue style", func() {
|
||||
metas := repo.ComposeMetas()
|
||||
So(metas["style"], ShouldEqual, markdown.ISSUE_NAME_STYLE_NUMERIC)
|
||||
So(metas["style"], ShouldEqual, markdown.IssueNameStyleNumeric)
|
||||
})
|
||||
Convey("It should pass through numeric issue style setting", func() {
|
||||
repo.ExternalTrackerStyle = markdown.ISSUE_NAME_STYLE_NUMERIC
|
||||
repo.ExternalTrackerStyle = markdown.IssueNameStyleNumeric
|
||||
metas := repo.ComposeMetas()
|
||||
So(metas["style"], ShouldEqual, markdown.ISSUE_NAME_STYLE_NUMERIC)
|
||||
So(metas["style"], ShouldEqual, markdown.IssueNameStyleNumeric)
|
||||
})
|
||||
Convey("It should pass through alphanumeric issue style setting", func() {
|
||||
repo.ExternalTrackerStyle = markdown.ISSUE_NAME_STYLE_ALPHANUMERIC
|
||||
repo.ExternalTrackerStyle = markdown.IssueNameStyleAlphanumeric
|
||||
metas := repo.ComposeMetas()
|
||||
So(metas["style"], ShouldEqual, markdown.ISSUE_NAME_STYLE_ALPHANUMERIC)
|
||||
So(metas["style"], ShouldEqual, markdown.IssueNameStyleAlphanumeric)
|
||||
})
|
||||
Convey("It should contain the user name", func() {
|
||||
metas := repo.ComposeMetas()
|
||||
|
||||
@@ -22,7 +22,6 @@ import (
|
||||
"github.com/go-xorm/xorm"
|
||||
"golang.org/x/crypto/ssh"
|
||||
|
||||
"code.gitea.io/gitea/modules/base"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/process"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
@@ -34,10 +33,13 @@ const (
|
||||
|
||||
var sshOpLocker sync.Mutex
|
||||
|
||||
// KeyType specifies the key type
|
||||
type KeyType int
|
||||
|
||||
const (
|
||||
// KeyTypeUser specifies the user key
|
||||
KeyTypeUser = iota + 1
|
||||
// KeyTypeDeploy specifies the deploy key
|
||||
KeyTypeDeploy
|
||||
)
|
||||
|
||||
@@ -59,28 +61,31 @@ type PublicKey struct {
|
||||
HasUsed bool `xorm:"-"`
|
||||
}
|
||||
|
||||
func (k *PublicKey) BeforeInsert() {
|
||||
k.CreatedUnix = time.Now().Unix()
|
||||
// BeforeInsert will be invoked by XORM before inserting a record
|
||||
func (key *PublicKey) BeforeInsert() {
|
||||
key.CreatedUnix = time.Now().Unix()
|
||||
}
|
||||
|
||||
func (k *PublicKey) BeforeUpdate() {
|
||||
k.UpdatedUnix = time.Now().Unix()
|
||||
// BeforeUpdate is invoked from XORM before updating this object.
|
||||
func (key *PublicKey) BeforeUpdate() {
|
||||
key.UpdatedUnix = time.Now().Unix()
|
||||
}
|
||||
|
||||
func (k *PublicKey) AfterSet(colName string, _ xorm.Cell) {
|
||||
// AfterSet is invoked from XORM after setting the value of a field of this object.
|
||||
func (key *PublicKey) AfterSet(colName string, _ xorm.Cell) {
|
||||
switch colName {
|
||||
case "created_unix":
|
||||
k.Created = time.Unix(k.CreatedUnix, 0).Local()
|
||||
key.Created = time.Unix(key.CreatedUnix, 0).Local()
|
||||
case "updated_unix":
|
||||
k.Updated = time.Unix(k.UpdatedUnix, 0).Local()
|
||||
k.HasUsed = k.Updated.After(k.Created)
|
||||
k.HasRecentActivity = k.Updated.Add(7 * 24 * time.Hour).After(time.Now())
|
||||
key.Updated = time.Unix(key.UpdatedUnix, 0).Local()
|
||||
key.HasUsed = key.Updated.After(key.Created)
|
||||
key.HasRecentActivity = key.Updated.Add(7 * 24 * time.Hour).After(time.Now())
|
||||
}
|
||||
}
|
||||
|
||||
// OmitEmail returns content of public key without email address.
|
||||
func (k *PublicKey) OmitEmail() string {
|
||||
return strings.Join(strings.Split(k.Content, " ")[:2], " ")
|
||||
func (key *PublicKey) OmitEmail() string {
|
||||
return strings.Join(strings.Split(key.Content, " ")[:2], " ")
|
||||
}
|
||||
|
||||
// AuthorizedString returns formatted public key string for authorized_keys file.
|
||||
@@ -106,6 +111,8 @@ func extractTypeFromBase64Key(key string) (string, error) {
|
||||
func parseKeyString(content string) (string, error) {
|
||||
// Transform all legal line endings to a single "\n".
|
||||
content = strings.NewReplacer("\r\n", "\n", "\r", "\n").Replace(content)
|
||||
// remove trailing newline (and beginning spaces too)
|
||||
content = strings.TrimSpace(content)
|
||||
lines := strings.Split(content, "\n")
|
||||
|
||||
var keyType, keyContent, keyComment string
|
||||
@@ -366,7 +373,12 @@ func addKey(e Engine, key *PublicKey) (err error) {
|
||||
// Calculate fingerprint.
|
||||
tmpPath := strings.Replace(path.Join(os.TempDir(), fmt.Sprintf("%d", time.Now().Nanosecond()),
|
||||
"id_rsa.pub"), "\\", "/", -1)
|
||||
os.MkdirAll(path.Dir(tmpPath), os.ModePerm)
|
||||
dir := path.Dir(tmpPath)
|
||||
|
||||
if err := os.MkdirAll(dir, os.ModePerm); err != nil {
|
||||
return fmt.Errorf("Fail to create dir %s: %v", dir, err)
|
||||
}
|
||||
|
||||
if err = ioutil.WriteFile(tmpPath, []byte(key.Content), 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -476,7 +488,7 @@ func deletePublicKeys(e *xorm.Session, keyIDs ...int64) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
_, err := e.In("id", strings.Join(base.Int64sToStrings(keyIDs), ",")).Delete(new(PublicKey))
|
||||
_, err := e.In("id", keyIDs).Delete(new(PublicKey))
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -572,32 +584,35 @@ type DeployKey struct {
|
||||
HasUsed bool `xorm:"-"`
|
||||
}
|
||||
|
||||
func (k *DeployKey) BeforeInsert() {
|
||||
k.CreatedUnix = time.Now().Unix()
|
||||
// BeforeInsert will be invoked by XORM before inserting a record
|
||||
func (key *DeployKey) BeforeInsert() {
|
||||
key.CreatedUnix = time.Now().Unix()
|
||||
}
|
||||
|
||||
func (k *DeployKey) BeforeUpdate() {
|
||||
k.UpdatedUnix = time.Now().Unix()
|
||||
// BeforeUpdate is invoked from XORM before updating this object.
|
||||
func (key *DeployKey) BeforeUpdate() {
|
||||
key.UpdatedUnix = time.Now().Unix()
|
||||
}
|
||||
|
||||
func (k *DeployKey) AfterSet(colName string, _ xorm.Cell) {
|
||||
// AfterSet is invoked from XORM after setting the value of a field of this object.
|
||||
func (key *DeployKey) AfterSet(colName string, _ xorm.Cell) {
|
||||
switch colName {
|
||||
case "created_unix":
|
||||
k.Created = time.Unix(k.CreatedUnix, 0).Local()
|
||||
key.Created = time.Unix(key.CreatedUnix, 0).Local()
|
||||
case "updated_unix":
|
||||
k.Updated = time.Unix(k.UpdatedUnix, 0).Local()
|
||||
k.HasUsed = k.Updated.After(k.Created)
|
||||
k.HasRecentActivity = k.Updated.Add(7 * 24 * time.Hour).After(time.Now())
|
||||
key.Updated = time.Unix(key.UpdatedUnix, 0).Local()
|
||||
key.HasUsed = key.Updated.After(key.Created)
|
||||
key.HasRecentActivity = key.Updated.Add(7 * 24 * time.Hour).After(time.Now())
|
||||
}
|
||||
}
|
||||
|
||||
// GetContent gets associated public key content.
|
||||
func (k *DeployKey) GetContent() error {
|
||||
pkey, err := GetPublicKeyByID(k.KeyID)
|
||||
func (key *DeployKey) GetContent() error {
|
||||
pkey, err := GetPublicKeyByID(key.KeyID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
k.Content = pkey.Content
|
||||
key.Content = pkey.Content
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -28,14 +28,17 @@ type AccessToken struct {
|
||||
HasUsed bool `xorm:"-"`
|
||||
}
|
||||
|
||||
// BeforeInsert will be invoked by XORM before inserting a record representing this object.
|
||||
func (t *AccessToken) BeforeInsert() {
|
||||
t.CreatedUnix = time.Now().Unix()
|
||||
}
|
||||
|
||||
// BeforeUpdate is invoked from XORM before updating this object.
|
||||
func (t *AccessToken) BeforeUpdate() {
|
||||
t.UpdatedUnix = time.Now().Unix()
|
||||
}
|
||||
|
||||
// AfterSet is invoked from XORM after setting the value of a field of this object.
|
||||
func (t *AccessToken) AfterSet(colName string, _ xorm.Cell) {
|
||||
switch colName {
|
||||
case "created_unix":
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
)
|
||||
|
||||
// UpdateTask defines an UpdateTask
|
||||
type UpdateTask struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
UUID string `xorm:"index"`
|
||||
@@ -23,6 +24,7 @@ type UpdateTask struct {
|
||||
NewCommitID string
|
||||
}
|
||||
|
||||
// AddUpdateTask adds an UpdateTask
|
||||
func AddUpdateTask(task *UpdateTask) error {
|
||||
_, err := x.Insert(task)
|
||||
return err
|
||||
@@ -42,6 +44,7 @@ func GetUpdateTaskByUUID(uuid string) (*UpdateTask, error) {
|
||||
return task, nil
|
||||
}
|
||||
|
||||
// DeleteUpdateTaskByUUID deletes an UpdateTask from the database
|
||||
func DeleteUpdateTaskByUUID(uuid string) error {
|
||||
_, err := x.Delete(&UpdateTask{UUID: uuid})
|
||||
return err
|
||||
@@ -60,8 +63,9 @@ func CommitToPushCommit(commit *git.Commit) *PushCommit {
|
||||
}
|
||||
}
|
||||
|
||||
// ListToPushCommits transforms a list.List to PushCommits type.
|
||||
func ListToPushCommits(l *list.List) *PushCommits {
|
||||
commits := make([]*PushCommit, 0)
|
||||
var commits []*PushCommit
|
||||
var actEmail string
|
||||
for e := l.Front(); e != nil; e = e.Next() {
|
||||
commit := e.Value.(*git.Commit)
|
||||
@@ -73,6 +77,7 @@ func ListToPushCommits(l *list.List) *PushCommits {
|
||||
return &PushCommits{l.Len(), commits, "", nil}
|
||||
}
|
||||
|
||||
// PushUpdateOptions defines the push update options
|
||||
type PushUpdateOptions struct {
|
||||
PusherID int64
|
||||
PusherName string
|
||||
|
||||
164
models/user.go
164
models/user.go
@@ -8,10 +8,12 @@ import (
|
||||
"bytes"
|
||||
"container/list"
|
||||
"crypto/sha256"
|
||||
"crypto/subtle"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"image"
|
||||
// Needed for jpeg support
|
||||
_ "image/jpeg"
|
||||
"image/png"
|
||||
"os"
|
||||
@@ -34,20 +36,35 @@ import (
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
)
|
||||
|
||||
// UserType defines the user type
|
||||
type UserType int
|
||||
|
||||
const (
|
||||
// UserTypeIndividual defines an individual user
|
||||
UserTypeIndividual UserType = iota // Historic reason to make it starts at 0.
|
||||
|
||||
// UserTypeOrganization defines an organization
|
||||
UserTypeOrganization
|
||||
)
|
||||
|
||||
var (
|
||||
ErrUserNotKeyOwner = errors.New("User does not the owner of public key")
|
||||
ErrEmailNotExist = errors.New("E-mail does not exist")
|
||||
ErrEmailNotActivated = errors.New("E-mail address has not been activated")
|
||||
ErrUserNameIllegal = errors.New("User name contains illegal characters")
|
||||
// ErrUserNotKeyOwner user does not own this key error
|
||||
ErrUserNotKeyOwner = errors.New("User does not own this public key")
|
||||
|
||||
// ErrEmailNotExist e-mail does not exist error
|
||||
ErrEmailNotExist = errors.New("E-mail does not exist")
|
||||
|
||||
// ErrEmailNotActivated e-mail address has not been activated error
|
||||
ErrEmailNotActivated = errors.New("E-mail address has not been activated")
|
||||
|
||||
// ErrUserNameIllegal user name contains illegal characters error
|
||||
ErrUserNameIllegal = errors.New("User name contains illegal characters")
|
||||
|
||||
// ErrLoginSourceNotActived login source is not actived error
|
||||
ErrLoginSourceNotActived = errors.New("Login source is not actived")
|
||||
ErrUnsupportedLoginType = errors.New("Login source is unknown")
|
||||
|
||||
// ErrUnsupportedLoginType login source is unknown error
|
||||
ErrUnsupportedLoginType = errors.New("Login source is unknown")
|
||||
)
|
||||
|
||||
// User represents the object of individual and member of organization.
|
||||
@@ -80,7 +97,7 @@ type User struct {
|
||||
|
||||
// Remember visibility choice for convenience, true for private
|
||||
LastRepoVisibility bool
|
||||
// Maximum repository creation limit, -1 means use gloabl default
|
||||
// Maximum repository creation limit, -1 means use global default
|
||||
MaxRepoCreation int `xorm:"NOT NULL DEFAULT -1"`
|
||||
|
||||
// Permissions
|
||||
@@ -107,13 +124,18 @@ type User struct {
|
||||
NumMembers int
|
||||
Teams []*Team `xorm:"-"`
|
||||
Members []*User `xorm:"-"`
|
||||
|
||||
// Preferences
|
||||
DiffViewStyle string `xorm:"NOT NULL DEFAULT ''"`
|
||||
}
|
||||
|
||||
// BeforeInsert is invoked from XORM before inserting an object of this type.
|
||||
func (u *User) BeforeInsert() {
|
||||
u.CreatedUnix = time.Now().Unix()
|
||||
u.UpdatedUnix = u.CreatedUnix
|
||||
}
|
||||
|
||||
// BeforeUpdate is invoked from XORM before updating this object.
|
||||
func (u *User) BeforeUpdate() {
|
||||
if u.MaxRepoCreation < -1 {
|
||||
u.MaxRepoCreation = -1
|
||||
@@ -121,11 +143,18 @@ func (u *User) BeforeUpdate() {
|
||||
u.UpdatedUnix = time.Now().Unix()
|
||||
}
|
||||
|
||||
// Set time to last login
|
||||
// SetLastLogin set time to last login
|
||||
func (u *User) SetLastLogin() {
|
||||
u.LastLoginUnix = time.Now().Unix()
|
||||
}
|
||||
|
||||
// UpdateDiffViewStyle updates the users diff view style
|
||||
func (u *User) UpdateDiffViewStyle(style string) error {
|
||||
u.DiffViewStyle = style
|
||||
return UpdateUser(u)
|
||||
}
|
||||
|
||||
// AfterSet is invoked from XORM after setting the value of a field of this object.
|
||||
func (u *User) AfterSet(colName string, _ xorm.Cell) {
|
||||
switch colName {
|
||||
case "full_name":
|
||||
@@ -139,17 +168,18 @@ func (u *User) AfterSet(colName string, _ xorm.Cell) {
|
||||
}
|
||||
}
|
||||
|
||||
// APIFormat converts a User to api.User
|
||||
func (u *User) APIFormat() *api.User {
|
||||
return &api.User{
|
||||
ID: u.ID,
|
||||
UserName: u.Name,
|
||||
FullName: u.FullName,
|
||||
Email: u.Email,
|
||||
AvatarUrl: u.AvatarLink(),
|
||||
AvatarURL: u.AvatarLink(),
|
||||
}
|
||||
}
|
||||
|
||||
// returns true if user login type is LoginPlain.
|
||||
// IsLocal returns true if user login type is LoginPlain.
|
||||
func (u *User) IsLocal() bool {
|
||||
return u.LoginType <= LoginPlain
|
||||
}
|
||||
@@ -160,6 +190,7 @@ func (u *User) HasForkedRepo(repoID int64) bool {
|
||||
return has
|
||||
}
|
||||
|
||||
// RepoCreationNum returns the number of repositories created by the user
|
||||
func (u *User) RepoCreationNum() int {
|
||||
if u.MaxRepoCreation <= -1 {
|
||||
return setting.Repository.MaxCreationLimit
|
||||
@@ -167,6 +198,7 @@ func (u *User) RepoCreationNum() int {
|
||||
return u.MaxRepoCreation
|
||||
}
|
||||
|
||||
// CanCreateRepo returns if user login can create a repository
|
||||
func (u *User) CanCreateRepo() bool {
|
||||
if u.MaxRepoCreation <= -1 {
|
||||
if setting.Repository.MaxCreationLimit <= -1 {
|
||||
@@ -190,14 +222,14 @@ func (u *User) CanImportLocal() bool {
|
||||
// DashboardLink returns the user dashboard page link.
|
||||
func (u *User) DashboardLink() string {
|
||||
if u.IsOrganization() {
|
||||
return setting.AppSubUrl + "/org/" + u.Name + "/dashboard/"
|
||||
return setting.AppSubURL + "/org/" + u.Name + "/dashboard/"
|
||||
}
|
||||
return setting.AppSubUrl + "/"
|
||||
return setting.AppSubURL + "/"
|
||||
}
|
||||
|
||||
// HomeLink returns the user or organization home page link.
|
||||
func (u *User) HomeLink() string {
|
||||
return setting.AppSubUrl + "/" + u.Name
|
||||
return setting.AppSubURL + "/" + u.Name
|
||||
}
|
||||
|
||||
// GenerateEmailActivateCode generates an activate code based on user information and given e-mail.
|
||||
@@ -253,17 +285,17 @@ func (u *User) GenerateRandomAvatar() error {
|
||||
// which includes app sub-url as prefix. However, it is possible
|
||||
// to return full URL if user enables Gravatar-like service.
|
||||
func (u *User) RelAvatarLink() string {
|
||||
defaultImgUrl := setting.AppSubUrl + "/img/avatar_default.png"
|
||||
defaultImgURL := setting.AppSubURL + "/img/avatar_default.png"
|
||||
if u.ID == -1 {
|
||||
return defaultImgUrl
|
||||
return defaultImgURL
|
||||
}
|
||||
|
||||
switch {
|
||||
case u.UseCustomAvatar:
|
||||
if !com.IsExist(u.CustomAvatarPath()) {
|
||||
return defaultImgUrl
|
||||
return defaultImgURL
|
||||
}
|
||||
return setting.AppSubUrl + "/avatars/" + com.ToStr(u.ID)
|
||||
return setting.AppSubURL + "/avatars/" + com.ToStr(u.ID)
|
||||
case setting.DisableGravatar, setting.OfflineMode:
|
||||
if !com.IsExist(u.CustomAvatarPath()) {
|
||||
if err := u.GenerateRandomAvatar(); err != nil {
|
||||
@@ -271,7 +303,7 @@ func (u *User) RelAvatarLink() string {
|
||||
}
|
||||
}
|
||||
|
||||
return setting.AppSubUrl + "/avatars/" + com.ToStr(u.ID)
|
||||
return setting.AppSubURL + "/avatars/" + com.ToStr(u.ID)
|
||||
}
|
||||
return base.AvatarLink(u.AvatarEmail)
|
||||
}
|
||||
@@ -280,12 +312,12 @@ func (u *User) RelAvatarLink() string {
|
||||
func (u *User) AvatarLink() string {
|
||||
link := u.RelAvatarLink()
|
||||
if link[0] == '/' && link[1] != '/' {
|
||||
return setting.AppUrl + strings.TrimPrefix(link, setting.AppSubUrl)[1:]
|
||||
return setting.AppURL + strings.TrimPrefix(link, setting.AppSubURL)[1:]
|
||||
}
|
||||
return link
|
||||
}
|
||||
|
||||
// User.GetFollwoers returns range of user's followers.
|
||||
// GetFollowers returns range of user's followers.
|
||||
func (u *User) GetFollowers(page int) ([]*User, error) {
|
||||
users := make([]*User, 0, ItemsPerPage)
|
||||
sess := x.
|
||||
@@ -299,6 +331,7 @@ func (u *User) GetFollowers(page int) ([]*User, error) {
|
||||
return users, sess.Find(&users)
|
||||
}
|
||||
|
||||
// IsFollowing returns true if user is following followID.
|
||||
func (u *User) IsFollowing(followID int64) bool {
|
||||
return IsFollowing(u.ID, followID)
|
||||
}
|
||||
@@ -336,7 +369,7 @@ func (u *User) EncodePasswd() {
|
||||
func (u *User) ValidatePassword(passwd string) bool {
|
||||
newUser := &User{Passwd: passwd, Salt: u.Salt}
|
||||
newUser.EncodePasswd()
|
||||
return u.Passwd == newUser.Passwd
|
||||
return subtle.ConstantTimeCompare([]byte(u.Passwd), []byte(newUser.Passwd)) == 1
|
||||
}
|
||||
|
||||
// UploadAvatar saves custom avatar for user.
|
||||
@@ -347,7 +380,7 @@ func (u *User) UploadAvatar(data []byte) error {
|
||||
return fmt.Errorf("Decode: %v", err)
|
||||
}
|
||||
|
||||
m := resize.Resize(avatar.AVATAR_SIZE, avatar.AVATAR_SIZE, img, resize.NearestNeighbor)
|
||||
m := resize.Resize(avatar.AvatarSize, avatar.AvatarSize, img, resize.NearestNeighbor)
|
||||
|
||||
sess := x.NewSession()
|
||||
defer sessionRelease(sess)
|
||||
@@ -360,7 +393,10 @@ func (u *User) UploadAvatar(data []byte) error {
|
||||
return fmt.Errorf("updateUser: %v", err)
|
||||
}
|
||||
|
||||
os.MkdirAll(setting.AvatarUploadPath, os.ModePerm)
|
||||
if err := os.MkdirAll(setting.AvatarUploadPath, os.ModePerm); err != nil {
|
||||
return fmt.Errorf("Fail to create dir %s: %v", setting.AvatarUploadPath, err)
|
||||
}
|
||||
|
||||
fw, err := os.Create(u.CustomAvatarPath())
|
||||
if err != nil {
|
||||
return fmt.Errorf("Create: %v", err)
|
||||
@@ -377,7 +413,10 @@ func (u *User) UploadAvatar(data []byte) error {
|
||||
// DeleteAvatar deletes the user's custom avatar.
|
||||
func (u *User) DeleteAvatar() error {
|
||||
log.Trace("DeleteAvatar[%d]: %s", u.ID, u.CustomAvatarPath())
|
||||
os.Remove(u.CustomAvatarPath())
|
||||
|
||||
if err := os.Remove(u.CustomAvatarPath()); err != nil {
|
||||
return fmt.Errorf("Fail to remove %s: %v", u.CustomAvatarPath(), err)
|
||||
}
|
||||
|
||||
u.UseCustomAvatar = false
|
||||
if err := UpdateUser(u); err != nil {
|
||||
@@ -410,13 +449,13 @@ func (u *User) IsOrganization() bool {
|
||||
}
|
||||
|
||||
// IsUserOrgOwner returns true if user is in the owner team of given organization.
|
||||
func (u *User) IsUserOrgOwner(orgId int64) bool {
|
||||
return IsOrganizationOwner(orgId, u.ID)
|
||||
func (u *User) IsUserOrgOwner(orgID int64) bool {
|
||||
return IsOrganizationOwner(orgID, u.ID)
|
||||
}
|
||||
|
||||
// IsPublicMember returns true if user public his/her membership in give organization.
|
||||
func (u *User) IsPublicMember(orgId int64) bool {
|
||||
return IsPublicMembership(orgId, u.ID)
|
||||
func (u *User) IsPublicMember(orgID int64) bool {
|
||||
return IsPublicMembership(orgID, u.ID)
|
||||
}
|
||||
|
||||
func (u *User) getOrganizationCount(e Engine) (int64, error) {
|
||||
@@ -436,7 +475,7 @@ func (u *User) GetRepositories(page, pageSize int) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
// GetRepositories returns mirror repositories that user owns, including private repositories.
|
||||
// GetMirrorRepositories returns mirror repositories that user owns, including private repositories.
|
||||
func (u *User) GetMirrorRepositories() ([]*Repository, error) {
|
||||
return GetUserMirrorRepositories(u.ID)
|
||||
}
|
||||
@@ -473,6 +512,7 @@ func (u *User) DisplayName() string {
|
||||
return u.Name
|
||||
}
|
||||
|
||||
// ShortName ellipses username to length
|
||||
func (u *User) ShortName(length int) string {
|
||||
return base.EllipsisString(u.Name, length)
|
||||
}
|
||||
@@ -505,12 +545,12 @@ func NewGhostUser() *User {
|
||||
}
|
||||
|
||||
var (
|
||||
reversedUsernames = []string{"debug", "raw", "install", "api", "avatar", "user", "org", "help", "stars", "issues", "pulls", "commits", "repo", "template", "admin", "new", ".", ".."}
|
||||
reversedUserPatterns = []string{"*.keys"}
|
||||
reservedUsernames = []string{"assets", "css", "img", "js", "less", "plugins", "debug", "raw", "install", "api", "avatar", "user", "org", "help", "stars", "issues", "pulls", "commits", "repo", "template", "admin", "new", ".", ".."}
|
||||
reservedUserPatterns = []string{"*.keys"}
|
||||
)
|
||||
|
||||
// isUsableName checks if name is reserved or pattern of name is not allowed
|
||||
// based on given reversed names and patterns.
|
||||
// based on given reserved names and patterns.
|
||||
// Names are exact match, patterns can be prefix or suffix match with placeholder '*'.
|
||||
func isUsableName(names, patterns []string, name string) error {
|
||||
name = strings.TrimSpace(strings.ToLower(name))
|
||||
@@ -534,8 +574,9 @@ func isUsableName(names, patterns []string, name string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsUsableUsername returns an error when a username is reserved
|
||||
func IsUsableUsername(name string) error {
|
||||
return isUsableName(reversedUsernames, reversedUserPatterns, name)
|
||||
return isUsableName(reservedUsernames, reservedUserPatterns, name)
|
||||
}
|
||||
|
||||
// CreateUser creates record of a new user.
|
||||
@@ -622,7 +663,7 @@ func getVerifyUser(code string) (user *User) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// verify active code when active account
|
||||
// VerifyUserActiveCode verifies active code when active account
|
||||
func VerifyUserActiveCode(code string) (user *User) {
|
||||
minutes := setting.Service.ActiveCodeLives
|
||||
|
||||
@@ -638,7 +679,7 @@ func VerifyUserActiveCode(code string) (user *User) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// verify active code when active account
|
||||
// VerifyActiveEmailCode verifies active email code when active account
|
||||
func VerifyActiveEmailCode(code, email string) *EmailAddress {
|
||||
minutes := setting.Service.ActiveCodeLives
|
||||
|
||||
@@ -832,9 +873,16 @@ func deleteUser(e *xorm.Session, u *User) error {
|
||||
// FIXME: system notice
|
||||
// Note: There are something just cannot be roll back,
|
||||
// so just keep error logs of those operations.
|
||||
path := UserPath(u.Name)
|
||||
|
||||
os.RemoveAll(UserPath(u.Name))
|
||||
os.Remove(u.CustomAvatarPath())
|
||||
if err := os.RemoveAll(path); err != nil {
|
||||
return fmt.Errorf("Fail to RemoveAll %s: %v", path, err)
|
||||
}
|
||||
|
||||
avatarPath := u.CustomAvatarPath()
|
||||
if err := os.Remove(avatarPath); err != nil {
|
||||
return fmt.Errorf("Fail to remove %s: %v", avatarPath, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -890,12 +938,19 @@ func UserPath(userName string) string {
|
||||
return filepath.Join(setting.RepoRootPath, strings.ToLower(userName))
|
||||
}
|
||||
|
||||
// GetUserByKeyID get user information by user's public key id
|
||||
func GetUserByKeyID(keyID int64) (*User, error) {
|
||||
user := new(User)
|
||||
return user, x.
|
||||
Join("INNER", "public_key", "`public_key`.owner_id = `user`.id").
|
||||
var user User
|
||||
has, err := x.Join("INNER", "public_key", "`public_key`.owner_id = `user`.id").
|
||||
Where("`public_key`.id=?", keyID).
|
||||
Find(user)
|
||||
Get(&user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !has {
|
||||
return nil, ErrUserNotExist{0, "", keyID}
|
||||
}
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
func getUserByID(e Engine, id int64) (*User, error) {
|
||||
@@ -904,7 +959,7 @@ func getUserByID(e Engine, id int64) (*User, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !has {
|
||||
return nil, ErrUserNotExist{id, ""}
|
||||
return nil, ErrUserNotExist{id, "", 0}
|
||||
}
|
||||
return u, nil
|
||||
}
|
||||
@@ -920,7 +975,7 @@ func GetAssigneeByID(repo *Repository, userID int64) (*User, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !has {
|
||||
return nil, ErrUserNotExist{userID, ""}
|
||||
return nil, ErrUserNotExist{userID, "", 0}
|
||||
}
|
||||
return GetUserByID(userID)
|
||||
}
|
||||
@@ -928,14 +983,14 @@ func GetAssigneeByID(repo *Repository, userID int64) (*User, error) {
|
||||
// GetUserByName returns user by given name.
|
||||
func GetUserByName(name string) (*User, error) {
|
||||
if len(name) == 0 {
|
||||
return nil, ErrUserNotExist{0, name}
|
||||
return nil, ErrUserNotExist{0, name, 0}
|
||||
}
|
||||
u := &User{LowerName: strings.ToLower(name)}
|
||||
has, err := x.Get(u)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !has {
|
||||
return nil, ErrUserNotExist{0, name}
|
||||
return nil, ErrUserNotExist{0, name, 0}
|
||||
}
|
||||
return u, nil
|
||||
}
|
||||
@@ -1021,7 +1076,7 @@ func ValidateCommitsWithEmails(oldCommits *list.List) *list.List {
|
||||
// GetUserByEmail returns the user object by given e-mail if exists.
|
||||
func GetUserByEmail(email string) (*User, error) {
|
||||
if len(email) == 0 {
|
||||
return nil, ErrUserNotExist{0, "email"}
|
||||
return nil, ErrUserNotExist{0, email, 0}
|
||||
}
|
||||
|
||||
email = strings.ToLower(email)
|
||||
@@ -1045,9 +1100,10 @@ func GetUserByEmail(email string) (*User, error) {
|
||||
return GetUserByID(emailAddress.UID)
|
||||
}
|
||||
|
||||
return nil, ErrUserNotExist{0, email}
|
||||
return nil, ErrUserNotExist{0, email, 0}
|
||||
}
|
||||
|
||||
// SearchUserOptions contains the options for searching
|
||||
type SearchUserOptions struct {
|
||||
Keyword string
|
||||
Type UserType
|
||||
@@ -1108,6 +1164,7 @@ type Follow struct {
|
||||
FollowID int64 `xorm:"UNIQUE(follow)"`
|
||||
}
|
||||
|
||||
// IsFollowing returns true if user is following followID.
|
||||
func IsFollowing(userID, followID int64) bool {
|
||||
has, _ := x.Get(&Follow{UserID: userID, FollowID: followID})
|
||||
return has
|
||||
@@ -1164,3 +1221,18 @@ func UnfollowUser(userID, followID int64) (err error) {
|
||||
}
|
||||
return sess.Commit()
|
||||
}
|
||||
|
||||
// GetStarredRepos returns the repos starred by a particular user
|
||||
func GetStarredRepos(userID int64, private bool) ([]*Repository, error) {
|
||||
sess := x.Where("star.uid=?", userID).
|
||||
Join("LEFT", "star", "`repository`.id=`star`.repo_id")
|
||||
if !private {
|
||||
sess = sess.And("is_private=?", false)
|
||||
}
|
||||
repos := make([]*Repository, 0, 10)
|
||||
err := sess.Find(&repos)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return repos, nil
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// EmailAdresses is the list of all email addresses of a user. Can contain the
|
||||
// EmailAddress is the list of all email addresses of a user. Can contain the
|
||||
// primary email address, but is not obligatory.
|
||||
type EmailAddress struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
@@ -81,10 +81,12 @@ func addEmailAddress(e Engine, email *EmailAddress) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// AddEmailAddress adds an email address to given user.
|
||||
func AddEmailAddress(email *EmailAddress) error {
|
||||
return addEmailAddress(x, email)
|
||||
}
|
||||
|
||||
// AddEmailAddresses adds an email address to given user.
|
||||
func AddEmailAddresses(emails []*EmailAddress) error {
|
||||
if len(emails) == 0 {
|
||||
return nil
|
||||
@@ -108,6 +110,7 @@ func AddEmailAddresses(emails []*EmailAddress) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Activate activates the email address to given user.
|
||||
func (email *EmailAddress) Activate() error {
|
||||
user, err := GetUserByID(email.UID)
|
||||
if err != nil {
|
||||
@@ -134,6 +137,7 @@ func (email *EmailAddress) Activate() error {
|
||||
return sess.Commit()
|
||||
}
|
||||
|
||||
// DeleteEmailAddress deletes an email address of given user.
|
||||
func DeleteEmailAddress(email *EmailAddress) (err error) {
|
||||
if email.ID > 0 {
|
||||
_, err = x.Id(email.ID).Delete(new(EmailAddress))
|
||||
@@ -145,6 +149,7 @@ func DeleteEmailAddress(email *EmailAddress) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteEmailAddresses deletes multiple email addresses
|
||||
func DeleteEmailAddresses(emails []*EmailAddress) (err error) {
|
||||
for i := range emails {
|
||||
if err = DeleteEmailAddress(emails[i]); err != nil {
|
||||
@@ -155,6 +160,7 @@ func DeleteEmailAddresses(emails []*EmailAddress) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MakeEmailPrimary sets primary email address of given user.
|
||||
func MakeEmailPrimary(email *EmailAddress) error {
|
||||
has, err := x.Get(email)
|
||||
if err != nil {
|
||||
@@ -172,7 +178,7 @@ func MakeEmailPrimary(email *EmailAddress) error {
|
||||
if err != nil {
|
||||
return err
|
||||
} else if !has {
|
||||
return ErrUserNotExist{email.UID, ""}
|
||||
return ErrUserNotExist{email.UID, "", 0}
|
||||
}
|
||||
|
||||
// Make sure the former primary email doesn't disappear.
|
||||
|
||||
@@ -23,12 +23,16 @@ import (
|
||||
"code.gitea.io/gitea/modules/sync"
|
||||
)
|
||||
|
||||
// HookQueue is a global queue of web hooks
|
||||
var HookQueue = sync.NewUniqueQueue(setting.Webhook.QueueLength)
|
||||
|
||||
// HookContentType is the content type of a web hook
|
||||
type HookContentType int
|
||||
|
||||
const (
|
||||
// ContentTypeJSON is a JSON payload for web hooks
|
||||
ContentTypeJSON HookContentType = iota + 1
|
||||
// ContentTypeForm is an url-encoded form payload for web hook
|
||||
ContentTypeForm
|
||||
)
|
||||
|
||||
@@ -42,6 +46,7 @@ func ToHookContentType(name string) HookContentType {
|
||||
return hookContentTypes[name]
|
||||
}
|
||||
|
||||
// Name returns the name of a given web hook's content type
|
||||
func (t HookContentType) Name() string {
|
||||
switch t {
|
||||
case ContentTypeJSON:
|
||||
@@ -58,6 +63,7 @@ func IsValidHookContentType(name string) bool {
|
||||
return ok
|
||||
}
|
||||
|
||||
// HookEvents is a set of web hook events
|
||||
type HookEvents struct {
|
||||
Create bool `json:"create"`
|
||||
Push bool `json:"push"`
|
||||
@@ -73,8 +79,10 @@ type HookEvent struct {
|
||||
HookEvents `json:"events"`
|
||||
}
|
||||
|
||||
// HookStatus is the status of a web hook
|
||||
type HookStatus int
|
||||
|
||||
// Possible statuses of a web hook
|
||||
const (
|
||||
HookStatusNone = iota
|
||||
HookStatusSucceed
|
||||
@@ -103,15 +111,20 @@ type Webhook struct {
|
||||
UpdatedUnix int64
|
||||
}
|
||||
|
||||
// BeforeInsert will be invoked by XORM before inserting a record
|
||||
// representing this object
|
||||
func (w *Webhook) BeforeInsert() {
|
||||
w.CreatedUnix = time.Now().Unix()
|
||||
w.UpdatedUnix = w.CreatedUnix
|
||||
}
|
||||
|
||||
// BeforeUpdate will be invoked by XORM before updating a record
|
||||
// representing this object
|
||||
func (w *Webhook) BeforeUpdate() {
|
||||
w.UpdatedUnix = time.Now().Unix()
|
||||
}
|
||||
|
||||
// AfterSet updates the webhook object upon setting a column
|
||||
func (w *Webhook) AfterSet(colName string, _ xorm.Cell) {
|
||||
var err error
|
||||
switch colName {
|
||||
@@ -127,6 +140,7 @@ func (w *Webhook) AfterSet(colName string, _ xorm.Cell) {
|
||||
}
|
||||
}
|
||||
|
||||
// GetSlackHook returns slack metadata
|
||||
func (w *Webhook) GetSlackHook() *SlackMeta {
|
||||
s := &SlackMeta{}
|
||||
if err := json.Unmarshal([]byte(w.Meta), s); err != nil {
|
||||
@@ -165,6 +179,7 @@ func (w *Webhook) HasPullRequestEvent() bool {
|
||||
(w.ChooseEvents && w.HookEvents.PullRequest)
|
||||
}
|
||||
|
||||
// EventsArray returns an array of hook events
|
||||
func (w *Webhook) EventsArray() []string {
|
||||
events := make([]string, 0, 3)
|
||||
if w.HasCreateEvent() {
|
||||
@@ -290,8 +305,10 @@ func GetActiveWebhooksByOrgID(orgID int64) (ws []*Webhook, err error) {
|
||||
// \___|_ / \____/ \____/|__|_ \ |____| (____ /____ >__|_ \
|
||||
// \/ \/ \/ \/ \/
|
||||
|
||||
// HookTaskType is the type of an hook task
|
||||
type HookTaskType int
|
||||
|
||||
// Types of hook tasks
|
||||
const (
|
||||
GOGS HookTaskType = iota + 1
|
||||
SLACK
|
||||
@@ -307,6 +324,7 @@ func ToHookTaskType(name string) HookTaskType {
|
||||
return hookTaskTypes[name]
|
||||
}
|
||||
|
||||
// Name returns the name of an hook task type
|
||||
func (t HookTaskType) Name() string {
|
||||
switch t {
|
||||
case GOGS:
|
||||
@@ -323,8 +341,10 @@ func IsValidHookTaskType(name string) bool {
|
||||
return ok
|
||||
}
|
||||
|
||||
// HookEventType is the type of an hook event
|
||||
type HookEventType string
|
||||
|
||||
// Types of hook events
|
||||
const (
|
||||
HookEventCreate HookEventType = "create"
|
||||
HookEventPush HookEventType = "push"
|
||||
@@ -368,15 +388,18 @@ type HookTask struct {
|
||||
ResponseInfo *HookResponse `xorm:"-"`
|
||||
}
|
||||
|
||||
// BeforeUpdate will be invoked by XORM before updating a record
|
||||
// representing this object
|
||||
func (t *HookTask) BeforeUpdate() {
|
||||
if t.RequestInfo != nil {
|
||||
t.RequestContent = t.SimpleMarshalJSON(t.RequestInfo)
|
||||
t.RequestContent = t.simpleMarshalJSON(t.RequestInfo)
|
||||
}
|
||||
if t.ResponseInfo != nil {
|
||||
t.ResponseContent = t.SimpleMarshalJSON(t.ResponseInfo)
|
||||
t.ResponseContent = t.simpleMarshalJSON(t.ResponseInfo)
|
||||
}
|
||||
}
|
||||
|
||||
// AfterSet updates the webhook object upon setting a column
|
||||
func (t *HookTask) AfterSet(colName string, _ xorm.Cell) {
|
||||
var err error
|
||||
switch colName {
|
||||
@@ -405,7 +428,7 @@ func (t *HookTask) AfterSet(colName string, _ xorm.Cell) {
|
||||
}
|
||||
}
|
||||
|
||||
func (t *HookTask) SimpleMarshalJSON(v interface{}) string {
|
||||
func (t *HookTask) simpleMarshalJSON(v interface{}) string {
|
||||
p, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
log.Error(3, "Marshal [%d]: %v", t.ID, err)
|
||||
@@ -624,6 +647,7 @@ func DeliverHooks() {
|
||||
}
|
||||
}
|
||||
|
||||
// InitDeliverHooks starts the hooks delivery thread
|
||||
func InitDeliverHooks() {
|
||||
go DeliverHooks()
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
)
|
||||
|
||||
// SlackMeta contains the slack metdata
|
||||
type SlackMeta struct {
|
||||
Channel string `json:"channel"`
|
||||
Username string `json:"username"`
|
||||
@@ -23,6 +24,7 @@ type SlackMeta struct {
|
||||
Color string `json:"color"`
|
||||
}
|
||||
|
||||
// SlackPayload contains the information about the slack channel
|
||||
type SlackPayload struct {
|
||||
Channel string `json:"channel"`
|
||||
Text string `json:"text"`
|
||||
@@ -33,6 +35,7 @@ type SlackPayload struct {
|
||||
Attachments []SlackAttachment `json:"attachments"`
|
||||
}
|
||||
|
||||
// SlackAttachment contains the slack message
|
||||
type SlackAttachment struct {
|
||||
Fallback string `json:"fallback"`
|
||||
Color string `json:"color"`
|
||||
@@ -40,8 +43,10 @@ type SlackAttachment struct {
|
||||
Text string `json:"text"`
|
||||
}
|
||||
|
||||
// SetSecret sets the slack secret
|
||||
func (p *SlackPayload) SetSecret(_ string) {}
|
||||
|
||||
// JSONPayload Marshals the SlackPayload to json
|
||||
func (p *SlackPayload) JSONPayload() ([]byte, error) {
|
||||
data, err := json.MarshalIndent(p, "", " ")
|
||||
if err != nil {
|
||||
@@ -50,6 +55,7 @@ func (p *SlackPayload) JSONPayload() ([]byte, error) {
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// SlackTextFormatter replaces &, <, > with HTML characters
|
||||
// see: https://api.slack.com/docs/formatting
|
||||
func SlackTextFormatter(s string) string {
|
||||
// replace & < >
|
||||
@@ -59,6 +65,7 @@ func SlackTextFormatter(s string) string {
|
||||
return s
|
||||
}
|
||||
|
||||
// SlackShortTextFormatter replaces &, <, > with HTML characters
|
||||
func SlackShortTextFormatter(s string) string {
|
||||
s = strings.Split(s, "\n")[0]
|
||||
// replace & < >
|
||||
@@ -68,6 +75,7 @@ func SlackShortTextFormatter(s string) string {
|
||||
return s
|
||||
}
|
||||
|
||||
// SlackLinkFormatter creates a link compatablie with slack
|
||||
func SlackLinkFormatter(url string, text string) string {
|
||||
return fmt.Sprintf("<%s|%s>", url, SlackTextFormatter(text))
|
||||
}
|
||||
@@ -134,7 +142,7 @@ func getSlackPushPayload(p *api.PushPayload, slack *SlackMeta) (*SlackPayload, e
|
||||
}
|
||||
|
||||
func getSlackPullRequestPayload(p *api.PullRequestPayload, slack *SlackMeta) (*SlackPayload, error) {
|
||||
senderLink := SlackLinkFormatter(setting.AppUrl+p.Sender.UserName, p.Sender.UserName)
|
||||
senderLink := SlackLinkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName)
|
||||
titleLink := SlackLinkFormatter(fmt.Sprintf("%s/pulls/%d", p.Repository.HTMLURL, p.Index),
|
||||
fmt.Sprintf("#%d %s", p.Index, p.PullRequest.Title))
|
||||
var text, title, attachmentText string
|
||||
@@ -149,14 +157,14 @@ func getSlackPullRequestPayload(p *api.PullRequestPayload, slack *SlackMeta) (*S
|
||||
} else {
|
||||
text = fmt.Sprintf("[%s] Pull request closed: %s by %s", p.Repository.FullName, titleLink, senderLink)
|
||||
}
|
||||
case api.HookIssueReopened:
|
||||
case api.HookIssueReOpened:
|
||||
text = fmt.Sprintf("[%s] Pull request re-opened: %s by %s", p.Repository.FullName, titleLink, senderLink)
|
||||
case api.HookIssueEdited:
|
||||
text = fmt.Sprintf("[%s] Pull request edited: %s by %s", p.Repository.FullName, titleLink, senderLink)
|
||||
attachmentText = SlackTextFormatter(p.PullRequest.Body)
|
||||
case api.HookIssueAssigned:
|
||||
text = fmt.Sprintf("[%s] Pull request assigned to %s: %s by %s", p.Repository.FullName,
|
||||
SlackLinkFormatter(setting.AppUrl+p.PullRequest.Assignee.UserName, p.PullRequest.Assignee.UserName),
|
||||
SlackLinkFormatter(setting.AppURL+p.PullRequest.Assignee.UserName, p.PullRequest.Assignee.UserName),
|
||||
titleLink, senderLink)
|
||||
case api.HookIssueUnassigned:
|
||||
text = fmt.Sprintf("[%s] Pull request unassigned: %s by %s", p.Repository.FullName, titleLink, senderLink)
|
||||
@@ -181,6 +189,7 @@ func getSlackPullRequestPayload(p *api.PullRequestPayload, slack *SlackMeta) (*S
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetSlackPayload converts a slack webhook into a SlackPayload
|
||||
func GetSlackPayload(p api.Payloader, event HookEventType, meta string) (*SlackPayload, error) {
|
||||
s := new(SlackPayload)
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@ func WikiPath(userName, repoName string) string {
|
||||
return filepath.Join(UserPath(userName), strings.ToLower(repoName)+".wiki.git")
|
||||
}
|
||||
|
||||
// WikiPath returns wiki data path for given repository.
|
||||
func (repo *Repository) WikiPath() string {
|
||||
return WikiPath(repo.MustOwner().Name, repo.Name)
|
||||
}
|
||||
@@ -70,6 +71,7 @@ func (repo *Repository) InitWiki() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// LocalWikiPath returns the path to the local wiki repository (?).
|
||||
func (repo *Repository) LocalWikiPath() string {
|
||||
return path.Join(setting.AppDataPath, "tmp/local-wiki", com.ToStr(repo.ID))
|
||||
}
|
||||
@@ -110,7 +112,11 @@ func (repo *Repository) updateWikiPage(doer *User, oldTitle, title, content, mes
|
||||
return ErrWikiAlreadyExist{filename}
|
||||
}
|
||||
} else {
|
||||
os.Remove(path.Join(localPath, oldTitle+".md"))
|
||||
file := path.Join(localPath, oldTitle+".md")
|
||||
|
||||
if err := os.Remove(file); err != nil {
|
||||
return fmt.Errorf("Fail to remove %s: %v", file, err)
|
||||
}
|
||||
}
|
||||
|
||||
// SECURITY: if new file is a symlink to non-exist critical file,
|
||||
@@ -118,7 +124,8 @@ func (repo *Repository) updateWikiPage(doer *User, oldTitle, title, content, mes
|
||||
// as a new page operation.
|
||||
// So we want to make sure the symlink is removed before write anything.
|
||||
// The new file we created will be in normal text format.
|
||||
os.Remove(filename)
|
||||
|
||||
_ = os.Remove(filename)
|
||||
|
||||
if err = ioutil.WriteFile(filename, []byte(content), 0666); err != nil {
|
||||
return fmt.Errorf("WriteFile: %v", err)
|
||||
@@ -141,14 +148,18 @@ func (repo *Repository) updateWikiPage(doer *User, oldTitle, title, content, mes
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddWikiPage adds a new wiki page with a given title.
|
||||
func (repo *Repository) AddWikiPage(doer *User, title, content, message string) error {
|
||||
return repo.updateWikiPage(doer, "", title, content, message, true)
|
||||
}
|
||||
|
||||
// EditWikiPage updates a wiki page identified by its title,
|
||||
// optionally also changing title.
|
||||
func (repo *Repository) EditWikiPage(doer *User, oldTitle, title, content, message string) error {
|
||||
return repo.updateWikiPage(doer, oldTitle, title, content, message, false)
|
||||
}
|
||||
|
||||
// DeleteWikiPage deletes a wiki page identified by its title.
|
||||
func (repo *Repository) DeleteWikiPage(doer *User, title string) (err error) {
|
||||
wikiWorkingPool.CheckIn(com.ToStr(repo.ID))
|
||||
defer wikiWorkingPool.CheckOut(com.ToStr(repo.ID))
|
||||
@@ -162,7 +173,10 @@ func (repo *Repository) DeleteWikiPage(doer *User, title string) (err error) {
|
||||
|
||||
title = ToWikiPageName(title)
|
||||
filename := path.Join(localPath, title+".md")
|
||||
os.Remove(filename)
|
||||
|
||||
if err := os.Remove(filename); err != nil {
|
||||
return fmt.Errorf("Fail to remove %s: %v", filename, err)
|
||||
}
|
||||
|
||||
message := "Delete page '" + title + "'"
|
||||
|
||||
|
||||
@@ -10,7 +10,8 @@ import (
|
||||
"github.com/go-macaron/binding"
|
||||
)
|
||||
|
||||
type AdminCrateUserForm struct {
|
||||
// AdminCreateUserForm form for admin to create user
|
||||
type AdminCreateUserForm struct {
|
||||
LoginType string `binding:"Required"`
|
||||
LoginName string
|
||||
UserName string `binding:"Required;AlphaDashDot;MaxSize(35)"`
|
||||
@@ -19,10 +20,12 @@ type AdminCrateUserForm struct {
|
||||
SendNotify bool
|
||||
}
|
||||
|
||||
func (f *AdminCrateUserForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
// Validate validates form fields
|
||||
func (f *AdminCreateUserForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||
}
|
||||
|
||||
// AdminEditUserForm form for admin to create user
|
||||
type AdminEditUserForm struct {
|
||||
LoginType string `binding:"Required"`
|
||||
LoginName string
|
||||
@@ -39,6 +42,7 @@ type AdminEditUserForm struct {
|
||||
ProhibitLogin bool
|
||||
}
|
||||
|
||||
// Validate validates form fields
|
||||
func (f *AdminEditUserForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
)
|
||||
|
||||
// IsAPIPath if URL is an api path
|
||||
func IsAPIPath(url string) bool {
|
||||
return strings.HasPrefix(url, "/api/")
|
||||
}
|
||||
@@ -34,6 +35,9 @@ func SignedInID(ctx *macaron.Context, sess session.Store) int64 {
|
||||
// Check access token.
|
||||
if IsAPIPath(ctx.Req.URL.Path) {
|
||||
tokenSHA := ctx.Query("token")
|
||||
if len(tokenSHA) <= 0 {
|
||||
tokenSHA = ctx.Query("access_token")
|
||||
}
|
||||
if len(tokenSHA) == 0 {
|
||||
// Well, check with header again.
|
||||
auHead := ctx.Req.Header.Get("Authorization")
|
||||
@@ -110,9 +114,8 @@ func SignedInUser(ctx *macaron.Context, sess session.Store) (*models.User, bool)
|
||||
// FIXME: should I create a system notice?
|
||||
log.Error(4, "CreateUser: %v", err)
|
||||
return nil, false
|
||||
} else {
|
||||
return u, false
|
||||
}
|
||||
return u, false
|
||||
}
|
||||
}
|
||||
return u, false
|
||||
@@ -148,6 +151,7 @@ func SignedInUser(ctx *macaron.Context, sess session.Store) (*models.User, bool)
|
||||
return u, false
|
||||
}
|
||||
|
||||
// Form form binding interface
|
||||
type Form interface {
|
||||
binding.Validator
|
||||
}
|
||||
@@ -190,18 +194,22 @@ func getRuleBody(field reflect.StructField, prefix string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// GetSize get size int form tag
|
||||
func GetSize(field reflect.StructField) string {
|
||||
return getRuleBody(field, "Size(")
|
||||
}
|
||||
|
||||
// GetMinSize get minimal size in form tag
|
||||
func GetMinSize(field reflect.StructField) string {
|
||||
return getRuleBody(field, "MinSize(")
|
||||
}
|
||||
|
||||
// GetMaxSize get max size in form tag
|
||||
func GetMaxSize(field reflect.StructField) string {
|
||||
return getRuleBody(field, "MaxSize(")
|
||||
}
|
||||
|
||||
// GetInclude get include in form tag
|
||||
func GetInclude(field reflect.StructField) string {
|
||||
return getRuleBody(field, "Include(")
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"gopkg.in/macaron.v1"
|
||||
)
|
||||
|
||||
// AuthenticationForm form for authentication
|
||||
type AuthenticationForm struct {
|
||||
ID int64
|
||||
Type int `binding:"Range(2,5)"`
|
||||
@@ -37,6 +38,7 @@ type AuthenticationForm struct {
|
||||
PAMServiceName string
|
||||
}
|
||||
|
||||
// Validate validates fields
|
||||
func (f *AuthenticationForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
)
|
||||
|
||||
// SecurityProtocol protocol type
|
||||
type SecurityProtocol int
|
||||
|
||||
// Note: new type must be added at the end of list to maintain compatibility.
|
||||
@@ -25,7 +26,7 @@ const (
|
||||
SecurityProtocolStartTLS
|
||||
)
|
||||
|
||||
// Basic LDAP authentication service
|
||||
// Source Basic LDAP authentication service
|
||||
type Source struct {
|
||||
Name string // canonical name (ie. corporate.ad)
|
||||
Host string // LDAP host
|
||||
@@ -148,7 +149,7 @@ func bindUser(l *ldap.Conn, userDN, passwd string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// searchEntry : search an LDAP source if an entry (name, passwd) is valid and in the specific filter
|
||||
// SearchEntry : search an LDAP source if an entry (name, passwd) is valid and in the specific filter
|
||||
func (ls *Source) SearchEntry(name, passwd string, directBind bool) (string, string, string, string, bool, bool) {
|
||||
l, err := dial(ls)
|
||||
if err != nil {
|
||||
|
||||
@@ -16,14 +16,17 @@ import (
|
||||
// \_______ /__| \___ (____ /___| /__/_____ \(____ /__| |__|\____/|___| /
|
||||
// \/ /_____/ \/ \/ \/ \/ \/
|
||||
|
||||
// CreateOrgForm form for creating organization
|
||||
type CreateOrgForm struct {
|
||||
OrgName string `binding:"Required;AlphaDashDot;MaxSize(35)" locale:"org.org_name_holder"`
|
||||
}
|
||||
|
||||
// Validate valideates the fields
|
||||
func (f *CreateOrgForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||
}
|
||||
|
||||
// UpdateOrgSettingForm form for updating organization settings
|
||||
type UpdateOrgSettingForm struct {
|
||||
Name string `binding:"Required;AlphaDashDot;MaxSize(35)" locale:"org.org_name_holder"`
|
||||
FullName string `binding:"MaxSize(100)"`
|
||||
@@ -33,6 +36,7 @@ type UpdateOrgSettingForm struct {
|
||||
MaxRepoCreation int
|
||||
}
|
||||
|
||||
// Validate valideates the fields
|
||||
func (f *UpdateOrgSettingForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||
}
|
||||
@@ -44,12 +48,14 @@ func (f *UpdateOrgSettingForm) Validate(ctx *macaron.Context, errs binding.Error
|
||||
// |____| \___ >____ /__|_| /
|
||||
// \/ \/ \/
|
||||
|
||||
// CreateTeamForm form for creating team
|
||||
type CreateTeamForm struct {
|
||||
TeamName string `binding:"Required;AlphaDashDot;MaxSize(30)"`
|
||||
Description string `binding:"MaxSize(255)"`
|
||||
Permission string
|
||||
}
|
||||
|
||||
// Validate valideates the fields
|
||||
func (f *CreateTeamForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||
}
|
||||
|
||||
@@ -12,7 +12,8 @@ import (
|
||||
"github.com/msteinert/pam"
|
||||
)
|
||||
|
||||
func PAMAuth(serviceName, userName, passwd string) error {
|
||||
// Auth pam auth service
|
||||
func Auth(serviceName, userName, passwd string) error {
|
||||
t, err := pam.StartFunc(serviceName, userName, func(s pam.Style, msg string) (string, error) {
|
||||
switch s {
|
||||
case pam.PromptEchoOff:
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
func PAMAuth(serviceName, userName, passwd string) error {
|
||||
// Auth not supported lack of pam tag
|
||||
func Auth(serviceName, userName, passwd string) error {
|
||||
return errors.New("PAM not supported")
|
||||
}
|
||||
|
||||
@@ -21,8 +21,9 @@ import (
|
||||
// |____|_ /_______ / |____| \_______ /_______ /|___| |____| \_______ /____|_ // ______|
|
||||
// \/ \/ \/ \/ \/ \/ \/
|
||||
|
||||
// CreateRepoForm form for creating repository
|
||||
type CreateRepoForm struct {
|
||||
Uid int64 `binding:"Required"`
|
||||
UID int64 `binding:"Required"`
|
||||
RepoName string `binding:"Required;AlphaDashDot;MaxSize(100)"`
|
||||
Private bool
|
||||
Description string `binding:"MaxSize(255)"`
|
||||
@@ -32,27 +33,30 @@ type CreateRepoForm struct {
|
||||
Readme string
|
||||
}
|
||||
|
||||
// Validate valideates the fields
|
||||
func (f *CreateRepoForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||
}
|
||||
|
||||
// MigrateRepoForm form for migrating repository
|
||||
type MigrateRepoForm struct {
|
||||
CloneAddr string `json:"clone_addr" binding:"Required"`
|
||||
AuthUsername string `json:"auth_username"`
|
||||
AuthPassword string `json:"auth_password"`
|
||||
Uid int64 `json:"uid" binding:"Required"`
|
||||
UID int64 `json:"uid" binding:"Required"`
|
||||
RepoName string `json:"repo_name" binding:"Required;AlphaDashDot;MaxSize(100)"`
|
||||
Mirror bool `json:"mirror"`
|
||||
Private bool `json:"private"`
|
||||
Description string `json:"description" binding:"MaxSize(255)"`
|
||||
}
|
||||
|
||||
// Validate valideates the fields
|
||||
func (f *MigrateRepoForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||
}
|
||||
|
||||
// ParseRemoteAddr checks if given remote address is valid,
|
||||
// and returns composed URL with needed username and passowrd.
|
||||
// and returns composed URL with needed username and password.
|
||||
// It also checks if given user has permission when remote address
|
||||
// is actually a local path.
|
||||
func (f MigrateRepoForm) ParseRemoteAddr(user *models.User) (string, error) {
|
||||
@@ -79,6 +83,7 @@ func (f MigrateRepoForm) ParseRemoteAddr(user *models.User) (string, error) {
|
||||
return remoteAddr, nil
|
||||
}
|
||||
|
||||
// RepoSettingForm form for changing repository settings
|
||||
type RepoSettingForm struct {
|
||||
RepoName string `binding:"Required;AlphaDashDot;MaxSize(100)"`
|
||||
Description string `binding:"MaxSize(255)"`
|
||||
@@ -101,6 +106,7 @@ type RepoSettingForm struct {
|
||||
EnablePulls bool
|
||||
}
|
||||
|
||||
// Validate valideates the fields
|
||||
func (f *RepoSettingForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||
}
|
||||
@@ -112,6 +118,7 @@ func (f *RepoSettingForm) Validate(ctx *macaron.Context, errs binding.Errors) bi
|
||||
// \__/\ / \___ >___ /___| /___| /\____/|__|_ \
|
||||
// \/ \/ \/ \/ \/ \/
|
||||
|
||||
// WebhookForm form for changing web hook
|
||||
type WebhookForm struct {
|
||||
Events string
|
||||
Create bool
|
||||
@@ -120,18 +127,22 @@ type WebhookForm struct {
|
||||
Active bool
|
||||
}
|
||||
|
||||
// PushOnly if the hook will be triggered when push
|
||||
func (f WebhookForm) PushOnly() bool {
|
||||
return f.Events == "push_only"
|
||||
}
|
||||
|
||||
// SendEverything if the hook will be triggered any event
|
||||
func (f WebhookForm) SendEverything() bool {
|
||||
return f.Events == "send_everything"
|
||||
}
|
||||
|
||||
// ChooseEvents if the hook will be triggered choose events
|
||||
func (f WebhookForm) ChooseEvents() bool {
|
||||
return f.Events == "choose_events"
|
||||
}
|
||||
|
||||
// NewWebhookForm form for creating web hook
|
||||
type NewWebhookForm struct {
|
||||
PayloadURL string `binding:"Required;Url"`
|
||||
ContentType int `binding:"Required"`
|
||||
@@ -139,10 +150,12 @@ type NewWebhookForm struct {
|
||||
WebhookForm
|
||||
}
|
||||
|
||||
// Validate valideates the fields
|
||||
func (f *NewWebhookForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||
}
|
||||
|
||||
// NewSlackHookForm form for creating slack hook
|
||||
type NewSlackHookForm struct {
|
||||
PayloadURL string `binding:"Required;Url"`
|
||||
Channel string `binding:"Required"`
|
||||
@@ -152,6 +165,7 @@ type NewSlackHookForm struct {
|
||||
WebhookForm
|
||||
}
|
||||
|
||||
// Validate valideates the fields
|
||||
func (f *NewSlackHookForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||
}
|
||||
@@ -163,6 +177,7 @@ func (f *NewSlackHookForm) Validate(ctx *macaron.Context, errs binding.Errors) b
|
||||
// |___/____ >____ >____/ \___ >
|
||||
// \/ \/ \/
|
||||
|
||||
// CreateIssueForm form for creating issue
|
||||
type CreateIssueForm struct {
|
||||
Title string `binding:"Required;MaxSize(255)"`
|
||||
LabelIDs string `form:"label_ids"`
|
||||
@@ -172,16 +187,19 @@ type CreateIssueForm struct {
|
||||
Files []string
|
||||
}
|
||||
|
||||
// Validate valideates the fields
|
||||
func (f *CreateIssueForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||
}
|
||||
|
||||
// CreateCommentForm form for creating comment
|
||||
type CreateCommentForm struct {
|
||||
Content string
|
||||
Status string `binding:"OmitEmpty;In(reopen,close)"`
|
||||
Files []string
|
||||
}
|
||||
|
||||
// Validate valideates the fields
|
||||
func (f *CreateCommentForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||
}
|
||||
@@ -193,12 +211,14 @@ func (f *CreateCommentForm) Validate(ctx *macaron.Context, errs binding.Errors)
|
||||
// \____|__ /__|____/\___ >____ > |__| \____/|___| /\___ >
|
||||
// \/ \/ \/ \/ \/
|
||||
|
||||
// CreateMilestoneForm form for creating milestone
|
||||
type CreateMilestoneForm struct {
|
||||
Title string `binding:"Required;MaxSize(50)"`
|
||||
Content string
|
||||
Deadline string
|
||||
}
|
||||
|
||||
// Validate valideates the fields
|
||||
func (f *CreateMilestoneForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||
}
|
||||
@@ -210,20 +230,24 @@ func (f *CreateMilestoneForm) Validate(ctx *macaron.Context, errs binding.Errors
|
||||
// |_______ (____ /___ /\___ >____/
|
||||
// \/ \/ \/ \/
|
||||
|
||||
// CreateLabelForm form for creating label
|
||||
type CreateLabelForm struct {
|
||||
ID int64
|
||||
Title string `binding:"Required;MaxSize(50)" locale:"repo.issues.label_name"`
|
||||
Color string `binding:"Required;Size(7)" locale:"repo.issues.label_color"`
|
||||
}
|
||||
|
||||
// Validate valideates the fields
|
||||
func (f *CreateLabelForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||
}
|
||||
|
||||
// InitializeLabelsForm form for initializing labels
|
||||
type InitializeLabelsForm struct {
|
||||
TemplateName string `binding:"Required"`
|
||||
}
|
||||
|
||||
// Validate valideates the fields
|
||||
func (f *InitializeLabelsForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||
}
|
||||
@@ -235,6 +259,7 @@ func (f *InitializeLabelsForm) Validate(ctx *macaron.Context, errs binding.Error
|
||||
// |____|_ /\___ >____/\___ >____ /____ >\___ >
|
||||
// \/ \/ \/ \/ \/ \/
|
||||
|
||||
// NewReleaseForm form for creating release
|
||||
type NewReleaseForm struct {
|
||||
TagName string `binding:"Required"`
|
||||
Target string `form:"tag_target" binding:"Required"`
|
||||
@@ -244,10 +269,12 @@ type NewReleaseForm struct {
|
||||
Prerelease bool
|
||||
}
|
||||
|
||||
// Validate valideates the fields
|
||||
func (f *NewReleaseForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||
}
|
||||
|
||||
// EditReleaseForm form for changing release
|
||||
type EditReleaseForm struct {
|
||||
Title string `form:"title" binding:"Required"`
|
||||
Content string `form:"content"`
|
||||
@@ -255,6 +282,7 @@ type EditReleaseForm struct {
|
||||
Prerelease bool `form:"prerelease"`
|
||||
}
|
||||
|
||||
// Validate valideates the fields
|
||||
func (f *EditReleaseForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||
}
|
||||
@@ -266,6 +294,7 @@ func (f *EditReleaseForm) Validate(ctx *macaron.Context, errs binding.Errors) bi
|
||||
// \__/\ / |__|__|_ \__|
|
||||
// \/ \/
|
||||
|
||||
// NewWikiForm form for creating wiki
|
||||
type NewWikiForm struct {
|
||||
OldTitle string
|
||||
Title string `binding:"Required"`
|
||||
@@ -273,6 +302,7 @@ type NewWikiForm struct {
|
||||
Message string
|
||||
}
|
||||
|
||||
// Validate valideates the fields
|
||||
// FIXME: use code generation to generate this method.
|
||||
func (f *NewWikiForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||
@@ -285,6 +315,7 @@ func (f *NewWikiForm) Validate(ctx *macaron.Context, errs binding.Errors) bindin
|
||||
// /_______ /\____ | |__||__|
|
||||
// \/ \/
|
||||
|
||||
// EditRepoFileForm form for changing repository file
|
||||
type EditRepoFileForm struct {
|
||||
TreePath string `binding:"Required;MaxSize(500)"`
|
||||
Content string `binding:"Required"`
|
||||
@@ -295,14 +326,17 @@ type EditRepoFileForm struct {
|
||||
LastCommit string
|
||||
}
|
||||
|
||||
// Validate valideates the fields
|
||||
func (f *EditRepoFileForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||
}
|
||||
|
||||
// EditPreviewDiffForm form for changing preview diff
|
||||
type EditPreviewDiffForm struct {
|
||||
Content string
|
||||
}
|
||||
|
||||
// Validate valideates the fields
|
||||
func (f *EditPreviewDiffForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||
}
|
||||
@@ -315,6 +349,7 @@ func (f *EditPreviewDiffForm) Validate(ctx *macaron.Context, errs binding.Errors
|
||||
// |__| \/ \/
|
||||
//
|
||||
|
||||
// UploadRepoFileForm form for uploading repository file
|
||||
type UploadRepoFileForm struct {
|
||||
TreePath string `binding:"MaxSize(500)"`
|
||||
CommitSummary string `binding:"MaxSize(100)"`
|
||||
@@ -324,14 +359,17 @@ type UploadRepoFileForm struct {
|
||||
Files []string
|
||||
}
|
||||
|
||||
// Validate valideates the fields
|
||||
func (f *UploadRepoFileForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||
}
|
||||
|
||||
// RemoveUploadFileForm form for removing uploaded file
|
||||
type RemoveUploadFileForm struct {
|
||||
File string `binding:"Required;MaxSize(50)"`
|
||||
}
|
||||
|
||||
// Validate valideates the fields
|
||||
func (f *RemoveUploadFileForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||
}
|
||||
@@ -343,6 +381,7 @@ func (f *RemoveUploadFileForm) Validate(ctx *macaron.Context, errs binding.Error
|
||||
// /_______ /\___ >____/\___ >__| \___ >
|
||||
// \/ \/ \/ \/
|
||||
|
||||
// DeleteRepoFileForm form for deleting repository file
|
||||
type DeleteRepoFileForm struct {
|
||||
CommitSummary string `binding:"MaxSize(100)"`
|
||||
CommitMessage string
|
||||
@@ -350,6 +389,7 @@ type DeleteRepoFileForm struct {
|
||||
NewBranchName string `binding:"AlphaDashDot;MaxSize(100)"`
|
||||
}
|
||||
|
||||
// Validate valideates the fields
|
||||
func (f *DeleteRepoFileForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"gopkg.in/macaron.v1"
|
||||
)
|
||||
|
||||
// InstallForm form for installation page
|
||||
type InstallForm struct {
|
||||
DbType string `binding:"Required"`
|
||||
DbHost string
|
||||
@@ -26,7 +27,7 @@ type InstallForm struct {
|
||||
Domain string `binding:"Required"`
|
||||
SSHPort int
|
||||
HTTPPort string `binding:"Required"`
|
||||
AppUrl string `binding:"Required"`
|
||||
AppURL string `binding:"Required"`
|
||||
LogRootPath string `binding:"Required"`
|
||||
|
||||
SMTPHost string
|
||||
@@ -49,6 +50,7 @@ type InstallForm struct {
|
||||
AdminEmail string `binding:"OmitEmpty;MinSize(3);MaxSize(254);Include(@)" locale:"install.admin_email"`
|
||||
}
|
||||
|
||||
// Validate valideates the fields
|
||||
func (f *InstallForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||
}
|
||||
@@ -60,6 +62,7 @@ func (f *InstallForm) Validate(ctx *macaron.Context, errs binding.Errors) bindin
|
||||
// \____|__ /______/ |____| \___|_ /
|
||||
// \/ \/
|
||||
|
||||
// RegisterForm form for registering
|
||||
type RegisterForm struct {
|
||||
UserName string `binding:"Required;AlphaDashDot;MaxSize(35)"`
|
||||
Email string `binding:"Required;Email;MaxSize(254)"`
|
||||
@@ -67,16 +70,19 @@ type RegisterForm struct {
|
||||
Retype string
|
||||
}
|
||||
|
||||
// Validate valideates the fields
|
||||
func (f *RegisterForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||
}
|
||||
|
||||
// SignInForm form for signing in
|
||||
type SignInForm struct {
|
||||
UserName string `binding:"Required;MaxSize(254)"`
|
||||
Password string `binding:"Required;MaxSize(255)"`
|
||||
Remember bool
|
||||
}
|
||||
|
||||
// Validate valideates the fields
|
||||
func (f *SignInForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||
}
|
||||
@@ -88,6 +94,7 @@ func (f *SignInForm) Validate(ctx *macaron.Context, errs binding.Errors) binding
|
||||
// /_______ //_______ / |____| |____| |___\____|__ /\______ /_______ /
|
||||
// \/ \/ \/ \/ \/
|
||||
|
||||
// UpdateProfileForm form for updating profile
|
||||
type UpdateProfileForm struct {
|
||||
Name string `binding:"OmitEmpty;MaxSize(35)"`
|
||||
FullName string `binding:"MaxSize(100)"`
|
||||
@@ -96,15 +103,18 @@ type UpdateProfileForm struct {
|
||||
Location string `binding:"MaxSize(50)"`
|
||||
}
|
||||
|
||||
// Validate valideates the fields
|
||||
func (f *UpdateProfileForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||
}
|
||||
|
||||
// Avatar types
|
||||
const (
|
||||
AvatarLocal string = "local"
|
||||
AvatarByMail string = "bymail"
|
||||
)
|
||||
|
||||
// AvatarForm form for changing avatar
|
||||
type AvatarForm struct {
|
||||
Source string
|
||||
Avatar *multipart.FileHeader
|
||||
@@ -112,41 +122,50 @@ type AvatarForm struct {
|
||||
Federavatar bool
|
||||
}
|
||||
|
||||
// Validate valideates the fields
|
||||
func (f *AvatarForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||
}
|
||||
|
||||
// AddEmailForm form for adding new email
|
||||
type AddEmailForm struct {
|
||||
Email string `binding:"Required;Email;MaxSize(254)"`
|
||||
}
|
||||
|
||||
// Validate valideates the fields
|
||||
func (f *AddEmailForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||
}
|
||||
|
||||
// ChangePasswordForm form for changing password
|
||||
type ChangePasswordForm struct {
|
||||
OldPassword string `form:"old_password" binding:"Required;MinSize(1);MaxSize(255)"`
|
||||
Password string `form:"password" binding:"Required;MaxSize(255)"`
|
||||
Retype string `form:"retype"`
|
||||
}
|
||||
|
||||
// Validate valideates the fields
|
||||
func (f *ChangePasswordForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||
}
|
||||
|
||||
// AddSSHKeyForm form for adding SSH key
|
||||
type AddSSHKeyForm struct {
|
||||
Title string `binding:"Required;MaxSize(50)"`
|
||||
Content string `binding:"Required"`
|
||||
}
|
||||
|
||||
// Validate valideates the fields
|
||||
func (f *AddSSHKeyForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||
}
|
||||
|
||||
// NewAccessTokenForm form for creating access token
|
||||
type NewAccessTokenForm struct {
|
||||
Name string `binding:"Required"`
|
||||
}
|
||||
|
||||
// Validate valideates the fields
|
||||
func (f *NewAccessTokenForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||
}
|
||||
|
||||
@@ -14,9 +14,10 @@ import (
|
||||
"github.com/issue9/identicon"
|
||||
)
|
||||
|
||||
const AVATAR_SIZE = 290
|
||||
// AvatarSize returns avatar's size
|
||||
const AvatarSize = 290
|
||||
|
||||
// RandomImage generates and returns a random avatar image unique to input data
|
||||
// RandomImageSize generates and returns a random avatar image unique to input data
|
||||
// in custom size (height and width).
|
||||
func RandomImageSize(size int, data []byte) (image.Image, error) {
|
||||
randExtent := len(palette.WebSafe) - 32
|
||||
@@ -39,5 +40,5 @@ func RandomImageSize(size int, data []byte) (image.Image, error) {
|
||||
// RandomImage generates and returns a random avatar image unique to input data
|
||||
// in default size (height and width).
|
||||
func RandomImage(data []byte) (image.Image, error) {
|
||||
return RandomImageSize(AVATAR_SIZE, data)
|
||||
return RandomImageSize(AvatarSize, data)
|
||||
}
|
||||
|
||||
@@ -4,8 +4,10 @@
|
||||
|
||||
package base
|
||||
|
||||
// DocURL api doc url
|
||||
const DocURL = "https://godoc.org/github.com/go-gitea/go-sdk/gitea"
|
||||
|
||||
type (
|
||||
// TplName template relative path type
|
||||
TplName string
|
||||
)
|
||||
|
||||
@@ -36,7 +36,7 @@ func EncodeMD5(str string) string {
|
||||
return hex.EncodeToString(m.Sum(nil))
|
||||
}
|
||||
|
||||
// Encode string to sha1 hex value.
|
||||
// EncodeSha1 string to sha1 hex value.
|
||||
func EncodeSha1(str string) string {
|
||||
h := sha1.New()
|
||||
h.Write([]byte(str))
|
||||
@@ -49,6 +49,7 @@ func ShortSha(sha1 string) string {
|
||||
return TruncateString(sha1, 10)
|
||||
}
|
||||
|
||||
// DetectEncoding detect the encoding of content
|
||||
func DetectEncoding(content []byte) (string, error) {
|
||||
if utf8.Valid(content) {
|
||||
log.Debug("Detected encoding: utf-8 (fast)")
|
||||
@@ -65,6 +66,7 @@ func DetectEncoding(content []byte) (string, error) {
|
||||
return result.Charset, err
|
||||
}
|
||||
|
||||
// BasicAuthDecode decode basic auth string
|
||||
func BasicAuthDecode(encoded string) (string, string, error) {
|
||||
s, err := base64.StdEncoding.DecodeString(encoded)
|
||||
if err != nil {
|
||||
@@ -75,6 +77,7 @@ func BasicAuthDecode(encoded string) (string, string, error) {
|
||||
return auth[0], auth[1], nil
|
||||
}
|
||||
|
||||
// BasicAuthEncode encode basic auth string
|
||||
func BasicAuthEncode(username, password string) string {
|
||||
return base64.StdEncoding.EncodeToString([]byte(username + ":" + password))
|
||||
}
|
||||
@@ -94,7 +97,7 @@ func GetRandomString(n int, alphabets ...byte) string {
|
||||
return string(bytes)
|
||||
}
|
||||
|
||||
// http://code.google.com/p/go/source/browse/pbkdf2/pbkdf2.go?repo=crypto
|
||||
// PBKDF2 http://code.google.com/p/go/source/browse/pbkdf2/pbkdf2.go?repo=crypto
|
||||
// FIXME: use https://godoc.org/golang.org/x/crypto/pbkdf2?
|
||||
func PBKDF2(password, salt []byte, iter, keyLen int, h func() hash.Hash) []byte {
|
||||
prf := hmac.New(h, password)
|
||||
@@ -133,7 +136,7 @@ func PBKDF2(password, salt []byte, iter, keyLen int, h func() hash.Hash) []byte
|
||||
return dk[:keyLen]
|
||||
}
|
||||
|
||||
// verify time limit code
|
||||
// VerifyTimeLimitCode verify time limit code
|
||||
func VerifyTimeLimitCode(data string, minutes int, code string) bool {
|
||||
if len(code) <= 18 {
|
||||
return false
|
||||
@@ -160,9 +163,10 @@ func VerifyTimeLimitCode(data string, minutes int, code string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// TimeLimitCodeLength default value for time limit code
|
||||
const TimeLimitCodeLength = 12 + 6 + 40
|
||||
|
||||
// create a time limit code
|
||||
// CreateTimeLimitCode create a time limit code
|
||||
// code format: 12 length date time string + 6 minutes string + 40 sha1 encoded string
|
||||
func CreateTimeLimitCode(data string, minutes int, startInf interface{}) string {
|
||||
format := "200601021504"
|
||||
@@ -213,7 +217,7 @@ func AvatarLink(email string) string {
|
||||
return setting.GravatarSource + HashEmail(email)
|
||||
}
|
||||
|
||||
return setting.AppSubUrl + "/img/avatar_default.png"
|
||||
return setting.AppSubURL + "/img/avatar_default.png"
|
||||
}
|
||||
|
||||
// Seconds-based time units
|
||||
@@ -355,6 +359,7 @@ func timeSince(then time.Time, lang string) string {
|
||||
}
|
||||
}
|
||||
|
||||
// RawTimeSince retrieves i18n key of time since t
|
||||
func RawTimeSince(t time.Time, lang string) string {
|
||||
return timeSince(t, lang)
|
||||
}
|
||||
@@ -364,6 +369,7 @@ func TimeSince(t time.Time, lang string) template.HTML {
|
||||
return template.HTML(fmt.Sprintf(`<span class="time-since" title="%s">%s</span>`, t.Format(setting.TimeFormat), timeSince(t, lang)))
|
||||
}
|
||||
|
||||
// Storage space size types
|
||||
const (
|
||||
Byte = 1
|
||||
KByte = Byte * 1024
|
||||
@@ -413,7 +419,7 @@ func FileSize(s int64) string {
|
||||
func Subtract(left interface{}, right interface{}) interface{} {
|
||||
var rleft, rright int64
|
||||
var fleft, fright float64
|
||||
var isInt bool = true
|
||||
var isInt = true
|
||||
switch left.(type) {
|
||||
case int:
|
||||
rleft = int64(left.(int))
|
||||
@@ -454,9 +460,8 @@ func Subtract(left interface{}, right interface{}) interface{} {
|
||||
|
||||
if isInt {
|
||||
return rleft - rright
|
||||
} else {
|
||||
return fleft + float64(rleft) - (fright + float64(rright))
|
||||
}
|
||||
return fleft + float64(rleft) - (fright + float64(rright))
|
||||
}
|
||||
|
||||
// EllipsisString returns a truncated short string,
|
||||
@@ -521,10 +526,12 @@ func IsTextFile(data []byte) bool {
|
||||
return strings.Index(http.DetectContentType(data), "text/") != -1
|
||||
}
|
||||
|
||||
// IsImageFile detectes if data is an image format
|
||||
func IsImageFile(data []byte) bool {
|
||||
return strings.Index(http.DetectContentType(data), "image/") != -1
|
||||
}
|
||||
|
||||
// IsPDFFile detectes if data is a pdf format
|
||||
func IsPDFFile(data []byte) bool {
|
||||
return strings.Index(http.DetectContentType(data), "application/pdf") != -1
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -8,6 +8,8 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/git"
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/base"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
@@ -15,6 +17,7 @@ import (
|
||||
macaron "gopkg.in/macaron.v1"
|
||||
)
|
||||
|
||||
// APIContext is a specific macaron context for API service
|
||||
type APIContext struct {
|
||||
*Context
|
||||
Org *APIOrganization
|
||||
@@ -45,16 +48,16 @@ func (ctx *APIContext) SetLinkHeader(total, pageSize int) {
|
||||
page := paginater.New(total, pageSize, ctx.QueryInt("page"), 0)
|
||||
links := make([]string, 0, 4)
|
||||
if page.HasNext() {
|
||||
links = append(links, fmt.Sprintf("<%s%s?page=%d>; rel=\"next\"", setting.AppUrl, ctx.Req.URL.Path[1:], page.Next()))
|
||||
links = append(links, fmt.Sprintf("<%s%s?page=%d>; rel=\"next\"", setting.AppURL, ctx.Req.URL.Path[1:], page.Next()))
|
||||
}
|
||||
if !page.IsLast() {
|
||||
links = append(links, fmt.Sprintf("<%s%s?page=%d>; rel=\"last\"", setting.AppUrl, ctx.Req.URL.Path[1:], page.TotalPages()))
|
||||
links = append(links, fmt.Sprintf("<%s%s?page=%d>; rel=\"last\"", setting.AppURL, ctx.Req.URL.Path[1:], page.TotalPages()))
|
||||
}
|
||||
if !page.IsFirst() {
|
||||
links = append(links, fmt.Sprintf("<%s%s?page=1>; rel=\"first\"", setting.AppUrl, ctx.Req.URL.Path[1:]))
|
||||
links = append(links, fmt.Sprintf("<%s%s?page=1>; rel=\"first\"", setting.AppURL, ctx.Req.URL.Path[1:]))
|
||||
}
|
||||
if page.HasPrevious() {
|
||||
links = append(links, fmt.Sprintf("<%s%s?page=%d>; rel=\"prev\"", setting.AppUrl, ctx.Req.URL.Path[1:], page.Previous()))
|
||||
links = append(links, fmt.Sprintf("<%s%s?page=%d>; rel=\"prev\"", setting.AppURL, ctx.Req.URL.Path[1:], page.Previous()))
|
||||
}
|
||||
|
||||
if len(links) > 0 {
|
||||
@@ -62,6 +65,7 @@ func (ctx *APIContext) SetLinkHeader(total, pageSize int) {
|
||||
}
|
||||
}
|
||||
|
||||
// APIContexter returns apicontext as macaron middleware
|
||||
func APIContexter() macaron.Handler {
|
||||
return func(c *Context) {
|
||||
ctx := &APIContext{
|
||||
@@ -70,3 +74,54 @@ func APIContexter() macaron.Handler {
|
||||
c.Map(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
// ExtractOwnerAndRepo returns a handler that populates the `Repo.Owner` and
|
||||
// `Repo.Repository` fields of an APIContext
|
||||
func ExtractOwnerAndRepo() macaron.Handler {
|
||||
return func(ctx *APIContext) {
|
||||
owner, err := models.GetUserByName(ctx.Params(":username"))
|
||||
if err != nil {
|
||||
if models.IsErrUserNotExist(err) {
|
||||
ctx.Error(422, "", err)
|
||||
} else {
|
||||
ctx.Error(500, "GetUserByName", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
repo, err := models.GetRepositoryByName(owner.ID, ctx.Params(":reponame"))
|
||||
if err != nil {
|
||||
if models.IsErrRepoNotExist(err) {
|
||||
ctx.Status(404)
|
||||
} else {
|
||||
ctx.Error(500, "GetRepositoryByName", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
ctx.Repo.Owner = owner
|
||||
ctx.Data["Owner"] = owner
|
||||
ctx.Repo.Repository = repo
|
||||
ctx.Data["Repository"] = repo
|
||||
}
|
||||
}
|
||||
|
||||
// ReferencesGitRepo injects the GitRepo into the Context
|
||||
func ReferencesGitRepo() macaron.Handler {
|
||||
return func(ctx *APIContext) {
|
||||
// Empty repository does not have reference information.
|
||||
if ctx.Repo.Repository.IsBare {
|
||||
return
|
||||
}
|
||||
|
||||
// For API calls.
|
||||
if ctx.Repo.GitRepo == nil {
|
||||
repoPath := models.RepoPath(ctx.Repo.Owner.Name, ctx.Repo.Repository.Name)
|
||||
gitRepo, err := git.OpenRepository(repoPath)
|
||||
if err != nil {
|
||||
ctx.Error(500, "RepoRef Invalid repo "+repoPath, err)
|
||||
return
|
||||
}
|
||||
ctx.Repo.GitRepo = gitRepo
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"code.gitea.io/gitea/models"
|
||||
)
|
||||
|
||||
// APIOrganization contains organization and team
|
||||
type APIOrganization struct {
|
||||
Organization *models.User
|
||||
Team *models.Team
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user