mirror of
https://github.com/go-gitea/gitea.git
synced 2025-12-11 01:40:26 +00:00
Compare commits
489 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e2c8d6fcb2 | ||
|
|
1a5df9e822 | ||
|
|
21dc5996a5 | ||
|
|
023a6604e2 | ||
|
|
9a5009a2cc | ||
|
|
3121a7a037 | ||
|
|
61cdc32496 | ||
|
|
5fcf218ed9 | ||
|
|
91836614cd | ||
|
|
ab4eb0daf9 | ||
|
|
155fb93b9b | ||
|
|
3a8c792303 | ||
|
|
807c64800c | ||
|
|
6ddb2dcd57 | ||
|
|
729ab80065 | ||
|
|
a5ea9b4f30 | ||
|
|
8f08ccdb9f | ||
|
|
10d73d38e0 | ||
|
|
fbb424c61d | ||
|
|
e8bac94d1f | ||
|
|
99c6556ff3 | ||
|
|
6aacf4d2f0 | ||
|
|
7b67347104 | ||
|
|
b0c6217c4d | ||
|
|
eaa7b3c3f5 | ||
|
|
770e8310bd | ||
|
|
1e6dd98d51 | ||
|
|
a822bba3e1 | ||
|
|
ec1fe1183d | ||
|
|
dfb547099d | ||
|
|
a12da66dfb | ||
|
|
b33078fa33 | ||
|
|
c21e2c4151 | ||
|
|
0e1392501d | ||
|
|
e0ecd9fd93 | ||
|
|
65b1875d2b | ||
|
|
11df7ebfc5 | ||
|
|
47a7529d96 | ||
|
|
0c5c34d7dd | ||
|
|
4c89a9c33c | ||
|
|
df7fa4e995 | ||
|
|
d5d21b67d2 | ||
|
|
c46eb3f5b3 | ||
|
|
f8d94cb440 | ||
|
|
2197d298cb | ||
|
|
ce21ed6c34 | ||
|
|
618407c018 | ||
|
|
111c95ecaf | ||
|
|
235eb4c3d2 | ||
|
|
380e32e129 | ||
|
|
952587dbae | ||
|
|
1d30457a94 | ||
|
|
6ade13e86e | ||
|
|
608a60fb94 | ||
|
|
8559d6f267 | ||
|
|
7c46667e71 | ||
|
|
44428fdc38 | ||
|
|
0d6e88baef | ||
|
|
b11843b8dc | ||
|
|
578a8e258e | ||
|
|
15c3d14d55 | ||
|
|
8aeeed0a23 | ||
|
|
c6b6a61bf1 | ||
|
|
1831ee2d1d | ||
|
|
b4c794058a | ||
|
|
d771e978a1 | ||
|
|
73710c00a8 | ||
|
|
1d1c01875d | ||
|
|
f0a989c1d0 | ||
|
|
abcd39f7d5 | ||
|
|
cbcb4361d5 | ||
|
|
7b5b5178e1 | ||
|
|
bab737bf02 | ||
|
|
401a8db0ed | ||
|
|
39d0db52de | ||
|
|
04b9a7e7a2 | ||
|
|
08b9af9ad8 | ||
|
|
026ad4aee7 | ||
|
|
83ed234472 | ||
|
|
1b5b297c39 | ||
|
|
2b63f32b8a | ||
|
|
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 | ||
|
|
e2aa991e10 | ||
|
|
5fc370e332 | ||
|
|
f07362b90f | ||
|
|
786cc5bbc8 | ||
|
|
ddee4c8b58 | ||
|
|
a1c5f02444 | ||
|
|
96c9fef35f | ||
|
|
52cc3fd36a | ||
|
|
b12f2a5916 | ||
|
|
b0ddced2b5 | ||
|
|
a4454f5d0f | ||
|
|
4247304f5a | ||
|
|
1c0a4e166f | ||
|
|
63ca42d17f | ||
|
|
c040f2fbb1 | ||
|
|
94b2747375 | ||
|
|
31da225309 | ||
|
|
24d7bae2b2 | ||
|
|
1b238fe4d5 | ||
|
|
c6c840faf7 | ||
|
|
7ea943d501 | ||
|
|
b5bfab9855 | ||
|
|
3a10a0c1ca | ||
|
|
7dcc3bc3d7 | ||
|
|
145648a233 | ||
|
|
90fb64b217 | ||
|
|
5cd093aa46 | ||
|
|
f91cbf0fed | ||
|
|
475ddd8d89 | ||
|
|
c511f1c6c3 | ||
|
|
a6c487f6ca | ||
|
|
45c4539c61 | ||
|
|
01c5233b53 | ||
|
|
f6d53ecbc1 | ||
|
|
1ceb56fc7f | ||
|
|
85b8b7f4e1 | ||
|
|
8b0576b377 | ||
|
|
8857d701fb | ||
|
|
e06e0f9bb9 | ||
|
|
4e4c0c2cd3 | ||
|
|
dad9dbacf7 | ||
|
|
5ea257be87 | ||
|
|
b4671c9aab | ||
|
|
5667d4daae | ||
|
|
e74868a850 | ||
|
|
95d4cd9292 | ||
|
|
2bb188ae79 | ||
|
|
4235fff9ee | ||
|
|
d417aedcfa | ||
|
|
b00d82d679 | ||
|
|
86c32f2706 | ||
|
|
030ba2894f | ||
|
|
f81711f40d | ||
|
|
a5d0b4de5b | ||
|
|
70fb1cf9d1 | ||
|
|
d874a9bf6b | ||
|
|
11c9160cd3 | ||
|
|
864d1b1f9f | ||
|
|
c8c748aea6 | ||
|
|
01a7674b5c | ||
|
|
b7bf9dfd28 | ||
|
|
aadd7dcdc3 | ||
|
|
b3828e38a5 | ||
|
|
05fd9d3f09 | ||
|
|
7612b5ec40 | ||
|
|
64196d4036 | ||
|
|
f14232d2e9 | ||
|
|
60c82a8780 | ||
|
|
f6a11e0de1 | ||
|
|
f388661bda | ||
|
|
0b62aeb495 | ||
|
|
80eea77953 | ||
|
|
d944bdec47 | ||
|
|
5d430c9e68 | ||
|
|
ee963f67c1 | ||
|
|
9c434ccc50 | ||
|
|
28bee28102 | ||
|
|
f430d26f7e | ||
|
|
3f18111cbe | ||
|
|
4c6c16f358 | ||
|
|
77f9c7e571 | ||
|
|
e378648c79 | ||
|
|
d1b14fef56 | ||
|
|
30be1f4826 | ||
|
|
145ab5c89a | ||
|
|
b7263f31a5 | ||
|
|
6f3097f9e6 | ||
|
|
3e13e16b3f | ||
|
|
a6d683f498 | ||
|
|
ceb920802a | ||
|
|
6bf9910975 | ||
|
|
1b962bac0b | ||
|
|
562f9b6eae | ||
|
|
86fb1a0cb1 | ||
|
|
be5607e510 | ||
|
|
5a6f7edde9 | ||
|
|
ef5fc3c959 | ||
|
|
92c48dabe3 | ||
|
|
530f6c1da4 | ||
|
|
6e4252dad4 | ||
|
|
55a4d46f5d | ||
|
|
fe8bfa54de | ||
|
|
d318f612a9 | ||
|
|
1cb5b0e2f6 | ||
|
|
27f99a16a6 | ||
|
|
9f437eb1ab | ||
|
|
92f39da802 | ||
|
|
f36544f98d | ||
|
|
a0e54c0512 | ||
|
|
bd898a10f8 | ||
|
|
953c099428 | ||
|
|
1f44b01e2a | ||
|
|
91b589f2f0 | ||
|
|
789dacdfbe | ||
|
|
984fa8d83b | ||
|
|
ab12596143 | ||
|
|
6111e09a97 | ||
|
|
c808e8c138 | ||
|
|
b58b634e0e | ||
|
|
c524078d2b | ||
|
|
b32776d533 | ||
|
|
3dd14ee522 | ||
|
|
ec054ba582 | ||
|
|
7a870080d6 | ||
|
|
eb25d1f252 | ||
|
|
5b5af7daee | ||
|
|
212a04a45e | ||
|
|
648c6fdd60 | ||
|
|
f960b776f0 | ||
|
|
e05a5ca36c | ||
|
|
ba4d255635 | ||
|
|
cda6baf02c | ||
|
|
a46efe240d | ||
|
|
fd6be0d17e | ||
|
|
747f86aa23 | ||
|
|
03902bb53d | ||
|
|
6a20711afd | ||
|
|
ccdbd8bf48 | ||
|
|
38c209e85b | ||
|
|
1aaa3a303d | ||
|
|
75ddcddd06 | ||
|
|
8e66e09cd8 | ||
|
|
4fdca026c7 | ||
|
|
13c3edde05 | ||
|
|
f01d927efb | ||
|
|
a2514904cb | ||
|
|
a4fa889ced | ||
|
|
ff54e4d929 | ||
|
|
587d870f1e | ||
|
|
fc55182a4c | ||
|
|
8dc49dc114 | ||
|
|
4a5faecd8f | ||
|
|
f8b4699a14 | ||
|
|
1ebb35b988 | ||
|
|
78f86abba4 | ||
|
|
90402a6328 | ||
|
|
998e7452b8 | ||
|
|
17f9ab4ff8 | ||
|
|
33df11e823 | ||
|
|
5a8cb2dac1 | ||
|
|
0d41394d6e | ||
|
|
55e804e078 | ||
|
|
a90b25226a | ||
|
|
87b593f93e | ||
|
|
137dcbf93d | ||
|
|
1adde07a3f | ||
|
|
42a744d9e6 | ||
|
|
2d68bd1ef9 | ||
|
|
8ea63f8c50 | ||
|
|
a79eb48de3 | ||
|
|
507ce134fa | ||
|
|
bc7e92a2b5 | ||
|
|
e4fe69365f | ||
|
|
980282a06f | ||
|
|
d8de2beb5b | ||
|
|
060d10a4cb | ||
|
|
af03d00780 | ||
|
|
5c54243014 | ||
|
|
d59a48a255 | ||
|
|
f3321d920d | ||
|
|
e54dec7ce5 | ||
|
|
93f1eabe30 | ||
|
|
1b59e6f910 | ||
|
|
9d66497abc |
19
.bra.toml
19
.bra.toml
@@ -1,19 +0,0 @@
|
|||||||
[run]
|
|
||||||
init_cmds = [
|
|
||||||
["make", "build-dev"],
|
|
||||||
["./gogs", "web"]
|
|
||||||
]
|
|
||||||
watch_all = true
|
|
||||||
watch_dirs = [
|
|
||||||
"$WORKDIR/cmd",
|
|
||||||
"$WORKDIR/models",
|
|
||||||
"$WORKDIR/modules",
|
|
||||||
"$WORKDIR/routers"
|
|
||||||
]
|
|
||||||
watch_exts = [".go"]
|
|
||||||
ignore_files = [".+_test.go"]
|
|
||||||
build_delay = 1500
|
|
||||||
cmds = [
|
|
||||||
["make", "build-dev"], # TAGS=sqlite cert pam tidb
|
|
||||||
["./gogs", "web"]
|
|
||||||
]
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
conf/**
|
|
||||||
docker/**
|
|
||||||
modules/bindata/**
|
|
||||||
packager/**
|
|
||||||
public/**
|
|
||||||
scripts/**
|
|
||||||
templates/**
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
{
|
|
||||||
"GOLANG": {
|
|
||||||
"TOTAL_LOC": [500, 999, 1999, 9999],
|
|
||||||
"TOO_MANY_FUNCTIONS": [50, 99, 199, 999],
|
|
||||||
"TOO_MANY_IVARS": [20, 50, 70, 99]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,19 +1,5 @@
|
|||||||
.git
|
*
|
||||||
.git/**
|
!gitea
|
||||||
packager
|
!docker
|
||||||
packager/**
|
!public
|
||||||
scripts
|
!templates
|
||||||
scripts/**
|
|
||||||
.github/
|
|
||||||
.github/**
|
|
||||||
config.codekit
|
|
||||||
.dockerignore
|
|
||||||
*.yml
|
|
||||||
*.md
|
|
||||||
.bra.toml
|
|
||||||
.editorconfig
|
|
||||||
.gitignore
|
|
||||||
Dockerfile*
|
|
||||||
vendor
|
|
||||||
vendor/**
|
|
||||||
gogs
|
|
||||||
|
|||||||
150
.drone.yml
Normal file
150
.drone.yml
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
workspace:
|
||||||
|
base: /srv/app
|
||||||
|
path: src/code.gitea.io/gitea
|
||||||
|
|
||||||
|
pipeline:
|
||||||
|
clone:
|
||||||
|
image: plugins/git
|
||||||
|
tags: true
|
||||||
|
|
||||||
|
test:
|
||||||
|
image: webhippie/golang:edge
|
||||||
|
pull: true
|
||||||
|
environment:
|
||||||
|
CGO_ENABLED: 1
|
||||||
|
TAGS: sqlite bindata
|
||||||
|
GOPATH: /srv/app
|
||||||
|
commands:
|
||||||
|
- apk -U add openssh-client
|
||||||
|
- make clean
|
||||||
|
- make generate
|
||||||
|
- 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 bindata
|
||||||
|
GOPATH: /srv/app
|
||||||
|
commands:
|
||||||
|
- make test-mysql
|
||||||
|
when:
|
||||||
|
event: [ push ]
|
||||||
|
|
||||||
|
test-pgsql:
|
||||||
|
image: webhippie/golang:edge
|
||||||
|
pull: true
|
||||||
|
environment:
|
||||||
|
CGO_ENABLED: 1
|
||||||
|
TAGS: sqlite bindata
|
||||||
|
GOPATH: /srv/app
|
||||||
|
commands:
|
||||||
|
- make test-pgsql
|
||||||
|
when:
|
||||||
|
event: [ push ]
|
||||||
|
|
||||||
|
updater:
|
||||||
|
image: karalabe/xgo-latest:latest
|
||||||
|
pull: true
|
||||||
|
environment:
|
||||||
|
CGO_ENABLED: 1
|
||||||
|
TAGS: sqlite bindata
|
||||||
|
GOPATH: /srv/app
|
||||||
|
commands:
|
||||||
|
- make release
|
||||||
|
when:
|
||||||
|
event: [ push, tag ]
|
||||||
|
branch: [ master, release/*, 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: [ '${DRONE_TAG##v}' ]
|
||||||
|
when:
|
||||||
|
event: [ tag ]
|
||||||
|
branch: [ refs/tags/* ]
|
||||||
|
|
||||||
|
docker:
|
||||||
|
image: plugins/docker
|
||||||
|
repo: gitea/gitea
|
||||||
|
tags: [ '${DRONE_BRANCH##release/v}' ]
|
||||||
|
when:
|
||||||
|
event: [ push ]
|
||||||
|
branch: [ release/* ]
|
||||||
|
|
||||||
|
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/${DRONE_TAG##v}
|
||||||
|
when:
|
||||||
|
event: [ tag ]
|
||||||
|
branch: [ refs/tags/* ]
|
||||||
|
|
||||||
|
release:
|
||||||
|
image: plugins/s3
|
||||||
|
path_style: true
|
||||||
|
strip_prefix: dist/release/
|
||||||
|
source: dist/release/*
|
||||||
|
target: /gitea/${DRONE_BRANCH##release/v}
|
||||||
|
when:
|
||||||
|
event: [ push ]
|
||||||
|
branch: [ release/* ]
|
||||||
|
|
||||||
|
release:
|
||||||
|
image: plugins/s3
|
||||||
|
path_style: true
|
||||||
|
strip_prefix: dist/release/
|
||||||
|
source: dist/release/*
|
||||||
|
target: /gitea/master
|
||||||
|
when:
|
||||||
|
event: [ push ]
|
||||||
|
branch: [ master ]
|
||||||
|
|
||||||
|
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.d29ya3NwYWNlOgogIGJhc2U6IC9zcnYvYXBwCiAgcGF0aDogc3JjL2NvZGUuZ2l0ZWEuaW8vZ2l0ZWEKCnBpcGVsaW5lOgogIGNsb25lOgogICAgaW1hZ2U6IHBsdWdpbnMvZ2l0CiAgICB0YWdzOiB0cnVlCgogIHRlc3Q6CiAgICBpbWFnZTogd2ViaGlwcGllL2dvbGFuZzplZGdlCiAgICBwdWxsOiB0cnVlCiAgICBlbnZpcm9ubWVudDoKICAgICAgQ0dPX0VOQUJMRUQ6IDEKICAgICAgVEFHUzogc3FsaXRlIGJpbmRhdGEKICAgICAgR09QQVRIOiAvc3J2L2FwcAogICAgY29tbWFuZHM6CiAgICAgIC0gYXBrIC1VIGFkZCBvcGVuc3NoLWNsaWVudAogICAgICAtIG1ha2UgY2xlYW4KICAgICAgLSBtYWtlIGdlbmVyYXRlCiAgICAgIC0gbWFrZSB2ZXQKICAgICAgLSBtYWtlIGxpbnQKICAgICAgLSBtYWtlIHRlc3QKICAgICAgLSBtYWtlIGJ1aWxkCiAgICB3aGVuOgogICAgICBldmVudDogWyBwdXNoLCB0YWcsIHB1bGxfcmVxdWVzdCBdCgogIHRlc3QtbXlzcWw6CiAgICBpbWFnZTogd2ViaGlwcGllL2dvbGFuZzplZGdlCiAgICBwdWxsOiB0cnVlCiAgICBlbnZpcm9ubWVudDoKICAgICAgQ0dPX0VOQUJMRUQ6IDEKICAgICAgVEFHUzogc3FsaXRlIGJpbmRhdGEKICAgICAgR09QQVRIOiAvc3J2L2FwcAogICAgY29tbWFuZHM6CiAgICAgIC0gbWFrZSB0ZXN0LW15c3FsCiAgICB3aGVuOgogICAgICBldmVudDogWyBwdXNoIF0KCiAgdGVzdC1wZ3NxbDoKICAgIGltYWdlOiB3ZWJoaXBwaWUvZ29sYW5nOmVkZ2UKICAgIHB1bGw6IHRydWUKICAgIGVudmlyb25tZW50OgogICAgICBDR09fRU5BQkxFRDogMQogICAgICBUQUdTOiBzcWxpdGUgYmluZGF0YQogICAgICBHT1BBVEg6IC9zcnYvYXBwCiAgICBjb21tYW5kczoKICAgICAgLSBtYWtlIHRlc3QtcGdzcWwKICAgIHdoZW46CiAgICAgIGV2ZW50OiBbIHB1c2ggXQoKICB1cGRhdGVyOgogICAgaW1hZ2U6IGthcmFsYWJlL3hnby1sYXRlc3Q6bGF0ZXN0CiAgICBwdWxsOiB0cnVlCiAgICBlbnZpcm9ubWVudDoKICAgICAgQ0dPX0VOQUJMRUQ6IDEKICAgICAgVEFHUzogc3FsaXRlIGJpbmRhdGEKICAgICAgR09QQVRIOiAvc3J2L2FwcAogICAgY29tbWFuZHM6CiAgICAgIC0gbWFrZSByZWxlYXNlCiAgICB3aGVuOgogICAgICBldmVudDogWyBwdXNoLCB0YWcgXQogICAgICBicmFuY2g6IFsgbWFzdGVyLCByZWxlYXNlLyosIHJlZnMvdGFncy8qIF0KCiAgY292ZXJhZ2U6CiAgICBpbWFnZTogcGx1Z2lucy9jb3ZlcmFnZQogICAgc2VydmVyOiBodHRwczovL2NvdmVyYWdlLmdpdGVhLmlvCiAgICB3aGVuOgogICAgICBldmVudDogWyBwdXNoLCB0YWcsIHB1bGxfcmVxdWVzdCBdCgogIGRvY2tlcjoKICAgIGltYWdlOiBwbHVnaW5zL2RvY2tlcgogICAgcmVwbzogZ2l0ZWEvZ2l0ZWEKICAgIHRhZ3M6IFsgJyR7RFJPTkVfVEFHIyN2fScgXQogICAgd2hlbjoKICAgICAgZXZlbnQ6IFsgdGFnIF0KICAgICAgYnJhbmNoOiBbIHJlZnMvdGFncy8qIF0KCiAgZG9ja2VyOgogICAgaW1hZ2U6IHBsdWdpbnMvZG9ja2VyCiAgICByZXBvOiBnaXRlYS9naXRlYQogICAgdGFnczogWyAnJHtEUk9ORV9CUkFOQ0gjI3JlbGVhc2Uvdn0nIF0KICAgIHdoZW46CiAgICAgIGV2ZW50OiBbIHB1c2ggXQogICAgICBicmFuY2g6IFsgcmVsZWFzZS8qIF0KCiAgZG9ja2VyOgogICAgaW1hZ2U6IHBsdWdpbnMvZG9ja2VyCiAgICByZXBvOiBnaXRlYS9naXRlYQogICAgdGFnczogWyAnbGF0ZXN0JyBdCiAgICB3aGVuOgogICAgICBldmVudDogWyBwdXNoIF0KICAgICAgYnJhbmNoOiBbIG1hc3RlciBdCgogIHJlbGVhc2U6CiAgICBpbWFnZTogcGx1Z2lucy9zMwogICAgcGF0aF9zdHlsZTogdHJ1ZQogICAgc3RyaXBfcHJlZml4OiBkaXN0L3JlbGVhc2UvCiAgICBzb3VyY2U6IGRpc3QvcmVsZWFzZS8qCiAgICB0YXJnZXQ6IC9naXRlYS8ke0RST05FX1RBRyMjdn0KICAgIHdoZW46CiAgICAgIGV2ZW50OiBbIHRhZyBdCiAgICAgIGJyYW5jaDogWyByZWZzL3RhZ3MvKiBdCgogIHJlbGVhc2U6CiAgICBpbWFnZTogcGx1Z2lucy9zMwogICAgcGF0aF9zdHlsZTogdHJ1ZQogICAgc3RyaXBfcHJlZml4OiBkaXN0L3JlbGVhc2UvCiAgICBzb3VyY2U6IGRpc3QvcmVsZWFzZS8qCiAgICB0YXJnZXQ6IC9naXRlYS8ke0RST05FX0JSQU5DSCMjcmVsZWFzZS92fQogICAgd2hlbjoKICAgICAgZXZlbnQ6IFsgcHVzaCBdCiAgICAgIGJyYW5jaDogWyByZWxlYXNlLyogXQoKICByZWxlYXNlOgogICAgaW1hZ2U6IHBsdWdpbnMvczMKICAgIHBhdGhfc3R5bGU6IHRydWUKICAgIHN0cmlwX3ByZWZpeDogZGlzdC9yZWxlYXNlLwogICAgc291cmNlOiBkaXN0L3JlbGVhc2UvKgogICAgdGFyZ2V0OiAvZ2l0ZWEvbWFzdGVyCiAgICB3aGVuOgogICAgICBldmVudDogWyBwdXNoIF0KICAgICAgYnJhbmNoOiBbIG1hc3RlciBdCgogIGdpdGh1YjoKICAgIGltYWdlOiBwbHVnaW5zL2dpdGh1Yi1yZWxlYXNlCiAgICBmaWxlczoKICAgICAgLSBkaXN0L3JlbGVhc2UvKgogICAgd2hlbjoKICAgICAgZXZlbnQ6IFsgdGFnIF0KICAgICAgYnJhbmNoOiBbIHJlZnMvdGFncy8qIF0KCiAgZ2l0dGVyOgogICAgaW1hZ2U6IHBsdWdpbnMvZ2l0dGVyCgpzZXJ2aWNlczoKICBteXNxbDoKICAgIGltYWdlOiBteXNxbDo1LjcKICAgIGVudmlyb25tZW50OgogICAgICAtIE1ZU1FMX0RBVEFCQVNFPXRlc3QKICAgICAgLSBNWVNRTF9BTExPV19FTVBUWV9QQVNTV09SRD15ZXMKICAgIHdoZW46CiAgICAgIGV2ZW50OiBbIHB1c2ggXQoKICBwZ3NxbDoKICAgIGltYWdlOiBwb3N0Z3Jlczo5LjUKICAgIGVudmlyb25tZW50OgogICAgICAtIFBPU1RHUkVTX0RCPXRlc3QKICAgIHdoZW46CiAgICAgIGV2ZW50OiBbIHB1c2ggXQo.NGE3UiNBappXiPimJXv1DzgjT3k2hofGPsCPhw7KsSM
|
||||||
@@ -4,22 +4,27 @@ root = true
|
|||||||
|
|
||||||
[*]
|
[*]
|
||||||
charset = utf-8
|
charset = utf-8
|
||||||
end_of_line = lf
|
|
||||||
insert_final_newline = true
|
insert_final_newline = true
|
||||||
trim_trailing_whitespace = true
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
[*.go]
|
[*.go]
|
||||||
indent_style = tab
|
indent_style = tab
|
||||||
indent_size = 4
|
indent_size = 8
|
||||||
|
|
||||||
[*.tmpl]
|
[*.{tmpl,html}]
|
||||||
indent_style = tab
|
indent_style = tab
|
||||||
indent_size = 2
|
indent_size = 4
|
||||||
|
|
||||||
[*.{less,yml}]
|
[*.{less,yml}]
|
||||||
indent_style = space
|
indent_style = space
|
||||||
indent_size = 2
|
indent_size = 4
|
||||||
|
|
||||||
[*.js]
|
[*.js]
|
||||||
indent_style = space
|
indent_style = space
|
||||||
indent_size = 4
|
indent_size = 4
|
||||||
|
|
||||||
|
[Makefile]
|
||||||
|
indent_style = tab
|
||||||
|
|
||||||
|
[*.md]
|
||||||
|
trim_trailing_whitespace = false
|
||||||
|
|||||||
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
|
|
||||||
63
.github/CONTRIBUTING.md
vendored
63
.github/CONTRIBUTING.md
vendored
@@ -1,63 +0,0 @@
|
|||||||
# Contributing to Gogs
|
|
||||||
|
|
||||||
> This guidelines sheet is forked from [CONTRIBUTING.md](https://github.com/drone/drone/blob/8d9c7cee56d6c2eac81dc156ce27be6716d97e68/CONTRIBUTING.md).
|
|
||||||
|
|
||||||
Gogs is not perfect, and it has bugs or incomplete features in rare cases. You're welcome to tell us, or to contribute some code. This document describes details about how can you contribute to Gogs project.
|
|
||||||
|
|
||||||
## Contribution guidelines
|
|
||||||
|
|
||||||
Depends on the situation, you will:
|
|
||||||
|
|
||||||
- Find a bug and create an issue
|
|
||||||
- Need more functionality and make a feature request
|
|
||||||
- Want to contribute code and open a pull request
|
|
||||||
- Run into issue and need help
|
|
||||||
|
|
||||||
### Bug Report
|
|
||||||
|
|
||||||
If you find something you consider a bug, please create a issue on [GitHub](https://github.com/gogits/gogs/issues). To avoid wasting time and reduce back-and-forth communication with team members, please include at least the following information in a form comfortable for you:
|
|
||||||
|
|
||||||
- Bug Description
|
|
||||||
- Gogs Version
|
|
||||||
- Git Version
|
|
||||||
- System Type
|
|
||||||
- Error Log
|
|
||||||
- Other information
|
|
||||||
|
|
||||||
Please take a moment to check that an issue on [GitHub](https://github.com/gogits/gogs/issues) doesn't already exist documenting your bug report or improvement proposal. If it does, it never hurts to add a quick "+1" or "I have this problem too". This will help prioritize the most common problems and requests.
|
|
||||||
|
|
||||||
#### Bug Report Example
|
|
||||||
|
|
||||||
Gogs crashed when creating a repository with a license, using v0.5.13.0207, SQLite3, Git 1.9.0, Ubuntu 12.04.
|
|
||||||
|
|
||||||
Error log:
|
|
||||||
|
|
||||||
```
|
|
||||||
2014/09/01 07:21:49 [E] nil pointer
|
|
||||||
```
|
|
||||||
|
|
||||||
### Feature Request
|
|
||||||
|
|
||||||
There is no standard form of making a feature request. Just try to describe the feature as clearly as possible, because team members may not have experience with the functionality you're talking about.
|
|
||||||
|
|
||||||
### Pull Request
|
|
||||||
|
|
||||||
Please read detailed information on [Wiki](https://github.com/gogits/gogs/wiki/Contributing-Code).
|
|
||||||
|
|
||||||
### Ask For Help
|
|
||||||
|
|
||||||
Before opening an issue, please make sure your problem isn't already addressed on the [Troubleshooting](https://gogs.io/docs/intro/troubleshooting.html) and [FAQs](https://gogs.io/docs/intro/faqs.html) pages.
|
|
||||||
|
|
||||||
## Code of conduct
|
|
||||||
|
|
||||||
As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
|
|
||||||
|
|
||||||
We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, age, or religion.
|
|
||||||
|
|
||||||
Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
|
|
||||||
|
|
||||||
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
|
|
||||||
|
|
||||||
Instances of abusive, harassing, or otherwise unacceptable behavior can be reported by emailing u@gogs.io
|
|
||||||
|
|
||||||
This Code of Conduct is adapted from the [Contributor Covenant](http:contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)
|
|
||||||
25
.github/ISSUE_TEMPLATE.md
vendored
25
.github/ISSUE_TEMPLATE.md
vendored
@@ -1,25 +0,0 @@
|
|||||||
The issue will be closed without any reasons if it does not satisfy any of following requirements:
|
|
||||||
|
|
||||||
1. Please speak English, we have forum in [Chinese](https://discuss.gogs.io/c/getting-help/getting-help-chinese).
|
|
||||||
2. Please post questions or config/deploy problems on our forum: https://discuss.gogs.io, 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.**
|
|
||||||
|
|
||||||
- Gogs version (or commit ref):
|
|
||||||
- Git version:
|
|
||||||
- Operating system:
|
|
||||||
- Database (use `[x]`):
|
|
||||||
- [ ] PostgreSQL
|
|
||||||
- [ ] MySQL
|
|
||||||
- [ ] SQLite
|
|
||||||
- Can you reproduce the bug at https://try.gogs.io:
|
|
||||||
- [ ] Yes (provide example URL)
|
|
||||||
- [ ] No
|
|
||||||
- [ ] Not relevant
|
|
||||||
- Log gist:
|
|
||||||
|
|
||||||
## Description
|
|
||||||
|
|
||||||
...
|
|
||||||
9
.github/PULL_REQUEST_TEMPLATE.md
vendored
9
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -1,9 +0,0 @@
|
|||||||
The pull request will be closed without any reasons if it does not satisfy any of following requirements:
|
|
||||||
|
|
||||||
1. Please make sure you are targeting the `develop` branch.
|
|
||||||
2. Please read contributing guidelines:
|
|
||||||
https://github.com/gogits/gogs/wiki/Contributing-Code
|
|
||||||
3. Please describe what your pull request does and which issue you're targeting
|
|
||||||
4. ... if it is not related to any particular issues, explain why we should not reject your pull request.
|
|
||||||
|
|
||||||
**You MUST delete above content including this line before posting; too lazy to take this action considered invalid pull request.**
|
|
||||||
23
.github/issue_template.md
vendored
Normal file
23
.github/issue_template.md
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
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
|
||||||
|
- Can you reproduce the bug at https://try.gitea.io:
|
||||||
|
- [ ] Yes (provide example URL)
|
||||||
|
- [ ] No
|
||||||
|
- [ ] Not relevant
|
||||||
|
- 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.**
|
||||||
61
.gitignore
vendored
61
.gitignore
vendored
@@ -1,20 +1,45 @@
|
|||||||
.DS_Store
|
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||||
|
*.o
|
||||||
|
*.a
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Folders
|
||||||
|
_obj
|
||||||
|
_test
|
||||||
|
|
||||||
|
# IntelliJ
|
||||||
|
.idea
|
||||||
|
|
||||||
|
# Architecture specific extensions/prefixes
|
||||||
|
*.[568vq]
|
||||||
|
[568vq].out
|
||||||
|
|
||||||
|
*.cgo1.go
|
||||||
|
*.cgo2.c
|
||||||
|
_cgo_defun.c
|
||||||
|
_cgo_gotypes.go
|
||||||
|
_cgo_export.*
|
||||||
|
|
||||||
|
_testmain.go
|
||||||
|
|
||||||
|
*.exe
|
||||||
|
*.test
|
||||||
|
*.prof
|
||||||
|
|
||||||
|
coverage.out
|
||||||
|
|
||||||
|
/modules/options/bindata.go
|
||||||
|
/modules/public/bindata.go
|
||||||
|
/modules/templates/bindata.go
|
||||||
|
|
||||||
*.db
|
*.db
|
||||||
*.log
|
*.log
|
||||||
log/
|
|
||||||
custom/
|
/gitea
|
||||||
data/
|
|
||||||
.vendor/
|
/bin
|
||||||
.idea/
|
/dist
|
||||||
*.iml
|
/custom
|
||||||
public/img/avatar/
|
/data
|
||||||
*.exe
|
/log
|
||||||
*.exe~
|
/public/img/avatar
|
||||||
/gogs
|
|
||||||
profile/
|
|
||||||
*.pem
|
|
||||||
output*
|
|
||||||
gogs.sublime-project
|
|
||||||
gogs.sublime-workspace
|
|
||||||
/release
|
|
||||||
vendor
|
|
||||||
|
|||||||
59
.gopmfile
59
.gopmfile
@@ -1,59 +0,0 @@
|
|||||||
[target]
|
|
||||||
path = github.com/gogits/gogs
|
|
||||||
|
|
||||||
[deps]
|
|
||||||
github.com/bradfitz/gomemcache = commit:fb1f79c
|
|
||||||
github.com/urfave/cli = commit:1efa31f
|
|
||||||
github.com/go-macaron/binding = commit:9440f33
|
|
||||||
github.com/go-macaron/cache = commit:5617353
|
|
||||||
github.com/go-macaron/captcha = commit:8aa5919
|
|
||||||
github.com/go-macaron/csrf = commit:6a9a7df
|
|
||||||
github.com/go-macaron/gzip = commit:cad1c65
|
|
||||||
github.com/go-macaron/i18n = commit:ef57533
|
|
||||||
github.com/go-macaron/inject = commit:c5ab7bf
|
|
||||||
github.com/go-macaron/session = commit:66031fc
|
|
||||||
github.com/go-macaron/toolbox = commit:82b5115
|
|
||||||
github.com/go-sql-driver/mysql = commit:0b58b37
|
|
||||||
github.com/go-xorm/core = commit:5bf745d
|
|
||||||
github.com/go-xorm/xorm = commit:c6c7056
|
|
||||||
github.com/gogits/chardet = commit:2404f77
|
|
||||||
github.com/gogits/cron = commit:7f3990a
|
|
||||||
github.com/gogits/git-module = commit:5e0c133
|
|
||||||
github.com/gogits/go-gogs-client = commit:c52f7ee
|
|
||||||
github.com/issue9/identicon = commit:d36b545
|
|
||||||
github.com/jaytaylor/html2text = commit:52d9b78
|
|
||||||
github.com/kardianos/minwinsvc = commit:cad6b2b
|
|
||||||
github.com/klauspost/compress = commit:14eb9c4
|
|
||||||
github.com/klauspost/cpuid = commit:09cded8
|
|
||||||
github.com/klauspost/crc32 = commit:19b0b33
|
|
||||||
github.com/lib/pq = commit:80f8150
|
|
||||||
github.com/mattn/go-sqlite3 = commit:e118d44
|
|
||||||
github.com/mcuadros/go-version = commit:d52711f
|
|
||||||
github.com/microcosm-cc/bluemonday = commit:9dc1992
|
|
||||||
github.com/msteinert/pam = commit:02ccfbf
|
|
||||||
github.com/nfnt/resize = commit:891127d
|
|
||||||
github.com/russross/blackfriday = commit:93622da
|
|
||||||
github.com/satori/go.uuid = commit:0aa62d5
|
|
||||||
github.com/sergi/go-diff = commit:ec7fdbb
|
|
||||||
github.com/strk/go-libravatar = commit:5eed7bf
|
|
||||||
github.com/shurcooL/sanitized_anchor_name = commit:10ef21a
|
|
||||||
github.com/Unknwon/cae = commit:7f5e046
|
|
||||||
github.com/Unknwon/com = commit:28b053d
|
|
||||||
github.com/Unknwon/i18n = commit:39d6f27
|
|
||||||
github.com/Unknwon/paginater = commit:7748a72
|
|
||||||
golang.org/x/crypto = commit:bc89c49
|
|
||||||
golang.org/x/net = commit:57bfaa8
|
|
||||||
golang.org/x/sys = commit:a646d33
|
|
||||||
golang.org/x/text = commit:2910a50
|
|
||||||
gopkg.in/alexcesaro/quotedprintable.v3 = commit:2caba25
|
|
||||||
gopkg.in/asn1-ber.v1 = commit:4e86f43
|
|
||||||
gopkg.in/bufio.v1 = commit:567b2bf
|
|
||||||
gopkg.in/editorconfig/editorconfig-core-go.v1 = commit:a872f05
|
|
||||||
gopkg.in/gomail.v2 = commit:81ebce5
|
|
||||||
gopkg.in/ini.v1 = commit:cf53f92
|
|
||||||
gopkg.in/ldap.v2 = commit:d0a5ced
|
|
||||||
gopkg.in/macaron.v1 = commit:7564489
|
|
||||||
gopkg.in/redis.v2 = commit:e617904
|
|
||||||
|
|
||||||
[res]
|
|
||||||
include = public|scripts|templates
|
|
||||||
2
.lgtm
Normal file
2
.lgtm
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
self_approval_off = false
|
||||||
|
ignore_maintainers_file = true
|
||||||
2
.mailmap
2
.mailmap
@@ -1,2 +0,0 @@
|
|||||||
Unknwon <u@gogs.io> <joe2010xtmf@163.com>
|
|
||||||
Unknwon <u@gogs.io> 无闻 <u@gogs.io>
|
|
||||||
27
.pkgr.yml
27
.pkgr.yml
@@ -1,27 +0,0 @@
|
|||||||
targets:
|
|
||||||
debian-7: &debian
|
|
||||||
build_dependencies:
|
|
||||||
- libpam0g-dev
|
|
||||||
dependencies:
|
|
||||||
- libpam0g
|
|
||||||
- git
|
|
||||||
debian-8:
|
|
||||||
<<: *debian
|
|
||||||
ubuntu-14.04:
|
|
||||||
<<: *debian
|
|
||||||
ubuntu-12.04:
|
|
||||||
<<: *debian
|
|
||||||
centos-6: &el
|
|
||||||
build_dependencies:
|
|
||||||
- pam-devel
|
|
||||||
dependencies:
|
|
||||||
- pam
|
|
||||||
- git
|
|
||||||
centos-7:
|
|
||||||
<<: *el
|
|
||||||
before:
|
|
||||||
- mv packager/Procfile .
|
|
||||||
- mv packager/.godir .
|
|
||||||
after:
|
|
||||||
- mv bin/main gogs
|
|
||||||
after_install: ./packager/hooks/postinst
|
|
||||||
30
.travis.yml
30
.travis.yml
@@ -1,30 +0,0 @@
|
|||||||
language: go
|
|
||||||
|
|
||||||
go:
|
|
||||||
- 1.4
|
|
||||||
- 1.5
|
|
||||||
- 1.6
|
|
||||||
- 1.7
|
|
||||||
|
|
||||||
before_install:
|
|
||||||
- sudo apt-get update -qq
|
|
||||||
- sudo apt-get install -y libpam-dev
|
|
||||||
- go get github.com/msteinert/pam
|
|
||||||
|
|
||||||
install:
|
|
||||||
- go get -t -v ./...
|
|
||||||
|
|
||||||
script:
|
|
||||||
- go build -v -tags "pam"
|
|
||||||
- go test -v -cover -race ./...
|
|
||||||
|
|
||||||
notifications:
|
|
||||||
email:
|
|
||||||
- u@gogs.io
|
|
||||||
slack: gophercn:o5pSanyTeNhnfYc3QnG0X7Wx
|
|
||||||
webhooks:
|
|
||||||
urls:
|
|
||||||
- https://webhooks.gitter.im/e/b590f8e03882f7aedc3e
|
|
||||||
on_success: change
|
|
||||||
on_failure: always
|
|
||||||
on_start: never
|
|
||||||
69
CHANGELOG.md
Normal file
69
CHANGELOG.md
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
## [1.0.2](https://github.com/go-gitea/gitea/releases/tag/v1.0.2) - 2017-02-21
|
||||||
|
|
||||||
|
* BUGFIXES
|
||||||
|
* Fixed issue counter [#882](https://github.com/go-gitea/gitea/pull/882)
|
||||||
|
* Fixed XSS vulnerability on wiki page [#955](https://github.com/go-gitea/gitea/pull/955)
|
||||||
|
* Add data dir without session to dump [#587](https://github.com/go-gitea/gitea/pull/587)
|
||||||
|
* Fixed wiki page renaming [#958](https://github.com/go-gitea/gitea/pull/958)
|
||||||
|
* Drop default console logger if not required [#960](https://github.com/go-gitea/gitea/pull/960)
|
||||||
|
* Fixed docker docs link on install page [#972](https://github.com/go-gitea/gitea/pull/972)
|
||||||
|
* Handle SetModel errors [#957](https://github.com/go-gitea/gitea/pull/957)
|
||||||
|
* Fixed XSS vulnerability on milestones [#977](https://github.com/go-gitea/gitea/pull/977)
|
||||||
|
* Fixed XSS vulnerability on alerts [#981](https://github.com/go-gitea/gitea/pull/981)
|
||||||
|
|
||||||
|
## [1.0.1](https://github.com/go-gitea/gitea/releases/tag/v1.0.1) - 2017-01-05
|
||||||
|
|
||||||
|
* BUGFIXES
|
||||||
|
* Fixed localized MIN_PASSWORD_LENGTH [#501](https://github.com/go-gitea/gitea/pull/501)
|
||||||
|
* Fixed 500 error on organization delete [#507](https://github.com/go-gitea/gitea/pull/507)
|
||||||
|
* Ignore empty wiki repo on migrate [#544](https://github.com/go-gitea/gitea/pull/544)
|
||||||
|
* Proper check access for forking [#563](https://github.com/go-gitea/gitea/pull/563)
|
||||||
|
* Fix SSH domain on installer [#506](https://github.com/go-gitea/gitea/pull/506)
|
||||||
|
* Fix missing data rows on admin UI [#580](https://github.com/go-gitea/gitea/pull/580)
|
||||||
|
* Do not delete tags with releases by default [#579](https://github.com/go-gitea/gitea/pull/579)
|
||||||
|
* Fix missing session config data on admin UI [#578](https://github.com/go-gitea/gitea/pull/578)
|
||||||
|
* Properly show the version within footer on the UI [#593](https://github.com/go-gitea/gitea/pull/593)
|
||||||
|
|
||||||
|
## [1.0.0](https://github.com/go-gitea/gitea/releases/tag/v1.0.0) - 2016-12-23
|
||||||
|
|
||||||
|
* BREAKING
|
||||||
|
* We have various changes on the API, scripting against API must be updated
|
||||||
|
* FEATURE
|
||||||
|
* Show last login for admins [#121](https://github.com/go-gitea/gitea/pull/121)
|
||||||
|
* BUGFIXES
|
||||||
|
* Fixed sender of notifications [#2](https://github.com/go-gitea/gitea/pull/2)
|
||||||
|
* Fixed keyword hijacking vulnerability [#20](https://github.com/go-gitea/gitea/pull/20)
|
||||||
|
* Fixed non-markdown readme rendering [#95](https://github.com/go-gitea/gitea/pull/95)
|
||||||
|
* Allow updating draft releases [#169](https://github.com/go-gitea/gitea/pull/169)
|
||||||
|
* GitHub API compliance [#227](https://github.com/go-gitea/gitea/pull/227)
|
||||||
|
* Added commit SHA to tag webhook [#286](https://github.com/go-gitea/gitea/issues/286)
|
||||||
|
* Secured links via noopener [#315](https://github.com/go-gitea/gitea/issues/315)
|
||||||
|
* Replace tabs with spaces on wiki title [#371](https://github.com/go-gitea/gitea/pull/371)
|
||||||
|
* Fixed vulnerability on labels and releases [#409](https://github.com/go-gitea/gitea/pull/409)
|
||||||
|
* Fixed issue comment API [#449](https://github.com/go-gitea/gitea/pull/449)
|
||||||
|
* ENHANCEMENT
|
||||||
|
* Use proper import path for libravatar [#3](https://github.com/go-gitea/gitea/pull/3)
|
||||||
|
* Integrated DroneCI for tests and builds [#24](https://github.com/go-gitea/gitea/issues/24)
|
||||||
|
* Integrated dependency manager [#29](https://github.com/go-gitea/gitea/issues/29)
|
||||||
|
* Embedded bindata optionally [#30](https://github.com/go-gitea/gitea/issues/30)
|
||||||
|
* Integrated pagination for releases [#73](https://github.com/go-gitea/gitea/pull/73)
|
||||||
|
* Autogenerate version on every build [#91](https://github.com/go-gitea/gitea/issues/91)
|
||||||
|
* Refactored Docker container [#104](https://github.com/go-gitea/gitea/issues/104)
|
||||||
|
* Added short-hash support for downloads [#211](https://github.com/go-gitea/gitea/issues/211)
|
||||||
|
* Display tooltip for downloads [#221](https://github.com/go-gitea/gitea/issues/221)
|
||||||
|
* Improved HTTP headers for issue attachments [#270](https://github.com/go-gitea/gitea/pull/270)
|
||||||
|
* Integrate public as bindata optionally [#293](https://github.com/go-gitea/gitea/pull/293)
|
||||||
|
* Integrate templates as bindata optionally [#314](https://github.com/go-gitea/gitea/pull/314)
|
||||||
|
* Inject more ENV variables into custom hooks [#316](https://github.com/go-gitea/gitea/issues/316)
|
||||||
|
* Correct LDAP login validation [#342](https://github.com/go-gitea/gitea/pull/342)
|
||||||
|
* Integrate conf as bindata optionally [#354](https://github.com/go-gitea/gitea/pull/354)
|
||||||
|
* Serve video files in browser [#418](https://github.com/go-gitea/gitea/pull/418)
|
||||||
|
* Configurable SSH host binding [#431](https://github.com/go-gitea/gitea/issues/431)
|
||||||
|
* MISC
|
||||||
|
* Forked from Gogs and renamed to Gitea
|
||||||
|
* Catching more errors with logs
|
||||||
|
* Fixed all linting errors
|
||||||
|
* Made the go linter entirely happy
|
||||||
|
* Really integrated vendoring
|
||||||
86
CONTRIBUTING.md
Normal file
86
CONTRIBUTING.md
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
# Contribution Guidelines
|
||||||
|
|
||||||
|
## 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).
|
||||||
|
|
||||||
|
## Bug reports
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
## 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. 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 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:
|
||||||
|
|
||||||
|
```
|
||||||
|
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`.
|
||||||
|
|
||||||
|
## Maintainers
|
||||||
|
|
||||||
|
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 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 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.
|
||||||
|
```
|
||||||
|
|
||||||
|
To honor the past owners, here's the history of the owners and the time they served:
|
||||||
|
|
||||||
|
* 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 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.
|
||||||
|
|
||||||
|
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 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 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.
|
||||||
36
DCO
Normal file
36
DCO
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
Developer Certificate of Origin
|
||||||
|
Version 1.1
|
||||||
|
|
||||||
|
Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
|
||||||
|
660 York Street, Suite 102,
|
||||||
|
San Francisco, CA 94110 USA
|
||||||
|
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies of this
|
||||||
|
license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
|
||||||
|
Developer's Certificate of Origin 1.1
|
||||||
|
|
||||||
|
By making a contribution to this project, I certify that:
|
||||||
|
|
||||||
|
(a) The contribution was created in whole or in part by me and I
|
||||||
|
have the right to submit it under the open source license
|
||||||
|
indicated in the file; or
|
||||||
|
|
||||||
|
(b) The contribution is based upon previous work that, to the best
|
||||||
|
of my knowledge, is covered under an appropriate open source
|
||||||
|
license and I have the right under that license to submit that
|
||||||
|
work with modifications, whether created in whole or in part
|
||||||
|
by me, under the same open source license (unless I am
|
||||||
|
permitted to submit under a different license), as indicated
|
||||||
|
in the file; or
|
||||||
|
|
||||||
|
(c) The contribution was provided directly to me by some other
|
||||||
|
person who certified (a), (b) or (c) and I have not modified
|
||||||
|
it.
|
||||||
|
|
||||||
|
(d) I understand and agree that this project and the contribution
|
||||||
|
are public and that a record of the contribution (including all
|
||||||
|
personal information I submit with it, including my sign-off) is
|
||||||
|
maintained indefinitely and may be redistributed consistent with
|
||||||
|
this project or the open source license(s) involved.
|
||||||
60
Dockerfile
60
Dockerfile
@@ -1,22 +1,42 @@
|
|||||||
FROM alpine:3.3
|
FROM alpine:3.4
|
||||||
MAINTAINER jp@roemer.im
|
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 GOGS_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
|
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 && \
|
||||||
|
echo "git:$(date +%s | sha256sum | base64 | head -c 32)" | chpasswd
|
||||||
|
|
||||||
|
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 gitea /app/gitea/gitea
|
||||||
|
|||||||
@@ -1,25 +1,44 @@
|
|||||||
FROM hypriot/rpi-alpine-scratch:v3.2
|
FROM hypriot/rpi-alpine-scratch:v3.4
|
||||||
MAINTAINER jp@roemer.im, raxetul@gmail.com
|
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 GOGS_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
|
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
|
||||||
|
|||||||
5
LICENSE
5
LICENSE
@@ -1,4 +1,5 @@
|
|||||||
Copyright (c) The Gogs Authors
|
Copyright (c) 2016 The Gitea Authors
|
||||||
|
Copyright (c) 2015 The Gogs Authors
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -16,4 +17,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
THE SOFTWARE.
|
THE SOFTWARE.
|
||||||
|
|||||||
12
MAINTAINERS
Normal file
12
MAINTAINERS
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
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)
|
||||||
|
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 <meyer.thibault@gmail.com> (@0xbaadf00d)
|
||||||
|
Thomas Boerger <thomas@webhippie.de> (@tboerger)
|
||||||
178
Makefile
178
Makefile
@@ -1,74 +1,134 @@
|
|||||||
LDFLAGS += -X "github.com/gogits/gogs/modules/setting.BuildTime=$(shell date -u '+%Y-%m-%d %I:%M:%S %Z')"
|
DIST := dist
|
||||||
LDFLAGS += -X "github.com/gogits/gogs/modules/setting.BuildGitHash=$(shell git rev-parse HEAD)"
|
EXECUTABLE := gitea
|
||||||
|
IMPORT := code.gitea.io/gitea
|
||||||
|
|
||||||
DATA_FILES := $(shell find conf | sed 's/ /\\ /g')
|
BINDATA := modules/{options,public,templates}/bindata.go
|
||||||
LESS_FILES := $(wildcard public/less/gogs.less public/less/_*.less)
|
STYLESHEETS := $(wildcard public/less/index.less public/less/_*.less)
|
||||||
GENERATED := modules/bindata/bindata.go public/css/gogs.css
|
JAVASCRIPTS :=
|
||||||
|
|
||||||
TAGS = ""
|
LDFLAGS += -X "main.Version=$(shell git describe --tags --always | sed 's/-/+/' | sed 's/^v//')"
|
||||||
BUILD_FLAGS = "-v"
|
|
||||||
|
|
||||||
RELEASE_ROOT = "release"
|
TARGETS ?= linux/*,darwin/*,windows/*
|
||||||
RELEASE_GOGS = "release/gogs"
|
PACKAGES ?= $(shell go list ./... | grep -v /vendor/)
|
||||||
NOW = $(shell date -u '+%Y%m%d%I%M%S')
|
SOURCES ?= $(shell find . -name "*.go" -type f)
|
||||||
GOVET = go tool vet -composites=false -methods=false -structtags=false
|
|
||||||
|
|
||||||
.PHONY: build pack release bindata clean
|
TAGS ?=
|
||||||
|
|
||||||
.IGNORE: public/css/gogs.css
|
ifneq ($(DRONE_TAG),)
|
||||||
|
VERSION ?= $(subst v,,$(DRONE_TAG))
|
||||||
|
else
|
||||||
|
ifneq ($(DRONE_BRANCH),)
|
||||||
|
VERSION ?= $(subst release/v,,$(DRONE_BRANCH))
|
||||||
|
else
|
||||||
|
VERSION ?= master
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
.PHONY: all
|
||||||
all: build
|
all: build
|
||||||
|
|
||||||
check: test
|
.PHONY: clean
|
||||||
|
|
||||||
dist: release
|
|
||||||
|
|
||||||
govet:
|
|
||||||
$(GOVET) gogs.go
|
|
||||||
$(GOVET) models modules routers
|
|
||||||
|
|
||||||
build: $(GENERATED)
|
|
||||||
go install $(BUILD_FLAGS) -ldflags '$(LDFLAGS)' -tags '$(TAGS)'
|
|
||||||
cp '$(GOPATH)/bin/gogs' .
|
|
||||||
|
|
||||||
build-dev: $(GENERATED) govet
|
|
||||||
go install $(BUILD_FLAGS) -tags '$(TAGS)'
|
|
||||||
cp '$(GOPATH)/bin/gogs' .
|
|
||||||
|
|
||||||
build-dev-race: $(GENERATED) govet
|
|
||||||
go install $(BUILD_FLAGS) -race -tags '$(TAGS)'
|
|
||||||
cp '$(GOPATH)/bin/gogs' .
|
|
||||||
|
|
||||||
pack:
|
|
||||||
rm -rf $(RELEASE_GOGS)
|
|
||||||
mkdir -p $(RELEASE_GOGS)
|
|
||||||
cp -r gogs LICENSE README.md README_ZH.md templates public scripts $(RELEASE_GOGS)
|
|
||||||
rm -rf $(RELEASE_GOGS)/public/config.codekit $(RELEASE_GOGS)/public/less
|
|
||||||
cd $(RELEASE_ROOT) && zip -r gogs.$(NOW).zip "gogs"
|
|
||||||
|
|
||||||
release: build pack
|
|
||||||
|
|
||||||
bindata: modules/bindata/bindata.go
|
|
||||||
|
|
||||||
modules/bindata/bindata.go: $(DATA_FILES)
|
|
||||||
go-bindata -o=$@ -ignore="\\.DS_Store|README.md|TRANSLATORS" -pkg=bindata conf/...
|
|
||||||
|
|
||||||
less: public/css/gogs.css
|
|
||||||
|
|
||||||
public/css/gogs.css: $(LESS_FILES)
|
|
||||||
lessc $< $@
|
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
go clean -i ./...
|
go clean -i ./...
|
||||||
|
rm -rf $(EXECUTABLE) $(DIST) $(BINDATA)
|
||||||
|
|
||||||
clean-mac: clean
|
.PHONY: fmt
|
||||||
find . -name ".DS_Store" -print0 | xargs -0 rm
|
fmt:
|
||||||
|
go fmt $(PACKAGES)
|
||||||
|
|
||||||
|
.PHONY: vet
|
||||||
|
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 \
|
||||||
|
go get -u github.com/golang/lint/golint; \
|
||||||
|
fi
|
||||||
|
for PKG in $(PACKAGES); do golint -set_exit_status $$PKG || exit 1; done;
|
||||||
|
|
||||||
|
.PHONY: test
|
||||||
test:
|
test:
|
||||||
go test -cover -race ./...
|
for PKG in $(PACKAGES); do go test -cover -coverprofile $$GOPATH/src/$$PKG/coverage.out $$PKG || exit 1; done;
|
||||||
|
|
||||||
fixme:
|
.PHONY: test-mysql
|
||||||
grep -rnw "FIXME" routers models modules
|
test-mysql:
|
||||||
|
@echo "Not integrated yet!"
|
||||||
|
|
||||||
todo:
|
.PHONY: test-pgsql
|
||||||
grep -rnw "TODO" routers models modules
|
test-pgsql:
|
||||||
|
@echo "Not integrated yet!"
|
||||||
|
|
||||||
|
.PHONY: check
|
||||||
|
check: test
|
||||||
|
|
||||||
|
.PHONY: install
|
||||||
|
install: $(wildcard *.go)
|
||||||
|
go install -v -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)'
|
||||||
|
|
||||||
|
.PHONY: build
|
||||||
|
build: $(EXECUTABLE)
|
||||||
|
|
||||||
|
$(EXECUTABLE): $(SOURCES)
|
||||||
|
go build -v -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)' -o $@
|
||||||
|
|
||||||
|
.PHONY: docker
|
||||||
|
docker:
|
||||||
|
docker run -ti --rm -v $(CURDIR):/srv/app/src/code.gitea.io/gitea -w /srv/app/src/code.gitea.io/gitea -e TAGS="$(TAGS)" webhippie/golang:edge make clean generate build
|
||||||
|
docker build -t gitea/gitea:latest .
|
||||||
|
|
||||||
|
.PHONY: release
|
||||||
|
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 $(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:
|
||||||
|
$(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: javascripts
|
||||||
|
javascripts: public/js/index.js
|
||||||
|
|
||||||
|
.IGNORE: public/js/index.js
|
||||||
|
public/js/index.js: $(JAVASCRIPTS)
|
||||||
|
cat $< >| $@
|
||||||
|
|
||||||
|
.PHONY: stylesheets
|
||||||
|
stylesheets: public/css/index.css
|
||||||
|
|
||||||
|
.IGNORE: public/css/index.css
|
||||||
|
public/css/index.css: $(STYLESHEETS)
|
||||||
|
lessc $< $@
|
||||||
|
|
||||||
|
.PHONY: assets
|
||||||
|
assets: javascripts stylesheets
|
||||||
|
|||||||
148
README.md
148
README.md
@@ -1,138 +1,46 @@
|
|||||||
Gogs - Go Git Service [](https://travis-ci.org/gogits/gogs) [](https://crowdin.com/project/gogs) [](https://gitter.im/gogits/gogs?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
[简体中文](https://github.com/go-gitea/gitea/blob/master/README_ZH.md)
|
||||||
=====================
|
|
||||||
|
|
||||||

|
# Gitea - Git with a cup of tea
|
||||||
|
|
||||||
##### Current tip version: 0.9.99 (see [Releases](https://github.com/gogits/gogs/releases) for binary versions or submit a task on [alpha stage automated binary building system](https://build.gogs.io/))
|
[](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/releases/latest)
|
||||||
|
|
||||||
| Web | UI | Preview |
|
||||
|
||||||
|:-------------:|:-------:|:-------:|
|
|:-------------:|:-------:|:-------:|
|
||||||
||||
|
||||
|
||||||
||||
|
||||
|
||||||
||||
|
||||
|
||||||
|
||||
|
||||||
### Important Notes
|
|
||||||
|
|
||||||
1. **YOU MUST READ [Contributing Code](https://github.com/gogits/gogs/wiki/Contributing-Code) BEFORE STARTING TO WORK ON A PULL REQUEST**.
|
|
||||||
2. Due to testing purpose, data of [try.gogs.io](https://try.gogs.io) was reset in **Jan 28, 2015** and will reset multiple times after. Please do **NOT** put your important data on the site.
|
|
||||||
3. The demo site [try.gogs.io](https://try.gogs.io) is running under `develop` branch.
|
|
||||||
4. If you think there are vulnerabilities in the project, please talk privately to **u@gogs.io**. Thanks!
|
|
||||||
5. If you're interested in using APIs, we have experimental support with [documentation](https://github.com/gogits/go-gogs-client/wiki).
|
|
||||||
6. If your team/company is using Gogs and would like to put your logo on [our website](https://gogs.io), contact us by any means.
|
|
||||||
|
|
||||||
[简体中文](README_ZH.md)
|
|
||||||
|
|
||||||
## Purpose
|
## Purpose
|
||||||
|
|
||||||
The goal of this project is to make the easiest, fastest, and most painless way of setting up a self-hosted Git service. With Go, this can be done with an independent binary distribution across **ALL platforms** that Go supports, including Linux, Mac OS X, Windows and ARM.
|
The goal of this project is to make the easiest, fastest, and most painless way of setting up a self-hosted Git service. With Go, this can be done with an independent binary distribution across **all platforms** that Go supports, including Linux, macOS, and Windows on x86, amd64, ARM and PowerPC architectures. Want to try it before doing anything else? Do it [online](https://try.gitea.io/)!
|
||||||
|
|
||||||
## Overview
|
## Notes
|
||||||
|
|
||||||
- Please see the [Documentation](https://gogs.io/docs/intro) for common usages and change log.
|
1. **YOU MUST READ THE [CONTRIBUTORS GUIDE](CONTRIBUTING.md) BEFORE STARTING TO WORK ON A PULL REQUEST.**
|
||||||
- See the [Trello Board](https://trello.com/b/uxAoeLUl/gogs-go-git-service) to follow the develop team.
|
2. If you found a vulnerability in the project, please write privately to **security@gitea.io**. Thanks!
|
||||||
- Want to try it before doing anything else? Do it [online](https://try.gogs.io/gogs/gogs)!
|
3. If you're interested in using our APIs, we have experimental support with [documentation](https://godoc.org/code.gitea.io/sdk/gitea).
|
||||||
- Having trouble? Get help with [Troubleshooting](https://gogs.io/docs/intro/troubleshooting.html) or [User Forum](https://discuss.gogs.io/).
|
|
||||||
- Want to help with localization? Check out the [guide](https://gogs.io/docs/features/i18n.html)!
|
|
||||||
|
|
||||||
## Features
|
## Docs
|
||||||
|
|
||||||
- Activity timeline
|
For further information or instructions how to install Gitea please take a look at our [documentation](https://docs.gitea.io/en-us/), if you can not find some specific information just head over to our [Gitter](https://gitter.im/go-gitea/gitea) channel to have a chat with us.
|
||||||
- SSH and HTTP/HTTPS protocols
|
|
||||||
- SMTP/LDAP/Reverse proxy authentication
|
|
||||||
- Reverse proxy with sub-path
|
|
||||||
- Account/Organization/Repository management
|
|
||||||
- Add/Remove repository collaborators
|
|
||||||
- Repository/Organization webhooks (including Slack)
|
|
||||||
- Repository Git hooks/deploy keys
|
|
||||||
- Repository issues, pull requests and wiki
|
|
||||||
- Migrate and mirror repository and its wiki
|
|
||||||
- Web editor for repository files and wiki
|
|
||||||
- Gravatar and Federated avatar with custom source
|
|
||||||
- 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))
|
|
||||||
|
|
||||||
## System Requirements
|
## Contributing
|
||||||
|
|
||||||
- A cheap Raspberry Pi is powerful enough for basic functionality.
|
Fork -> Patch -> Push -> Pull Request
|
||||||
- 2 CPU cores and 1GB RAM would be the baseline for teamwork.
|
|
||||||
|
|
||||||
## Browser Support
|
## Authors
|
||||||
|
|
||||||
- Please see [Semantic UI](https://github.com/Semantic-Org/Semantic-UI#browser-support) for specific versions of supported browsers.
|
* [Maintainers](https://github.com/orgs/go-gitea/people)
|
||||||
- The official support minimal size is **1024*768**, UI may still looks right in smaller size but no promises and fixes.
|
* [Contributors](https://github.com/go-gitea/gitea/graphs/contributors)
|
||||||
|
* [Translators](options/locale/TRANSLATORS)
|
||||||
## Installation
|
|
||||||
|
|
||||||
Make sure you install the [prerequisites](https://gogs.io/docs/installation) first.
|
|
||||||
|
|
||||||
There are 5 ways to install Gogs:
|
|
||||||
|
|
||||||
- [Install from binary](https://gogs.io/docs/installation/install_from_binary.html)
|
|
||||||
- [Install from source](https://gogs.io/docs/installation/install_from_source.html)
|
|
||||||
- [Install from packages](https://gogs.io/docs/installation/install_from_packages.html)
|
|
||||||
- [Ship with Docker](https://github.com/gogits/gogs/tree/master/docker)
|
|
||||||
- [Install with Vagrant](https://github.com/geerlingguy/ansible-vagrant-examples/tree/master/gogs)
|
|
||||||
|
|
||||||
### Tutorials
|
|
||||||
|
|
||||||
- [How To Set Up Gogs on Ubuntu 14.04](https://www.digitalocean.com/community/tutorials/how-to-set-up-gogs-on-ubuntu-14-04)
|
|
||||||
- [Run your own GitHub-like service with the help of Docker](http://blog.hypriot.com/post/run-your-own-github-like-service-with-docker/)
|
|
||||||
- [Dockerized Gogs git server and alpine postgres in 20 minutes or less](http://garthwaite.org/docker-gogs.html)
|
|
||||||
- [Host Your Own Private GitHub with Gogs.io](https://eladnava.com/host-your-own-private-github-with-gogs-io/)
|
|
||||||
- [使用 Gogs 搭建自己的 Git 服务器](https://mynook.info/blog/post/host-your-own-git-server-using-gogs) (Chinese)
|
|
||||||
- [阿里云上 Ubuntu 14.04 64 位安装 Gogs](http://my.oschina.net/luyao/blog/375654) (Chinese)
|
|
||||||
- [Installing Gogs on FreeBSD](https://www.codejam.info/2015/03/installing-gogs-on-freebsd.html)
|
|
||||||
- [Gogs on Raspberry Pi](http://blog.meinside.pe.kr/Gogs-on-Raspberry-Pi/)
|
|
||||||
- [Cloudflare Full SSL with GOGS (Go Git Service) using NGINX](http://www.listekconsulting.com/articles/cloudflare-full-ssl-with-gogs-go-git-service-using-nginx/)
|
|
||||||
|
|
||||||
### Screencasts
|
|
||||||
|
|
||||||
- [How to install Gogs on a Linux Server (DigitalOcean)](https://www.youtube.com/watch?v=deSfX0gqefE)
|
|
||||||
- [Instalando Gogs no Ubuntu](https://www.youtube.com/watch?v=4UkHAR1F7ZA) (Português)
|
|
||||||
|
|
||||||
### Deploy to Cloud
|
|
||||||
|
|
||||||
- [OpenShift](https://github.com/tkisme/gogs-openshift)
|
|
||||||
- [Cloudron](https://cloudron.io/appstore.html#io.gogs.cloudronapp)
|
|
||||||
- [Scaleway](https://www.scaleway.com/imagehub/gogs/)
|
|
||||||
- [Portal](https://portaldemo.xyz/cloud/)
|
|
||||||
- [Sandstorm](https://github.com/cem/gogs-sandstorm)
|
|
||||||
- [sloppy.io](https://github.com/sloppyio/quickstarters/tree/master/gogs)
|
|
||||||
- [YunoHost](https://github.com/mbugeia/gogs_ynh)
|
|
||||||
- [DPlatform](https://github.com/j8r/DPlatform)
|
|
||||||
|
|
||||||
## Software and Service Support
|
|
||||||
|
|
||||||
- [Drone](https://github.com/drone/drone) (CI)
|
|
||||||
- [Fabric8](http://fabric8.io/) (DevOps)
|
|
||||||
- [Taiga](https://taiga.io/) (Project Management)
|
|
||||||
- [Puppet](https://forge.puppetlabs.com/Siteminds/gogs) (IT)
|
|
||||||
- [Kanboard](http://kanboard.net/plugin/gogs-webhook) (Project Management)
|
|
||||||
- [BearyChat](https://bearychat.com/) (Team Communication)
|
|
||||||
- [HiWork](http://www.hiwork.cc/) (Team Communication)
|
|
||||||
|
|
||||||
### Product Support
|
|
||||||
|
|
||||||
- [Synology](https://www.synology.com) (Docker)
|
|
||||||
- [One Space](http://www.onespace.cc) (App Store)
|
|
||||||
|
|
||||||
## Acknowledgments
|
|
||||||
|
|
||||||
- Router and middleware mechanism of [Macaron](https://github.com/go-macaron/macaron).
|
|
||||||
- System Monitor Status is inspired by [GoBlog](https://github.com/fuxiaohei/goblog).
|
|
||||||
- Thanks [Rocker](http://weibo.com/rocker1989) for designing Logo.
|
|
||||||
- Thanks [Crowdin](https://crowdin.com/project/gogs) for providing open source translation plan.
|
|
||||||
- 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
|
|
||||||
|
|
||||||
- Ex-team members [@lunny](https://github.com/lunny), [@fuxiaohei](https://github.com/fuxiaohei) and [@slene](https://github.com/slene).
|
|
||||||
- See [contributors page](https://github.com/gogits/gogs/graphs/contributors) for full list of contributors.
|
|
||||||
- See [TRANSLATORS](conf/locale/TRANSLATORS) for public list of translators.
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
This project is under the MIT License. See the [LICENSE](https://github.com/gogits/gogs/blob/master/LICENSE) file for the full license text.
|
This project is under the MIT License. See the [LICENSE](https://github.com/go-gitea/gitea/blob/master/LICENSE) file for the full license text.
|
||||||
|
|||||||
121
README_ZH.md
121
README_ZH.md
@@ -1,107 +1,48 @@
|
|||||||
Gogs - Go Git Service [](https://travis-ci.org/gogits/gogs)
|
[English](https://github.com/go-gitea/gitea/blob/master/README.md)
|
||||||
=====================
|
|
||||||
|
|
||||||
Gogs (Go Git Service) 是一款极易搭建的自助 Git 服务。
|
# Gitea - Git with a cup of tea
|
||||||
|
|
||||||
## 开发目的
|
[](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/releases/latest)
|
||||||
|
|
||||||
Gogs 的目标是打造一个最简单、最快速和最轻松的方式搭建自助 Git 服务。使用 Go 语言开发使得 Gogs 能够通过独立的二进制分发,并且支持 Go 语言支持的 **所有平台**,包括 Linux、Mac OS X、Windows 以及 ARM 平台。
|
||||
|
||||||
|
|:-------------:|:-------:|:-------:|
|
||||||
|
||||
|
||||||
|
||||
|
||||||
|
||||
|
||||||
|
||||
|
||||||
|
|
||||||
## 项目概览
|
## 目标
|
||||||
|
|
||||||
- 有关基本用法和变更日志,请通过 [使用手册](https://gogs.io/docs/intro/) 查看。
|
Gitea的首要目标是创建一个极易安装,运行非常快速,安装和使用体验良好的自建 Git 服务。我们采用Go作为后端语言,这使我们只要生成一个可执行程序即可。并且他还支持跨平台,支持 Linux, macOS 和 Windows 以及各种架构,除了x86,amd64,还包括 ARM 和 PowerPC。
|
||||||
- 您可以到 [Trello Board](https://trello.com/b/uxAoeLUl/gogs-go-git-service) 跟随开发团队的脚步。
|
|
||||||
- 想要先睹为快?直接去 [在线体验](https://try.gogs.io/gogs/gogs) 。
|
|
||||||
- 使用过程中遇到问题?尝试从 [故障排查](https://gogs.io/docs/intro/troubleshooting.html) 页面或 [用户论坛](https://discuss.gogs.io/) 获取帮助。
|
|
||||||
- 希望帮助多国语言界面的翻译吗?请立即访问 [详情页面](https://gogs.io/docs/features/i18n.html)!
|
|
||||||
|
|
||||||
## 功能特性
|
如果您想试用一下,请访问 [在线Demo](https://try.gitea.io/)!
|
||||||
|
|
||||||
- 支持活动时间线
|
## 提示
|
||||||
- 支持 SSH 以及 HTTP/HTTPS 协议
|
|
||||||
- 支持 SMTP、LDAP 和反向代理的用户认证
|
|
||||||
- 支持反向代理子路径
|
|
||||||
- 支持用户、组织和仓库管理系统
|
|
||||||
- 支持添加和删除仓库协作者
|
|
||||||
- 支持仓库和组织级别 Web 钩子(包括 Slack 集成)
|
|
||||||
- 支持仓库 Git 钩子和部署密钥
|
|
||||||
- 支持仓库工单(Issue)、合并请求(Pull Request)以及 Wiki
|
|
||||||
- 支持迁移和镜像仓库以及它的 Wiki
|
|
||||||
- 支持在线编辑仓库文件和 Wiki
|
|
||||||
- 支持自定义源的 Gravatar 和 Federated Avatar
|
|
||||||
- 支持邮件服务
|
|
||||||
- 支持后台管理面板
|
|
||||||
- 支持 MySQL、PostgreSQL、SQLite3 和 [TiDB](https://github.com/pingcap/tidb)(实验性支持) 数据库
|
|
||||||
- 支持多语言本地化([19 种语言]([more](https://crowdin.com/project/gogs)))
|
|
||||||
|
|
||||||
## 系统要求
|
1. **开始贡献代码之前请确保你已经看过了 [贡献者向导(英文)](CONTRIBUTING.md)**.
|
||||||
|
2. 所有的安全问题,请私下发送邮件给 **security@gitea.io**。谢谢!
|
||||||
|
3. 如果你要使用API,请参见 [API 文档](https://godoc.org/code.gitea.io/sdk/gitea).
|
||||||
|
|
||||||
- 最低的系统硬件要求为一个廉价的树莓派
|
## 文档
|
||||||
- 如果用于团队项目,建议使用 2 核 CPU 及 1GB 内存
|
|
||||||
|
|
||||||
## 浏览器支持
|
关于如何安装请访问我们的 [文档站](https://docs.gitea.io/zh-cn/),如果没有找到对应的文档,你也可以通过 [Gitter - 英文](https://gitter.im/go-gitea/gitea) 和 QQ群 328432459 来和我们交流。
|
||||||
|
|
||||||
- 请根据 [Semantic UI](https://github.com/Semantic-Org/Semantic-UI#browser-support) 查看具体支持的浏览器版本。
|
## 贡献流程
|
||||||
- 官方支持的最小 UI 尺寸为 **1024*768**,UI 不一定会在更小尺寸的设备上被破坏,但我们无法保证且不会修复。
|
|
||||||
|
|
||||||
## 安装部署
|
Fork -> Patch -> Push -> Pull Request
|
||||||
|
|
||||||
在安装 Gogs 之前,您需要先安装 [基本环境](https://gogs.io/docs/installation)。
|
## 作者
|
||||||
|
|
||||||
然后,您可以通过以下 5 种方式来安装 Gogs:
|
* [Maintainers](https://github.com/orgs/go-gitea/people)
|
||||||
|
* [Contributors](https://github.com/go-gitea/gitea/graphs/contributors)
|
||||||
- [二进制安装](https://gogs.io/docs/installation/install_from_binary.html)
|
* [Translators](options/locale/TRANSLATORS)
|
||||||
- [源码安装](https://gogs.io/docs/installation/install_from_source.html)
|
|
||||||
- [包管理安装](https://gogs.io/docs/installation/install_from_packages.html)
|
|
||||||
- [采用 Docker 部署](https://github.com/gogits/gogs/tree/master/docker)
|
|
||||||
- [通过 Vagrant 安装](https://github.com/geerlingguy/ansible-vagrant-examples/tree/master/gogs)
|
|
||||||
|
|
||||||
### 使用教程
|
|
||||||
|
|
||||||
- [使用 Gogs 搭建自己的 Git 服务器](https://mynook.info/blog/post/host-your-own-git-server-using-gogs)
|
|
||||||
- [阿里云上 Ubuntu 14.04 64 位安装 Gogs](http://my.oschina.net/luyao/blog/375654)
|
|
||||||
|
|
||||||
### 云端部署
|
|
||||||
|
|
||||||
- [OpenShift](https://github.com/tkisme/gogs-openshift)
|
|
||||||
- [Cloudron](https://cloudron.io/appstore.html#io.gogs.cloudronapp)
|
|
||||||
- [Scaleway](https://www.scaleway.com/imagehub/gogs/)
|
|
||||||
- [Portal](https://portaldemo.xyz/cloud/)
|
|
||||||
- [Sandstorm](https://github.com/cem/gogs-sandstorm)
|
|
||||||
- [sloppy.io](https://github.com/sloppyio/quickstarters/tree/master/gogs)
|
|
||||||
- [YunoHost](https://github.com/mbugeia/gogs_ynh)
|
|
||||||
- [DPlatform](https://github.com/j8r/DPlatform)
|
|
||||||
|
|
||||||
## 软件及服务支持
|
|
||||||
|
|
||||||
- [Drone](https://github.com/drone/drone)(CI)
|
|
||||||
- [Fabric8](http://fabric8.io/)(DevOps)
|
|
||||||
- [Taiga](https://taiga.io/)(项目管理)
|
|
||||||
- [Puppet](https://forge.puppetlabs.com/Siteminds/gogs)(IT)
|
|
||||||
- [Kanboard](http://kanboard.net/plugin/gogs-webhook)(项目管理)
|
|
||||||
- [BearyChat](https://bearychat.com/)(团队交流)
|
|
||||||
- [HiWork](http://www.hiwork.cc/)(团队交流)
|
|
||||||
|
|
||||||
### 产品支持
|
|
||||||
|
|
||||||
- [Synology](https://www.synology.com)(Docker)
|
|
||||||
- [One Space](http://www.onespace.cc)(应用商店)
|
|
||||||
|
|
||||||
## 特别鸣谢
|
|
||||||
|
|
||||||
- 基于 [Macaron](https://github.com/go-macaron/macaron) 的路由与中间件机制。
|
|
||||||
- 基于 [GoBlog](https://github.com/fuxiaohei/goblog) 修改的系统监视状态。
|
|
||||||
- 感谢 [Rocker](http://weibo.com/rocker1989) 设计的 Logo。
|
|
||||||
- 感谢 [Crowdin](https://crowdin.com/project/gogs) 提供免费的开源项目本地化支持。
|
|
||||||
- 感谢 [DigitalOcean](https://www.digitalocean.com) 提供主站和体验站点的服务器赞助。
|
|
||||||
- 感谢 [KeyCDN](https://www.keycdn.com/) 和 [七牛云存储](http://www.qiniu.com/) 提供 CDN 服务赞助。
|
|
||||||
|
|
||||||
## 贡献成员
|
|
||||||
|
|
||||||
- 前团队成员 [@lunny](https://github.com/lunny)、[@fuxiaohei](https://github.com/fuxiaohei) 和 [@slene](https://github.com/slene)。
|
|
||||||
- 您可以通过查看 [贡献者页面](https://github.com/gogits/gogs/graphs/contributors) 获取完整的贡献者列表。
|
|
||||||
- 您可以通过查看 [TRANSLATORS](conf/locale/TRANSLATORS) 文件获取公开的翻译人员列表。
|
|
||||||
|
|
||||||
## 授权许可
|
## 授权许可
|
||||||
|
|
||||||
本项目采用 MIT 开源授权许可证,完整的授权说明已放置在 [LICENSE](https://github.com/gogits/gogs/blob/master/LICENSE) 文件中。
|
本项目采用 MIT 开源授权许可证,完整的授权说明已放置在 [LICENSE](https://github.com/go-gitea/gitea/blob/master/LICENSE) 文件中。
|
||||||
|
|||||||
41
cmd/admin.go
41
cmd/admin.go
@@ -1,4 +1,5 @@
|
|||||||
// Copyright 2016 The Gogs Authors. All rights reserved.
|
// Copyright 2016 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
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
@@ -9,15 +10,16 @@ import (
|
|||||||
|
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
|
|
||||||
"github.com/gogits/gogs/models"
|
"code.gitea.io/gitea/models"
|
||||||
"github.com/gogits/gogs/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
// CmdAdmin represents the available admin sub-command.
|
||||||
CmdAdmin = cli.Command{
|
CmdAdmin = cli.Command{
|
||||||
Name: "admin",
|
Name: "admin",
|
||||||
Usage: "Preform admin operations on command line",
|
Usage: "Preform admin operations on command line",
|
||||||
Description: `Allow using internal logic of Gogs without hacking into the source code
|
Description: `Allow using internal logic of Gitea without hacking into the source code
|
||||||
to make automatic initialization process more smoothly`,
|
to make automatic initialization process more smoothly`,
|
||||||
Subcommands: []cli.Command{
|
Subcommands: []cli.Command{
|
||||||
subcmdCreateUser,
|
subcmdCreateUser,
|
||||||
@@ -29,11 +31,30 @@ to make automatic initialization process more smoothly`,
|
|||||||
Usage: "Create a new user in database",
|
Usage: "Create a new user in database",
|
||||||
Action: runCreateUser,
|
Action: runCreateUser,
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
stringFlag("name", "", "Username"),
|
cli.StringFlag{
|
||||||
stringFlag("password", "", "User password"),
|
Name: "name",
|
||||||
stringFlag("email", "", "User email address"),
|
Value: "",
|
||||||
boolFlag("admin", "User is an admin"),
|
Usage: "Username",
|
||||||
stringFlag("config, c", "custom/conf/app.ini", "Custom configuration file path"),
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "password",
|
||||||
|
Value: "",
|
||||||
|
Usage: "User password",
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "email",
|
||||||
|
Value: "",
|
||||||
|
Usage: "User email address",
|
||||||
|
},
|
||||||
|
cli.BoolFlag{
|
||||||
|
Name: "admin",
|
||||||
|
Usage: "User is an admin",
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "config, c",
|
||||||
|
Value: "custom/conf/app.ini",
|
||||||
|
Usage: "Custom configuration file path",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -53,7 +74,9 @@ func runCreateUser(c *cli.Context) error {
|
|||||||
|
|
||||||
setting.NewContext()
|
setting.NewContext()
|
||||||
models.LoadConfigs()
|
models.LoadConfigs()
|
||||||
models.SetEngine()
|
if err := models.SetEngine(); err != nil {
|
||||||
|
return fmt.Errorf("models.SetEngine: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
if err := models.CreateUser(&models.User{
|
if err := models.CreateUser(&models.User{
|
||||||
Name: c.String("name"),
|
Name: c.String("name"),
|
||||||
|
|||||||
43
cmd/cert.go
43
cmd/cert.go
@@ -1,7 +1,6 @@
|
|||||||
// +build cert
|
|
||||||
|
|
||||||
// Copyright 2009 The Go Authors. All rights reserved.
|
// Copyright 2009 The Go Authors. All rights reserved.
|
||||||
// Copyright 2014 The Gogs 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
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
@@ -25,19 +24,43 @@ import (
|
|||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// CmdCert represents the available cert sub-command.
|
||||||
var CmdCert = cli.Command{
|
var CmdCert = cli.Command{
|
||||||
Name: "cert",
|
Name: "cert",
|
||||||
Usage: "Generate self-signed certificate",
|
Usage: "Generate self-signed certificate",
|
||||||
Description: `Generate a self-signed X.509 certificate for a TLS server.
|
Description: `Generate a self-signed X.509 certificate for a TLS server.
|
||||||
Outputs to 'cert.pem' and 'key.pem' and will overwrite existing files.`,
|
Outputs to 'cert.pem' and 'key.pem' and will overwrite existing files.`,
|
||||||
Action: runCert,
|
Action: runCert,
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
stringFlag("host", "", "Comma-separated hostnames and IPs to generate a certificate for"),
|
cli.StringFlag{
|
||||||
stringFlag("ecdsa-curve", "", "ECDSA curve to use to generate a key. Valid values are P224, P256, P384, P521"),
|
Name: "host",
|
||||||
intFlag("rsa-bits", 2048, "Size of RSA key to generate. Ignored if --ecdsa-curve is set"),
|
Value: "",
|
||||||
stringFlag("start-date", "", "Creation date formatted as Jan 1 15:04:05 2011"),
|
Usage: "Comma-separated hostnames and IPs to generate a certificate for",
|
||||||
durationFlag("duration", 365*24*time.Hour, "Duration that certificate is valid for"),
|
},
|
||||||
boolFlag("ca", "whether this cert should be its own Certificate Authority"),
|
cli.StringFlag{
|
||||||
|
Name: "ecdsa-curve",
|
||||||
|
Value: "",
|
||||||
|
Usage: "ECDSA curve to use to generate a key. Valid values are P224, P256, P384, P521",
|
||||||
|
},
|
||||||
|
cli.IntFlag{
|
||||||
|
Name: "rsa-bits",
|
||||||
|
Value: 2048,
|
||||||
|
Usage: "Size of RSA key to generate. Ignored if --ecdsa-curve is set",
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "start-date",
|
||||||
|
Value: "",
|
||||||
|
Usage: "Creation date formatted as Jan 1 15:04:05 2011",
|
||||||
|
},
|
||||||
|
cli.DurationFlag{
|
||||||
|
Name: "duration",
|
||||||
|
Value: 365 * 24 * time.Hour,
|
||||||
|
Usage: "Duration that certificate is valid for",
|
||||||
|
},
|
||||||
|
cli.BoolFlag{
|
||||||
|
Name: "ca",
|
||||||
|
Usage: "whether this cert should be its own Certificate Authority",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,7 +137,7 @@ func runCert(ctx *cli.Context) error {
|
|||||||
SerialNumber: serialNumber,
|
SerialNumber: serialNumber,
|
||||||
Subject: pkix.Name{
|
Subject: pkix.Name{
|
||||||
Organization: []string{"Acme Co"},
|
Organization: []string{"Acme Co"},
|
||||||
CommonName: "Gogs",
|
CommonName: "Gitea",
|
||||||
},
|
},
|
||||||
NotBefore: notBefore,
|
NotBefore: notBefore,
|
||||||
NotAfter: notAfter,
|
NotAfter: notAfter,
|
||||||
|
|||||||
@@ -1,28 +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"
|
|
||||||
)
|
|
||||||
|
|
||||||
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(ctx *cli.Context) error {
|
|
||||||
fmt.Println("Command cert not available, please use build tags 'cert' to rebuild.")
|
|
||||||
os.Exit(1)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
42
cmd/cmd.go
42
cmd/cmd.go
@@ -1,42 +0,0 @@
|
|||||||
// Copyright 2015 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 (
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/urfave/cli"
|
|
||||||
)
|
|
||||||
|
|
||||||
func stringFlag(name, value, usage string) cli.StringFlag {
|
|
||||||
return cli.StringFlag{
|
|
||||||
Name: name,
|
|
||||||
Value: value,
|
|
||||||
Usage: usage,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func boolFlag(name, usage string) cli.BoolFlag {
|
|
||||||
return cli.BoolFlag{
|
|
||||||
Name: name,
|
|
||||||
Usage: usage,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func intFlag(name string, value int, usage string) cli.IntFlag {
|
|
||||||
return cli.IntFlag{
|
|
||||||
Name: name,
|
|
||||||
Value: value,
|
|
||||||
Usage: usage,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func durationFlag(name string, value time.Duration, usage string) cli.DurationFlag {
|
|
||||||
return cli.DurationFlag{
|
|
||||||
Name: name,
|
|
||||||
Value: value,
|
|
||||||
Usage: usage,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
108
cmd/dump.go
108
cmd/dump.go
@@ -1,4 +1,5 @@
|
|||||||
// Copyright 2014 The Gogs 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
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
@@ -6,30 +7,41 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"io/ioutil"
|
"code.gitea.io/gitea/models"
|
||||||
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"github.com/Unknwon/cae/zip"
|
"github.com/Unknwon/cae/zip"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
|
|
||||||
"github.com/gogits/gogs/models"
|
|
||||||
"github.com/gogits/gogs/modules/setting"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// CmdDump represents the available dump sub-command.
|
||||||
var CmdDump = cli.Command{
|
var CmdDump = cli.Command{
|
||||||
Name: "dump",
|
Name: "dump",
|
||||||
Usage: "Dump Gogs files and database",
|
Usage: "Dump Gitea files and database",
|
||||||
Description: `Dump compresses all related files and database into zip file.
|
Description: `Dump compresses all related files and database into zip file.
|
||||||
It can be used for backup and capture Gogs server image to send to maintainer`,
|
It can be used for backup and capture Gitea server image to send to maintainer`,
|
||||||
Action: runDump,
|
Action: runDump,
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
stringFlag("config, c", "custom/conf/app.ini", "Custom configuration file path"),
|
cli.StringFlag{
|
||||||
boolFlag("verbose, v", "Show process details"),
|
Name: "config, c",
|
||||||
stringFlag("tempdir, t", os.TempDir(), "Temporary dir path"),
|
Value: "custom/conf/app.ini",
|
||||||
|
Usage: "Custom configuration file path",
|
||||||
|
},
|
||||||
|
cli.BoolFlag{
|
||||||
|
Name: "verbose, v",
|
||||||
|
Usage: "Show process details",
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "tempdir, t",
|
||||||
|
Value: os.TempDir(),
|
||||||
|
Usage: "Temporary dir path",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,6 +50,7 @@ func runDump(ctx *cli.Context) error {
|
|||||||
setting.CustomConf = ctx.String("config")
|
setting.CustomConf = ctx.String("config")
|
||||||
}
|
}
|
||||||
setting.NewContext()
|
setting.NewContext()
|
||||||
|
setting.NewServices() // cannot access session settings otherwise
|
||||||
models.LoadConfigs()
|
models.LoadConfigs()
|
||||||
models.SetEngine()
|
models.SetEngine()
|
||||||
|
|
||||||
@@ -45,14 +58,14 @@ func runDump(ctx *cli.Context) error {
|
|||||||
if _, err := os.Stat(tmpDir); os.IsNotExist(err) {
|
if _, err := os.Stat(tmpDir); os.IsNotExist(err) {
|
||||||
log.Fatalf("Path does not exist: %s", tmpDir)
|
log.Fatalf("Path does not exist: %s", tmpDir)
|
||||||
}
|
}
|
||||||
TmpWorkDir, err := ioutil.TempDir(tmpDir, "gogs-dump-")
|
TmpWorkDir, err := ioutil.TempDir(tmpDir, "gitea-dump-")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Fail to create tmp work directory: %v", err)
|
log.Fatalf("Fail to create tmp work directory: %v", err)
|
||||||
}
|
}
|
||||||
log.Printf("Creating tmp work dir: %s", TmpWorkDir)
|
log.Printf("Creating tmp work dir: %s", TmpWorkDir)
|
||||||
|
|
||||||
reposDump := path.Join(TmpWorkDir, "gogs-repo.zip")
|
reposDump := path.Join(TmpWorkDir, "gitea-repo.zip")
|
||||||
dbDump := path.Join(TmpWorkDir, "gogs-db.sql")
|
dbDump := path.Join(TmpWorkDir, "gitea-db.sql")
|
||||||
|
|
||||||
log.Printf("Dumping local repositories...%s", setting.RepoRootPath)
|
log.Printf("Dumping local repositories...%s", setting.RepoRootPath)
|
||||||
zip.Verbose = ctx.Bool("verbose")
|
zip.Verbose = ctx.Bool("verbose")
|
||||||
@@ -65,19 +78,18 @@ func runDump(ctx *cli.Context) error {
|
|||||||
log.Fatalf("Fail to dump database: %v", err)
|
log.Fatalf("Fail to dump database: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fileName := fmt.Sprintf("gogs-dump-%d.zip", time.Now().Unix())
|
fileName := fmt.Sprintf("gitea-dump-%d.zip", time.Now().Unix())
|
||||||
log.Printf("Packing dump files...")
|
log.Printf("Packing dump files...")
|
||||||
z, err := zip.Create(fileName)
|
z, err := zip.Create(fileName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
os.Remove(fileName)
|
|
||||||
log.Fatalf("Fail to create %s: %v", fileName, err)
|
log.Fatalf("Fail to create %s: %v", fileName, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := z.AddFile("gogs-repo.zip", reposDump); err != nil {
|
if err := z.AddFile("gitea-repo.zip", reposDump); err != nil {
|
||||||
log.Fatalf("Fail to include gogs-repo.zip: %v", err)
|
log.Fatalf("Fail to include gitea-repo.zip: %v", err)
|
||||||
}
|
}
|
||||||
if err := z.AddFile("gogs-db.sql", dbDump); err != nil {
|
if err := z.AddFile("gitea-db.sql", dbDump); err != nil {
|
||||||
log.Fatalf("Fail to include gogs-db.sql: %v", err)
|
log.Fatalf("Fail to include gitea-db.sql: %v", err)
|
||||||
}
|
}
|
||||||
customDir, err := os.Stat(setting.CustomPath)
|
customDir, err := os.Stat(setting.CustomPath)
|
||||||
if err == nil && customDir.IsDir() {
|
if err == nil && customDir.IsDir() {
|
||||||
@@ -87,12 +99,26 @@ func runDump(ctx *cli.Context) error {
|
|||||||
} else {
|
} else {
|
||||||
log.Printf("Custom dir %s doesn't exist, skipped", setting.CustomPath)
|
log.Printf("Custom dir %s doesn't exist, skipped", setting.CustomPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Printf("Packing data directory...%s", setting.AppDataPath)
|
||||||
|
var sessionAbsPath string
|
||||||
|
if setting.SessionConfig.Provider == "file" {
|
||||||
|
if len(setting.SessionConfig.ProviderConfig) == 0 {
|
||||||
|
setting.SessionConfig.ProviderConfig = "data/sessions"
|
||||||
|
}
|
||||||
|
sessionAbsPath, _ = filepath.Abs(setting.SessionConfig.ProviderConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := zipAddDirectoryExclude(z, "data", setting.AppDataPath, sessionAbsPath); err != nil {
|
||||||
|
log.Fatalf("Fail to include data directory: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
if err := z.AddDir("log", setting.LogRootPath); err != nil {
|
if err := z.AddDir("log", setting.LogRootPath); err != nil {
|
||||||
log.Fatalf("Fail to include log: %v", err)
|
log.Fatalf("Fail to include log: %v", err)
|
||||||
}
|
}
|
||||||
// FIXME: SSH key file.
|
// FIXME: SSH key file.
|
||||||
if err = z.Close(); err != nil {
|
if err = z.Close(); err != nil {
|
||||||
os.Remove(fileName)
|
_ = os.Remove(fileName)
|
||||||
log.Fatalf("Fail to save %s: %v", fileName, err)
|
log.Fatalf("Fail to save %s: %v", fileName, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,8 +127,48 @@ func runDump(ctx *cli.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("Removing tmp work dir: %s", TmpWorkDir)
|
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)
|
log.Printf("Finish dumping in file %s", fileName)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// zipAddDirectoryExclude zips absPath to specified zipPath inside z excluding excludeAbsPath
|
||||||
|
func zipAddDirectoryExclude(zip *zip.ZipArchive, zipPath, absPath string, excludeAbsPath string) error {
|
||||||
|
absPath, err := filepath.Abs(absPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
dir, err := os.Open(absPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer dir.Close()
|
||||||
|
|
||||||
|
zip.AddEmptyDir(zipPath)
|
||||||
|
|
||||||
|
files, err := dir.Readdir(0)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, file := range files {
|
||||||
|
currentAbsPath := path.Join(absPath, file.Name())
|
||||||
|
currentZipPath := path.Join(zipPath, file.Name())
|
||||||
|
if file.IsDir() {
|
||||||
|
if currentAbsPath != excludeAbsPath {
|
||||||
|
if err = zipAddDirectoryExclude(zip, currentZipPath, currentAbsPath, excludeAbsPath); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if err = zip.AddFile(currentZipPath, currentAbsPath); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
61
cmd/serve.go
61
cmd/serve.go
@@ -1,4 +1,5 @@
|
|||||||
// Copyright 2014 The Gogs 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
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
@@ -13,29 +14,33 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"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"
|
"github.com/Unknwon/com"
|
||||||
git "github.com/gogits/git-module"
|
|
||||||
gouuid "github.com/satori/go.uuid"
|
gouuid "github.com/satori/go.uuid"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
|
|
||||||
"github.com/gogits/gogs/models"
|
|
||||||
"github.com/gogits/gogs/modules/base"
|
|
||||||
"github.com/gogits/gogs/modules/httplib"
|
|
||||||
"github.com/gogits/gogs/modules/log"
|
|
||||||
"github.com/gogits/gogs/modules/setting"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
_ACCESS_DENIED_MESSAGE = "Repository does not exist or you do not have access"
|
accessDenied = "Repository does not exist or you do not have access"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// CmdServ represents the available serv sub-command.
|
||||||
var CmdServ = cli.Command{
|
var CmdServ = cli.Command{
|
||||||
Name: "serv",
|
Name: "serv",
|
||||||
Usage: "This command should only be called by SSH shell",
|
Usage: "This command should only be called by SSH shell",
|
||||||
Description: `Serv provide access auth for repositories`,
|
Description: `Serv provide access auth for repositories`,
|
||||||
Action: runServ,
|
Action: runServ,
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
stringFlag("config, c", "custom/conf/app.ini", "Custom configuration file path"),
|
cli.StringFlag{
|
||||||
|
Name: "config, c",
|
||||||
|
Value: "custom/conf/app.ini",
|
||||||
|
Usage: "Custom configuration file path",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,7 +52,9 @@ func setup(logPath string) {
|
|||||||
|
|
||||||
if setting.UseSQLite3 || setting.UseTiDB {
|
if setting.UseSQLite3 || setting.UseTiDB {
|
||||||
workDir, _ := setting.WorkDir()
|
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()
|
models.SetEngine()
|
||||||
@@ -63,14 +70,14 @@ func parseCmd(cmd string) (string, string) {
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
allowedCommands = map[string]models.AccessMode{
|
allowedCommands = map[string]models.AccessMode{
|
||||||
"git-upload-pack": models.ACCESS_MODE_READ,
|
"git-upload-pack": models.AccessModeRead,
|
||||||
"git-upload-archive": models.ACCESS_MODE_READ,
|
"git-upload-archive": models.AccessModeRead,
|
||||||
"git-receive-pack": models.ACCESS_MODE_WRITE,
|
"git-receive-pack": models.AccessModeWrite,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func fail(userMessage, logMessage string, args ...interface{}) {
|
func fail(userMessage, logMessage string, args ...interface{}) {
|
||||||
fmt.Fprintln(os.Stderr, "Gogs:", userMessage)
|
fmt.Fprintln(os.Stderr, "Gitea:", userMessage)
|
||||||
|
|
||||||
if len(logMessage) > 0 {
|
if len(logMessage) > 0 {
|
||||||
if !setting.ProdMode {
|
if !setting.ProdMode {
|
||||||
@@ -114,7 +121,7 @@ func handleUpdateTask(uuid string, user, repoUser *models.User, reponame string,
|
|||||||
|
|
||||||
// Ask for running deliver hook and test pull request tasks.
|
// Ask for running deliver hook and test pull request tasks.
|
||||||
reqURL := setting.LocalURL + repoUser.Name + "/" + reponame + "/tasks/trigger?branch=" +
|
reqURL := setting.LocalURL + repoUser.Name + "/" + reponame + "/tasks/trigger?branch=" +
|
||||||
strings.TrimPrefix(task.RefName, git.BRANCH_PREFIX) + "&secret=" + base.EncodeMD5(repoUser.Salt) + "&pusher=" + com.ToStr(user.ID)
|
strings.TrimPrefix(task.RefName, git.BranchPrefix) + "&secret=" + base.EncodeMD5(repoUser.Salt) + "&pusher=" + com.ToStr(user.ID)
|
||||||
log.GitLogger.Trace("Trigger task: %s", reqURL)
|
log.GitLogger.Trace("Trigger task: %s", reqURL)
|
||||||
|
|
||||||
resp, err := httplib.Head(reqURL).SetTLSClientConfig(&tls.Config{
|
resp, err := httplib.Head(reqURL).SetTLSClientConfig(&tls.Config{
|
||||||
@@ -138,7 +145,7 @@ func runServ(c *cli.Context) error {
|
|||||||
setup("serv.log")
|
setup("serv.log")
|
||||||
|
|
||||||
if setting.SSH.Disabled {
|
if setting.SSH.Disabled {
|
||||||
println("Gogs: SSH has been disabled")
|
println("Gitea: SSH has been disabled")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,8 +155,8 @@ func runServ(c *cli.Context) error {
|
|||||||
|
|
||||||
cmd := os.Getenv("SSH_ORIGINAL_COMMAND")
|
cmd := os.Getenv("SSH_ORIGINAL_COMMAND")
|
||||||
if len(cmd) == 0 {
|
if len(cmd) == 0 {
|
||||||
println("Hi there, You've successfully authenticated, but Gogs does not provide shell access.")
|
println("Hi there, You've successfully authenticated, but Gitea does not provide shell access.")
|
||||||
println("If this is unexpected, please log in with password and setup Gogs under another user.")
|
println("If this is unexpected, please log in with password and setup Gitea under another user.")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,7 +186,7 @@ func runServ(c *cli.Context) error {
|
|||||||
repo, err := models.GetRepositoryByName(repoUser.ID, reponame)
|
repo, err := models.GetRepositoryByName(repoUser.ID, reponame)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if models.IsErrRepoNotExist(err) {
|
if models.IsErrRepoNotExist(err) {
|
||||||
fail(_ACCESS_DENIED_MESSAGE, "Repository does not exist: %s/%s", repoUser.Name, reponame)
|
fail(accessDenied, "Repository does not exist: %s/%s", repoUser.Name, reponame)
|
||||||
}
|
}
|
||||||
fail("Internal error", "Failed to get repository: %v", err)
|
fail("Internal error", "Failed to get repository: %v", err)
|
||||||
}
|
}
|
||||||
@@ -190,7 +197,7 @@ func runServ(c *cli.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Prohibit push to mirror repositories.
|
// Prohibit push to mirror repositories.
|
||||||
if requestedMode > models.ACCESS_MODE_READ && repo.IsMirror {
|
if requestedMode > models.AccessModeRead && repo.IsMirror {
|
||||||
fail("mirror repository is read-only", "")
|
fail("mirror repository is read-only", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,7 +206,7 @@ func runServ(c *cli.Context) error {
|
|||||||
keyID int64
|
keyID int64
|
||||||
user *models.User
|
user *models.User
|
||||||
)
|
)
|
||||||
if requestedMode == models.ACCESS_MODE_WRITE || repo.IsPrivate {
|
if requestedMode == models.AccessModeWrite || repo.IsPrivate {
|
||||||
keys := strings.Split(c.Args()[0], "-")
|
keys := strings.Split(c.Args()[0], "-")
|
||||||
if len(keys) != 2 {
|
if len(keys) != 2 {
|
||||||
fail("Key ID format error", "Invalid key argument: %s", c.Args()[0])
|
fail("Key ID format error", "Invalid key argument: %s", c.Args()[0])
|
||||||
@@ -212,7 +219,7 @@ func runServ(c *cli.Context) error {
|
|||||||
keyID = key.ID
|
keyID = key.ID
|
||||||
|
|
||||||
// Check deploy key or user key.
|
// Check deploy key or user key.
|
||||||
if key.Type == models.KEY_TYPE_DEPLOY {
|
if key.Type == models.KeyTypeDeploy {
|
||||||
if key.Mode < requestedMode {
|
if key.Mode < requestedMode {
|
||||||
fail("Key permission denied", "Cannot push with deployment key: %d", key.ID)
|
fail("Key permission denied", "Cannot push with deployment key: %d", key.ID)
|
||||||
}
|
}
|
||||||
@@ -241,18 +248,22 @@ func runServ(c *cli.Context) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
fail("Internal error", "Fail to check access: %v", err)
|
fail("Internal error", "Fail to check access: %v", err)
|
||||||
} else if mode < requestedMode {
|
} else if mode < requestedMode {
|
||||||
clientMessage := _ACCESS_DENIED_MESSAGE
|
clientMessage := accessDenied
|
||||||
if mode >= models.ACCESS_MODE_READ {
|
if mode >= models.AccessModeRead {
|
||||||
clientMessage = "You do not have sufficient authorization for this action"
|
clientMessage = "You do not have sufficient authorization for this action"
|
||||||
}
|
}
|
||||||
fail(clientMessage,
|
fail(clientMessage,
|
||||||
"User %s does not have level %v access to repository %s",
|
"User %s does not have level %v access to repository %s",
|
||||||
user.Name, requestedMode, repoPath)
|
user.Name, requestedMode, repoPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
os.Setenv("GITEA_PUSHER_NAME", user.Name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uuid := gouuid.NewV4().String()
|
uuid := gouuid.NewV4().String()
|
||||||
|
os.Setenv("GITEA_UUID", uuid)
|
||||||
|
// Keep the old env variable name for backward compability
|
||||||
os.Setenv("uuid", uuid)
|
os.Setenv("uuid", uuid)
|
||||||
|
|
||||||
// Special handle for Windows.
|
// Special handle for Windows.
|
||||||
@@ -275,7 +286,7 @@ func runServ(c *cli.Context) error {
|
|||||||
fail("Internal error", "Failed to execute git command: %v", err)
|
fail("Internal error", "Failed to execute git command: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if requestedMode == models.ACCESS_MODE_WRITE {
|
if requestedMode == models.AccessModeWrite {
|
||||||
handleUpdateTask(uuid, user, repoUser, reponame, isWiki)
|
handleUpdateTask(uuid, user, repoUser, reponame, isWiki)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,18 +9,23 @@ import (
|
|||||||
|
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
|
|
||||||
"github.com/gogits/gogs/models"
|
"code.gitea.io/gitea/models"
|
||||||
"github.com/gogits/gogs/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"github.com/gogits/gogs/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// CmdUpdate represents the available update sub-command.
|
||||||
var CmdUpdate = cli.Command{
|
var CmdUpdate = cli.Command{
|
||||||
Name: "update",
|
Name: "update",
|
||||||
Usage: "This command should only be called by Git hook",
|
Usage: "This command should only be called by Git hook",
|
||||||
Description: `Update get pushed info and insert into database`,
|
Description: `Update get pushed info and insert into database`,
|
||||||
Action: runUpdate,
|
Action: runUpdate,
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
stringFlag("config, c", "custom/conf/app.ini", "Custom configuration file path"),
|
cli.StringFlag{
|
||||||
|
Name: "config, c",
|
||||||
|
Value: "custom/conf/app.ini",
|
||||||
|
Usage: "Custom configuration file path",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,7 +49,7 @@ func runUpdate(c *cli.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
task := models.UpdateTask{
|
task := models.UpdateTask{
|
||||||
UUID: os.Getenv("uuid"),
|
UUID: os.Getenv("GITEA_UUID"),
|
||||||
RefName: args[0],
|
RefName: args[0],
|
||||||
OldCommitID: args[1],
|
OldCommitID: args[1],
|
||||||
NewCommitID: args[2],
|
NewCommitID: args[2],
|
||||||
|
|||||||
190
cmd/web.go
190
cmd/web.go
@@ -7,7 +7,6 @@ package cmd
|
|||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/fcgi"
|
"net/http/fcgi"
|
||||||
@@ -15,6 +14,21 @@ import (
|
|||||||
"path"
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models"
|
||||||
|
"code.gitea.io/gitea/modules/auth"
|
||||||
|
"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/templates"
|
||||||
|
"code.gitea.io/gitea/routers"
|
||||||
|
"code.gitea.io/gitea/routers/admin"
|
||||||
|
apiv1 "code.gitea.io/gitea/routers/api/v1"
|
||||||
|
"code.gitea.io/gitea/routers/dev"
|
||||||
|
"code.gitea.io/gitea/routers/org"
|
||||||
|
"code.gitea.io/gitea/routers/repo"
|
||||||
|
"code.gitea.io/gitea/routers/user"
|
||||||
"github.com/go-macaron/binding"
|
"github.com/go-macaron/binding"
|
||||||
"github.com/go-macaron/cache"
|
"github.com/go-macaron/cache"
|
||||||
"github.com/go-macaron/captcha"
|
"github.com/go-macaron/captcha"
|
||||||
@@ -23,89 +37,38 @@ import (
|
|||||||
"github.com/go-macaron/i18n"
|
"github.com/go-macaron/i18n"
|
||||||
"github.com/go-macaron/session"
|
"github.com/go-macaron/session"
|
||||||
"github.com/go-macaron/toolbox"
|
"github.com/go-macaron/toolbox"
|
||||||
"github.com/go-xorm/xorm"
|
|
||||||
"github.com/mcuadros/go-version"
|
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
"gopkg.in/ini.v1"
|
macaron "gopkg.in/macaron.v1"
|
||||||
"gopkg.in/macaron.v1"
|
|
||||||
|
|
||||||
"github.com/gogits/git-module"
|
|
||||||
"github.com/gogits/go-gogs-client"
|
|
||||||
|
|
||||||
"github.com/gogits/gogs/models"
|
|
||||||
"github.com/gogits/gogs/modules/auth"
|
|
||||||
"github.com/gogits/gogs/modules/bindata"
|
|
||||||
"github.com/gogits/gogs/modules/context"
|
|
||||||
"github.com/gogits/gogs/modules/log"
|
|
||||||
"github.com/gogits/gogs/modules/setting"
|
|
||||||
"github.com/gogits/gogs/modules/template"
|
|
||||||
"github.com/gogits/gogs/routers"
|
|
||||||
"github.com/gogits/gogs/routers/admin"
|
|
||||||
apiv1 "github.com/gogits/gogs/routers/api/v1"
|
|
||||||
"github.com/gogits/gogs/routers/dev"
|
|
||||||
"github.com/gogits/gogs/routers/org"
|
|
||||||
"github.com/gogits/gogs/routers/repo"
|
|
||||||
"github.com/gogits/gogs/routers/user"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// CmdWeb represents the available web sub-command.
|
||||||
var CmdWeb = cli.Command{
|
var CmdWeb = cli.Command{
|
||||||
Name: "web",
|
Name: "web",
|
||||||
Usage: "Start Gogs web server",
|
Usage: "Start Gitea web server",
|
||||||
Description: `Gogs web server is the only thing you need to run,
|
Description: `Gitea web server is the only thing you need to run,
|
||||||
and it takes care of all the other things for you`,
|
and it takes care of all the other things for you`,
|
||||||
Action: runWeb,
|
Action: runWeb,
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
stringFlag("port, p", "3000", "Temporary port number to prevent conflict"),
|
cli.StringFlag{
|
||||||
stringFlag("config, c", "custom/conf/app.ini", "Custom configuration file path"),
|
Name: "port, p",
|
||||||
|
Value: "3000",
|
||||||
|
Usage: "Temporary port number to prevent conflict",
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "config, c",
|
||||||
|
Value: "custom/conf/app.ini",
|
||||||
|
Usage: "Custom configuration file path",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// VerChecker is a listing of required dependency versions.
|
||||||
type VerChecker struct {
|
type VerChecker struct {
|
||||||
ImportPath string
|
ImportPath string
|
||||||
Version func() string
|
Version func() string
|
||||||
Expected string
|
Expected string
|
||||||
}
|
}
|
||||||
|
|
||||||
// checkVersion checks if binary matches the version of templates files.
|
|
||||||
func checkVersion() {
|
|
||||||
// Templates.
|
|
||||||
data, err := ioutil.ReadFile(setting.StaticRootPath + "/templates/.VERSION")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(4, "Fail to read 'templates/.VERSION': %v", err)
|
|
||||||
}
|
|
||||||
tplVer := string(data)
|
|
||||||
if tplVer != setting.AppVer {
|
|
||||||
if version.Compare(tplVer, setting.AppVer, ">") {
|
|
||||||
log.Fatal(4, "Binary version is lower than template file version, did you forget to recompile Gogs?")
|
|
||||||
} else {
|
|
||||||
log.Fatal(4, "Binary version is higher than template file version, did you forget to update template files?")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check dependency version.
|
|
||||||
checkers := []VerChecker{
|
|
||||||
{"github.com/go-xorm/xorm", func() string { return xorm.Version }, "0.5.5"},
|
|
||||||
{"github.com/go-macaron/binding", binding.Version, "0.3.2"},
|
|
||||||
{"github.com/go-macaron/cache", cache.Version, "0.1.2"},
|
|
||||||
{"github.com/go-macaron/csrf", csrf.Version, "0.1.0"},
|
|
||||||
{"github.com/go-macaron/i18n", i18n.Version, "0.3.0"},
|
|
||||||
{"github.com/go-macaron/session", session.Version, "0.1.6"},
|
|
||||||
{"github.com/go-macaron/toolbox", toolbox.Version, "0.1.0"},
|
|
||||||
{"gopkg.in/ini.v1", ini.Version, "1.8.4"},
|
|
||||||
{"gopkg.in/macaron.v1", macaron.Version, "1.1.7"},
|
|
||||||
{"github.com/gogits/git-module", git.Version, "0.4.1"},
|
|
||||||
{"github.com/gogits/go-gogs-client", gogs.Version, "0.12.1"},
|
|
||||||
}
|
|
||||||
for _, c := range checkers {
|
|
||||||
if !version.Compare(c.Version(), c.Expected, ">=") {
|
|
||||||
log.Fatal(4, `Dependency outdated!
|
|
||||||
Package '%s' current version (%s) is below requirement (%s),
|
|
||||||
please use following command to update this package and recompile Gogs:
|
|
||||||
go get -u %[1]s`, c.ImportPath, c.Version(), c.Expected)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// newMacaron initializes Macaron instance.
|
// newMacaron initializes Macaron instance.
|
||||||
func newMacaron() *macaron.Macaron {
|
func newMacaron() *macaron.Macaron {
|
||||||
m := macaron.New()
|
m := macaron.New()
|
||||||
@@ -117,11 +80,11 @@ func newMacaron() *macaron.Macaron {
|
|||||||
m.Use(gzip.Gziper())
|
m.Use(gzip.Gziper())
|
||||||
}
|
}
|
||||||
if setting.Protocol == setting.FCGI {
|
if setting.Protocol == setting.FCGI {
|
||||||
m.SetURLPrefix(setting.AppSubUrl)
|
m.SetURLPrefix(setting.AppSubURL)
|
||||||
}
|
}
|
||||||
m.Use(macaron.Static(
|
m.Use(public.Static(
|
||||||
path.Join(setting.StaticRootPath, "public"),
|
&public.Options{
|
||||||
macaron.StaticOptions{
|
Directory: path.Join(setting.StaticRootPath, "public"),
|
||||||
SkipLogging: setting.DisableRouterLog,
|
SkipLogging: setting.DisableRouterLog,
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
@@ -133,32 +96,32 @@ func newMacaron() *macaron.Macaron {
|
|||||||
},
|
},
|
||||||
))
|
))
|
||||||
|
|
||||||
funcMap := template.NewFuncMap()
|
m.Use(templates.Renderer())
|
||||||
m.Use(macaron.Renderer(macaron.RenderOptions{
|
models.InitMailRender(templates.Mailer())
|
||||||
Directory: path.Join(setting.StaticRootPath, "templates"),
|
|
||||||
AppendDirectories: []string{path.Join(setting.CustomPath, "templates")},
|
localeNames, err := options.Dir("locale")
|
||||||
Funcs: funcMap,
|
|
||||||
IndentJSON: macaron.Env != macaron.PROD,
|
|
||||||
}))
|
|
||||||
models.InitMailRender(path.Join(setting.StaticRootPath, "templates/mail"),
|
|
||||||
path.Join(setting.CustomPath, "templates/mail"), funcMap)
|
|
||||||
|
|
||||||
localeNames, err := bindata.AssetDir("conf/locale")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(4, "Fail to list locale files: %v", err)
|
log.Fatal(4, "Fail to list locale files: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
localFiles := make(map[string][]byte)
|
localFiles := make(map[string][]byte)
|
||||||
|
|
||||||
for _, name := range localeNames {
|
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{
|
m.Use(i18n.I18n(i18n.Options{
|
||||||
SubURL: setting.AppSubUrl,
|
SubURL: setting.AppSubURL,
|
||||||
Files: localFiles,
|
Files: localFiles,
|
||||||
CustomDirectory: path.Join(setting.CustomPath, "conf/locale"),
|
Langs: setting.Langs,
|
||||||
Langs: setting.Langs,
|
Names: setting.Names,
|
||||||
Names: setting.Names,
|
DefaultLang: "en-US",
|
||||||
DefaultLang: "en-US",
|
Redirect: true,
|
||||||
Redirect: true,
|
|
||||||
}))
|
}))
|
||||||
m.Use(cache.Cacher(cache.Options{
|
m.Use(cache.Cacher(cache.Options{
|
||||||
Adapter: setting.CacheAdapter,
|
Adapter: setting.CacheAdapter,
|
||||||
@@ -166,7 +129,7 @@ func newMacaron() *macaron.Macaron {
|
|||||||
Interval: setting.CacheInterval,
|
Interval: setting.CacheInterval,
|
||||||
}))
|
}))
|
||||||
m.Use(captcha.Captchaer(captcha.Options{
|
m.Use(captcha.Captchaer(captcha.Options{
|
||||||
SubURL: setting.AppSubUrl,
|
SubURL: setting.AppSubURL,
|
||||||
}))
|
}))
|
||||||
m.Use(session.Sessioner(setting.SessionConfig))
|
m.Use(session.Sessioner(setting.SessionConfig))
|
||||||
m.Use(csrf.Csrfer(csrf.Options{
|
m.Use(csrf.Csrfer(csrf.Options{
|
||||||
@@ -174,11 +137,11 @@ func newMacaron() *macaron.Macaron {
|
|||||||
Cookie: setting.CSRFCookieName,
|
Cookie: setting.CSRFCookieName,
|
||||||
SetCookie: true,
|
SetCookie: true,
|
||||||
Header: "X-Csrf-Token",
|
Header: "X-Csrf-Token",
|
||||||
CookiePath: setting.AppSubUrl,
|
CookiePath: setting.AppSubURL,
|
||||||
}))
|
}))
|
||||||
m.Use(toolbox.Toolboxer(m, toolbox.Options{
|
m.Use(toolbox.Toolboxer(m, toolbox.Options{
|
||||||
HealthCheckFuncs: []*toolbox.HealthCheckFuncDesc{
|
HealthCheckFuncs: []*toolbox.HealthCheckFuncDesc{
|
||||||
&toolbox.HealthCheckFuncDesc{
|
{
|
||||||
Desc: "Database connection",
|
Desc: "Database connection",
|
||||||
Func: models.Ping,
|
Func: models.Ping,
|
||||||
},
|
},
|
||||||
@@ -193,7 +156,6 @@ func runWeb(ctx *cli.Context) error {
|
|||||||
setting.CustomConf = ctx.String("config")
|
setting.CustomConf = ctx.String("config")
|
||||||
}
|
}
|
||||||
routers.GlobalInit()
|
routers.GlobalInit()
|
||||||
checkVersion()
|
|
||||||
|
|
||||||
m := newMacaron()
|
m := newMacaron()
|
||||||
|
|
||||||
@@ -210,7 +172,7 @@ func runWeb(ctx *cli.Context) error {
|
|||||||
m.Get("/", ignSignIn, routers.Home)
|
m.Get("/", ignSignIn, routers.Home)
|
||||||
m.Group("/explore", func() {
|
m.Group("/explore", func() {
|
||||||
m.Get("", func(ctx *context.Context) {
|
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("/repos", routers.ExploreRepos)
|
||||||
m.Get("/users", routers.ExploreUsers)
|
m.Get("/users", routers.ExploreUsers)
|
||||||
@@ -274,7 +236,7 @@ func runWeb(ctx *cli.Context) error {
|
|||||||
|
|
||||||
m.Group("/users", func() {
|
m.Group("/users", func() {
|
||||||
m.Get("", admin.Users)
|
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.Combo("/:userid").Get(admin.EditUser).Post(bindIgnErr(auth.AdminEditUserForm{}), admin.EditUserPost)
|
||||||
m.Post("/:userid/delete", admin.DeleteUser)
|
m.Post("/:userid/delete", admin.DeleteUser)
|
||||||
})
|
})
|
||||||
@@ -330,11 +292,7 @@ func runWeb(ctx *cli.Context) error {
|
|||||||
}
|
}
|
||||||
defer fr.Close()
|
defer fr.Close()
|
||||||
|
|
||||||
ctx.Header().Set("Cache-Control", "public,max-age=86400")
|
if err = repo.ServeData(ctx, attach.Name, fr); err != nil {
|
||||||
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 {
|
|
||||||
ctx.Handle(500, "ServeData", err)
|
ctx.Handle(500, "ServeData", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -518,7 +476,8 @@ func runWeb(ctx *cli.Context) error {
|
|||||||
ctx.Data["CommitsCount"] = ctx.Repo.CommitsCount
|
ctx.Data["CommitsCount"] = ctx.Repo.CommitsCount
|
||||||
})
|
})
|
||||||
|
|
||||||
m.Combo("/compare/*", repo.MustAllowPulls).Get(repo.CompareAndPullRequest).
|
m.Combo("/compare/*", repo.MustAllowPulls, repo.SetEditorconfigIfExists).
|
||||||
|
Get(repo.CompareAndPullRequest).
|
||||||
Post(bindIgnErr(auth.CreateIssueForm{}), repo.CompareAndPullRequestPost)
|
Post(bindIgnErr(auth.CreateIssueForm{}), repo.CompareAndPullRequestPost)
|
||||||
|
|
||||||
m.Group("", func() {
|
m.Group("", func() {
|
||||||
@@ -577,20 +536,20 @@ func runWeb(ctx *cli.Context) error {
|
|||||||
|
|
||||||
m.Group("/pulls/:index", func() {
|
m.Group("/pulls/:index", func() {
|
||||||
m.Get("/commits", context.RepoRef(), repo.ViewPullCommits)
|
m.Get("/commits", context.RepoRef(), repo.ViewPullCommits)
|
||||||
m.Get("/files", context.RepoRef(), repo.ViewPullFiles)
|
m.Get("/files", context.RepoRef(), repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.ViewPullFiles)
|
||||||
m.Post("/merge", reqRepoWriter, repo.MergePullRequest)
|
m.Post("/merge", reqRepoWriter, repo.MergePullRequest)
|
||||||
}, repo.MustAllowPulls)
|
}, repo.MustAllowPulls)
|
||||||
|
|
||||||
m.Group("", func() {
|
m.Group("", func() {
|
||||||
m.Get("/src/*", repo.Home)
|
m.Get("/src/*", repo.SetEditorconfigIfExists, repo.Home)
|
||||||
m.Get("/raw/*", repo.SingleDownload)
|
m.Get("/raw/*", repo.SingleDownload)
|
||||||
m.Get("/commits/*", repo.RefCommits)
|
m.Get("/commits/*", repo.RefCommits)
|
||||||
m.Get("/commit/:sha([a-z0-9]{7,40})$", repo.Diff)
|
m.Get("/commit/:sha([a-f0-9]{7,40})$", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.Diff)
|
||||||
m.Get("/forks", repo.Forks)
|
m.Get("/forks", repo.Forks)
|
||||||
}, context.RepoRef())
|
}, context.RepoRef())
|
||||||
m.Get("/commit/:sha([a-z0-9]{7,40})\\.:ext(patch|diff)", repo.RawDiff)
|
m.Get("/commit/:sha([a-f0-9]{7,40})\\.:ext(patch|diff)", repo.RawDiff)
|
||||||
|
|
||||||
m.Get("/compare/:before([a-z0-9]{7,40})\\.\\.\\.:after([a-z0-9]{7,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)
|
}, ignSignIn, context.RepoAssignment(), repo.MustBeNotBare)
|
||||||
m.Group("/:username/:reponame", func() {
|
m.Group("/:username/:reponame", func() {
|
||||||
m.Get("/stars", repo.Stars)
|
m.Get("/stars", repo.Stars)
|
||||||
@@ -599,8 +558,8 @@ func runWeb(ctx *cli.Context) error {
|
|||||||
|
|
||||||
m.Group("/:username", func() {
|
m.Group("/:username", func() {
|
||||||
m.Group("/:reponame", func() {
|
m.Group("/:reponame", func() {
|
||||||
m.Get("", repo.Home)
|
m.Get("", repo.SetEditorconfigIfExists, repo.Home)
|
||||||
m.Get("\\.git$", repo.Home)
|
m.Get("\\.git$", repo.SetEditorconfigIfExists, repo.Home)
|
||||||
}, ignSignIn, context.RepoAssignment(true), context.RepoRef())
|
}, ignSignIn, context.RepoAssignment(true), context.RepoRef())
|
||||||
|
|
||||||
m.Group("/:reponame", func() {
|
m.Group("/:reponame", func() {
|
||||||
@@ -628,17 +587,17 @@ func runWeb(ctx *cli.Context) error {
|
|||||||
|
|
||||||
// Flag for port number in case first time run conflict.
|
// Flag for port number in case first time run conflict.
|
||||||
if ctx.IsSet("port") {
|
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")
|
setting.HTTPPort = ctx.String("port")
|
||||||
}
|
}
|
||||||
|
|
||||||
var listenAddr string
|
var listenAddr string
|
||||||
if setting.Protocol == setting.UNIX_SOCKET {
|
if setting.Protocol == setting.UnixSocket {
|
||||||
listenAddr = fmt.Sprintf("%s", setting.HTTPAddr)
|
listenAddr = fmt.Sprintf("%s", setting.HTTPAddr)
|
||||||
} else {
|
} else {
|
||||||
listenAddr = fmt.Sprintf("%s:%s", setting.HTTPAddr, setting.HTTPPort)
|
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
|
var err error
|
||||||
switch setting.Protocol {
|
switch setting.Protocol {
|
||||||
@@ -649,11 +608,12 @@ func runWeb(ctx *cli.Context) error {
|
|||||||
err = server.ListenAndServeTLS(setting.CertFile, setting.KeyFile)
|
err = server.ListenAndServeTLS(setting.CertFile, setting.KeyFile)
|
||||||
case setting.FCGI:
|
case setting.FCGI:
|
||||||
err = fcgi.Serve(nil, m)
|
err = fcgi.Serve(nil, m)
|
||||||
case setting.UNIX_SOCKET:
|
case setting.UnixSocket:
|
||||||
os.Remove(listenAddr)
|
if err := os.Remove(listenAddr); err != nil {
|
||||||
|
log.Fatal(4, "Fail to remove unix socket directory %s: %v", listenAddr, err)
|
||||||
|
}
|
||||||
var listener *net.UnixListener
|
var listener *net.UnixListener
|
||||||
listener, err = net.ListenUnix("unix", &net.UnixAddr{listenAddr, "unix"})
|
listener, err = net.ListenUnix("unix", &net.UnixAddr{Name: listenAddr, Net: "unix"})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
break // Handle error after switch
|
break // Handle error after switch
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
Execute following command in ROOT directory when anything is changed:
|
|
||||||
|
|
||||||
$ make bindata
|
|
||||||
44
conf/app.ini
44
conf/app.ini
@@ -1,8 +1,5 @@
|
|||||||
# NEVER EVER MODIFY THIS FILE
|
|
||||||
# PLEASE MAKE CHANGES ON CORRESPONDING CUSTOM CONFIG FILE
|
|
||||||
|
|
||||||
; App name that shows on every page title
|
; App name that shows on every page title
|
||||||
APP_NAME = Gogs: Go Git Service
|
APP_NAME = Gitea: Git with a cup of tea
|
||||||
; Change it if you run locally
|
; Change it if you run locally
|
||||||
RUN_USER = git
|
RUN_USER = git
|
||||||
; Either "dev", "prod" or "test", default is "dev"
|
; Either "dev", "prod" or "test", default is "dev"
|
||||||
@@ -24,6 +21,8 @@ PULL_REQUEST_QUEUE_LENGTH = 1000
|
|||||||
; Preferred Licenses to place at the top of the List
|
; Preferred Licenses to place at the top of the List
|
||||||
; Name must match file name in conf/license or custom/conf/license
|
; Name must match file name in conf/license or custom/conf/license
|
||||||
PREFERRED_LICENSES = Apache License 2.0,MIT License
|
PREFERRED_LICENSES = Apache License 2.0,MIT License
|
||||||
|
; Disable ability to interact with repositories by HTTP protocol
|
||||||
|
DISABLE_HTTP_GIT = false
|
||||||
|
|
||||||
[repository.editor]
|
[repository.editor]
|
||||||
; List of file extensions that should have line wraps in the CodeMirror editor
|
; List of file extensions that should have line wraps in the CodeMirror editor
|
||||||
@@ -36,7 +35,7 @@ PREVIEWABLE_FILE_MODES = markdown
|
|||||||
[repository.upload]
|
[repository.upload]
|
||||||
; Whether repository file uploads are enabled. Defaults to `true`
|
; Whether repository file uploads are enabled. Defaults to `true`
|
||||||
ENABLED = true
|
ENABLED = true
|
||||||
; Path for uploads. Defaults to `data/tmp/uploads` (tmp gets deleted on gogs restart)
|
; Path for uploads. Defaults to `data/tmp/uploads` (tmp gets deleted on gitea restart)
|
||||||
TEMP_PATH = data/tmp/uploads
|
TEMP_PATH = data/tmp/uploads
|
||||||
; One or more allowed types, e.g. image/jpeg|image/png. Nothing means any file type
|
; One or more allowed types, e.g. image/jpeg|image/png. Nothing means any file type
|
||||||
ALLOWED_TYPES =
|
ALLOWED_TYPES =
|
||||||
@@ -55,7 +54,7 @@ FEED_MAX_COMMIT_NUM = 5
|
|||||||
; Value of `theme-color` meta tag, used by Android >= 5.0
|
; Value of `theme-color` meta tag, used by Android >= 5.0
|
||||||
; An invalid color like "none" or "disable" will have the default style
|
; An invalid color like "none" or "disable" will have the default style
|
||||||
; More info: https://developers.google.com/web/updates/2014/11/Support-for-theme-color-in-Chrome-39-for-Android
|
; More info: https://developers.google.com/web/updates/2014/11/Support-for-theme-color-in-Chrome-39-for-Android
|
||||||
THEME_COLOR_META_TAG = `#ff5343`
|
THEME_COLOR_META_TAG = `#6cc644`
|
||||||
; Max size of files to be displayed (defaults is 8MiB)
|
; Max size of files to be displayed (defaults is 8MiB)
|
||||||
MAX_DISPLAY_FILE_SIZE = 8388608
|
MAX_DISPLAY_FILE_SIZE = 8388608
|
||||||
|
|
||||||
@@ -91,7 +90,7 @@ HTTP_ADDR = 0.0.0.0
|
|||||||
HTTP_PORT = 3000
|
HTTP_PORT = 3000
|
||||||
; Permission for unix socket
|
; Permission for unix socket
|
||||||
UNIX_SOCKET_PERMISSION = 666
|
UNIX_SOCKET_PERMISSION = 666
|
||||||
; Local (DMZ) URL for Gogs workers (such as SSH update) accessing web service.
|
; Local (DMZ) URL for Gitea workers (such as SSH update) accessing web service.
|
||||||
; In most cases you do not need to change the default value.
|
; In most cases you do not need to change the default value.
|
||||||
; Alter it only if your SSH server node is not the same as HTTP node.
|
; Alter it only if your SSH server node is not the same as HTTP node.
|
||||||
LOCAL_ROOT_URL = %(PROTOCOL)s://%(HTTP_ADDR)s:%(HTTP_PORT)s/
|
LOCAL_ROOT_URL = %(PROTOCOL)s://%(HTTP_ADDR)s:%(HTTP_PORT)s/
|
||||||
@@ -101,6 +100,8 @@ DISABLE_SSH = false
|
|||||||
START_SSH_SERVER = false
|
START_SSH_SERVER = false
|
||||||
; Domain name to be exposed in clone URL
|
; Domain name to be exposed in clone URL
|
||||||
SSH_DOMAIN = %(DOMAIN)s
|
SSH_DOMAIN = %(DOMAIN)s
|
||||||
|
; Network interface builtin SSH server listens on
|
||||||
|
SSH_LISTEN_HOST =
|
||||||
; Port number to be exposed in clone URL
|
; Port number to be exposed in clone URL
|
||||||
SSH_PORT = 22
|
SSH_PORT = 22
|
||||||
; Port number builtin SSH server listens on
|
; Port number builtin SSH server listens on
|
||||||
@@ -118,7 +119,7 @@ MINIMUM_KEY_SIZE_CHECK = false
|
|||||||
OFFLINE_MODE = false
|
OFFLINE_MODE = false
|
||||||
DISABLE_ROUTER_LOG = false
|
DISABLE_ROUTER_LOG = false
|
||||||
; Generate steps:
|
; Generate steps:
|
||||||
; $ ./gogs cert -ca=true -duration=8760h0m0s -host=myhost.example.com
|
; $ ./gitea cert -ca=true -duration=8760h0m0s -host=myhost.example.com
|
||||||
;
|
;
|
||||||
; Or from a .pfx file exported from the Windows certificate store (do
|
; Or from a .pfx file exported from the Windows certificate store (do
|
||||||
; not forget to export the private key):
|
; not forget to export the private key):
|
||||||
@@ -127,7 +128,7 @@ DISABLE_ROUTER_LOG = false
|
|||||||
CERT_FILE = custom/https/cert.pem
|
CERT_FILE = custom/https/cert.pem
|
||||||
KEY_FILE = custom/https/key.pem
|
KEY_FILE = custom/https/key.pem
|
||||||
; Upper level of template and static file path
|
; Upper level of template and static file path
|
||||||
; default is the path where Gogs is executed
|
; default is the path where Gitea is executed
|
||||||
STATIC_ROOT_PATH =
|
STATIC_ROOT_PATH =
|
||||||
; Default path for App data
|
; Default path for App data
|
||||||
APP_DATA_PATH = data
|
APP_DATA_PATH = data
|
||||||
@@ -147,13 +148,13 @@ DSA = 1024
|
|||||||
; Either "mysql", "postgres" or "sqlite3", it's your choice
|
; Either "mysql", "postgres" or "sqlite3", it's your choice
|
||||||
DB_TYPE = mysql
|
DB_TYPE = mysql
|
||||||
HOST = 127.0.0.1:3306
|
HOST = 127.0.0.1:3306
|
||||||
NAME = gogs
|
NAME = gitea
|
||||||
USER = root
|
USER = root
|
||||||
PASSWD =
|
PASSWD =
|
||||||
; For "postgres" only, either "disable", "require" or "verify-full"
|
; For "postgres" only, either "disable", "require" or "verify-full"
|
||||||
SSL_MODE = disable
|
SSL_MODE = disable
|
||||||
; For "sqlite3" and "tidb", use absolute path when you start as service
|
; For "sqlite3" and "tidb", use absolute path when you start as service
|
||||||
PATH = data/gogs.db
|
PATH = data/gitea.db
|
||||||
|
|
||||||
[admin]
|
[admin]
|
||||||
|
|
||||||
@@ -163,8 +164,8 @@ INSTALL_LOCK = false
|
|||||||
SECRET_KEY = !#@FDEWREWR&*(
|
SECRET_KEY = !#@FDEWREWR&*(
|
||||||
; Auto-login remember days
|
; Auto-login remember days
|
||||||
LOGIN_REMEMBER_DAYS = 7
|
LOGIN_REMEMBER_DAYS = 7
|
||||||
COOKIE_USERNAME = gogs_awesome
|
COOKIE_USERNAME = gitea_awesome
|
||||||
COOKIE_REMEMBER_NAME = gogs_incredible
|
COOKIE_REMEMBER_NAME = gitea_incredible
|
||||||
; Reverse proxy authentication header name of user name
|
; Reverse proxy authentication header name of user name
|
||||||
REVERSE_PROXY_AUTHENTICATION_USER = X-WEBAUTH-USER
|
REVERSE_PROXY_AUTHENTICATION_USER = X-WEBAUTH-USER
|
||||||
|
|
||||||
@@ -179,7 +180,7 @@ DISABLE_REGISTRATION = false
|
|||||||
REQUIRE_SIGNIN_VIEW = false
|
REQUIRE_SIGNIN_VIEW = false
|
||||||
; Mail notification
|
; Mail notification
|
||||||
ENABLE_NOTIFY_MAIL = false
|
ENABLE_NOTIFY_MAIL = false
|
||||||
; More detail: https://github.com/gogits/gogs/issues/165
|
; More detail: https://github.com/go-gitea/gitea/issues/165
|
||||||
ENABLE_REVERSE_PROXY_AUTHENTICATION = false
|
ENABLE_REVERSE_PROXY_AUTHENTICATION = false
|
||||||
ENABLE_REVERSE_PROXY_AUTO_REGISTRATION = false
|
ENABLE_REVERSE_PROXY_AUTO_REGISTRATION = false
|
||||||
; Enable captcha validation for registration
|
; Enable captcha validation for registration
|
||||||
@@ -334,7 +335,7 @@ HOST =
|
|||||||
; Mailer user name and password
|
; Mailer user name and password
|
||||||
USER =
|
USER =
|
||||||
PASSWD =
|
PASSWD =
|
||||||
; Receivers, can be one or more, e.g. ["1@example.com","2@example.com"]
|
; Receivers, can be one or more, e.g. 1@example.com,2@example.com
|
||||||
RECEIVERS =
|
RECEIVERS =
|
||||||
|
|
||||||
; For "database" mode only
|
; For "database" mode only
|
||||||
@@ -342,13 +343,13 @@ RECEIVERS =
|
|||||||
LEVEL =
|
LEVEL =
|
||||||
; Either "mysql" or "postgres"
|
; Either "mysql" or "postgres"
|
||||||
DRIVER =
|
DRIVER =
|
||||||
; Based on xorm, e.g.: root:root@localhost/gogs?charset=utf8
|
; Based on xorm, e.g.: root:root@localhost/gitea?charset=utf8
|
||||||
CONN =
|
CONN =
|
||||||
|
|
||||||
[cron]
|
[cron]
|
||||||
; Enable running cron tasks periodically.
|
; Enable running cron tasks periodically.
|
||||||
ENABLED = true
|
ENABLED = true
|
||||||
; Run cron tasks when Gogs starts.
|
; Run cron tasks when Gitea starts.
|
||||||
RUN_AT_START = false
|
RUN_AT_START = false
|
||||||
|
|
||||||
; Update mirrors
|
; Update mirrors
|
||||||
@@ -398,8 +399,8 @@ DEFAULT_INTERVAL = 8
|
|||||||
MAX_RESPONSE_ITEMS = 50
|
MAX_RESPONSE_ITEMS = 50
|
||||||
|
|
||||||
[i18n]
|
[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
|
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,ko-KR
|
||||||
NAMES = English,简体中文,繁體中文(香港),繁體中文(台湾),Deutsch,Français,Nederlands,Latviešu,Русский,日本語,Español,Português do Brasil,Polski,български,Italiano,Suomalainen,Türkçe,čeština,Српски
|
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
|
; Used for datetimepicker
|
||||||
[i18n.datelang]
|
[i18n.datelang]
|
||||||
@@ -421,6 +422,9 @@ it-IT = it
|
|||||||
fi-FI = fi
|
fi-FI = fi
|
||||||
tr-TR = tr
|
tr-TR = tr
|
||||||
cs-CZ = cs-CZ
|
cs-CZ = cs-CZ
|
||||||
|
sr-SP = sr
|
||||||
|
sv-SE = sv
|
||||||
|
ko-KR = ko
|
||||||
|
|
||||||
; Extension mapping to highlight class
|
; Extension mapping to highlight class
|
||||||
; e.g. .toml=ini
|
; e.g. .toml=ini
|
||||||
@@ -428,7 +432,7 @@ cs-CZ = cs-CZ
|
|||||||
|
|
||||||
[other]
|
[other]
|
||||||
SHOW_FOOTER_BRANDING = false
|
SHOW_FOOTER_BRANDING = false
|
||||||
; Show version information about Gogs and Go in the footer
|
; Show version information about Gitea and Go in the footer
|
||||||
SHOW_FOOTER_VERSION = true
|
SHOW_FOOTER_VERSION = true
|
||||||
; Show time of template execution in the footer
|
; Show time of template execution in the footer
|
||||||
SHOW_FOOTER_TEMPLATE_LOAD_TIME = true
|
SHOW_FOOTER_TEMPLATE_LOAD_TIME = true
|
||||||
|
|||||||
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/gogits/gogs
|
|
||||||
cd ${GOPATH}/src/github.com/gogits/gogs
|
|
||||||
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 GOGS_CUSTOM=${GOGS_CUSTOM}" >> /etc/profile
|
|
||||||
@@ -13,4 +13,3 @@ ethers: db files
|
|||||||
rpc: db files
|
rpc: db files
|
||||||
|
|
||||||
netgroup: nis
|
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 -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,32 @@
|
|||||||
Port 22
|
Port 22
|
||||||
|
Protocol 2
|
||||||
|
|
||||||
AddressFamily any
|
AddressFamily any
|
||||||
ListenAddress 0.0.0.0
|
ListenAddress 0.0.0.0
|
||||||
ListenAddress ::
|
ListenAddress ::
|
||||||
Protocol 2
|
|
||||||
LogLevel INFO
|
LogLevel INFO
|
||||||
|
|
||||||
|
HostKey /data/ssh/ssh_host_ed25519_key
|
||||||
HostKey /data/ssh/ssh_host_rsa_key
|
HostKey /data/ssh/ssh_host_rsa_key
|
||||||
HostKey /data/ssh/ssh_host_dsa_key
|
HostKey /data/ssh/ssh_host_dsa_key
|
||||||
HostKey /data/ssh/ssh_host_ecdsa_key
|
HostKey /data/ssh/ssh_host_ecdsa_key
|
||||||
HostKey /data/ssh/ssh_host_ed25519_key
|
|
||||||
PermitRootLogin no
|
AuthorizedKeysFile .ssh/authorized_keys
|
||||||
AuthorizedKeysFile .ssh/authorized_keys
|
|
||||||
PasswordAuthentication no
|
UseDNS no
|
||||||
UsePrivilegeSeparation no
|
AllowAgentForwarding no
|
||||||
|
AllowTcpForwarding no
|
||||||
|
PrintMotd no
|
||||||
|
|
||||||
PermitUserEnvironment yes
|
PermitUserEnvironment yes
|
||||||
|
PermitRootLogin no
|
||||||
|
ChallengeResponseAuthentication no
|
||||||
|
PasswordAuthentication no
|
||||||
|
PermitEmptyPasswords no
|
||||||
|
|
||||||
AllowUsers git
|
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 "GOGS_CUSTOM=${GOGS_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
|
||||||
152
glide.lock
generated
152
glide.lock
generated
@@ -1,152 +0,0 @@
|
|||||||
hash: 1d5fcf2a90f7621ecbc0b1abed548e11d13bda3fea49b4326c829a523268e5cf
|
|
||||||
updated: 2016-06-12T17:35:14.27036884+08:00
|
|
||||||
imports:
|
|
||||||
- name: github.com/bradfitz/gomemcache
|
|
||||||
version: fb1f79c6b65acda83063cbc69f6bba1522558bfc
|
|
||||||
subpackages:
|
|
||||||
- memcache
|
|
||||||
- name: github.com/urfave/cli
|
|
||||||
version: 1efa31f08b9333f1bd4882d61f9d668a70cd902e
|
|
||||||
- name: github.com/go-macaron/binding
|
|
||||||
version: 9440f336b443056c90d7d448a0a55ad8c7599880
|
|
||||||
- name: github.com/go-macaron/cache
|
|
||||||
version: 56173531277692bc2925924d51fda1cd0a6b8178
|
|
||||||
subpackages:
|
|
||||||
- memcache
|
|
||||||
- redis
|
|
||||||
- name: github.com/go-macaron/captcha
|
|
||||||
version: 8aa5919789ab301e865595eb4b1114d6b9847deb
|
|
||||||
- name: github.com/go-macaron/csrf
|
|
||||||
version: 6a9a7df172cc1fcd81e4585f44b09200b6087cc0
|
|
||||||
- name: github.com/go-macaron/gzip
|
|
||||||
version: cad1c6580a07c56f5f6bc52d66002a05985c5854
|
|
||||||
- name: github.com/go-macaron/i18n
|
|
||||||
version: ef57533c3b0fc2d8581deda14937e52f11a203ab
|
|
||||||
- name: github.com/go-macaron/inject
|
|
||||||
version: c5ab7bf3a307593cd44cb272d1a5beea473dd072
|
|
||||||
- name: github.com/go-macaron/session
|
|
||||||
version: 66031fcb37a0fff002a1f028eb0b3a815c78306b
|
|
||||||
subpackages:
|
|
||||||
- redis
|
|
||||||
- name: github.com/go-macaron/toolbox
|
|
||||||
version: 82b511550b0aefc36b3a28062ad3a52e812bee38
|
|
||||||
- name: github.com/go-sql-driver/mysql
|
|
||||||
version: 0b58b37b664c21f3010e836f1b931e1d0b0b0685
|
|
||||||
- name: github.com/go-xorm/core
|
|
||||||
version: 5bf745d7d163f4380e6c2bba8c4afa60534dd087
|
|
||||||
- name: github.com/go-xorm/xorm
|
|
||||||
version: c6c705684057842d9854e8299dd51abb06ae29f5
|
|
||||||
- name: github.com/gogits/chardet
|
|
||||||
version: 2404f777256163ea3eadb273dada5dcb037993c0
|
|
||||||
- name: github.com/gogits/cron
|
|
||||||
version: 7f3990acf1833faa5ebd0e86f0a4c72a4b5eba3c
|
|
||||||
- name: github.com/gogits/git-module
|
|
||||||
version: 5e0c1330d7853d1affbc193885d517db0f8d1ca5
|
|
||||||
- name: github.com/gogits/go-gogs-client
|
|
||||||
version: c52f7ee0cc58d3cd6e379025552873a8df6de322
|
|
||||||
- name: github.com/issue9/identicon
|
|
||||||
version: d36b54562f4cf70c83653e13dc95c220c79ef521
|
|
||||||
- name: github.com/jaytaylor/html2text
|
|
||||||
version: 52d9b785554a1918cb09909b89a1509a98b853fd
|
|
||||||
- name: github.com/kardianos/minwinsvc
|
|
||||||
version: cad6b2b879b0970e4245a20ebf1a81a756e2bb70
|
|
||||||
- name: github.com/klauspost/compress
|
|
||||||
version: 14eb9c4951195779ecfbec34431a976de7335b0a
|
|
||||||
subpackages:
|
|
||||||
- gzip
|
|
||||||
- flate
|
|
||||||
- name: github.com/klauspost/cpuid
|
|
||||||
version: 09cded8978dc9e80714c4d85b0322337b0a1e5e0
|
|
||||||
- name: github.com/klauspost/crc32
|
|
||||||
version: 19b0b332c9e4516a6370a0456e6182c3b5036720
|
|
||||||
- name: github.com/lib/pq
|
|
||||||
version: 80f8150043c80fb52dee6bc863a709cdac7ec8f8
|
|
||||||
subpackages:
|
|
||||||
- oid
|
|
||||||
- name: github.com/mattn/go-sqlite3
|
|
||||||
version: e118d4451349065b8e7ce0f0af32e033995363f8
|
|
||||||
- name: github.com/mcuadros/go-version
|
|
||||||
version: d52711f8d6bea8dc01efafdb68ad95a4e2606630
|
|
||||||
- name: github.com/microcosm-cc/bluemonday
|
|
||||||
version: 9dc199233bf72cc1aad9b61f73daf2f0075b9ee4
|
|
||||||
- name: github.com/msteinert/pam
|
|
||||||
version: 02ccfbfaf0cc627aa3aec8ef7ed5cfeec5b43f63
|
|
||||||
- name: github.com/nfnt/resize
|
|
||||||
version: 891127d8d1b52734debe1b3c3d7e747502b6c366
|
|
||||||
- name: github.com/russross/blackfriday
|
|
||||||
version: 93622da34e54fb6529bfb7c57e710f37a8d9cbd8
|
|
||||||
- name: github.com/satori/go.uuid
|
|
||||||
version: 0aa62d5ddceb50dbcb909d790b5345affd3669b6
|
|
||||||
- name: github.com/sergi/go-diff
|
|
||||||
version: ec7fdbb58eb3e300c8595ad5ac74a5aa50019cc7
|
|
||||||
subpackages:
|
|
||||||
- diffmatchpatch
|
|
||||||
- name: github.com/strk/go-libravatar
|
|
||||||
version: 5eed7bff870ae19ef51c5773dbc8f3e9fcbd0982
|
|
||||||
- name: github.com/shurcooL/sanitized_anchor_name
|
|
||||||
version: 10ef21a441db47d8b13ebcc5fd2310f636973c77
|
|
||||||
- name: github.com/Unknwon/cae
|
|
||||||
version: 7f5e046bc8a6c3cde743c233b96ee4fd84ee6ecd
|
|
||||||
subpackages:
|
|
||||||
- zip
|
|
||||||
- name: github.com/Unknwon/com
|
|
||||||
version: 28b053d5a2923b87ce8c5a08f3af779894a72758
|
|
||||||
- name: github.com/Unknwon/i18n
|
|
||||||
version: 39d6f2727e0698b1021ceb6a77c1801aa92e7d5d
|
|
||||||
- name: github.com/Unknwon/paginater
|
|
||||||
version: 7748a72e01415173a27d79866b984328e7b0c12b
|
|
||||||
- name: golang.org/x/crypto
|
|
||||||
version: bc89c496413265e715159bdc8478ee9a92fdc265
|
|
||||||
subpackages:
|
|
||||||
- ssh
|
|
||||||
- curve25519
|
|
||||||
- ed25519
|
|
||||||
- ed25519/internal/edwards25519
|
|
||||||
- name: golang.org/x/net
|
|
||||||
version: 57bfaa875b96fb91b4766077f34470528d4b03e9
|
|
||||||
subpackages:
|
|
||||||
- html
|
|
||||||
- html/charset
|
|
||||||
- html/atom
|
|
||||||
- name: golang.org/x/sys
|
|
||||||
version: a646d33e2ee3172a661fc09bca23bb4889a41bc8
|
|
||||||
subpackages:
|
|
||||||
- windows/svc
|
|
||||||
- windows
|
|
||||||
- name: golang.org/x/text
|
|
||||||
version: 2910a502d2bf9e43193af9d68ca516529614eed3
|
|
||||||
subpackages:
|
|
||||||
- transform
|
|
||||||
- language
|
|
||||||
- encoding
|
|
||||||
- encoding/charmap
|
|
||||||
- encoding/htmlindex
|
|
||||||
- internal/tag
|
|
||||||
- encoding/internal/identifier
|
|
||||||
- encoding/internal
|
|
||||||
- encoding/japanese
|
|
||||||
- encoding/korean
|
|
||||||
- encoding/simplifiedchinese
|
|
||||||
- encoding/traditionalchinese
|
|
||||||
- encoding/unicode
|
|
||||||
- internal/utf8internal
|
|
||||||
- runes
|
|
||||||
- name: gopkg.in/alexcesaro/quotedprintable.v3
|
|
||||||
version: 2caba252f4dc53eaf6b553000885530023f54623
|
|
||||||
- name: gopkg.in/asn1-ber.v1
|
|
||||||
version: 4e86f4367175e39f69d9358a5f17b4dda270378d
|
|
||||||
- name: gopkg.in/bufio.v1
|
|
||||||
version: 567b2bfa514e796916c4747494d6ff5132a1dfce
|
|
||||||
- name: gopkg.in/editorconfig/editorconfig-core-go.v1
|
|
||||||
version: a872f05c2e34b37b567401384d202aff11ba06d4
|
|
||||||
- name: gopkg.in/gomail.v2
|
|
||||||
version: 81ebce5c23dfd25c6c67194b37d3dd3f338c98b1
|
|
||||||
- name: gopkg.in/ini.v1
|
|
||||||
version: cf53f9204df4fbdd7ec4164b57fa6184ba168292
|
|
||||||
- name: gopkg.in/ldap.v2
|
|
||||||
version: d0a5ced67b4dc310b9158d63a2c6f9c5ec13f105
|
|
||||||
- name: gopkg.in/macaron.v1
|
|
||||||
version: 7564489a79f3f96b7ac8034652b35eeebb468eb4
|
|
||||||
- name: gopkg.in/redis.v2
|
|
||||||
version: e6179049628164864e6e84e973cfb56335748dea
|
|
||||||
devImports: []
|
|
||||||
59
glide.yaml
59
glide.yaml
@@ -1,59 +0,0 @@
|
|||||||
package: github.com/gogits/gogs
|
|
||||||
import:
|
|
||||||
- package: github.com/Unknwon/cae
|
|
||||||
subpackages:
|
|
||||||
- zip
|
|
||||||
- package: github.com/Unknwon/com
|
|
||||||
- package: github.com/Unknwon/i18n
|
|
||||||
- package: github.com/Unknwon/paginater
|
|
||||||
- package: github.com/urfave/cli
|
|
||||||
- package: github.com/go-macaron/binding
|
|
||||||
- package: github.com/go-macaron/cache
|
|
||||||
subpackages:
|
|
||||||
- memcache
|
|
||||||
- redis
|
|
||||||
- package: github.com/go-macaron/captcha
|
|
||||||
- package: github.com/go-macaron/csrf
|
|
||||||
- package: github.com/go-macaron/gzip
|
|
||||||
- package: github.com/go-macaron/i18n
|
|
||||||
- package: github.com/go-macaron/session
|
|
||||||
subpackages:
|
|
||||||
- redis
|
|
||||||
- package: github.com/go-macaron/toolbox
|
|
||||||
- package: github.com/go-sql-driver/mysql
|
|
||||||
- package: github.com/go-xorm/core
|
|
||||||
- package: github.com/go-xorm/xorm
|
|
||||||
- package: github.com/gogits/chardet
|
|
||||||
- package: github.com/gogits/cron
|
|
||||||
- package: github.com/gogits/git-module
|
|
||||||
- package: github.com/gogits/go-gogs-client
|
|
||||||
- package: github.com/issue9/identicon
|
|
||||||
- package: github.com/kardianos/minwinsvc
|
|
||||||
- package: github.com/lib/pq
|
|
||||||
- package: github.com/mattn/go-sqlite3
|
|
||||||
- package: github.com/mcuadros/go-version
|
|
||||||
- package: github.com/microcosm-cc/bluemonday
|
|
||||||
- package: github.com/msteinert/pam
|
|
||||||
- package: github.com/nfnt/resize
|
|
||||||
- package: github.com/russross/blackfriday
|
|
||||||
- package: github.com/satori/go.uuid
|
|
||||||
- package: github.com/sergi/go-diff
|
|
||||||
subpackages:
|
|
||||||
- diffmatchpatch
|
|
||||||
- package: github.com/strk/go-libravatar
|
|
||||||
- package: golang.org/x/crypto
|
|
||||||
subpackages:
|
|
||||||
- ssh
|
|
||||||
- package: golang.org/x/net
|
|
||||||
subpackages:
|
|
||||||
- html
|
|
||||||
- html/charset
|
|
||||||
- package: golang.org/x/text
|
|
||||||
subpackages:
|
|
||||||
- transform
|
|
||||||
- language
|
|
||||||
- package: gopkg.in/editorconfig/editorconfig-core-go.v1
|
|
||||||
- package: gopkg.in/gomail.v2
|
|
||||||
- package: gopkg.in/ini.v1
|
|
||||||
- package: gopkg.in/ldap.v2
|
|
||||||
- package: gopkg.in/macaron.v1
|
|
||||||
42
gogs.go
42
gogs.go
@@ -1,42 +0,0 @@
|
|||||||
// +build go1.4
|
|
||||||
|
|
||||||
// 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.
|
|
||||||
|
|
||||||
// Gogs (Go Git Service) is a painless self-hosted Git Service.
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"runtime"
|
|
||||||
|
|
||||||
"github.com/urfave/cli"
|
|
||||||
|
|
||||||
"github.com/gogits/gogs/cmd"
|
|
||||||
"github.com/gogits/gogs/modules/setting"
|
|
||||||
)
|
|
||||||
|
|
||||||
const APP_VER = "0.9.99.0915"
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
runtime.GOMAXPROCS(runtime.NumCPU())
|
|
||||||
setting.AppVer = APP_VER
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
app := cli.NewApp()
|
|
||||||
app.Name = "Gogs"
|
|
||||||
app.Usage = "Go Git Service: a painless self-hosted Git service"
|
|
||||||
app.Version = APP_VER
|
|
||||||
app.Commands = []cli.Command{
|
|
||||||
cmd.CmdWeb,
|
|
||||||
cmd.CmdServ,
|
|
||||||
cmd.CmdUpdate,
|
|
||||||
cmd.CmdDump,
|
|
||||||
cmd.CmdCert,
|
|
||||||
cmd.CmdAdmin,
|
|
||||||
}
|
|
||||||
app.Flags = append(app.Flags, []cli.Flag{}...)
|
|
||||||
app.Run(os.Args)
|
|
||||||
}
|
|
||||||
47
main.go
Normal file
47
main.go
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
// Gitea (git with a cup of tea) is a painless self-hosted Git Service.
|
||||||
|
package main // import "code.gitea.io/gitea"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/cmd"
|
||||||
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
"github.com/urfave/cli"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Version holds the current Gitea version
|
||||||
|
var Version = "1.0.2+dev"
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
runtime.GOMAXPROCS(runtime.NumCPU())
|
||||||
|
setting.AppVer = Version
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
app := cli.NewApp()
|
||||||
|
app.Name = "Gitea"
|
||||||
|
app.Usage = "A painless self-hosted Git service"
|
||||||
|
app.Version = Version
|
||||||
|
app.Commands = []cli.Command{
|
||||||
|
cmd.CmdWeb,
|
||||||
|
cmd.CmdServ,
|
||||||
|
cmd.CmdUpdate,
|
||||||
|
cmd.CmdDump,
|
||||||
|
cmd.CmdCert,
|
||||||
|
cmd.CmdAdmin,
|
||||||
|
}
|
||||||
|
app.Flags = append(app.Flags, []cli.Flag{}...)
|
||||||
|
err := app.Run(os.Args)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(4, "Fail to run app with %s: %v", os.Args, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -7,28 +7,34 @@ package models
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/gogits/gogs/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// AccessMode specifies the users access mode
|
||||||
type AccessMode int
|
type AccessMode int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ACCESS_MODE_NONE AccessMode = iota // 0
|
// AccessModeNone no access
|
||||||
ACCESS_MODE_READ // 1
|
AccessModeNone AccessMode = iota // 0
|
||||||
ACCESS_MODE_WRITE // 2
|
// AccessModeRead read access
|
||||||
ACCESS_MODE_ADMIN // 3
|
AccessModeRead // 1
|
||||||
ACCESS_MODE_OWNER // 4
|
// AccessModeWrite write access
|
||||||
|
AccessModeWrite // 2
|
||||||
|
// AccessModeAdmin admin access
|
||||||
|
AccessModeAdmin // 3
|
||||||
|
// AccessModeOwner owner access
|
||||||
|
AccessModeOwner // 4
|
||||||
)
|
)
|
||||||
|
|
||||||
func (mode AccessMode) String() string {
|
func (mode AccessMode) String() string {
|
||||||
switch mode {
|
switch mode {
|
||||||
case ACCESS_MODE_READ:
|
case AccessModeRead:
|
||||||
return "read"
|
return "read"
|
||||||
case ACCESS_MODE_WRITE:
|
case AccessModeWrite:
|
||||||
return "write"
|
return "write"
|
||||||
case ACCESS_MODE_ADMIN:
|
case AccessModeAdmin:
|
||||||
return "admin"
|
return "admin"
|
||||||
case ACCESS_MODE_OWNER:
|
case AccessModeOwner:
|
||||||
return "owner"
|
return "owner"
|
||||||
default:
|
default:
|
||||||
return "none"
|
return "none"
|
||||||
@@ -39,11 +45,11 @@ func (mode AccessMode) String() string {
|
|||||||
func ParseAccessMode(permission string) AccessMode {
|
func ParseAccessMode(permission string) AccessMode {
|
||||||
switch permission {
|
switch permission {
|
||||||
case "write":
|
case "write":
|
||||||
return ACCESS_MODE_WRITE
|
return AccessModeWrite
|
||||||
case "admin":
|
case "admin":
|
||||||
return ACCESS_MODE_ADMIN
|
return AccessModeAdmin
|
||||||
default:
|
default:
|
||||||
return ACCESS_MODE_READ
|
return AccessModeRead
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,21 +63,21 @@ type Access struct {
|
|||||||
Mode AccessMode
|
Mode AccessMode
|
||||||
}
|
}
|
||||||
|
|
||||||
func accessLevel(e Engine, u *User, repo *Repository) (AccessMode, error) {
|
func accessLevel(e Engine, user *User, repo *Repository) (AccessMode, error) {
|
||||||
mode := ACCESS_MODE_NONE
|
mode := AccessModeNone
|
||||||
if !repo.IsPrivate {
|
if !repo.IsPrivate {
|
||||||
mode = ACCESS_MODE_READ
|
mode = AccessModeRead
|
||||||
}
|
}
|
||||||
|
|
||||||
if u == nil {
|
if user == nil {
|
||||||
return mode, nil
|
return mode, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if u.ID == repo.OwnerID {
|
if user.ID == repo.OwnerID {
|
||||||
return ACCESS_MODE_OWNER, nil
|
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 {
|
if has, err := e.Get(a); !has || err != nil {
|
||||||
return mode, err
|
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
|
// AccessLevel returns the Access a user has to a repository. Will return NoneAccess if the
|
||||||
// user does not have access. User can be nil!
|
// user does not have access. User can be nil!
|
||||||
func AccessLevel(u *User, repo *Repository) (AccessMode, error) {
|
func AccessLevel(user *User, repo *Repository) (AccessMode, error) {
|
||||||
return accessLevel(x, u, repo)
|
return accessLevel(x, user, repo)
|
||||||
}
|
}
|
||||||
|
|
||||||
func hasAccess(e Engine, u *User, repo *Repository, testMode AccessMode) (bool, error) {
|
func hasAccess(e Engine, user *User, repo *Repository, testMode AccessMode) (bool, error) {
|
||||||
mode, err := accessLevel(e, u, repo)
|
mode, err := accessLevel(e, user, repo)
|
||||||
return testMode <= mode, err
|
return testMode <= mode, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasAccess returns true if someone has the request access level. User can be nil!
|
// HasAccess returns true if someone has the request access level. User can be nil!
|
||||||
func HasAccess(u *User, repo *Repository, testMode AccessMode) (bool, error) {
|
func HasAccess(user *User, repo *Repository, testMode AccessMode) (bool, error) {
|
||||||
return hasAccess(x, u, repo, testMode)
|
return hasAccess(x, user, repo, testMode)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetRepositoryAccesses finds all repositories with their access mode where a user has access but does not own.
|
// 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)
|
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
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,7 +119,7 @@ func (u *User) GetRepositoryAccesses() (map[*Repository]AccessMode, error) {
|
|||||||
}
|
}
|
||||||
if err = repo.GetOwner(); err != nil {
|
if err = repo.GetOwner(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if repo.OwnerID == u.ID {
|
} else if repo.OwnerID == user.ID {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
repos[repo] = access.Mode
|
repos[repo] = access.Mode
|
||||||
@@ -124,18 +130,22 @@ func (u *User) GetRepositoryAccesses() (map[*Repository]AccessMode, error) {
|
|||||||
// GetAccessibleRepositories finds repositories which the user has access but does not own.
|
// GetAccessibleRepositories finds repositories which the user has access but does not own.
|
||||||
// If limit is smaller than 1 means returns all found results.
|
// If limit is smaller than 1 means returns all found results.
|
||||||
func (user *User) GetAccessibleRepositories(limit int) (repos []*Repository, _ error) {
|
func (user *User) GetAccessibleRepositories(limit int) (repos []*Repository, _ error) {
|
||||||
sess := x.Where("owner_id !=? ", user.ID).Desc("updated_unix")
|
sess := x.
|
||||||
|
Where("owner_id !=? ", user.ID).
|
||||||
|
Desc("updated_unix")
|
||||||
if limit > 0 {
|
if limit > 0 {
|
||||||
sess.Limit(limit)
|
sess.Limit(limit)
|
||||||
repos = make([]*Repository, 0, limit)
|
repos = make([]*Repository, 0, limit)
|
||||||
} else {
|
} else {
|
||||||
repos = make([]*Repository, 0, 10)
|
repos = make([]*Repository, 0, 10)
|
||||||
}
|
}
|
||||||
return repos, sess.Join("INNER", "access", "access.user_id = ? AND access.repo_id = repository.id", user.ID).Find(&repos)
|
return repos, sess.
|
||||||
|
Join("INNER", "access", "access.user_id = ? AND access.repo_id = repository.id", user.ID).
|
||||||
|
Find(&repos)
|
||||||
}
|
}
|
||||||
|
|
||||||
func maxAccessMode(modes ...AccessMode) AccessMode {
|
func maxAccessMode(modes ...AccessMode) AccessMode {
|
||||||
max := ACCESS_MODE_NONE
|
max := AccessModeNone
|
||||||
for _, mode := range modes {
|
for _, mode := range modes {
|
||||||
if mode > max {
|
if mode > max {
|
||||||
max = mode
|
max = mode
|
||||||
@@ -146,9 +156,9 @@ func maxAccessMode(modes ...AccessMode) AccessMode {
|
|||||||
|
|
||||||
// FIXME: do corss-comparison so reduce deletions and additions to the minimum?
|
// FIXME: do corss-comparison so reduce deletions and additions to the minimum?
|
||||||
func (repo *Repository) refreshAccesses(e Engine, accessMap map[int64]AccessMode) (err error) {
|
func (repo *Repository) refreshAccesses(e Engine, accessMap map[int64]AccessMode) (err error) {
|
||||||
minMode := ACCESS_MODE_READ
|
minMode := AccessModeRead
|
||||||
if !repo.IsPrivate {
|
if !repo.IsPrivate {
|
||||||
minMode = ACCESS_MODE_WRITE
|
minMode = AccessModeWrite
|
||||||
}
|
}
|
||||||
|
|
||||||
newAccesses := make([]Access, 0, len(accessMap))
|
newAccesses := make([]Access, 0, len(accessMap))
|
||||||
@@ -212,7 +222,7 @@ func (repo *Repository) recalculateTeamAccesses(e Engine, ignTeamID int64) (err
|
|||||||
// Owner team gets owner access, and skip for teams that do not
|
// Owner team gets owner access, and skip for teams that do not
|
||||||
// have relations with repository.
|
// have relations with repository.
|
||||||
if t.IsOwnerTeam() {
|
if t.IsOwnerTeam() {
|
||||||
t.Authorize = ACCESS_MODE_OWNER
|
t.Authorize = AccessModeOwner
|
||||||
} else if !t.hasRepository(e, repo.ID) {
|
} else if !t.hasRepository(e, repo.ID) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -241,6 +251,6 @@ func (repo *Repository) recalculateAccesses(e Engine) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RecalculateAccesses recalculates all accesses for repository.
|
// RecalculateAccesses recalculates all accesses for repository.
|
||||||
func (r *Repository) RecalculateAccesses() error {
|
func (repo *Repository) RecalculateAccesses() error {
|
||||||
return r.recalculateAccesses(x)
|
return repo.recalculateAccesses(x)
|
||||||
}
|
}
|
||||||
|
|||||||
183
models/action.go
183
models/action.go
@@ -16,41 +16,44 @@ import (
|
|||||||
"github.com/Unknwon/com"
|
"github.com/Unknwon/com"
|
||||||
"github.com/go-xorm/xorm"
|
"github.com/go-xorm/xorm"
|
||||||
|
|
||||||
"github.com/gogits/git-module"
|
"code.gitea.io/git"
|
||||||
api "github.com/gogits/go-gogs-client"
|
api "code.gitea.io/sdk/gitea"
|
||||||
|
|
||||||
"github.com/gogits/gogs/modules/base"
|
"code.gitea.io/gitea/modules/base"
|
||||||
"github.com/gogits/gogs/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"github.com/gogits/gogs/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ActionType represents the type of an action.
|
||||||
type ActionType int
|
type ActionType int
|
||||||
|
|
||||||
|
// Possible action types.
|
||||||
const (
|
const (
|
||||||
ACTION_CREATE_REPO ActionType = iota + 1 // 1
|
ActionCreateRepo ActionType = iota + 1 // 1
|
||||||
ACTION_RENAME_REPO // 2
|
ActionRenameRepo // 2
|
||||||
ACTION_STAR_REPO // 3
|
ActionStarRepo // 3
|
||||||
ACTION_WATCH_REPO // 4
|
ActionWatchRepo // 4
|
||||||
ACTION_COMMIT_REPO // 5
|
ActionCommitRepo // 5
|
||||||
ACTION_CREATE_ISSUE // 6
|
ActionCreateIssue // 6
|
||||||
ACTION_CREATE_PULL_REQUEST // 7
|
ActionCreatePullRequest // 7
|
||||||
ACTION_TRANSFER_REPO // 8
|
ActionTransferRepo // 8
|
||||||
ACTION_PUSH_TAG // 9
|
ActionPushTag // 9
|
||||||
ACTION_COMMENT_ISSUE // 10
|
ActionCommentIssue // 10
|
||||||
ACTION_MERGE_PULL_REQUEST // 11
|
ActionMergePullRequest // 11
|
||||||
ACTION_CLOSE_ISSUE // 12
|
ActionCloseIssue // 12
|
||||||
ACTION_REOPEN_ISSUE // 13
|
ActionReopenIssue // 13
|
||||||
ACTION_CLOSE_PULL_REQUEST // 14
|
ActionClosePullRequest // 14
|
||||||
ACTION_REOPEN_PULL_REQUEST // 15
|
ActionReopenPullRequest // 15
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// Same as Github. See https://help.github.com/articles/closing-issues-via-commit-messages
|
// Same as Github. See
|
||||||
IssueCloseKeywords = []string{"close", "closes", "closed", "fix", "fixes", "fixed", "resolve", "resolves", "resolved"}
|
// https://help.github.com/articles/closing-issues-via-commit-messages
|
||||||
IssueReopenKeywords = []string{"reopen", "reopens", "reopened"}
|
issueCloseKeywords = []string{"close", "closes", "closed", "fix", "fixes", "fixed", "resolve", "resolves", "resolved"}
|
||||||
|
issueReopenKeywords = []string{"reopen", "reopens", "reopened"}
|
||||||
|
|
||||||
IssueCloseKeywordsPat, IssueReopenKeywordsPat *regexp.Regexp
|
issueCloseKeywordsPat, issueReopenKeywordsPat *regexp.Regexp
|
||||||
IssueReferenceKeywordsPat *regexp.Regexp
|
issueReferenceKeywordsPat *regexp.Regexp
|
||||||
)
|
)
|
||||||
|
|
||||||
func assembleKeywordsPattern(words []string) string {
|
func assembleKeywordsPattern(words []string) string {
|
||||||
@@ -58,13 +61,14 @@ func assembleKeywordsPattern(words []string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
IssueCloseKeywordsPat = regexp.MustCompile(assembleKeywordsPattern(IssueCloseKeywords))
|
issueCloseKeywordsPat = regexp.MustCompile(assembleKeywordsPattern(issueCloseKeywords))
|
||||||
IssueReopenKeywordsPat = regexp.MustCompile(assembleKeywordsPattern(IssueReopenKeywords))
|
issueReopenKeywordsPat = regexp.MustCompile(assembleKeywordsPattern(issueReopenKeywords))
|
||||||
IssueReferenceKeywordsPat = regexp.MustCompile(`(?i)(?:)(^| )\S+`)
|
issueReferenceKeywordsPat = regexp.MustCompile(`(?i)(?:)(^| )\S+`)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Action represents user operation type and other information to repository.,
|
// Action represents user operation type and other information to
|
||||||
// it implemented interface base.Actioner so that can be used in template render.
|
// repository. It implemented interface base.Actioner so that can be
|
||||||
|
// used in template render.
|
||||||
type Action struct {
|
type Action struct {
|
||||||
ID int64 `xorm:"pk autoincr"`
|
ID int64 `xorm:"pk autoincr"`
|
||||||
UserID int64 // Receiver user id.
|
UserID int64 // Receiver user id.
|
||||||
@@ -82,10 +86,13 @@ type Action struct {
|
|||||||
CreatedUnix int64
|
CreatedUnix int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BeforeInsert will be invoked by XORM before inserting a record
|
||||||
|
// representing this object.
|
||||||
func (a *Action) BeforeInsert() {
|
func (a *Action) BeforeInsert() {
|
||||||
a.CreatedUnix = time.Now().Unix()
|
a.CreatedUnix = time.Now().Unix()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AfterSet updates the webhook object upon setting a column.
|
||||||
func (a *Action) AfterSet(colName string, _ xorm.Cell) {
|
func (a *Action) AfterSet(colName string, _ xorm.Cell) {
|
||||||
switch colName {
|
switch colName {
|
||||||
case "created_unix":
|
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 {
|
func (a *Action) GetOpType() int {
|
||||||
return int(a.OpType)
|
return int(a.OpType)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetActUserName gets the action's user name.
|
||||||
func (a *Action) GetActUserName() string {
|
func (a *Action) GetActUserName() string {
|
||||||
return a.ActUserName
|
return a.ActUserName
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ShortActUserName gets the action's user name trimmed to max 20
|
||||||
|
// chars.
|
||||||
func (a *Action) ShortActUserName() string {
|
func (a *Action) ShortActUserName() string {
|
||||||
return base.EllipsisString(a.ActUserName, 20)
|
return base.EllipsisString(a.ActUserName, 20)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetRepoUserName returns the name of the action repository owner.
|
||||||
func (a *Action) GetRepoUserName() string {
|
func (a *Action) GetRepoUserName() string {
|
||||||
return a.RepoUserName
|
return a.RepoUserName
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ShortRepoUserName returns the name of the action repository owner
|
||||||
|
// trimmed to max 20 chars.
|
||||||
func (a *Action) ShortRepoUserName() string {
|
func (a *Action) ShortRepoUserName() string {
|
||||||
return base.EllipsisString(a.RepoUserName, 20)
|
return base.EllipsisString(a.RepoUserName, 20)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetRepoName returns the name of the action repository.
|
||||||
func (a *Action) GetRepoName() string {
|
func (a *Action) GetRepoName() string {
|
||||||
return a.RepoName
|
return a.RepoName
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ShortRepoName returns the name of the action repository
|
||||||
|
// trimmed to max 33 chars.
|
||||||
func (a *Action) ShortRepoName() string {
|
func (a *Action) ShortRepoName() string {
|
||||||
return base.EllipsisString(a.RepoName, 33)
|
return base.EllipsisString(a.RepoName, 33)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetRepoPath returns the virtual path to the action repository.
|
||||||
func (a *Action) GetRepoPath() string {
|
func (a *Action) GetRepoPath() string {
|
||||||
return path.Join(a.RepoUserName, a.RepoName)
|
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 {
|
func (a *Action) ShortRepoPath() string {
|
||||||
return path.Join(a.ShortRepoUserName(), a.ShortRepoName())
|
return path.Join(a.ShortRepoUserName(), a.ShortRepoName())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetRepoLink returns relative link to action repository.
|
||||||
func (a *Action) GetRepoLink() string {
|
func (a *Action) GetRepoLink() string {
|
||||||
if len(setting.AppSubUrl) > 0 {
|
if len(setting.AppSubURL) > 0 {
|
||||||
return path.Join(setting.AppSubUrl, a.GetRepoPath())
|
return path.Join(setting.AppSubURL, a.GetRepoPath())
|
||||||
}
|
}
|
||||||
return "/" + a.GetRepoPath()
|
return "/" + a.GetRepoPath()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetBranch returns the action's repository branch.
|
||||||
func (a *Action) GetBranch() string {
|
func (a *Action) GetBranch() string {
|
||||||
return a.RefName
|
return a.RefName
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetContent returns the action's content.
|
||||||
func (a *Action) GetContent() string {
|
func (a *Action) GetContent() string {
|
||||||
return a.Content
|
return a.Content
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetCreate returns the action creation time.
|
||||||
func (a *Action) GetCreate() time.Time {
|
func (a *Action) GetCreate() time.Time {
|
||||||
return a.Created
|
return a.Created
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetIssueInfos returns a list of issues associated with
|
||||||
|
// the action.
|
||||||
func (a *Action) GetIssueInfos() []string {
|
func (a *Action) GetIssueInfos() []string {
|
||||||
return strings.SplitN(a.Content, "|", 2)
|
return strings.SplitN(a.Content, "|", 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetIssueTitle returns the title of first issue associated
|
||||||
|
// with the action.
|
||||||
func (a *Action) GetIssueTitle() string {
|
func (a *Action) GetIssueTitle() string {
|
||||||
index := com.StrTo(a.GetIssueInfos()[0]).MustInt64()
|
index := com.StrTo(a.GetIssueInfos()[0]).MustInt64()
|
||||||
issue, err := GetIssueByIndex(a.RepoID, index)
|
issue, err := GetIssueByIndex(a.RepoID, index)
|
||||||
@@ -162,6 +191,8 @@ func (a *Action) GetIssueTitle() string {
|
|||||||
return issue.Title
|
return issue.Title
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetIssueContent returns the content of first issue associated with
|
||||||
|
// this action.
|
||||||
func (a *Action) GetIssueContent() string {
|
func (a *Action) GetIssueContent() string {
|
||||||
index := com.StrTo(a.GetIssueInfos()[0]).MustInt64()
|
index := com.StrTo(a.GetIssueInfos()[0]).MustInt64()
|
||||||
issue, err := GetIssueByIndex(a.RepoID, index)
|
issue, err := GetIssueByIndex(a.RepoID, index)
|
||||||
@@ -176,7 +207,7 @@ func newRepoAction(e Engine, u *User, repo *Repository) (err error) {
|
|||||||
if err = notifyWatchers(e, &Action{
|
if err = notifyWatchers(e, &Action{
|
||||||
ActUserID: u.ID,
|
ActUserID: u.ID,
|
||||||
ActUserName: u.Name,
|
ActUserName: u.Name,
|
||||||
OpType: ACTION_CREATE_REPO,
|
OpType: ActionCreateRepo,
|
||||||
RepoID: repo.ID,
|
RepoID: repo.ID,
|
||||||
RepoUserName: repo.Owner.Name,
|
RepoUserName: repo.Owner.Name,
|
||||||
RepoName: repo.Name,
|
RepoName: repo.Name,
|
||||||
@@ -198,7 +229,7 @@ func renameRepoAction(e Engine, actUser *User, oldRepoName string, repo *Reposit
|
|||||||
if err = notifyWatchers(e, &Action{
|
if err = notifyWatchers(e, &Action{
|
||||||
ActUserID: actUser.ID,
|
ActUserID: actUser.ID,
|
||||||
ActUserName: actUser.Name,
|
ActUserName: actUser.Name,
|
||||||
OpType: ACTION_RENAME_REPO,
|
OpType: ActionRenameRepo,
|
||||||
RepoID: repo.ID,
|
RepoID: repo.ID,
|
||||||
RepoUserName: repo.Owner.Name,
|
RepoUserName: repo.Owner.Name,
|
||||||
RepoName: repo.Name,
|
RepoName: repo.Name,
|
||||||
@@ -221,6 +252,7 @@ func issueIndexTrimRight(c rune) bool {
|
|||||||
return !unicode.IsDigit(c)
|
return !unicode.IsDigit(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PushCommit represents a commit in a push operation.
|
||||||
type PushCommit struct {
|
type PushCommit struct {
|
||||||
Sha1 string
|
Sha1 string
|
||||||
Message string
|
Message string
|
||||||
@@ -231,6 +263,7 @@ type PushCommit struct {
|
|||||||
Timestamp time.Time
|
Timestamp time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PushCommits represents list of commits in a push operation.
|
||||||
type PushCommits struct {
|
type PushCommits struct {
|
||||||
Len int
|
Len int
|
||||||
Commits []*PushCommit
|
Commits []*PushCommit
|
||||||
@@ -239,13 +272,16 @@ type PushCommits struct {
|
|||||||
avatars map[string]string
|
avatars map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewPushCommits creates a new PushCommits object.
|
||||||
func NewPushCommits() *PushCommits {
|
func NewPushCommits() *PushCommits {
|
||||||
return &PushCommits{
|
return &PushCommits{
|
||||||
avatars: make(map[string]string),
|
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))
|
commits := make([]*api.PayloadCommit, len(pc.Commits))
|
||||||
for i, commit := range pc.Commits {
|
for i, commit := range pc.Commits {
|
||||||
authorUsername := ""
|
authorUsername := ""
|
||||||
@@ -281,21 +317,21 @@ func (pc *PushCommits) ToApiPayloadCommits(repoLink string) []*api.PayloadCommit
|
|||||||
|
|
||||||
// AvatarLink tries to match user in database with e-mail
|
// AvatarLink tries to match user in database with e-mail
|
||||||
// in order to show custom avatar, and falls back to general avatar link.
|
// in order to show custom avatar, and falls back to general avatar link.
|
||||||
func (push *PushCommits) AvatarLink(email string) string {
|
func (pc *PushCommits) AvatarLink(email string) string {
|
||||||
_, ok := push.avatars[email]
|
_, ok := pc.avatars[email]
|
||||||
if !ok {
|
if !ok {
|
||||||
u, err := GetUserByEmail(email)
|
u, err := GetUserByEmail(email)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
push.avatars[email] = base.AvatarLink(email)
|
pc.avatars[email] = base.AvatarLink(email)
|
||||||
if !IsErrUserNotExist(err) {
|
if !IsErrUserNotExist(err) {
|
||||||
log.Error(4, "GetUserByEmail: %v", err)
|
log.Error(4, "GetUserByEmail: %v", err)
|
||||||
}
|
}
|
||||||
} else {
|
} 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.
|
// 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]
|
c := commits[i]
|
||||||
|
|
||||||
refMarked := make(map[int64]bool)
|
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 = ref[strings.IndexByte(ref, byte(' '))+1:]
|
||||||
ref = strings.TrimRightFunc(ref, issueIndexTrimRight)
|
ref = strings.TrimRightFunc(ref, issueIndexTrimRight)
|
||||||
|
|
||||||
@@ -343,7 +379,7 @@ func UpdateIssuesCommit(doer *User, repo *Repository, commits []*PushCommit) err
|
|||||||
|
|
||||||
refMarked = make(map[int64]bool)
|
refMarked = make(map[int64]bool)
|
||||||
// FIXME: can merge this one and next one to a common function.
|
// 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 = ref[strings.IndexByte(ref, byte(' '))+1:]
|
||||||
ref = strings.TrimRightFunc(ref, issueIndexTrimRight)
|
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.
|
// 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 = ref[strings.IndexByte(ref, byte(' '))+1:]
|
||||||
ref = strings.TrimRightFunc(ref, issueIndexTrimRight)
|
ref = strings.TrimRightFunc(ref, issueIndexTrimRight)
|
||||||
|
|
||||||
@@ -425,6 +461,7 @@ func UpdateIssuesCommit(doer *User, repo *Repository, commits []*PushCommit) err
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CommitRepoActionOptions represent options of a new commit action.
|
||||||
type CommitRepoActionOptions struct {
|
type CommitRepoActionOptions struct {
|
||||||
PusherName string
|
PusherName string
|
||||||
RepoOwnerID int64
|
RepoOwnerID int64
|
||||||
@@ -435,7 +472,8 @@ type CommitRepoActionOptions struct {
|
|||||||
Commits *PushCommits
|
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 {
|
func CommitRepoAction(opts CommitRepoActionOptions) error {
|
||||||
pusher, err := GetUserByName(opts.PusherName)
|
pusher, err := GetUserByName(opts.PusherName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -454,14 +492,14 @@ func CommitRepoAction(opts CommitRepoActionOptions) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
isNewBranch := false
|
isNewBranch := false
|
||||||
opType := ACTION_COMMIT_REPO
|
opType := ActionCommitRepo
|
||||||
// Check it's tag push or branch.
|
// Check it's tag push or branch.
|
||||||
if strings.HasPrefix(opts.RefFullName, git.TAG_PREFIX) {
|
if strings.HasPrefix(opts.RefFullName, git.TagPrefix) {
|
||||||
opType = ACTION_PUSH_TAG
|
opType = ActionPushTag
|
||||||
opts.Commits = &PushCommits{}
|
opts.Commits = &PushCommits{}
|
||||||
} else {
|
} else {
|
||||||
// if not the first commit, set the compare URL.
|
// if not the first commit, set the compare URL.
|
||||||
if opts.OldCommitID == git.EMPTY_SHA {
|
if opts.OldCommitID == git.EmptySHA {
|
||||||
isNewBranch = true
|
isNewBranch = true
|
||||||
} else {
|
} else {
|
||||||
opts.Commits.CompareURL = repo.ComposeCompareURL(opts.OldCommitID, opts.NewCommitID)
|
opts.Commits.CompareURL = repo.ComposeCompareURL(opts.OldCommitID, opts.NewCommitID)
|
||||||
@@ -501,15 +539,17 @@ func CommitRepoAction(opts CommitRepoActionOptions) error {
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
apiPusher := pusher.APIFormat()
|
apiPusher := pusher.APIFormat()
|
||||||
apiRepo := repo.APIFormat(nil)
|
apiRepo := repo.APIFormat(AccessModeNone)
|
||||||
|
|
||||||
|
var shaSum string
|
||||||
switch opType {
|
switch opType {
|
||||||
case ACTION_COMMIT_REPO: // Push
|
case ActionCommitRepo: // Push
|
||||||
if err = PrepareWebhooks(repo, HOOK_EVENT_PUSH, &api.PushPayload{
|
if err = PrepareWebhooks(repo, HookEventPush, &api.PushPayload{
|
||||||
Ref: opts.RefFullName,
|
Ref: opts.RefFullName,
|
||||||
Before: opts.OldCommitID,
|
Before: opts.OldCommitID,
|
||||||
After: opts.NewCommitID,
|
After: opts.NewCommitID,
|
||||||
CompareURL: setting.AppUrl + opts.Commits.CompareURL,
|
CompareURL: setting.AppURL + opts.Commits.CompareURL,
|
||||||
Commits: opts.Commits.ToApiPayloadCommits(repo.HTMLURL()),
|
Commits: opts.Commits.ToAPIPayloadCommits(repo.HTMLURL()),
|
||||||
Repo: apiRepo,
|
Repo: apiRepo,
|
||||||
Pusher: apiPusher,
|
Pusher: apiPusher,
|
||||||
Sender: apiPusher,
|
Sender: apiPusher,
|
||||||
@@ -518,17 +558,35 @@ func CommitRepoAction(opts CommitRepoActionOptions) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if isNewBranch {
|
if isNewBranch {
|
||||||
return PrepareWebhooks(repo, HOOK_EVENT_CREATE, &api.CreatePayload{
|
gitRepo, err := git.OpenRepository(repo.RepoPath())
|
||||||
|
if err != nil {
|
||||||
|
log.Error(4, "OpenRepository[%s]: %v", repo.RepoPath(), err)
|
||||||
|
}
|
||||||
|
shaSum, err = gitRepo.GetBranchCommitID(refName)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(4, "GetBranchCommitID[%s]: %v", opts.RefFullName, err)
|
||||||
|
}
|
||||||
|
return PrepareWebhooks(repo, HookEventCreate, &api.CreatePayload{
|
||||||
Ref: refName,
|
Ref: refName,
|
||||||
|
Sha: shaSum,
|
||||||
RefType: "branch",
|
RefType: "branch",
|
||||||
Repo: apiRepo,
|
Repo: apiRepo,
|
||||||
Sender: apiPusher,
|
Sender: apiPusher,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
case ACTION_PUSH_TAG: // Create
|
case ActionPushTag: // Create
|
||||||
return PrepareWebhooks(repo, HOOK_EVENT_CREATE, &api.CreatePayload{
|
gitRepo, err := git.OpenRepository(repo.RepoPath())
|
||||||
|
if err != nil {
|
||||||
|
log.Error(4, "OpenRepository[%s]: %v", repo.RepoPath(), err)
|
||||||
|
}
|
||||||
|
shaSum, err = gitRepo.GetTagCommitID(refName)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(4, "GetTagCommitID[%s]: %v", opts.RefFullName, err)
|
||||||
|
}
|
||||||
|
return PrepareWebhooks(repo, HookEventCreate, &api.CreatePayload{
|
||||||
Ref: refName,
|
Ref: refName,
|
||||||
|
Sha: shaSum,
|
||||||
RefType: "tag",
|
RefType: "tag",
|
||||||
Repo: apiRepo,
|
Repo: apiRepo,
|
||||||
Sender: apiPusher,
|
Sender: apiPusher,
|
||||||
@@ -542,7 +600,7 @@ func transferRepoAction(e Engine, doer, oldOwner *User, repo *Repository) (err e
|
|||||||
if err = notifyWatchers(e, &Action{
|
if err = notifyWatchers(e, &Action{
|
||||||
ActUserID: doer.ID,
|
ActUserID: doer.ID,
|
||||||
ActUserName: doer.Name,
|
ActUserName: doer.Name,
|
||||||
OpType: ACTION_TRANSFER_REPO,
|
OpType: ActionTransferRepo,
|
||||||
RepoID: repo.ID,
|
RepoID: repo.ID,
|
||||||
RepoUserName: repo.Owner.Name,
|
RepoUserName: repo.Owner.Name,
|
||||||
RepoName: repo.Name,
|
RepoName: repo.Name,
|
||||||
@@ -572,7 +630,7 @@ func mergePullRequestAction(e Engine, doer *User, repo *Repository, issue *Issue
|
|||||||
return notifyWatchers(e, &Action{
|
return notifyWatchers(e, &Action{
|
||||||
ActUserID: doer.ID,
|
ActUserID: doer.ID,
|
||||||
ActUserName: doer.Name,
|
ActUserName: doer.Name,
|
||||||
OpType: ACTION_MERGE_PULL_REQUEST,
|
OpType: ActionMergePullRequest,
|
||||||
Content: fmt.Sprintf("%d|%s", issue.Index, issue.Title),
|
Content: fmt.Sprintf("%d|%s", issue.Index, issue.Title),
|
||||||
RepoID: repo.ID,
|
RepoID: repo.ID,
|
||||||
RepoUserName: repo.Owner.Name,
|
RepoUserName: repo.Owner.Name,
|
||||||
@@ -591,9 +649,14 @@ func MergePullRequestAction(actUser *User, repo *Repository, pull *Issue) error
|
|||||||
// actorID can be -1 when isProfile is true or to skip the permission check.
|
// actorID can be -1 when isProfile is true or to skip the permission check.
|
||||||
func GetFeeds(ctxUser *User, actorID, offset int64, isProfile bool) ([]*Action, error) {
|
func GetFeeds(ctxUser *User, actorID, offset int64, isProfile bool) ([]*Action, error) {
|
||||||
actions := make([]*Action, 0, 20)
|
actions := make([]*Action, 0, 20)
|
||||||
sess := x.Limit(20, int(offset)).Desc("id").Where("user_id = ?", ctxUser.ID)
|
sess := x.
|
||||||
|
Limit(20, int(offset)).
|
||||||
|
Desc("id").
|
||||||
|
Where("user_id = ?", ctxUser.ID)
|
||||||
if isProfile {
|
if isProfile {
|
||||||
sess.And("is_private = ?", false).And("act_user_id = ?", ctxUser.ID)
|
sess.
|
||||||
|
And("is_private = ?", false).
|
||||||
|
And("act_user_id = ?", ctxUser.ID)
|
||||||
} else if actorID != -1 && ctxUser.IsOrganization() {
|
} else if actorID != -1 && ctxUser.IsOrganization() {
|
||||||
// FIXME: only need to get IDs here, not all fields of repository.
|
// FIXME: only need to get IDs here, not all fields of repository.
|
||||||
repos, _, err := ctxUser.GetUserRepositories(actorID, 1, ctxUser.NumRepos)
|
repos, _, err := ctxUser.GetUserRepositories(actorID, 1, ctxUser.NumRepos)
|
||||||
|
|||||||
@@ -14,15 +14,16 @@ import (
|
|||||||
"github.com/Unknwon/com"
|
"github.com/Unknwon/com"
|
||||||
"github.com/go-xorm/xorm"
|
"github.com/go-xorm/xorm"
|
||||||
|
|
||||||
"github.com/gogits/gogs/modules/base"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"github.com/gogits/gogs/modules/log"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"github.com/gogits/gogs/modules/setting"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//NoticeType describes the notice type
|
||||||
type NoticeType int
|
type NoticeType int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
NOTICE_REPOSITORY NoticeType = iota + 1
|
//NoticeRepository type
|
||||||
|
NoticeRepository NoticeType = iota + 1
|
||||||
)
|
)
|
||||||
|
|
||||||
// Notice represents a system notice for admin.
|
// Notice represents a system notice for admin.
|
||||||
@@ -34,10 +35,12 @@ type Notice struct {
|
|||||||
CreatedUnix int64
|
CreatedUnix int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BeforeInsert is invoked from XORM before inserting an object of this type.
|
||||||
func (n *Notice) BeforeInsert() {
|
func (n *Notice) BeforeInsert() {
|
||||||
n.CreatedUnix = time.Now().Unix()
|
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) {
|
func (n *Notice) AfterSet(colName string, _ xorm.Cell) {
|
||||||
switch colName {
|
switch colName {
|
||||||
case "created_unix":
|
case "created_unix":
|
||||||
@@ -65,9 +68,9 @@ func CreateNotice(tp NoticeType, desc string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateRepositoryNotice creates new system notice with type NOTICE_REPOSITORY.
|
// CreateRepositoryNotice creates new system notice with type NoticeRepository.
|
||||||
func CreateRepositoryNotice(desc string) error {
|
func CreateRepositoryNotice(desc string) error {
|
||||||
return CreateNotice(NOTICE_REPOSITORY, desc)
|
return CreateNotice(NoticeRepository, desc)
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveAllWithNotice removes all directories in given path and
|
// RemoveAllWithNotice removes all directories in given path and
|
||||||
@@ -103,7 +106,10 @@ func CountNotices() int64 {
|
|||||||
// Notices returns number of notices in given page.
|
// Notices returns number of notices in given page.
|
||||||
func Notices(page, pageSize int) ([]*Notice, error) {
|
func Notices(page, pageSize int) ([]*Notice, error) {
|
||||||
notices := make([]*Notice, 0, pageSize)
|
notices := make([]*Notice, 0, pageSize)
|
||||||
return notices, x.Limit(pageSize, (page-1)*pageSize).Desc("id").Find(¬ices)
|
return notices, x.
|
||||||
|
Limit(pageSize, (page-1)*pageSize).
|
||||||
|
Desc("id").
|
||||||
|
Find(¬ices)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteNotice deletes a system notice by given ID.
|
// DeleteNotice deletes a system notice by given ID.
|
||||||
@@ -127,6 +133,8 @@ func DeleteNoticesByIDs(ids []int64) error {
|
|||||||
if len(ids) == 0 {
|
if len(ids) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
_, err := x.Where("id IN (" + strings.Join(base.Int64sToStrings(ids), ",") + ")").Delete(new(Notice))
|
_, err := x.
|
||||||
|
In("id", ids).
|
||||||
|
Delete(new(Notice))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
112
models/error.go
112
models/error.go
@@ -8,10 +8,12 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ErrNameReserved represents a "reserved name" error.
|
||||||
type ErrNameReserved struct {
|
type ErrNameReserved struct {
|
||||||
Name string
|
Name string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsErrNameReserved checks if an error is a ErrNameReserved.
|
||||||
func IsErrNameReserved(err error) bool {
|
func IsErrNameReserved(err error) bool {
|
||||||
_, ok := err.(ErrNameReserved)
|
_, ok := err.(ErrNameReserved)
|
||||||
return ok
|
return ok
|
||||||
@@ -21,10 +23,13 @@ func (err ErrNameReserved) Error() string {
|
|||||||
return fmt.Sprintf("name is reserved [name: %s]", err.Name)
|
return fmt.Sprintf("name is reserved [name: %s]", err.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ErrNamePatternNotAllowed represents a "pattern not allowed" error.
|
||||||
type ErrNamePatternNotAllowed struct {
|
type ErrNamePatternNotAllowed struct {
|
||||||
Pattern string
|
Pattern string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsErrNamePatternNotAllowed checks if an error is an
|
||||||
|
// ErrNamePatternNotAllowed.
|
||||||
func IsErrNamePatternNotAllowed(err error) bool {
|
func IsErrNamePatternNotAllowed(err error) bool {
|
||||||
_, ok := err.(ErrNamePatternNotAllowed)
|
_, ok := err.(ErrNamePatternNotAllowed)
|
||||||
return ok
|
return ok
|
||||||
@@ -41,10 +46,12 @@ func (err ErrNamePatternNotAllowed) Error() string {
|
|||||||
// |______//____ >\___ >__|
|
// |______//____ >\___ >__|
|
||||||
// \/ \/
|
// \/ \/
|
||||||
|
|
||||||
|
// ErrUserAlreadyExist represents a "user already exists" error.
|
||||||
type ErrUserAlreadyExist struct {
|
type ErrUserAlreadyExist struct {
|
||||||
Name string
|
Name string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsErrUserAlreadyExist checks if an error is a ErrUserAlreadyExists.
|
||||||
func IsErrUserAlreadyExist(err error) bool {
|
func IsErrUserAlreadyExist(err error) bool {
|
||||||
_, ok := err.(ErrUserAlreadyExist)
|
_, ok := err.(ErrUserAlreadyExist)
|
||||||
return ok
|
return ok
|
||||||
@@ -54,24 +61,29 @@ func (err ErrUserAlreadyExist) Error() string {
|
|||||||
return fmt.Sprintf("user already exists [name: %s]", err.Name)
|
return fmt.Sprintf("user already exists [name: %s]", err.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ErrUserNotExist represents a "UserNotExist" kind of error.
|
||||||
type ErrUserNotExist struct {
|
type ErrUserNotExist struct {
|
||||||
UID int64
|
UID int64
|
||||||
Name string
|
Name string
|
||||||
|
KeyID int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsErrUserNotExist checks if an error is a ErrUserNotExist.
|
||||||
func IsErrUserNotExist(err error) bool {
|
func IsErrUserNotExist(err error) bool {
|
||||||
_, ok := err.(ErrUserNotExist)
|
_, ok := err.(ErrUserNotExist)
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
func (err ErrUserNotExist) Error() string {
|
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 {
|
type ErrEmailAlreadyUsed struct {
|
||||||
Email string
|
Email string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsErrEmailAlreadyUsed checks if an error is a ErrEmailAlreadyUsed.
|
||||||
func IsErrEmailAlreadyUsed(err error) bool {
|
func IsErrEmailAlreadyUsed(err error) bool {
|
||||||
_, ok := err.(ErrEmailAlreadyUsed)
|
_, ok := err.(ErrEmailAlreadyUsed)
|
||||||
return ok
|
return ok
|
||||||
@@ -81,10 +93,12 @@ func (err ErrEmailAlreadyUsed) Error() string {
|
|||||||
return fmt.Sprintf("e-mail has been used [email: %s]", err.Email)
|
return fmt.Sprintf("e-mail has been used [email: %s]", err.Email)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ErrUserOwnRepos represents a "UserOwnRepos" kind of error.
|
||||||
type ErrUserOwnRepos struct {
|
type ErrUserOwnRepos struct {
|
||||||
UID int64
|
UID int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsErrUserOwnRepos checks if an error is a ErrUserOwnRepos.
|
||||||
func IsErrUserOwnRepos(err error) bool {
|
func IsErrUserOwnRepos(err error) bool {
|
||||||
_, ok := err.(ErrUserOwnRepos)
|
_, ok := err.(ErrUserOwnRepos)
|
||||||
return ok
|
return ok
|
||||||
@@ -94,10 +108,12 @@ func (err ErrUserOwnRepos) Error() string {
|
|||||||
return fmt.Sprintf("user still has ownership of repositories [uid: %d]", err.UID)
|
return fmt.Sprintf("user still has ownership of repositories [uid: %d]", err.UID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ErrUserHasOrgs represents a "UserHasOrgs" kind of error.
|
||||||
type ErrUserHasOrgs struct {
|
type ErrUserHasOrgs struct {
|
||||||
UID int64
|
UID int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsErrUserHasOrgs checks if an error is a ErrUserHasOrgs.
|
||||||
func IsErrUserHasOrgs(err error) bool {
|
func IsErrUserHasOrgs(err error) bool {
|
||||||
_, ok := err.(ErrUserHasOrgs)
|
_, ok := err.(ErrUserHasOrgs)
|
||||||
return ok
|
return ok
|
||||||
@@ -107,10 +123,12 @@ func (err ErrUserHasOrgs) Error() string {
|
|||||||
return fmt.Sprintf("user still has membership of organizations [uid: %d]", err.UID)
|
return fmt.Sprintf("user still has membership of organizations [uid: %d]", err.UID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ErrReachLimitOfRepo represents a "ReachLimitOfRepo" kind of error.
|
||||||
type ErrReachLimitOfRepo struct {
|
type ErrReachLimitOfRepo struct {
|
||||||
Limit int
|
Limit int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsErrReachLimitOfRepo checks if an error is a ErrReachLimitOfRepo.
|
||||||
func IsErrReachLimitOfRepo(err error) bool {
|
func IsErrReachLimitOfRepo(err error) bool {
|
||||||
_, ok := err.(ErrReachLimitOfRepo)
|
_, ok := err.(ErrReachLimitOfRepo)
|
||||||
return ok
|
return ok
|
||||||
@@ -127,10 +145,12 @@ func (err ErrReachLimitOfRepo) Error() string {
|
|||||||
// \__/\ / |__|__|_ \__|
|
// \__/\ / |__|__|_ \__|
|
||||||
// \/ \/
|
// \/ \/
|
||||||
|
|
||||||
|
// ErrWikiAlreadyExist represents a "WikiAlreadyExist" kind of error.
|
||||||
type ErrWikiAlreadyExist struct {
|
type ErrWikiAlreadyExist struct {
|
||||||
Title string
|
Title string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsErrWikiAlreadyExist checks if an error is a ErrWikiAlreadyExist.
|
||||||
func IsErrWikiAlreadyExist(err error) bool {
|
func IsErrWikiAlreadyExist(err error) bool {
|
||||||
_, ok := err.(ErrWikiAlreadyExist)
|
_, ok := err.(ErrWikiAlreadyExist)
|
||||||
return ok
|
return ok
|
||||||
@@ -147,10 +167,12 @@ func (err ErrWikiAlreadyExist) Error() string {
|
|||||||
// |____| |____/|___ /____/__|\___ > |____|__ \___ > ____|
|
// |____| |____/|___ /____/__|\___ > |____|__ \___ > ____|
|
||||||
// \/ \/ \/ \/\/
|
// \/ \/ \/ \/\/
|
||||||
|
|
||||||
|
// ErrKeyUnableVerify represents a "KeyUnableVerify" kind of error.
|
||||||
type ErrKeyUnableVerify struct {
|
type ErrKeyUnableVerify struct {
|
||||||
Result string
|
Result string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsErrKeyUnableVerify checks if an error is a ErrKeyUnableVerify.
|
||||||
func IsErrKeyUnableVerify(err error) bool {
|
func IsErrKeyUnableVerify(err error) bool {
|
||||||
_, ok := err.(ErrKeyUnableVerify)
|
_, ok := err.(ErrKeyUnableVerify)
|
||||||
return ok
|
return ok
|
||||||
@@ -160,10 +182,12 @@ func (err ErrKeyUnableVerify) Error() string {
|
|||||||
return fmt.Sprintf("Unable to verify key content [result: %s]", err.Result)
|
return fmt.Sprintf("Unable to verify key content [result: %s]", err.Result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ErrKeyNotExist represents a "KeyNotExist" kind of error.
|
||||||
type ErrKeyNotExist struct {
|
type ErrKeyNotExist struct {
|
||||||
ID int64
|
ID int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsErrKeyNotExist checks if an error is a ErrKeyNotExist.
|
||||||
func IsErrKeyNotExist(err error) bool {
|
func IsErrKeyNotExist(err error) bool {
|
||||||
_, ok := err.(ErrKeyNotExist)
|
_, ok := err.(ErrKeyNotExist)
|
||||||
return ok
|
return ok
|
||||||
@@ -173,11 +197,13 @@ func (err ErrKeyNotExist) Error() string {
|
|||||||
return fmt.Sprintf("public key does not exist [id: %d]", err.ID)
|
return fmt.Sprintf("public key does not exist [id: %d]", err.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ErrKeyAlreadyExist represents a "KeyAlreadyExist" kind of error.
|
||||||
type ErrKeyAlreadyExist struct {
|
type ErrKeyAlreadyExist struct {
|
||||||
OwnerID int64
|
OwnerID int64
|
||||||
Content string
|
Content string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsErrKeyAlreadyExist checks if an error is a ErrKeyAlreadyExist.
|
||||||
func IsErrKeyAlreadyExist(err error) bool {
|
func IsErrKeyAlreadyExist(err error) bool {
|
||||||
_, ok := err.(ErrKeyAlreadyExist)
|
_, ok := err.(ErrKeyAlreadyExist)
|
||||||
return ok
|
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)
|
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 {
|
type ErrKeyNameAlreadyUsed struct {
|
||||||
OwnerID int64
|
OwnerID int64
|
||||||
Name string
|
Name string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsErrKeyNameAlreadyUsed checks if an error is a ErrKeyNameAlreadyUsed.
|
||||||
func IsErrKeyNameAlreadyUsed(err error) bool {
|
func IsErrKeyNameAlreadyUsed(err error) bool {
|
||||||
_, ok := err.(ErrKeyNameAlreadyUsed)
|
_, ok := err.(ErrKeyNameAlreadyUsed)
|
||||||
return ok
|
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)
|
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 {
|
type ErrKeyAccessDenied struct {
|
||||||
UserID int64
|
UserID int64
|
||||||
KeyID int64
|
KeyID int64
|
||||||
Note string
|
Note string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsErrKeyAccessDenied checks if an error is a ErrKeyAccessDenied.
|
||||||
func IsErrKeyAccessDenied(err error) bool {
|
func IsErrKeyAccessDenied(err error) bool {
|
||||||
_, ok := err.(ErrKeyAccessDenied)
|
_, ok := err.(ErrKeyAccessDenied)
|
||||||
return ok
|
return ok
|
||||||
@@ -217,12 +247,14 @@ func (err ErrKeyAccessDenied) Error() string {
|
|||||||
err.UserID, err.KeyID, err.Note)
|
err.UserID, err.KeyID, err.Note)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ErrDeployKeyNotExist represents a "DeployKeyNotExist" kind of error.
|
||||||
type ErrDeployKeyNotExist struct {
|
type ErrDeployKeyNotExist struct {
|
||||||
ID int64
|
ID int64
|
||||||
KeyID int64
|
KeyID int64
|
||||||
RepoID int64
|
RepoID int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsErrDeployKeyNotExist checks if an error is a ErrDeployKeyNotExist.
|
||||||
func IsErrDeployKeyNotExist(err error) bool {
|
func IsErrDeployKeyNotExist(err error) bool {
|
||||||
_, ok := err.(ErrDeployKeyNotExist)
|
_, ok := err.(ErrDeployKeyNotExist)
|
||||||
return ok
|
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)
|
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 {
|
type ErrDeployKeyAlreadyExist struct {
|
||||||
KeyID int64
|
KeyID int64
|
||||||
RepoID int64
|
RepoID int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsErrDeployKeyAlreadyExist checks if an error is a ErrDeployKeyAlreadyExist.
|
||||||
func IsErrDeployKeyAlreadyExist(err error) bool {
|
func IsErrDeployKeyAlreadyExist(err error) bool {
|
||||||
_, ok := err.(ErrDeployKeyAlreadyExist)
|
_, ok := err.(ErrDeployKeyAlreadyExist)
|
||||||
return ok
|
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)
|
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 {
|
type ErrDeployKeyNameAlreadyUsed struct {
|
||||||
RepoID int64
|
RepoID int64
|
||||||
Name string
|
Name string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsErrDeployKeyNameAlreadyUsed checks if an error is a ErrDeployKeyNameAlreadyUsed.
|
||||||
func IsErrDeployKeyNameAlreadyUsed(err error) bool {
|
func IsErrDeployKeyNameAlreadyUsed(err error) bool {
|
||||||
_, ok := err.(ErrDeployKeyNameAlreadyUsed)
|
_, ok := err.(ErrDeployKeyNameAlreadyUsed)
|
||||||
return ok
|
return ok
|
||||||
@@ -267,10 +303,12 @@ func (err ErrDeployKeyNameAlreadyUsed) Error() string {
|
|||||||
// \____|__ /\___ >___ >___ >____ >____ > |____| \____/|__|_ \\___ >___| /
|
// \____|__ /\___ >___ >___ >____ >____ > |____| \____/|__|_ \\___ >___| /
|
||||||
// \/ \/ \/ \/ \/ \/ \/ \/ \/
|
// \/ \/ \/ \/ \/ \/ \/ \/ \/
|
||||||
|
|
||||||
|
// ErrAccessTokenNotExist represents a "AccessTokenNotExist" kind of error.
|
||||||
type ErrAccessTokenNotExist struct {
|
type ErrAccessTokenNotExist struct {
|
||||||
SHA string
|
SHA string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsErrAccessTokenNotExist checks if an error is a ErrAccessTokenNotExist.
|
||||||
func IsErrAccessTokenNotExist(err error) bool {
|
func IsErrAccessTokenNotExist(err error) bool {
|
||||||
_, ok := err.(ErrAccessTokenNotExist)
|
_, ok := err.(ErrAccessTokenNotExist)
|
||||||
return ok
|
return ok
|
||||||
@@ -280,9 +318,11 @@ func (err ErrAccessTokenNotExist) Error() string {
|
|||||||
return fmt.Sprintf("access token does not exist [sha: %s]", err.SHA)
|
return fmt.Sprintf("access token does not exist [sha: %s]", err.SHA)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ErrAccessTokenEmpty represents a "AccessTokenEmpty" kind of error.
|
||||||
type ErrAccessTokenEmpty struct {
|
type ErrAccessTokenEmpty struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsErrAccessTokenEmpty checks if an error is a ErrAccessTokenEmpty.
|
||||||
func IsErrAccessTokenEmpty(err error) bool {
|
func IsErrAccessTokenEmpty(err error) bool {
|
||||||
_, ok := err.(ErrAccessTokenEmpty)
|
_, ok := err.(ErrAccessTokenEmpty)
|
||||||
return ok
|
return ok
|
||||||
@@ -299,10 +339,12 @@ func (err ErrAccessTokenEmpty) Error() string {
|
|||||||
// \_______ /__| \___ (____ /___| /__/_____ \(____ /__| |__|\____/|___| /
|
// \_______ /__| \___ (____ /___| /__/_____ \(____ /__| |__|\____/|___| /
|
||||||
// \/ /_____/ \/ \/ \/ \/ \/
|
// \/ /_____/ \/ \/ \/ \/ \/
|
||||||
|
|
||||||
|
// ErrLastOrgOwner represents a "LastOrgOwner" kind of error.
|
||||||
type ErrLastOrgOwner struct {
|
type ErrLastOrgOwner struct {
|
||||||
UID int64
|
UID int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsErrLastOrgOwner checks if an error is a ErrLastOrgOwner.
|
||||||
func IsErrLastOrgOwner(err error) bool {
|
func IsErrLastOrgOwner(err error) bool {
|
||||||
_, ok := err.(ErrLastOrgOwner)
|
_, ok := err.(ErrLastOrgOwner)
|
||||||
return ok
|
return ok
|
||||||
@@ -319,12 +361,14 @@ func (err ErrLastOrgOwner) Error() string {
|
|||||||
// |____|_ /\___ > __/ \____/____ >__||__| \____/|__| / ____|
|
// |____|_ /\___ > __/ \____/____ >__||__| \____/|__| / ____|
|
||||||
// \/ \/|__| \/ \/
|
// \/ \/|__| \/ \/
|
||||||
|
|
||||||
|
// ErrRepoNotExist represents a "RepoNotExist" kind of error.
|
||||||
type ErrRepoNotExist struct {
|
type ErrRepoNotExist struct {
|
||||||
ID int64
|
ID int64
|
||||||
UID int64
|
UID int64
|
||||||
Name string
|
Name string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsErrRepoNotExist checks if an error is a ErrRepoNotExist.
|
||||||
func IsErrRepoNotExist(err error) bool {
|
func IsErrRepoNotExist(err error) bool {
|
||||||
_, ok := err.(ErrRepoNotExist)
|
_, ok := err.(ErrRepoNotExist)
|
||||||
return ok
|
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)
|
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 {
|
type ErrRepoAlreadyExist struct {
|
||||||
Uname string
|
Uname string
|
||||||
Name string
|
Name string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsErrRepoAlreadyExist checks if an error is a ErrRepoAlreadyExist.
|
||||||
func IsErrRepoAlreadyExist(err error) bool {
|
func IsErrRepoAlreadyExist(err error) bool {
|
||||||
_, ok := err.(ErrRepoAlreadyExist)
|
_, ok := err.(ErrRepoAlreadyExist)
|
||||||
return ok
|
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)
|
return fmt.Sprintf("repository already exists [uname: %s, name: %s]", err.Uname, err.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ErrInvalidCloneAddr represents a "InvalidCloneAddr" kind of error.
|
||||||
type ErrInvalidCloneAddr struct {
|
type ErrInvalidCloneAddr struct {
|
||||||
IsURLError bool
|
IsURLError bool
|
||||||
IsInvalidPath bool
|
IsInvalidPath bool
|
||||||
IsPermissionDenied bool
|
IsPermissionDenied bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsErrInvalidCloneAddr checks if an error is a ErrInvalidCloneAddr.
|
||||||
func IsErrInvalidCloneAddr(err error) bool {
|
func IsErrInvalidCloneAddr(err error) bool {
|
||||||
_, ok := err.(ErrInvalidCloneAddr)
|
_, ok := err.(ErrInvalidCloneAddr)
|
||||||
return ok
|
return ok
|
||||||
@@ -364,10 +412,12 @@ func (err ErrInvalidCloneAddr) Error() string {
|
|||||||
err.IsURLError, err.IsInvalidPath, err.IsPermissionDenied)
|
err.IsURLError, err.IsInvalidPath, err.IsPermissionDenied)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ErrUpdateTaskNotExist represents a "UpdateTaskNotExist" kind of error.
|
||||||
type ErrUpdateTaskNotExist struct {
|
type ErrUpdateTaskNotExist struct {
|
||||||
UUID string
|
UUID string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsErrUpdateTaskNotExist checks if an error is a ErrUpdateTaskNotExist.
|
||||||
func IsErrUpdateTaskNotExist(err error) bool {
|
func IsErrUpdateTaskNotExist(err error) bool {
|
||||||
_, ok := err.(ErrUpdateTaskNotExist)
|
_, ok := err.(ErrUpdateTaskNotExist)
|
||||||
return ok
|
return ok
|
||||||
@@ -377,10 +427,12 @@ func (err ErrUpdateTaskNotExist) Error() string {
|
|||||||
return fmt.Sprintf("update task does not exist [uuid: %s]", err.UUID)
|
return fmt.Sprintf("update task does not exist [uuid: %s]", err.UUID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ErrReleaseAlreadyExist represents a "ReleaseAlreadyExist" kind of error.
|
||||||
type ErrReleaseAlreadyExist struct {
|
type ErrReleaseAlreadyExist struct {
|
||||||
TagName string
|
TagName string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsErrReleaseAlreadyExist checks if an error is a ErrReleaseAlreadyExist.
|
||||||
func IsErrReleaseAlreadyExist(err error) bool {
|
func IsErrReleaseAlreadyExist(err error) bool {
|
||||||
_, ok := err.(ErrReleaseAlreadyExist)
|
_, ok := err.(ErrReleaseAlreadyExist)
|
||||||
return ok
|
return ok
|
||||||
@@ -390,11 +442,13 @@ func (err ErrReleaseAlreadyExist) Error() string {
|
|||||||
return fmt.Sprintf("release tag already exist [tag_name: %s]", err.TagName)
|
return fmt.Sprintf("release tag already exist [tag_name: %s]", err.TagName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ErrReleaseNotExist represents a "ReleaseNotExist" kind of error.
|
||||||
type ErrReleaseNotExist struct {
|
type ErrReleaseNotExist struct {
|
||||||
ID int64
|
ID int64
|
||||||
TagName string
|
TagName string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsErrReleaseNotExist checks if an error is a ErrReleaseNotExist.
|
||||||
func IsErrReleaseNotExist(err error) bool {
|
func IsErrReleaseNotExist(err error) bool {
|
||||||
_, ok := err.(ErrReleaseNotExist)
|
_, ok := err.(ErrReleaseNotExist)
|
||||||
return ok
|
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)
|
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 {
|
type ErrInvalidTagName struct {
|
||||||
TagName string
|
TagName string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsErrInvalidTagName checks if an error is a ErrInvalidTagName.
|
||||||
func IsErrInvalidTagName(err error) bool {
|
func IsErrInvalidTagName(err error) bool {
|
||||||
_, ok := err.(ErrInvalidTagName)
|
_, ok := err.(ErrInvalidTagName)
|
||||||
return ok
|
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)
|
return fmt.Sprintf("release tag name is not valid [tag_name: %s]", err.TagName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ErrRepoFileAlreadyExist represents a "RepoFileAlreadyExist" kind of error.
|
||||||
type ErrRepoFileAlreadyExist struct {
|
type ErrRepoFileAlreadyExist struct {
|
||||||
FileName string
|
FileName string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsErrRepoFileAlreadyExist checks if an error is a ErrRepoFileAlreadyExist.
|
||||||
func IsErrRepoFileAlreadyExist(err error) bool {
|
func IsErrRepoFileAlreadyExist(err error) bool {
|
||||||
_, ok := err.(ErrRepoFileAlreadyExist)
|
_, ok := err.(ErrRepoFileAlreadyExist)
|
||||||
return ok
|
return ok
|
||||||
@@ -437,10 +495,12 @@ func (err ErrRepoFileAlreadyExist) Error() string {
|
|||||||
// |______ / |__| (____ /___| /\___ >___| /
|
// |______ / |__| (____ /___| /\___ >___| /
|
||||||
// \/ \/ \/ \/ \/
|
// \/ \/ \/ \/ \/
|
||||||
|
|
||||||
|
// ErrBranchNotExist represents a "BranchNotExist" kind of error.
|
||||||
type ErrBranchNotExist struct {
|
type ErrBranchNotExist struct {
|
||||||
Name string
|
Name string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsErrBranchNotExist checks if an error is a ErrBranchNotExist.
|
||||||
func IsErrBranchNotExist(err error) bool {
|
func IsErrBranchNotExist(err error) bool {
|
||||||
_, ok := err.(ErrBranchNotExist)
|
_, ok := err.(ErrBranchNotExist)
|
||||||
return ok
|
return ok
|
||||||
@@ -457,10 +517,12 @@ func (err ErrBranchNotExist) Error() string {
|
|||||||
// \__/\ / \___ >___ /___| /\____/ \____/|__|_ \
|
// \__/\ / \___ >___ /___| /\____/ \____/|__|_ \
|
||||||
// \/ \/ \/ \/ \/
|
// \/ \/ \/ \/ \/
|
||||||
|
|
||||||
|
// ErrWebhookNotExist represents a "WebhookNotExist" kind of error.
|
||||||
type ErrWebhookNotExist struct {
|
type ErrWebhookNotExist struct {
|
||||||
ID int64
|
ID int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsErrWebhookNotExist checks if an error is a ErrWebhookNotExist.
|
||||||
func IsErrWebhookNotExist(err error) bool {
|
func IsErrWebhookNotExist(err error) bool {
|
||||||
_, ok := err.(ErrWebhookNotExist)
|
_, ok := err.(ErrWebhookNotExist)
|
||||||
return ok
|
return ok
|
||||||
@@ -477,12 +539,14 @@ func (err ErrWebhookNotExist) Error() string {
|
|||||||
// |___/____ >____ >____/ \___ >
|
// |___/____ >____ >____/ \___ >
|
||||||
// \/ \/ \/
|
// \/ \/ \/
|
||||||
|
|
||||||
|
// ErrIssueNotExist represents a "IssueNotExist" kind of error.
|
||||||
type ErrIssueNotExist struct {
|
type ErrIssueNotExist struct {
|
||||||
ID int64
|
ID int64
|
||||||
RepoID int64
|
RepoID int64
|
||||||
Index int64
|
Index int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsErrIssueNotExist checks if an error is a ErrIssueNotExist.
|
||||||
func IsErrIssueNotExist(err error) bool {
|
func IsErrIssueNotExist(err error) bool {
|
||||||
_, ok := err.(ErrIssueNotExist)
|
_, ok := err.(ErrIssueNotExist)
|
||||||
return ok
|
return ok
|
||||||
@@ -499,6 +563,7 @@ func (err ErrIssueNotExist) Error() string {
|
|||||||
// |____| |____/|____/____/____|_ /\___ >__ |____/ \___ >____ > |__|
|
// |____| |____/|____/____/____|_ /\___ >__ |____/ \___ >____ > |__|
|
||||||
// \/ \/ |__| \/ \/
|
// \/ \/ |__| \/ \/
|
||||||
|
|
||||||
|
// ErrPullRequestNotExist represents a "PullRequestNotExist" kind of error.
|
||||||
type ErrPullRequestNotExist struct {
|
type ErrPullRequestNotExist struct {
|
||||||
ID int64
|
ID int64
|
||||||
IssueID int64
|
IssueID int64
|
||||||
@@ -508,6 +573,7 @@ type ErrPullRequestNotExist struct {
|
|||||||
BaseBranch string
|
BaseBranch string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsErrPullRequestNotExist checks if an error is a ErrPullRequestNotExist.
|
||||||
func IsErrPullRequestNotExist(err error) bool {
|
func IsErrPullRequestNotExist(err error) bool {
|
||||||
_, ok := err.(ErrPullRequestNotExist)
|
_, ok := err.(ErrPullRequestNotExist)
|
||||||
return ok
|
return ok
|
||||||
@@ -518,6 +584,28 @@ func (err ErrPullRequestNotExist) Error() string {
|
|||||||
err.ID, err.IssueID, err.HeadRepoID, err.BaseRepoID, err.HeadBarcnh, err.BaseBranch)
|
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 {
|
type ErrCommentNotExist struct {
|
||||||
ID int64
|
ID int64
|
||||||
IssueID int64
|
IssueID int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsErrCommentNotExist checks if an error is a ErrCommentNotExist.
|
||||||
func IsErrCommentNotExist(err error) bool {
|
func IsErrCommentNotExist(err error) bool {
|
||||||
_, ok := err.(ErrCommentNotExist)
|
_, ok := err.(ErrCommentNotExist)
|
||||||
return ok
|
return ok
|
||||||
@@ -546,11 +636,13 @@ func (err ErrCommentNotExist) Error() string {
|
|||||||
// |_______ (____ /___ /\___ >____/
|
// |_______ (____ /___ /\___ >____/
|
||||||
// \/ \/ \/ \/
|
// \/ \/ \/ \/
|
||||||
|
|
||||||
|
// ErrLabelNotExist represents a "LabelNotExist" kind of error.
|
||||||
type ErrLabelNotExist struct {
|
type ErrLabelNotExist struct {
|
||||||
LabelID int64
|
LabelID int64
|
||||||
RepoID int64
|
RepoID int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsErrLabelNotExist checks if an error is a ErrLabelNotExist.
|
||||||
func IsErrLabelNotExist(err error) bool {
|
func IsErrLabelNotExist(err error) bool {
|
||||||
_, ok := err.(ErrLabelNotExist)
|
_, ok := err.(ErrLabelNotExist)
|
||||||
return ok
|
return ok
|
||||||
@@ -567,11 +659,13 @@ func (err ErrLabelNotExist) Error() string {
|
|||||||
// \____|__ /__|____/\___ >____ > |__| \____/|___| /\___ >
|
// \____|__ /__|____/\___ >____ > |__| \____/|___| /\___ >
|
||||||
// \/ \/ \/ \/ \/
|
// \/ \/ \/ \/ \/
|
||||||
|
|
||||||
|
// ErrMilestoneNotExist represents a "MilestoneNotExist" kind of error.
|
||||||
type ErrMilestoneNotExist struct {
|
type ErrMilestoneNotExist struct {
|
||||||
ID int64
|
ID int64
|
||||||
RepoID int64
|
RepoID int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsErrMilestoneNotExist checks if an error is a ErrMilestoneNotExist.
|
||||||
func IsErrMilestoneNotExist(err error) bool {
|
func IsErrMilestoneNotExist(err error) bool {
|
||||||
_, ok := err.(ErrMilestoneNotExist)
|
_, ok := err.(ErrMilestoneNotExist)
|
||||||
return ok
|
return ok
|
||||||
@@ -588,11 +682,13 @@ func (err ErrMilestoneNotExist) Error() string {
|
|||||||
// \____|__ /__| |__| (____ /\___ >___| /__|_| /\___ >___| /__|
|
// \____|__ /__| |__| (____ /\___ >___| /__|_| /\___ >___| /__|
|
||||||
// \/ \/ \/ \/ \/ \/ \/
|
// \/ \/ \/ \/ \/ \/ \/
|
||||||
|
|
||||||
|
// ErrAttachmentNotExist represents a "AttachmentNotExist" kind of error.
|
||||||
type ErrAttachmentNotExist struct {
|
type ErrAttachmentNotExist struct {
|
||||||
ID int64
|
ID int64
|
||||||
UUID string
|
UUID string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsErrAttachmentNotExist checks if an error is a ErrAttachmentNotExist.
|
||||||
func IsErrAttachmentNotExist(err error) bool {
|
func IsErrAttachmentNotExist(err error) bool {
|
||||||
_, ok := err.(ErrAttachmentNotExist)
|
_, ok := err.(ErrAttachmentNotExist)
|
||||||
return ok
|
return ok
|
||||||
@@ -609,10 +705,12 @@ func (err ErrAttachmentNotExist) Error() string {
|
|||||||
// |_______ \____/\___ /|__|___| / /_______ /\____/|____/ |__| \___ >___ >
|
// |_______ \____/\___ /|__|___| / /_______ /\____/|____/ |__| \___ >___ >
|
||||||
// \/ /_____/ \/ \/ \/ \/
|
// \/ /_____/ \/ \/ \/ \/
|
||||||
|
|
||||||
|
// ErrLoginSourceNotExist represents a "LoginSourceNotExist" kind of error.
|
||||||
type ErrLoginSourceNotExist struct {
|
type ErrLoginSourceNotExist struct {
|
||||||
ID int64
|
ID int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsErrLoginSourceNotExist checks if an error is a ErrLoginSourceNotExist.
|
||||||
func IsErrLoginSourceNotExist(err error) bool {
|
func IsErrLoginSourceNotExist(err error) bool {
|
||||||
_, ok := err.(ErrLoginSourceNotExist)
|
_, ok := err.(ErrLoginSourceNotExist)
|
||||||
return ok
|
return ok
|
||||||
@@ -622,10 +720,12 @@ func (err ErrLoginSourceNotExist) Error() string {
|
|||||||
return fmt.Sprintf("login source does not exist [id: %d]", err.ID)
|
return fmt.Sprintf("login source does not exist [id: %d]", err.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ErrLoginSourceAlreadyExist represents a "LoginSourceAlreadyExist" kind of error.
|
||||||
type ErrLoginSourceAlreadyExist struct {
|
type ErrLoginSourceAlreadyExist struct {
|
||||||
Name string
|
Name string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsErrLoginSourceAlreadyExist checks if an error is a ErrLoginSourceAlreadyExist.
|
||||||
func IsErrLoginSourceAlreadyExist(err error) bool {
|
func IsErrLoginSourceAlreadyExist(err error) bool {
|
||||||
_, ok := err.(ErrLoginSourceAlreadyExist)
|
_, ok := err.(ErrLoginSourceAlreadyExist)
|
||||||
return ok
|
return ok
|
||||||
@@ -635,10 +735,12 @@ func (err ErrLoginSourceAlreadyExist) Error() string {
|
|||||||
return fmt.Sprintf("login source already exists [name: %s]", err.Name)
|
return fmt.Sprintf("login source already exists [name: %s]", err.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ErrLoginSourceInUse represents a "LoginSourceInUse" kind of error.
|
||||||
type ErrLoginSourceInUse struct {
|
type ErrLoginSourceInUse struct {
|
||||||
ID int64
|
ID int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsErrLoginSourceInUse checks if an error is a ErrLoginSourceInUse.
|
||||||
func IsErrLoginSourceInUse(err error) bool {
|
func IsErrLoginSourceInUse(err error) bool {
|
||||||
_, ok := err.(ErrLoginSourceInUse)
|
_, ok := err.(ErrLoginSourceInUse)
|
||||||
return ok
|
return ok
|
||||||
@@ -655,11 +757,13 @@ func (err ErrLoginSourceInUse) Error() string {
|
|||||||
// |____| \___ >____ /__|_| /
|
// |____| \___ >____ /__|_| /
|
||||||
// \/ \/ \/
|
// \/ \/ \/
|
||||||
|
|
||||||
|
// ErrTeamAlreadyExist represents a "TeamAlreadyExist" kind of error.
|
||||||
type ErrTeamAlreadyExist struct {
|
type ErrTeamAlreadyExist struct {
|
||||||
OrgID int64
|
OrgID int64
|
||||||
Name string
|
Name string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsErrTeamAlreadyExist checks if an error is a ErrTeamAlreadyExist.
|
||||||
func IsErrTeamAlreadyExist(err error) bool {
|
func IsErrTeamAlreadyExist(err error) bool {
|
||||||
_, ok := err.(ErrTeamAlreadyExist)
|
_, ok := err.(ErrTeamAlreadyExist)
|
||||||
return ok
|
return ok
|
||||||
@@ -677,11 +781,13 @@ func (err ErrTeamAlreadyExist) Error() string {
|
|||||||
// |__| \/ \/
|
// |__| \/ \/
|
||||||
//
|
//
|
||||||
|
|
||||||
|
// ErrUploadNotExist represents a "UploadNotExist" kind of error.
|
||||||
type ErrUploadNotExist struct {
|
type ErrUploadNotExist struct {
|
||||||
ID int64
|
ID int64
|
||||||
UUID string
|
UUID string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsErrUploadNotExist checks if an error is a ErrUploadNotExist.
|
||||||
func IsErrUploadNotExist(err error) bool {
|
func IsErrUploadNotExist(err error) bool {
|
||||||
_, ok := err.(ErrAttachmentNotExist)
|
_, ok := err.(ErrAttachmentNotExist)
|
||||||
return ok
|
return ok
|
||||||
|
|||||||
@@ -16,38 +16,41 @@ import (
|
|||||||
"os/exec"
|
"os/exec"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"code.gitea.io/git"
|
||||||
|
"code.gitea.io/gitea/modules/base"
|
||||||
|
"code.gitea.io/gitea/modules/highlight"
|
||||||
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
"code.gitea.io/gitea/modules/process"
|
||||||
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"github.com/Unknwon/com"
|
"github.com/Unknwon/com"
|
||||||
"github.com/sergi/go-diff/diffmatchpatch"
|
"github.com/sergi/go-diff/diffmatchpatch"
|
||||||
"golang.org/x/net/html/charset"
|
"golang.org/x/net/html/charset"
|
||||||
"golang.org/x/text/transform"
|
"golang.org/x/text/transform"
|
||||||
|
|
||||||
"github.com/gogits/git-module"
|
|
||||||
|
|
||||||
"github.com/gogits/gogs/modules/base"
|
|
||||||
"github.com/gogits/gogs/modules/log"
|
|
||||||
"github.com/gogits/gogs/modules/process"
|
|
||||||
"github.com/gogits/gogs/modules/setting"
|
|
||||||
"github.com/gogits/gogs/modules/template/highlight"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// DiffLineType represents the type of a DiffLine.
|
||||||
type DiffLineType uint8
|
type DiffLineType uint8
|
||||||
|
|
||||||
|
// DiffLineType possible values.
|
||||||
const (
|
const (
|
||||||
DIFF_LINE_PLAIN DiffLineType = iota + 1
|
DiffLinePlain DiffLineType = iota + 1
|
||||||
DIFF_LINE_ADD
|
DiffLineAdd
|
||||||
DIFF_LINE_DEL
|
DiffLineDel
|
||||||
DIFF_LINE_SECTION
|
DiffLineSection
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// DiffFileType represents the type of a DiffFile.
|
||||||
type DiffFileType uint8
|
type DiffFileType uint8
|
||||||
|
|
||||||
|
// DiffFileType possible values.
|
||||||
const (
|
const (
|
||||||
DIFF_FILE_ADD DiffFileType = iota + 1
|
DiffFileAdd DiffFileType = iota + 1
|
||||||
DIFF_FILE_CHANGE
|
DiffFileChange
|
||||||
DIFF_FILE_DEL
|
DiffFileDel
|
||||||
DIFF_FILE_RENAME
|
DiffFileRename
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// DiffLine represents a line difference in a DiffSection.
|
||||||
type DiffLine struct {
|
type DiffLine struct {
|
||||||
LeftIdx int
|
LeftIdx int
|
||||||
RightIdx int
|
RightIdx int
|
||||||
@@ -55,10 +58,12 @@ type DiffLine struct {
|
|||||||
Content string
|
Content string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetType returns the type of a DiffLine.
|
||||||
func (d *DiffLine) GetType() int {
|
func (d *DiffLine) GetType() int {
|
||||||
return int(d.Type)
|
return int(d.Type)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DiffSection represents a section of a DiffFile.
|
||||||
type DiffSection struct {
|
type DiffSection struct {
|
||||||
Name string
|
Name string
|
||||||
Lines []*DiffLine
|
Lines []*DiffLine
|
||||||
@@ -75,19 +80,19 @@ func diffToHTML(diffs []diffmatchpatch.Diff, lineType DiffLineType) template.HTM
|
|||||||
|
|
||||||
// Reproduce signs which are cutted for inline diff before.
|
// Reproduce signs which are cutted for inline diff before.
|
||||||
switch lineType {
|
switch lineType {
|
||||||
case DIFF_LINE_ADD:
|
case DiffLineAdd:
|
||||||
buf.WriteByte('+')
|
buf.WriteByte('+')
|
||||||
case DIFF_LINE_DEL:
|
case DiffLineDel:
|
||||||
buf.WriteByte('-')
|
buf.WriteByte('-')
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := range diffs {
|
for i := range diffs {
|
||||||
switch {
|
switch {
|
||||||
case diffs[i].Type == diffmatchpatch.DiffInsert && lineType == DIFF_LINE_ADD:
|
case diffs[i].Type == diffmatchpatch.DiffInsert && lineType == DiffLineAdd:
|
||||||
buf.Write(addedCodePrefix)
|
buf.Write(addedCodePrefix)
|
||||||
buf.WriteString(html.EscapeString(diffs[i].Text))
|
buf.WriteString(html.EscapeString(diffs[i].Text))
|
||||||
buf.Write(codeTagSuffix)
|
buf.Write(codeTagSuffix)
|
||||||
case diffs[i].Type == diffmatchpatch.DiffDelete && lineType == DIFF_LINE_DEL:
|
case diffs[i].Type == diffmatchpatch.DiffDelete && lineType == DiffLineDel:
|
||||||
buf.Write(removedCodePrefix)
|
buf.Write(removedCodePrefix)
|
||||||
buf.WriteString(html.EscapeString(diffs[i].Text))
|
buf.WriteString(html.EscapeString(diffs[i].Text))
|
||||||
buf.Write(codeTagSuffix)
|
buf.Write(codeTagSuffix)
|
||||||
@@ -99,7 +104,7 @@ func diffToHTML(diffs []diffmatchpatch.Diff, lineType DiffLineType) template.HTM
|
|||||||
return template.HTML(buf.Bytes())
|
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 {
|
func (diffSection *DiffSection) GetLine(lineType DiffLineType, idx int) *DiffLine {
|
||||||
var (
|
var (
|
||||||
difference = 0
|
difference = 0
|
||||||
@@ -111,9 +116,9 @@ func (diffSection *DiffSection) GetLine(lineType DiffLineType, idx int) *DiffLin
|
|||||||
LOOP:
|
LOOP:
|
||||||
for _, diffLine := range diffSection.Lines {
|
for _, diffLine := range diffSection.Lines {
|
||||||
switch diffLine.Type {
|
switch diffLine.Type {
|
||||||
case DIFF_LINE_ADD:
|
case DiffLineAdd:
|
||||||
addCount++
|
addCount++
|
||||||
case DIFF_LINE_DEL:
|
case DiffLineDel:
|
||||||
delCount++
|
delCount++
|
||||||
default:
|
default:
|
||||||
if matchDiffLine != nil {
|
if matchDiffLine != nil {
|
||||||
@@ -125,11 +130,11 @@ LOOP:
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch lineType {
|
switch lineType {
|
||||||
case DIFF_LINE_DEL:
|
case DiffLineDel:
|
||||||
if diffLine.RightIdx == 0 && diffLine.LeftIdx == idx-difference {
|
if diffLine.RightIdx == 0 && diffLine.LeftIdx == idx-difference {
|
||||||
matchDiffLine = diffLine
|
matchDiffLine = diffLine
|
||||||
}
|
}
|
||||||
case DIFF_LINE_ADD:
|
case DiffLineAdd:
|
||||||
if diffLine.LeftIdx == 0 && diffLine.RightIdx == idx+difference {
|
if diffLine.LeftIdx == 0 && diffLine.RightIdx == idx+difference {
|
||||||
matchDiffLine = diffLine
|
matchDiffLine = diffLine
|
||||||
}
|
}
|
||||||
@@ -148,7 +153,7 @@ func init() {
|
|||||||
diffMatchPatch.DiffEditCost = 100
|
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 {
|
func (diffSection *DiffSection) GetComputedInlineDiffFor(diffLine *DiffLine) template.HTML {
|
||||||
if setting.Git.DisableDiffHighlight {
|
if setting.Git.DisableDiffHighlight {
|
||||||
return template.HTML(html.EscapeString(diffLine.Content[1:]))
|
return template.HTML(html.EscapeString(diffLine.Content[1:]))
|
||||||
@@ -161,15 +166,15 @@ func (diffSection *DiffSection) GetComputedInlineDiffFor(diffLine *DiffLine) tem
|
|||||||
|
|
||||||
// try to find equivalent diff line. ignore, otherwise
|
// try to find equivalent diff line. ignore, otherwise
|
||||||
switch diffLine.Type {
|
switch diffLine.Type {
|
||||||
case DIFF_LINE_ADD:
|
case DiffLineAdd:
|
||||||
compareDiffLine = diffSection.GetLine(DIFF_LINE_DEL, diffLine.RightIdx)
|
compareDiffLine = diffSection.GetLine(DiffLineDel, diffLine.RightIdx)
|
||||||
if compareDiffLine == nil {
|
if compareDiffLine == nil {
|
||||||
return template.HTML(html.EscapeString(diffLine.Content))
|
return template.HTML(html.EscapeString(diffLine.Content))
|
||||||
}
|
}
|
||||||
diff1 = compareDiffLine.Content
|
diff1 = compareDiffLine.Content
|
||||||
diff2 = diffLine.Content
|
diff2 = diffLine.Content
|
||||||
case DIFF_LINE_DEL:
|
case DiffLineDel:
|
||||||
compareDiffLine = diffSection.GetLine(DIFF_LINE_ADD, diffLine.LeftIdx)
|
compareDiffLine = diffSection.GetLine(DiffLineAdd, diffLine.LeftIdx)
|
||||||
if compareDiffLine == nil {
|
if compareDiffLine == nil {
|
||||||
return template.HTML(html.EscapeString(diffLine.Content))
|
return template.HTML(html.EscapeString(diffLine.Content))
|
||||||
}
|
}
|
||||||
@@ -185,6 +190,7 @@ func (diffSection *DiffSection) GetComputedInlineDiffFor(diffLine *DiffLine) tem
|
|||||||
return diffToHTML(diffRecord, diffLine.Type)
|
return diffToHTML(diffRecord, diffLine.Type)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DiffFile represents a file diff.
|
||||||
type DiffFile struct {
|
type DiffFile struct {
|
||||||
Name string
|
Name string
|
||||||
OldName string
|
OldName string
|
||||||
@@ -200,26 +206,32 @@ type DiffFile struct {
|
|||||||
IsIncomplete bool
|
IsIncomplete bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetType returns type of diff file.
|
||||||
func (diffFile *DiffFile) GetType() int {
|
func (diffFile *DiffFile) GetType() int {
|
||||||
return int(diffFile.Type)
|
return int(diffFile.Type)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetHighlightClass returns highlight class for a filename.
|
||||||
func (diffFile *DiffFile) GetHighlightClass() string {
|
func (diffFile *DiffFile) GetHighlightClass() string {
|
||||||
return highlight.FileNameToHighlightClass(diffFile.Name)
|
return highlight.FileNameToHighlightClass(diffFile.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Diff represents a difference between two git trees.
|
||||||
type Diff struct {
|
type Diff struct {
|
||||||
TotalAddition, TotalDeletion int
|
TotalAddition, TotalDeletion int
|
||||||
Files []*DiffFile
|
Files []*DiffFile
|
||||||
IsIncomplete bool
|
IsIncomplete bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NumFiles returns number of files changes in a diff.
|
||||||
func (diff *Diff) NumFiles() int {
|
func (diff *Diff) NumFiles() int {
|
||||||
return len(diff.Files)
|
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
|
// TODO: move this function to gogits/git-module
|
||||||
func ParsePatch(maxLines, maxLineCharacteres, maxFiles int, reader io.Reader) (*Diff, error) {
|
func ParsePatch(maxLines, maxLineCharacteres, maxFiles int, reader io.Reader) (*Diff, error) {
|
||||||
var (
|
var (
|
||||||
@@ -266,7 +278,7 @@ func ParsePatch(maxLines, maxLineCharacteres, maxFiles int, reader io.Reader) (*
|
|||||||
|
|
||||||
switch {
|
switch {
|
||||||
case line[0] == ' ':
|
case line[0] == ' ':
|
||||||
diffLine := &DiffLine{Type: DIFF_LINE_PLAIN, Content: line, LeftIdx: leftLine, RightIdx: rightLine}
|
diffLine := &DiffLine{Type: DiffLinePlain, Content: line, LeftIdx: leftLine, RightIdx: rightLine}
|
||||||
leftLine++
|
leftLine++
|
||||||
rightLine++
|
rightLine++
|
||||||
curSection.Lines = append(curSection.Lines, diffLine)
|
curSection.Lines = append(curSection.Lines, diffLine)
|
||||||
@@ -275,7 +287,7 @@ func ParsePatch(maxLines, maxLineCharacteres, maxFiles int, reader io.Reader) (*
|
|||||||
curSection = &DiffSection{}
|
curSection = &DiffSection{}
|
||||||
curFile.Sections = append(curFile.Sections, curSection)
|
curFile.Sections = append(curFile.Sections, curSection)
|
||||||
ss := strings.Split(line, "@@")
|
ss := strings.Split(line, "@@")
|
||||||
diffLine := &DiffLine{Type: DIFF_LINE_SECTION, Content: line}
|
diffLine := &DiffLine{Type: DiffLineSection, Content: line}
|
||||||
curSection.Lines = append(curSection.Lines, diffLine)
|
curSection.Lines = append(curSection.Lines, diffLine)
|
||||||
|
|
||||||
// Parse line number.
|
// Parse line number.
|
||||||
@@ -291,14 +303,14 @@ func ParsePatch(maxLines, maxLineCharacteres, maxFiles int, reader io.Reader) (*
|
|||||||
case line[0] == '+':
|
case line[0] == '+':
|
||||||
curFile.Addition++
|
curFile.Addition++
|
||||||
diff.TotalAddition++
|
diff.TotalAddition++
|
||||||
diffLine := &DiffLine{Type: DIFF_LINE_ADD, Content: line, RightIdx: rightLine}
|
diffLine := &DiffLine{Type: DiffLineAdd, Content: line, RightIdx: rightLine}
|
||||||
rightLine++
|
rightLine++
|
||||||
curSection.Lines = append(curSection.Lines, diffLine)
|
curSection.Lines = append(curSection.Lines, diffLine)
|
||||||
continue
|
continue
|
||||||
case line[0] == '-':
|
case line[0] == '-':
|
||||||
curFile.Deletion++
|
curFile.Deletion++
|
||||||
diff.TotalDeletion++
|
diff.TotalDeletion++
|
||||||
diffLine := &DiffLine{Type: DIFF_LINE_DEL, Content: line, LeftIdx: leftLine}
|
diffLine := &DiffLine{Type: DiffLineDel, Content: line, LeftIdx: leftLine}
|
||||||
if leftLine > 0 {
|
if leftLine > 0 {
|
||||||
leftLine++
|
leftLine++
|
||||||
}
|
}
|
||||||
@@ -309,19 +321,19 @@ func ParsePatch(maxLines, maxLineCharacteres, maxFiles int, reader io.Reader) (*
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get new file.
|
// Get new file.
|
||||||
if strings.HasPrefix(line, DIFF_HEAD) {
|
if strings.HasPrefix(line, cmdDiffHead) {
|
||||||
middle := -1
|
middle := -1
|
||||||
|
|
||||||
// Note: In case file name is surrounded by double quotes (it happens only in git-shell).
|
// Note: In case file name is surrounded by double quotes (it happens only in git-shell).
|
||||||
// e.g. diff --git "a/xxx" "b/xxx"
|
// e.g. diff --git "a/xxx" "b/xxx"
|
||||||
hasQuote := line[len(DIFF_HEAD)] == '"'
|
hasQuote := line[len(cmdDiffHead)] == '"'
|
||||||
if hasQuote {
|
if hasQuote {
|
||||||
middle = strings.Index(line, ` "b/`)
|
middle = strings.Index(line, ` "b/`)
|
||||||
} else {
|
} else {
|
||||||
middle = strings.Index(line, " b/")
|
middle = strings.Index(line, " b/")
|
||||||
}
|
}
|
||||||
|
|
||||||
beg := len(DIFF_HEAD)
|
beg := len(cmdDiffHead)
|
||||||
a := line[beg+2 : middle]
|
a := line[beg+2 : middle]
|
||||||
b := line[middle+3:]
|
b := line[middle+3:]
|
||||||
if hasQuote {
|
if hasQuote {
|
||||||
@@ -332,7 +344,7 @@ func ParsePatch(maxLines, maxLineCharacteres, maxFiles int, reader io.Reader) (*
|
|||||||
curFile = &DiffFile{
|
curFile = &DiffFile{
|
||||||
Name: a,
|
Name: a,
|
||||||
Index: len(diff.Files) + 1,
|
Index: len(diff.Files) + 1,
|
||||||
Type: DIFF_FILE_CHANGE,
|
Type: DiffFileChange,
|
||||||
Sections: make([]*DiffSection, 0, 10),
|
Sections: make([]*DiffSection, 0, 10),
|
||||||
}
|
}
|
||||||
diff.Files = append(diff.Files, curFile)
|
diff.Files = append(diff.Files, curFile)
|
||||||
@@ -356,15 +368,15 @@ func ParsePatch(maxLines, maxLineCharacteres, maxFiles int, reader io.Reader) (*
|
|||||||
|
|
||||||
switch {
|
switch {
|
||||||
case strings.HasPrefix(line, "new file"):
|
case strings.HasPrefix(line, "new file"):
|
||||||
curFile.Type = DIFF_FILE_ADD
|
curFile.Type = DiffFileAdd
|
||||||
curFile.IsCreated = true
|
curFile.IsCreated = true
|
||||||
case strings.HasPrefix(line, "deleted"):
|
case strings.HasPrefix(line, "deleted"):
|
||||||
curFile.Type = DIFF_FILE_DEL
|
curFile.Type = DiffFileDel
|
||||||
curFile.IsDeleted = true
|
curFile.IsDeleted = true
|
||||||
case strings.HasPrefix(line, "index"):
|
case strings.HasPrefix(line, "index"):
|
||||||
curFile.Type = DIFF_FILE_CHANGE
|
curFile.Type = DiffFileChange
|
||||||
case strings.HasPrefix(line, "similarity index 100%"):
|
case strings.HasPrefix(line, "similarity index 100%"):
|
||||||
curFile.Type = DIFF_FILE_RENAME
|
curFile.Type = DiffFileRename
|
||||||
curFile.IsRenamed = true
|
curFile.IsRenamed = true
|
||||||
curFile.OldName = curFile.Name
|
curFile.OldName = curFile.Name
|
||||||
curFile.Name = b
|
curFile.Name = b
|
||||||
@@ -407,6 +419,9 @@ func ParsePatch(maxLines, maxLineCharacteres, maxFiles int, reader io.Reader) (*
|
|||||||
return diff, nil
|
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) {
|
func GetDiffRange(repoPath, beforeCommitID, afterCommitID string, maxLines, maxLineCharacteres, maxFiles int) (*Diff, error) {
|
||||||
gitRepo, err := git.OpenRepository(repoPath)
|
gitRepo, err := git.OpenRepository(repoPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -458,11 +473,13 @@ func GetDiffRange(repoPath, beforeCommitID, afterCommitID string, maxLines, maxL
|
|||||||
return diff, nil
|
return diff, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RawDiffType type of a raw diff.
|
||||||
type RawDiffType string
|
type RawDiffType string
|
||||||
|
|
||||||
|
// RawDiffType possible values.
|
||||||
const (
|
const (
|
||||||
RAW_DIFF_NORMAL RawDiffType = "diff"
|
RawDiffNormal RawDiffType = "diff"
|
||||||
RAW_DIFF_PATCH RawDiffType = "patch"
|
RawDiffPatch RawDiffType = "patch"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetRawDiff dumps diff results of repository in given commit ID to io.Writer.
|
// GetRawDiff dumps diff results of repository in given commit ID to io.Writer.
|
||||||
@@ -480,14 +497,14 @@ func GetRawDiff(repoPath, commitID string, diffType RawDiffType, writer io.Write
|
|||||||
|
|
||||||
var cmd *exec.Cmd
|
var cmd *exec.Cmd
|
||||||
switch diffType {
|
switch diffType {
|
||||||
case RAW_DIFF_NORMAL:
|
case RawDiffNormal:
|
||||||
if commit.ParentCount() == 0 {
|
if commit.ParentCount() == 0 {
|
||||||
cmd = exec.Command("git", "show", commitID)
|
cmd = exec.Command("git", "show", commitID)
|
||||||
} else {
|
} else {
|
||||||
c, _ := commit.Parent(0)
|
c, _ := commit.Parent(0)
|
||||||
cmd = exec.Command("git", "diff", "-M", c.ID.String(), commitID)
|
cmd = exec.Command("git", "diff", "-M", c.ID.String(), commitID)
|
||||||
}
|
}
|
||||||
case RAW_DIFF_PATCH:
|
case RawDiffPatch:
|
||||||
if commit.ParentCount() == 0 {
|
if commit.ParentCount() == 0 {
|
||||||
cmd = exec.Command("git", "format-patch", "--no-signature", "--stdout", "--root", commitID)
|
cmd = exec.Command("git", "format-patch", "--no-signature", "--stdout", "--root", commitID)
|
||||||
} else {
|
} else {
|
||||||
@@ -511,6 +528,7 @@ func GetRawDiff(repoPath, commitID string, diffType RawDiffType, writer io.Write
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetDiffCommit builds a Diff representing the given commitID.
|
||||||
func GetDiffCommit(repoPath, commitID string, maxLines, maxLineCharacteres, maxFiles int) (*Diff, error) {
|
func GetDiffCommit(repoPath, commitID string, maxLines, maxLineCharacteres, maxFiles int) (*Diff, error) {
|
||||||
return GetDiffRange(repoPath, "", commitID, maxLines, maxLineCharacteres, maxFiles)
|
return GetDiffRange(repoPath, "", commitID, maxLines, maxLineCharacteres, maxFiles)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,12 +24,12 @@ func TestDiffToHTML(t *testing.T) {
|
|||||||
dmp.Diff{dmp.DiffInsert, "bar"},
|
dmp.Diff{dmp.DiffInsert, "bar"},
|
||||||
dmp.Diff{dmp.DiffDelete, " baz"},
|
dmp.Diff{dmp.DiffDelete, " baz"},
|
||||||
dmp.Diff{dmp.DiffEqual, " biz"},
|
dmp.Diff{dmp.DiffEqual, " biz"},
|
||||||
}, DIFF_LINE_ADD))
|
}, DiffLineAdd))
|
||||||
|
|
||||||
assertEqual(t, "-foo <span class=\"removed-code\">bar</span> biz", diffToHTML([]dmp.Diff{
|
assertEqual(t, "-foo <span class=\"removed-code\">bar</span> biz", diffToHTML([]dmp.Diff{
|
||||||
dmp.Diff{dmp.DiffEqual, "foo "},
|
dmp.Diff{dmp.DiffEqual, "foo "},
|
||||||
dmp.Diff{dmp.DiffDelete, "bar"},
|
dmp.Diff{dmp.DiffDelete, "bar"},
|
||||||
dmp.Diff{dmp.DiffInsert, " baz"},
|
dmp.Diff{dmp.DiffInsert, " baz"},
|
||||||
dmp.Diff{dmp.DiffEqual, " biz"},
|
dmp.Diff{dmp.DiffEqual, " biz"},
|
||||||
}, DIFF_LINE_DEL))
|
}, DiffLineDel))
|
||||||
}
|
}
|
||||||
|
|||||||
341
models/issue.go
341
models/issue.go
@@ -14,18 +14,18 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
api "code.gitea.io/sdk/gitea"
|
||||||
"github.com/Unknwon/com"
|
"github.com/Unknwon/com"
|
||||||
"github.com/go-xorm/xorm"
|
"github.com/go-xorm/xorm"
|
||||||
api "github.com/gogits/go-gogs-client"
|
|
||||||
gouuid "github.com/satori/go.uuid"
|
gouuid "github.com/satori/go.uuid"
|
||||||
|
|
||||||
"github.com/gogits/gogs/modules/base"
|
"code.gitea.io/gitea/modules/base"
|
||||||
"github.com/gogits/gogs/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"github.com/gogits/gogs/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrMissingIssueNumber = errors.New("No issue number specified")
|
errMissingIssueNumber = errors.New("No issue number specified")
|
||||||
)
|
)
|
||||||
|
|
||||||
// Issue represents an issue or pull request of repository.
|
// Issue represents an issue or pull request of repository.
|
||||||
@@ -62,16 +62,20 @@ type Issue struct {
|
|||||||
Comments []*Comment `xorm:"-"`
|
Comments []*Comment `xorm:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BeforeInsert is invoked from XORM before inserting an object of this type.
|
||||||
func (issue *Issue) BeforeInsert() {
|
func (issue *Issue) BeforeInsert() {
|
||||||
issue.CreatedUnix = time.Now().Unix()
|
issue.CreatedUnix = time.Now().Unix()
|
||||||
issue.UpdatedUnix = issue.CreatedUnix
|
issue.UpdatedUnix = issue.CreatedUnix
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BeforeUpdate is invoked from XORM before updating this object.
|
||||||
func (issue *Issue) BeforeUpdate() {
|
func (issue *Issue) BeforeUpdate() {
|
||||||
issue.UpdatedUnix = time.Now().Unix()
|
issue.UpdatedUnix = time.Now().Unix()
|
||||||
issue.DeadlineUnix = issue.Deadline.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) {
|
func (issue *Issue) AfterSet(colName string, _ xorm.Cell) {
|
||||||
switch colName {
|
switch colName {
|
||||||
case "deadline_unix":
|
case "deadline_unix":
|
||||||
@@ -83,23 +87,30 @@ func (issue *Issue) AfterSet(colName string, _ xorm.Cell) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (issue *Issue) loadAttributes(e Engine) (err error) {
|
func (issue *Issue) loadRepo(e Engine) (err error) {
|
||||||
if issue.Repo == nil {
|
if issue.Repo == nil {
|
||||||
issue.Repo, err = getRepositoryByID(e, issue.RepoID)
|
issue.Repo, err = getRepositoryByID(e, issue.RepoID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("getRepositoryByID [%d]: %v", issue.RepoID, err)
|
return fmt.Errorf("getRepositoryByID [%d]: %v", issue.RepoID, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (issue *Issue) loadAttributes(e Engine) (err error) {
|
||||||
|
if err := issue.loadRepo(e); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if issue.Poster == nil {
|
if issue.Poster == nil {
|
||||||
issue.Poster, err = getUserByID(e, issue.PosterID)
|
issue.Poster, err = getUserByID(e, issue.PosterID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if IsErrUserNotExist(err) {
|
issue.PosterID = -1
|
||||||
issue.PosterID = -1
|
issue.Poster = NewGhostUser()
|
||||||
issue.Poster = NewGhostUser()
|
if !IsErrUserNotExist(err) {
|
||||||
} else {
|
|
||||||
return fmt.Errorf("getUserByID.(poster) [%d]: %v", issue.PosterID, err)
|
return fmt.Errorf("getUserByID.(poster) [%d]: %v", issue.PosterID, err)
|
||||||
}
|
}
|
||||||
|
err = nil
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -150,10 +161,12 @@ func (issue *Issue) loadAttributes(e Engine) (err error) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LoadAttributes loads the attribute of this issue.
|
||||||
func (issue *Issue) LoadAttributes() error {
|
func (issue *Issue) LoadAttributes() error {
|
||||||
return issue.loadAttributes(x)
|
return issue.loadAttributes(x)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HTMLURL returns the absolute URL to this issue.
|
||||||
func (issue *Issue) HTMLURL() string {
|
func (issue *Issue) HTMLURL() string {
|
||||||
var path string
|
var path string
|
||||||
if issue.IsPull {
|
if issue.IsPull {
|
||||||
@@ -164,15 +177,31 @@ func (issue *Issue) HTMLURL() string {
|
|||||||
return fmt.Sprintf("%s/%s/%d", issue.Repo.HTMLURL(), path, issue.Index)
|
return fmt.Sprintf("%s/%s/%d", issue.Repo.HTMLURL(), path, issue.Index)
|
||||||
}
|
}
|
||||||
|
|
||||||
// State returns string representation of issue status.
|
// DiffURL returns the absolute URL to this diff
|
||||||
func (i *Issue) State() api.StateType {
|
func (issue *Issue) DiffURL() string {
|
||||||
if i.IsClosed {
|
if issue.IsPull {
|
||||||
return api.STATE_CLOSED
|
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,
|
// Required - Poster, Labels,
|
||||||
// Optional - Milestone, Assignee, PullRequest
|
// Optional - Milestone, Assignee, PullRequest
|
||||||
func (issue *Issue) APIFormat() *api.Issue {
|
func (issue *Issue) APIFormat() *api.Issue {
|
||||||
@@ -213,22 +242,22 @@ func (issue *Issue) APIFormat() *api.Issue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// HashTag returns unique hash tag for issue.
|
// HashTag returns unique hash tag for issue.
|
||||||
func (i *Issue) HashTag() string {
|
func (issue *Issue) HashTag() string {
|
||||||
return "issue-" + com.ToStr(i.ID)
|
return "issue-" + com.ToStr(issue.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsPoster returns true if given user by ID is the poster.
|
// IsPoster returns true if given user by ID is the poster.
|
||||||
func (i *Issue) IsPoster(uid int64) bool {
|
func (issue *Issue) IsPoster(uid int64) bool {
|
||||||
return i.PosterID == uid
|
return issue.PosterID == uid
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Issue) hasLabel(e Engine, labelID int64) bool {
|
func (issue *Issue) hasLabel(e Engine, labelID int64) bool {
|
||||||
return hasIssueLabel(e, i.ID, labelID)
|
return hasIssueLabel(e, issue.ID, labelID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasLabel returns true if issue has been labeled by given ID.
|
// HasLabel returns true if issue has been labeled by given ID.
|
||||||
func (i *Issue) HasLabel(labelID int64) bool {
|
func (issue *Issue) HasLabel(labelID int64) bool {
|
||||||
return i.hasLabel(x, labelID)
|
return issue.hasLabel(x, labelID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (issue *Issue) sendLabelUpdatedWebhook(doer *User) {
|
func (issue *Issue) sendLabelUpdatedWebhook(doer *User) {
|
||||||
@@ -239,11 +268,11 @@ func (issue *Issue) sendLabelUpdatedWebhook(doer *User) {
|
|||||||
log.Error(4, "LoadIssue: %v", err)
|
log.Error(4, "LoadIssue: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err = PrepareWebhooks(issue.Repo, HOOK_EVENT_PULL_REQUEST, &api.PullRequestPayload{
|
err = PrepareWebhooks(issue.Repo, HookEventPullRequest, &api.PullRequestPayload{
|
||||||
Action: api.HOOK_ISSUE_LABEL_UPDATED,
|
Action: api.HookIssueLabelUpdated,
|
||||||
Index: issue.Index,
|
Index: issue.Index,
|
||||||
PullRequest: issue.PullRequest.APIFormat(),
|
PullRequest: issue.PullRequest.APIFormat(),
|
||||||
Repository: issue.Repo.APIFormat(nil),
|
Repository: issue.Repo.APIFormat(AccessModeNone),
|
||||||
Sender: doer.APIFormat(),
|
Sender: doer.APIFormat(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -254,8 +283,8 @@ func (issue *Issue) sendLabelUpdatedWebhook(doer *User) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Issue) addLabel(e *xorm.Session, label *Label) error {
|
func (issue *Issue) addLabel(e *xorm.Session, label *Label) error {
|
||||||
return newIssueLabel(e, i, label)
|
return newIssueLabel(e, issue, label)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddLabel adds a new label to the issue.
|
// AddLabel adds a new label to the issue.
|
||||||
@@ -300,6 +329,16 @@ func (issue *Issue) removeLabel(e *xorm.Session, label *Label) error {
|
|||||||
|
|
||||||
// RemoveLabel removes a label from issue by given ID.
|
// RemoveLabel removes a label from issue by given ID.
|
||||||
func (issue *Issue) RemoveLabel(doer *User, label *Label) error {
|
func (issue *Issue) RemoveLabel(doer *User, label *Label) error {
|
||||||
|
if err := issue.loadRepo(x); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if has, err := HasAccess(doer, issue.Repo, AccessModeWrite); err != nil {
|
||||||
|
return err
|
||||||
|
} else if !has {
|
||||||
|
return ErrLabelNotExist{}
|
||||||
|
}
|
||||||
|
|
||||||
if err := DeleteIssueLabel(issue, label); err != nil {
|
if err := DeleteIssueLabel(issue, label); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -322,6 +361,8 @@ func (issue *Issue) clearLabels(e *xorm.Session) (err error) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ClearLabels removes all issue labels as the given user.
|
||||||
|
// Triggers appropriate WebHooks, if any.
|
||||||
func (issue *Issue) ClearLabels(doer *User) (err error) {
|
func (issue *Issue) ClearLabels(doer *User) (err error) {
|
||||||
sess := x.NewSession()
|
sess := x.NewSession()
|
||||||
defer sessionRelease(sess)
|
defer sessionRelease(sess)
|
||||||
@@ -329,6 +370,16 @@ func (issue *Issue) ClearLabels(doer *User) (err error) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := issue.loadRepo(sess); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if has, err := hasAccess(sess, doer, issue.Repo, AccessModeWrite); err != nil {
|
||||||
|
return err
|
||||||
|
} else if !has {
|
||||||
|
return ErrLabelNotExist{}
|
||||||
|
}
|
||||||
|
|
||||||
if err = issue.clearLabels(sess); err != nil {
|
if err = issue.clearLabels(sess); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -343,11 +394,11 @@ func (issue *Issue) ClearLabels(doer *User) (err error) {
|
|||||||
log.Error(4, "LoadIssue: %v", err)
|
log.Error(4, "LoadIssue: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err = PrepareWebhooks(issue.Repo, HOOK_EVENT_PULL_REQUEST, &api.PullRequestPayload{
|
err = PrepareWebhooks(issue.Repo, HookEventPullRequest, &api.PullRequestPayload{
|
||||||
Action: api.HOOK_ISSUE_LABEL_CLEARED,
|
Action: api.HookIssueLabelCleared,
|
||||||
Index: issue.Index,
|
Index: issue.Index,
|
||||||
PullRequest: issue.PullRequest.APIFormat(),
|
PullRequest: issue.PullRequest.APIFormat(),
|
||||||
Repository: issue.Repo.APIFormat(nil),
|
Repository: issue.Repo.APIFormat(AccessModeNone),
|
||||||
Sender: doer.APIFormat(),
|
Sender: doer.APIFormat(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -361,6 +412,7 @@ func (issue *Issue) ClearLabels(doer *User) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ReplaceLabels removes all current labels and add new labels to the issue.
|
// 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) {
|
func (issue *Issue) ReplaceLabels(labels []*Label) (err error) {
|
||||||
sess := x.NewSession()
|
sess := x.NewSession()
|
||||||
defer sessionRelease(sess)
|
defer sessionRelease(sess)
|
||||||
@@ -377,12 +429,13 @@ func (issue *Issue) ReplaceLabels(labels []*Label) (err error) {
|
|||||||
return sess.Commit()
|
return sess.Commit()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Issue) GetAssignee() (err error) {
|
// GetAssignee sets the Assignee attribute of this issue.
|
||||||
if i.AssigneeID == 0 || i.Assignee != nil {
|
func (issue *Issue) GetAssignee() (err error) {
|
||||||
|
if issue.AssigneeID == 0 || issue.Assignee != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
i.Assignee, err = GetUserByID(i.AssigneeID)
|
issue.Assignee, err = GetUserByID(issue.AssigneeID)
|
||||||
if IsErrUserNotExist(err) {
|
if IsErrUserNotExist(err) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -390,8 +443,8 @@ func (i *Issue) GetAssignee() (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ReadBy sets issue to be read by given user.
|
// ReadBy sets issue to be read by given user.
|
||||||
func (i *Issue) ReadBy(uid int64) error {
|
func (issue *Issue) ReadBy(uid int64) error {
|
||||||
return UpdateIssueUserByRead(uid, i.ID)
|
return UpdateIssueUserByRead(uid, issue.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateIssueCols(e Engine, issue *Issue, cols ...string) error {
|
func updateIssueCols(e Engine, issue *Issue, cols ...string) error {
|
||||||
@@ -404,41 +457,41 @@ func UpdateIssueCols(issue *Issue, cols ...string) error {
|
|||||||
return updateIssueCols(x, issue, cols...)
|
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
|
// Nothing should be performed if current status is same as target status
|
||||||
if i.IsClosed == isClosed {
|
if issue.IsClosed == isClosed {
|
||||||
return nil
|
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
|
return err
|
||||||
} else if err = updateIssueUsersByStatus(e, i.ID, isClosed); err != nil {
|
} else if err = updateIssueUsersByStatus(e, issue.ID, isClosed); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update issue count of labels
|
// Update issue count of labels
|
||||||
if err = i.getLabels(e); err != nil {
|
if err = issue.getLabels(e); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for idx := range i.Labels {
|
for idx := range issue.Labels {
|
||||||
if i.IsClosed {
|
if issue.IsClosed {
|
||||||
i.Labels[idx].NumClosedIssues++
|
issue.Labels[idx].NumClosedIssues++
|
||||||
} else {
|
} 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
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update issue count of milestone
|
// Update issue count of milestone
|
||||||
if err = changeMilestoneIssueStats(e, i); err != nil {
|
if err = changeMilestoneIssueStats(e, issue); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// New action comment
|
// New action comment
|
||||||
if _, err = createStatusComment(e, doer, repo, i); err != nil {
|
if _, err = createStatusComment(e, doer, repo, issue); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -467,15 +520,15 @@ func (issue *Issue) ChangeStatus(doer *User, repo *Repository, isClosed bool) (e
|
|||||||
apiPullRequest := &api.PullRequestPayload{
|
apiPullRequest := &api.PullRequestPayload{
|
||||||
Index: issue.Index,
|
Index: issue.Index,
|
||||||
PullRequest: issue.PullRequest.APIFormat(),
|
PullRequest: issue.PullRequest.APIFormat(),
|
||||||
Repository: repo.APIFormat(nil),
|
Repository: repo.APIFormat(AccessModeNone),
|
||||||
Sender: doer.APIFormat(),
|
Sender: doer.APIFormat(),
|
||||||
}
|
}
|
||||||
if isClosed {
|
if isClosed {
|
||||||
apiPullRequest.Action = api.HOOK_ISSUE_CLOSED
|
apiPullRequest.Action = api.HookIssueClosed
|
||||||
} else {
|
} else {
|
||||||
apiPullRequest.Action = api.HOOK_ISSUE_REOPENED
|
apiPullRequest.Action = api.HookIssueReOpened
|
||||||
}
|
}
|
||||||
err = PrepareWebhooks(repo, HOOK_EVENT_PULL_REQUEST, apiPullRequest)
|
err = PrepareWebhooks(repo, HookEventPullRequest, apiPullRequest)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(4, "PrepareWebhooks [is_pull: %v, is_closed: %v]: %v", issue.IsPull, isClosed, err)
|
log.Error(4, "PrepareWebhooks [is_pull: %v, is_closed: %v]: %v", issue.IsPull, isClosed, err)
|
||||||
@@ -486,6 +539,7 @@ func (issue *Issue) ChangeStatus(doer *User, repo *Repository, isClosed bool) (e
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ChangeTitle changes the title of this issue, as the given user.
|
||||||
func (issue *Issue) ChangeTitle(doer *User, title string) (err error) {
|
func (issue *Issue) ChangeTitle(doer *User, title string) (err error) {
|
||||||
oldTitle := issue.Title
|
oldTitle := issue.Title
|
||||||
issue.Title = title
|
issue.Title = title
|
||||||
@@ -495,8 +549,8 @@ func (issue *Issue) ChangeTitle(doer *User, title string) (err error) {
|
|||||||
|
|
||||||
if issue.IsPull {
|
if issue.IsPull {
|
||||||
issue.PullRequest.Issue = issue
|
issue.PullRequest.Issue = issue
|
||||||
err = PrepareWebhooks(issue.Repo, HOOK_EVENT_PULL_REQUEST, &api.PullRequestPayload{
|
err = PrepareWebhooks(issue.Repo, HookEventPullRequest, &api.PullRequestPayload{
|
||||||
Action: api.HOOK_ISSUE_EDITED,
|
Action: api.HookIssueEdited,
|
||||||
Index: issue.Index,
|
Index: issue.Index,
|
||||||
Changes: &api.ChangesPayload{
|
Changes: &api.ChangesPayload{
|
||||||
Title: &api.ChangesFromPayload{
|
Title: &api.ChangesFromPayload{
|
||||||
@@ -504,7 +558,7 @@ func (issue *Issue) ChangeTitle(doer *User, title string) (err error) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
PullRequest: issue.PullRequest.APIFormat(),
|
PullRequest: issue.PullRequest.APIFormat(),
|
||||||
Repository: issue.Repo.APIFormat(nil),
|
Repository: issue.Repo.APIFormat(AccessModeNone),
|
||||||
Sender: doer.APIFormat(),
|
Sender: doer.APIFormat(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -517,6 +571,7 @@ func (issue *Issue) ChangeTitle(doer *User, title string) (err error) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ChangeContent changes issue content, as the given user.
|
||||||
func (issue *Issue) ChangeContent(doer *User, content string) (err error) {
|
func (issue *Issue) ChangeContent(doer *User, content string) (err error) {
|
||||||
oldContent := issue.Content
|
oldContent := issue.Content
|
||||||
issue.Content = content
|
issue.Content = content
|
||||||
@@ -526,8 +581,8 @@ func (issue *Issue) ChangeContent(doer *User, content string) (err error) {
|
|||||||
|
|
||||||
if issue.IsPull {
|
if issue.IsPull {
|
||||||
issue.PullRequest.Issue = issue
|
issue.PullRequest.Issue = issue
|
||||||
err = PrepareWebhooks(issue.Repo, HOOK_EVENT_PULL_REQUEST, &api.PullRequestPayload{
|
err = PrepareWebhooks(issue.Repo, HookEventPullRequest, &api.PullRequestPayload{
|
||||||
Action: api.HOOK_ISSUE_EDITED,
|
Action: api.HookIssueEdited,
|
||||||
Index: issue.Index,
|
Index: issue.Index,
|
||||||
Changes: &api.ChangesPayload{
|
Changes: &api.ChangesPayload{
|
||||||
Body: &api.ChangesFromPayload{
|
Body: &api.ChangesFromPayload{
|
||||||
@@ -535,7 +590,7 @@ func (issue *Issue) ChangeContent(doer *User, content string) (err error) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
PullRequest: issue.PullRequest.APIFormat(),
|
PullRequest: issue.PullRequest.APIFormat(),
|
||||||
Repository: issue.Repo.APIFormat(nil),
|
Repository: issue.Repo.APIFormat(AccessModeNone),
|
||||||
Sender: doer.APIFormat(),
|
Sender: doer.APIFormat(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -548,6 +603,7 @@ func (issue *Issue) ChangeContent(doer *User, content string) (err error) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ChangeAssignee changes the Asssignee field of this issue.
|
||||||
func (issue *Issue) ChangeAssignee(doer *User, assigneeID int64) (err error) {
|
func (issue *Issue) ChangeAssignee(doer *User, assigneeID int64) (err error) {
|
||||||
issue.AssigneeID = assigneeID
|
issue.AssigneeID = assigneeID
|
||||||
if err = UpdateIssueUserByAssignee(issue); err != nil {
|
if err = UpdateIssueUserByAssignee(issue); err != nil {
|
||||||
@@ -567,15 +623,15 @@ func (issue *Issue) ChangeAssignee(doer *User, assigneeID int64) (err error) {
|
|||||||
apiPullRequest := &api.PullRequestPayload{
|
apiPullRequest := &api.PullRequestPayload{
|
||||||
Index: issue.Index,
|
Index: issue.Index,
|
||||||
PullRequest: issue.PullRequest.APIFormat(),
|
PullRequest: issue.PullRequest.APIFormat(),
|
||||||
Repository: issue.Repo.APIFormat(nil),
|
Repository: issue.Repo.APIFormat(AccessModeNone),
|
||||||
Sender: doer.APIFormat(),
|
Sender: doer.APIFormat(),
|
||||||
}
|
}
|
||||||
if isRemoveAssignee {
|
if isRemoveAssignee {
|
||||||
apiPullRequest.Action = api.HOOK_ISSUE_UNASSIGNED
|
apiPullRequest.Action = api.HookIssueUnassigned
|
||||||
} else {
|
} else {
|
||||||
apiPullRequest.Action = api.HOOK_ISSUE_ASSIGNED
|
apiPullRequest.Action = api.HookIssueAssigned
|
||||||
}
|
}
|
||||||
err = PrepareWebhooks(issue.Repo, HOOK_EVENT_PULL_REQUEST, apiPullRequest)
|
err = PrepareWebhooks(issue.Repo, HookEventPullRequest, apiPullRequest)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(4, "PrepareWebhooks [is_pull: %v, remove_assignee: %v]: %v", issue.IsPull, isRemoveAssignee, err)
|
log.Error(4, "PrepareWebhooks [is_pull: %v, remove_assignee: %v]: %v", issue.IsPull, isRemoveAssignee, err)
|
||||||
@@ -586,6 +642,7 @@ func (issue *Issue) ChangeAssignee(doer *User, assigneeID int64) (err error) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewIssueOptions represents the options of a new issue.
|
||||||
type NewIssueOptions struct {
|
type NewIssueOptions struct {
|
||||||
Repo *Repository
|
Repo *Repository
|
||||||
Issue *Issue
|
Issue *Issue
|
||||||
@@ -624,7 +681,7 @@ func newIssue(e *xorm.Session, opts NewIssueOptions) (err error) {
|
|||||||
// Assume assignee is invalid and drop silently.
|
// Assume assignee is invalid and drop silently.
|
||||||
opts.Issue.AssigneeID = 0
|
opts.Issue.AssigneeID = 0
|
||||||
if assignee != nil {
|
if assignee != nil {
|
||||||
valid, err := hasAccess(e, assignee, opts.Repo, ACCESS_MODE_WRITE)
|
valid, err := hasAccess(e, assignee, opts.Repo, AccessModeWrite)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("hasAccess [user_id: %d, repo_id: %d]: %v", assignee.ID, opts.Repo.ID, err)
|
return fmt.Errorf("hasAccess [user_id: %d, repo_id: %d]: %v", assignee.ID, opts.Repo.ID, err)
|
||||||
}
|
}
|
||||||
@@ -650,7 +707,7 @@ func newIssue(e *xorm.Session, opts NewIssueOptions) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(opts.LableIDs) > 0 {
|
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.
|
// So we have to get all needed labels first.
|
||||||
labels := make([]*Label, 0, len(opts.LableIDs))
|
labels := make([]*Label, 0, len(opts.LableIDs))
|
||||||
if err = e.In("id", opts.LableIDs).Find(&labels); err != nil {
|
if err = e.In("id", opts.LableIDs).Find(&labels); err != nil {
|
||||||
@@ -714,7 +771,7 @@ func NewIssue(repo *Repository, issue *Issue, labelIDs []int64, uuids []string)
|
|||||||
if err = NotifyWatchers(&Action{
|
if err = NotifyWatchers(&Action{
|
||||||
ActUserID: issue.Poster.ID,
|
ActUserID: issue.Poster.ID,
|
||||||
ActUserName: issue.Poster.Name,
|
ActUserName: issue.Poster.Name,
|
||||||
OpType: ACTION_CREATE_ISSUE,
|
OpType: ActionCreateIssue,
|
||||||
Content: fmt.Sprintf("%d|%s", issue.Index, issue.Title),
|
Content: fmt.Sprintf("%d|%s", issue.Index, issue.Title),
|
||||||
RepoID: repo.ID,
|
RepoID: repo.ID,
|
||||||
RepoUserName: repo.Owner.Name,
|
RepoUserName: repo.Owner.Name,
|
||||||
@@ -735,7 +792,7 @@ func NewIssue(repo *Repository, issue *Issue, labelIDs []int64, uuids []string)
|
|||||||
func GetIssueByRef(ref string) (*Issue, error) {
|
func GetIssueByRef(ref string) (*Issue, error) {
|
||||||
n := strings.IndexByte(ref, byte('#'))
|
n := strings.IndexByte(ref, byte('#'))
|
||||||
if n == -1 {
|
if n == -1 {
|
||||||
return nil, ErrMissingIssueNumber
|
return nil, errMissingIssueNumber
|
||||||
}
|
}
|
||||||
|
|
||||||
index, err := com.StrTo(ref[n+1:]).Int64()
|
index, err := com.StrTo(ref[n+1:]).Int64()
|
||||||
@@ -756,7 +813,7 @@ func GetIssueByRef(ref string) (*Issue, error) {
|
|||||||
return issue, issue.LoadAttributes()
|
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) {
|
func GetRawIssueByIndex(repoID, index int64) (*Issue, error) {
|
||||||
issue := &Issue{
|
issue := &Issue{
|
||||||
RepoID: repoID,
|
RepoID: repoID,
|
||||||
@@ -796,6 +853,7 @@ func GetIssueByID(id int64) (*Issue, error) {
|
|||||||
return getIssueByID(x, id)
|
return getIssueByID(x, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IssuesOptions represents options of an issue.
|
||||||
type IssuesOptions struct {
|
type IssuesOptions struct {
|
||||||
UserID int64
|
UserID int64
|
||||||
AssigneeID int64
|
AssigneeID int64
|
||||||
@@ -820,16 +878,12 @@ func Issues(opts *IssuesOptions) ([]*Issue, error) {
|
|||||||
sess := x.Limit(setting.UI.IssuePagingNum, (opts.Page-1)*setting.UI.IssuePagingNum)
|
sess := x.Limit(setting.UI.IssuePagingNum, (opts.Page-1)*setting.UI.IssuePagingNum)
|
||||||
|
|
||||||
if opts.RepoID > 0 {
|
if opts.RepoID > 0 {
|
||||||
sess.Where("issue.repo_id=?", opts.RepoID).And("issue.is_closed=?", opts.IsClosed)
|
sess.And("issue.repo_id=?", opts.RepoID)
|
||||||
} else if opts.RepoIDs != nil {
|
} else if len(opts.RepoIDs) > 0 {
|
||||||
// In case repository IDs are provided but actually no repository has issue.
|
// In case repository IDs are provided but actually no repository has issue.
|
||||||
if len(opts.RepoIDs) == 0 {
|
sess.In("issue.repo_id", opts.RepoIDs)
|
||||||
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.And("issue.is_closed=?", opts.IsClosed)
|
||||||
|
|
||||||
if opts.AssigneeID > 0 {
|
if opts.AssigneeID > 0 {
|
||||||
sess.And("issue.assignee_id=?", opts.AssigneeID)
|
sess.And("issue.assignee_id=?", opts.AssigneeID)
|
||||||
@@ -861,14 +915,21 @@ func Issues(opts *IssuesOptions) ([]*Issue, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(opts.Labels) > 0 && opts.Labels != "0" {
|
if len(opts.Labels) > 0 && opts.Labels != "0" {
|
||||||
labelIDs := base.StringsToInt64s(strings.Split(opts.Labels, ","))
|
labelIDs, err := base.StringsToInt64s(strings.Split(opts.Labels, ","))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
if len(labelIDs) > 0 {
|
if len(labelIDs) > 0 {
|
||||||
sess.Join("INNER", "issue_label", "issue.id = issue_label.issue_id").In("issue_label.label_id", labelIDs)
|
sess.
|
||||||
|
Join("INNER", "issue_label", "issue.id = issue_label.issue_id").
|
||||||
|
In("issue_label.label_id", labelIDs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.IsMention {
|
if opts.IsMention {
|
||||||
sess.Join("INNER", "issue_user", "issue.id = issue_user.issue_id").And("issue_user.is_mentioned = ?", true)
|
sess.
|
||||||
|
Join("INNER", "issue_user", "issue.id = issue_user.issue_id").
|
||||||
|
And("issue_user.is_mentioned = ?", true)
|
||||||
|
|
||||||
if opts.UserID > 0 {
|
if opts.UserID > 0 {
|
||||||
sess.And("issue_user.uid = ?", opts.UserID)
|
sess.And("issue_user.uid = ?", opts.UserID)
|
||||||
@@ -967,9 +1028,9 @@ func NewIssueUsers(repo *Repository, issue *Issue) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// PairsContains returns true when pairs list contains given issue.
|
// 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 {
|
for i := range ius {
|
||||||
if ius[i].IssueID == issueId &&
|
if ius[i].IssueID == issueID &&
|
||||||
ius[i].UID == uid {
|
ius[i].UID == uid {
|
||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
@@ -991,7 +1052,10 @@ func GetIssueUserPairsByRepoIds(rids []int64, isClosed bool, page int) ([]*Issue
|
|||||||
}
|
}
|
||||||
|
|
||||||
ius := make([]*IssueUser, 0, 10)
|
ius := make([]*IssueUser, 0, 10)
|
||||||
sess := x.Limit(20, (page-1)*20).Where("is_closed=?", isClosed).In("repo_id", rids)
|
sess := x.
|
||||||
|
Limit(20, (page-1)*20).
|
||||||
|
Where("is_closed=?", isClosed).
|
||||||
|
In("repo_id", rids)
|
||||||
err := sess.Find(&ius)
|
err := sess.Find(&ius)
|
||||||
return ius, err
|
return ius, err
|
||||||
}
|
}
|
||||||
@@ -999,15 +1063,18 @@ func GetIssueUserPairsByRepoIds(rids []int64, isClosed bool, page int) ([]*Issue
|
|||||||
// GetIssueUserPairsByMode returns issue-user pairs by given repository and user.
|
// GetIssueUserPairsByMode returns issue-user pairs by given repository and user.
|
||||||
func GetIssueUserPairsByMode(uid, rid int64, isClosed bool, page, filterMode int) ([]*IssueUser, error) {
|
func GetIssueUserPairsByMode(uid, rid int64, isClosed bool, page, filterMode int) ([]*IssueUser, error) {
|
||||||
ius := make([]*IssueUser, 0, 10)
|
ius := make([]*IssueUser, 0, 10)
|
||||||
sess := x.Limit(20, (page-1)*20).Where("uid=?", uid).And("is_closed=?", isClosed)
|
sess := x.
|
||||||
|
Limit(20, (page-1)*20).
|
||||||
|
Where("uid=?", uid).
|
||||||
|
And("is_closed=?", isClosed)
|
||||||
if rid > 0 {
|
if rid > 0 {
|
||||||
sess.And("repo_id=?", rid)
|
sess.And("repo_id=?", rid)
|
||||||
}
|
}
|
||||||
|
|
||||||
switch filterMode {
|
switch filterMode {
|
||||||
case FM_ASSIGN:
|
case FilterModeAssign:
|
||||||
sess.And("is_assigned=?", true)
|
sess.And("is_assigned=?", true)
|
||||||
case FM_CREATE:
|
case FilterModeCreate:
|
||||||
sess.And("is_poster=?", true)
|
sess.And("is_poster=?", true)
|
||||||
default:
|
default:
|
||||||
return ius, nil
|
return ius, nil
|
||||||
@@ -1018,7 +1085,7 @@ func GetIssueUserPairsByMode(uid, rid int64, isClosed bool, page, filterMode int
|
|||||||
|
|
||||||
// UpdateIssueMentions extracts mentioned people from content and
|
// UpdateIssueMentions extracts mentioned people from content and
|
||||||
// updates issue-user relations for them.
|
// updates issue-user relations for them.
|
||||||
func UpdateIssueMentions(issueID int64, mentions []string) error {
|
func UpdateIssueMentions(e Engine, issueID int64, mentions []string) error {
|
||||||
if len(mentions) == 0 {
|
if len(mentions) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -1028,7 +1095,7 @@ func UpdateIssueMentions(issueID int64, mentions []string) error {
|
|||||||
}
|
}
|
||||||
users := make([]*User, 0, len(mentions))
|
users := make([]*User, 0, len(mentions))
|
||||||
|
|
||||||
if err := x.In("lower_name", mentions).Asc("lower_name").Find(&users); err != nil {
|
if err := e.In("lower_name", mentions).Asc("lower_name").Find(&users); err != nil {
|
||||||
return fmt.Errorf("find mentioned users: %v", err)
|
return fmt.Errorf("find mentioned users: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1052,7 +1119,7 @@ func UpdateIssueMentions(issueID int64, mentions []string) error {
|
|||||||
ids = append(ids, memberIDs...)
|
ids = append(ids, memberIDs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := UpdateIssueUsersByMentions(issueID, ids); err != nil {
|
if err := UpdateIssueUsersByMentions(e, issueID, ids); err != nil {
|
||||||
return fmt.Errorf("UpdateIssueUsersByMentions: %v", err)
|
return fmt.Errorf("UpdateIssueUsersByMentions: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1070,10 +1137,10 @@ type IssueStats struct {
|
|||||||
|
|
||||||
// Filter modes.
|
// Filter modes.
|
||||||
const (
|
const (
|
||||||
FM_ALL = iota
|
FilterModeAll = iota
|
||||||
FM_ASSIGN
|
FilterModeAssign
|
||||||
FM_CREATE
|
FilterModeCreate
|
||||||
FM_MENTION
|
FilterModeMention
|
||||||
)
|
)
|
||||||
|
|
||||||
func parseCountResult(results []map[string][]byte) int64 {
|
func parseCountResult(results []map[string][]byte) int64 {
|
||||||
@@ -1086,6 +1153,7 @@ func parseCountResult(results []map[string][]byte) int64 {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IssueStatsOptions contains parameters accepted by GetIssueStats.
|
||||||
type IssueStatsOptions struct {
|
type IssueStatsOptions struct {
|
||||||
RepoID int64
|
RepoID int64
|
||||||
UserID int64
|
UserID int64
|
||||||
@@ -1101,12 +1169,17 @@ func GetIssueStats(opts *IssueStatsOptions) *IssueStats {
|
|||||||
stats := &IssueStats{}
|
stats := &IssueStats{}
|
||||||
|
|
||||||
countSession := func(opts *IssueStatsOptions) *xorm.Session {
|
countSession := func(opts *IssueStatsOptions) *xorm.Session {
|
||||||
sess := x.Where("issue.repo_id = ?", opts.RepoID).And("is_pull = ?", opts.IsPull)
|
sess := x.
|
||||||
|
Where("issue.repo_id = ?", opts.RepoID).
|
||||||
|
And("is_pull = ?", opts.IsPull)
|
||||||
|
|
||||||
if len(opts.Labels) > 0 && opts.Labels != "0" {
|
if len(opts.Labels) > 0 && opts.Labels != "0" {
|
||||||
labelIDs := base.StringsToInt64s(strings.Split(opts.Labels, ","))
|
labelIDs, err := base.StringsToInt64s(strings.Split(opts.Labels, ","))
|
||||||
if len(labelIDs) > 0 {
|
if err != nil {
|
||||||
sess.Join("INNER", "issue_label", "issue.id = issue_id").In("label_id", labelIDs)
|
log.Warn("Malformed Labels argument: %s", opts.Labels)
|
||||||
|
} else if len(labelIDs) > 0 {
|
||||||
|
sess.Join("INNER", "issue_label", "issue.id = issue_id").
|
||||||
|
In("label_id", labelIDs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1122,7 +1195,7 @@ func GetIssueStats(opts *IssueStatsOptions) *IssueStats {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch opts.FilterMode {
|
switch opts.FilterMode {
|
||||||
case FM_ALL, FM_ASSIGN:
|
case FilterModeAll, FilterModeAssign:
|
||||||
stats.OpenCount, _ = countSession(opts).
|
stats.OpenCount, _ = countSession(opts).
|
||||||
And("is_closed = ?", false).
|
And("is_closed = ?", false).
|
||||||
Count(&Issue{})
|
Count(&Issue{})
|
||||||
@@ -1130,7 +1203,7 @@ func GetIssueStats(opts *IssueStatsOptions) *IssueStats {
|
|||||||
stats.ClosedCount, _ = countSession(opts).
|
stats.ClosedCount, _ = countSession(opts).
|
||||||
And("is_closed = ?", true).
|
And("is_closed = ?", true).
|
||||||
Count(&Issue{})
|
Count(&Issue{})
|
||||||
case FM_CREATE:
|
case FilterModeCreate:
|
||||||
stats.OpenCount, _ = countSession(opts).
|
stats.OpenCount, _ = countSession(opts).
|
||||||
And("poster_id = ?", opts.UserID).
|
And("poster_id = ?", opts.UserID).
|
||||||
And("is_closed = ?", false).
|
And("is_closed = ?", false).
|
||||||
@@ -1140,7 +1213,7 @@ func GetIssueStats(opts *IssueStatsOptions) *IssueStats {
|
|||||||
And("poster_id = ?", opts.UserID).
|
And("poster_id = ?", opts.UserID).
|
||||||
And("is_closed = ?", true).
|
And("is_closed = ?", true).
|
||||||
Count(&Issue{})
|
Count(&Issue{})
|
||||||
case FM_MENTION:
|
case FilterModeMention:
|
||||||
stats.OpenCount, _ = countSession(opts).
|
stats.OpenCount, _ = countSession(opts).
|
||||||
Join("INNER", "issue_user", "issue.id = issue_user.issue_id").
|
Join("INNER", "issue_user", "issue.id = issue_user.issue_id").
|
||||||
And("issue_user.uid = ?", opts.UserID).
|
And("issue_user.uid = ?", opts.UserID).
|
||||||
@@ -1163,11 +1236,13 @@ func GetUserIssueStats(repoID, uid int64, repoIDs []int64, filterMode int, isPul
|
|||||||
stats := &IssueStats{}
|
stats := &IssueStats{}
|
||||||
|
|
||||||
countSession := func(isClosed, isPull bool, repoID int64, repoIDs []int64) *xorm.Session {
|
countSession := func(isClosed, isPull bool, repoID int64, repoIDs []int64) *xorm.Session {
|
||||||
sess := x.Where("issue.is_closed = ?", isClosed).And("issue.is_pull = ?", isPull)
|
sess := x.
|
||||||
|
Where("issue.is_closed = ?", isClosed).
|
||||||
|
And("issue.is_pull = ?", isPull)
|
||||||
|
|
||||||
if repoID > 0 || len(repoIDs) == 0 {
|
if repoID > 0 {
|
||||||
sess.And("repo_id = ?", repoID)
|
sess.And("repo_id = ?", repoID)
|
||||||
} else {
|
} else if len(repoIDs) > 0 {
|
||||||
sess.In("repo_id", repoIDs)
|
sess.In("repo_id", repoIDs)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1186,10 +1261,10 @@ func GetUserIssueStats(repoID, uid int64, repoIDs []int64, filterMode int, isPul
|
|||||||
closedCountSession := countSession(true, isPull, repoID, repoIDs)
|
closedCountSession := countSession(true, isPull, repoID, repoIDs)
|
||||||
|
|
||||||
switch filterMode {
|
switch filterMode {
|
||||||
case FM_ASSIGN:
|
case FilterModeAssign:
|
||||||
openCountSession.And("assignee_id = ?", uid)
|
openCountSession.And("assignee_id = ?", uid)
|
||||||
closedCountSession.And("assignee_id = ?", uid)
|
closedCountSession.And("assignee_id = ?", uid)
|
||||||
case FM_CREATE:
|
case FilterModeCreate:
|
||||||
openCountSession.And("poster_id = ?", uid)
|
openCountSession.And("poster_id = ?", uid)
|
||||||
closedCountSession.And("poster_id = ?", uid)
|
closedCountSession.And("poster_id = ?", uid)
|
||||||
}
|
}
|
||||||
@@ -1203,7 +1278,8 @@ func GetUserIssueStats(repoID, uid int64, repoIDs []int64, filterMode int, isPul
|
|||||||
// GetRepoIssueStats returns number of open and closed repository issues by given filter mode.
|
// GetRepoIssueStats returns number of open and closed repository issues by given filter mode.
|
||||||
func GetRepoIssueStats(repoID, uid int64, filterMode int, isPull bool) (numOpen int64, numClosed int64) {
|
func GetRepoIssueStats(repoID, uid int64, filterMode int, isPull bool) (numOpen int64, numClosed int64) {
|
||||||
countSession := func(isClosed, isPull bool, repoID int64) *xorm.Session {
|
countSession := func(isClosed, isPull bool, repoID int64) *xorm.Session {
|
||||||
sess := x.Where("issue.repo_id = ?", isClosed).
|
sess := x.
|
||||||
|
Where("is_closed = ?", isClosed).
|
||||||
And("is_pull = ?", isPull).
|
And("is_pull = ?", isPull).
|
||||||
And("repo_id = ?", repoID)
|
And("repo_id = ?", repoID)
|
||||||
|
|
||||||
@@ -1214,10 +1290,10 @@ func GetRepoIssueStats(repoID, uid int64, filterMode int, isPull bool) (numOpen
|
|||||||
closedCountSession := countSession(true, isPull, repoID)
|
closedCountSession := countSession(true, isPull, repoID)
|
||||||
|
|
||||||
switch filterMode {
|
switch filterMode {
|
||||||
case FM_ASSIGN:
|
case FilterModeAssign:
|
||||||
openCountSession.And("assignee_id = ?", uid)
|
openCountSession.And("assignee_id = ?", uid)
|
||||||
closedCountSession.And("assignee_id = ?", uid)
|
closedCountSession.And("assignee_id = ?", uid)
|
||||||
case FM_CREATE:
|
case FilterModeCreate:
|
||||||
openCountSession.And("poster_id = ?", uid)
|
openCountSession.And("poster_id = ?", uid)
|
||||||
closedCountSession.And("poster_id = ?", uid)
|
closedCountSession.And("poster_id = ?", uid)
|
||||||
}
|
}
|
||||||
@@ -1285,22 +1361,22 @@ func UpdateIssueUserByRead(uid, issueID int64) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// UpdateIssueUsersByMentions updates issue-user pairs by mentioning.
|
// UpdateIssueUsersByMentions updates issue-user pairs by mentioning.
|
||||||
func UpdateIssueUsersByMentions(issueID int64, uids []int64) error {
|
func UpdateIssueUsersByMentions(e Engine, issueID int64, uids []int64) error {
|
||||||
for _, uid := range uids {
|
for _, uid := range uids {
|
||||||
iu := &IssueUser{
|
iu := &IssueUser{
|
||||||
UID: uid,
|
UID: uid,
|
||||||
IssueID: issueID,
|
IssueID: issueID,
|
||||||
}
|
}
|
||||||
has, err := x.Get(iu)
|
has, err := e.Get(iu)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
iu.IsMentioned = true
|
iu.IsMentioned = true
|
||||||
if has {
|
if has {
|
||||||
_, err = x.Id(iu.ID).AllCols().Update(iu)
|
_, err = e.Id(iu.ID).AllCols().Update(iu)
|
||||||
} else {
|
} else {
|
||||||
_, err = x.Insert(iu)
|
_, err = e.Insert(iu)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -1337,10 +1413,12 @@ type Milestone struct {
|
|||||||
ClosedDateUnix int64
|
ClosedDateUnix int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BeforeInsert is invoked from XORM before inserting an object of this type.
|
||||||
func (m *Milestone) BeforeInsert() {
|
func (m *Milestone) BeforeInsert() {
|
||||||
m.DeadlineUnix = m.Deadline.Unix()
|
m.DeadlineUnix = m.Deadline.Unix()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BeforeUpdate is invoked from XORM before updating this object.
|
||||||
func (m *Milestone) BeforeUpdate() {
|
func (m *Milestone) BeforeUpdate() {
|
||||||
if m.NumIssues > 0 {
|
if m.NumIssues > 0 {
|
||||||
m.Completeness = m.NumClosedIssues * 100 / m.NumIssues
|
m.Completeness = m.NumClosedIssues * 100 / m.NumIssues
|
||||||
@@ -1352,6 +1430,8 @@ func (m *Milestone) BeforeUpdate() {
|
|||||||
m.ClosedDateUnix = m.ClosedDate.Unix()
|
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) {
|
func (m *Milestone) AfterSet(colName string, _ xorm.Cell) {
|
||||||
switch colName {
|
switch colName {
|
||||||
case "num_closed_issues":
|
case "num_closed_issues":
|
||||||
@@ -1376,11 +1456,12 @@ func (m *Milestone) AfterSet(colName string, _ xorm.Cell) {
|
|||||||
// State returns string representation of milestone status.
|
// State returns string representation of milestone status.
|
||||||
func (m *Milestone) State() api.StateType {
|
func (m *Milestone) State() api.StateType {
|
||||||
if m.IsClosed {
|
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 {
|
func (m *Milestone) APIFormat() *api.Milestone {
|
||||||
apiMilestone := &api.Milestone{
|
apiMilestone := &api.Milestone{
|
||||||
ID: m.ID,
|
ID: m.ID,
|
||||||
@@ -1431,7 +1512,7 @@ func getMilestoneByRepoID(e Engine, repoID, id int64) (*Milestone, error) {
|
|||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetWebhookByRepoID returns the milestone in a repository.
|
// GetMilestoneByRepoID returns the milestone in a repository.
|
||||||
func GetMilestoneByRepoID(repoID, id int64) (*Milestone, error) {
|
func GetMilestoneByRepoID(repoID, id int64) (*Milestone, error) {
|
||||||
return getMilestoneByRepoID(x, repoID, id)
|
return getMilestoneByRepoID(x, repoID, id)
|
||||||
}
|
}
|
||||||
@@ -1463,7 +1544,9 @@ func UpdateMilestone(m *Milestone) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func countRepoMilestones(e Engine, repoID int64) int64 {
|
func countRepoMilestones(e Engine, repoID int64) int64 {
|
||||||
count, _ := e.Where("repo_id=?", repoID).Count(new(Milestone))
|
count, _ := e.
|
||||||
|
Where("repo_id=?", repoID).
|
||||||
|
Count(new(Milestone))
|
||||||
return count
|
return count
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1473,7 +1556,9 @@ func CountRepoMilestones(repoID int64) int64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func countRepoClosedMilestones(e Engine, repoID int64) int64 {
|
func countRepoClosedMilestones(e Engine, repoID int64) int64 {
|
||||||
closed, _ := e.Where("repo_id=? AND is_closed=?", repoID, true).Count(new(Milestone))
|
closed, _ := e.
|
||||||
|
Where("repo_id=? AND is_closed=?", repoID, true).
|
||||||
|
Count(new(Milestone))
|
||||||
return closed
|
return closed
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1484,7 +1569,9 @@ func CountRepoClosedMilestones(repoID int64) int64 {
|
|||||||
|
|
||||||
// MilestoneStats returns number of open and closed milestones of given repository.
|
// MilestoneStats returns number of open and closed milestones of given repository.
|
||||||
func MilestoneStats(repoID int64) (open int64, closed int64) {
|
func MilestoneStats(repoID int64) (open int64, closed int64) {
|
||||||
open, _ = x.Where("repo_id=? AND is_closed=?", repoID, false).Count(new(Milestone))
|
open, _ = x.
|
||||||
|
Where("repo_id=? AND is_closed=?", repoID, false).
|
||||||
|
Count(new(Milestone))
|
||||||
return open, CountRepoClosedMilestones(repoID)
|
return open, CountRepoClosedMilestones(repoID)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1657,10 +1744,13 @@ type Attachment struct {
|
|||||||
CreatedUnix int64
|
CreatedUnix int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BeforeInsert is invoked from XORM before inserting an object of this type.
|
||||||
func (a *Attachment) BeforeInsert() {
|
func (a *Attachment) BeforeInsert() {
|
||||||
a.CreatedUnix = time.Now().Unix()
|
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) {
|
func (a *Attachment) AfterSet(colName string, _ xorm.Cell) {
|
||||||
switch colName {
|
switch colName {
|
||||||
case "created_unix":
|
case "created_unix":
|
||||||
@@ -1668,14 +1758,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 {
|
func AttachmentLocalPath(uuid string) string {
|
||||||
return path.Join(setting.AttachmentPath, uuid[0:1], uuid[1:2], uuid)
|
return path.Join(setting.AttachmentPath, uuid[0:1], uuid[1:2], uuid)
|
||||||
}
|
}
|
||||||
|
|
||||||
// LocalPath returns where attachment is stored in local file system.
|
// LocalPath returns where attachment is stored in local file system.
|
||||||
func (attach *Attachment) LocalPath() string {
|
func (a *Attachment) LocalPath() string {
|
||||||
return AttachmentLocalPath(attach.UUID)
|
return AttachmentLocalPath(a.UUID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewAttachment creates a new attachment object.
|
// NewAttachment creates a new attachment object.
|
||||||
@@ -1775,8 +1866,8 @@ func DeleteAttachments(attachments []*Attachment, remove bool) (int, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// DeleteAttachmentsByIssue deletes all attachments associated with the given issue.
|
// DeleteAttachmentsByIssue deletes all attachments associated with the given issue.
|
||||||
func DeleteAttachmentsByIssue(issueId int64, remove bool) (int, error) {
|
func DeleteAttachmentsByIssue(issueID int64, remove bool) (int, error) {
|
||||||
attachments, err := GetAttachmentsByIssueID(issueId)
|
attachments, err := GetAttachmentsByIssueID(issueID)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
@@ -1786,8 +1877,8 @@ func DeleteAttachmentsByIssue(issueId int64, remove bool) (int, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// DeleteAttachmentsByComment deletes all attachments associated with the given comment.
|
// DeleteAttachmentsByComment deletes all attachments associated with the given comment.
|
||||||
func DeleteAttachmentsByComment(commentId int64, remove bool) (int, error) {
|
func DeleteAttachmentsByComment(commentID int64, remove bool) (int, error) {
|
||||||
attachments, err := GetAttachmentsByCommentID(commentId)
|
attachments, err := GetAttachmentsByCommentID(commentID)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
|
|||||||
@@ -12,38 +12,41 @@ import (
|
|||||||
"github.com/Unknwon/com"
|
"github.com/Unknwon/com"
|
||||||
"github.com/go-xorm/xorm"
|
"github.com/go-xorm/xorm"
|
||||||
|
|
||||||
api "github.com/gogits/go-gogs-client"
|
api "code.gitea.io/sdk/gitea"
|
||||||
|
|
||||||
"github.com/gogits/gogs/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"github.com/gogits/gogs/modules/markdown"
|
"code.gitea.io/gitea/modules/markdown"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CommentType defines whether a comment is just a simple comment, an action (like close) or a reference.
|
// CommentType defines whether a comment is just a simple comment, an action (like close) or a reference.
|
||||||
type CommentType int
|
type CommentType int
|
||||||
|
|
||||||
|
// Enumerate all the comment types
|
||||||
const (
|
const (
|
||||||
// Plain comment, can be associated with a commit (CommitID > 0) and a line (LineNum > 0)
|
// Plain comment, can be associated with a commit (CommitID > 0) and a line (LineNum > 0)
|
||||||
COMMENT_TYPE_COMMENT CommentType = iota
|
CommentTypeComment CommentType = iota
|
||||||
COMMENT_TYPE_REOPEN
|
CommentTypeReopen
|
||||||
COMMENT_TYPE_CLOSE
|
CommentTypeClose
|
||||||
|
|
||||||
// References.
|
// References.
|
||||||
COMMENT_TYPE_ISSUE_REF
|
CommentTypeIssueRef
|
||||||
// Reference from a commit (not part of a pull request)
|
// Reference from a commit (not part of a pull request)
|
||||||
COMMENT_TYPE_COMMIT_REF
|
CommentTypeCommitRef
|
||||||
// Reference from a comment
|
// Reference from a comment
|
||||||
COMMENT_TYPE_COMMENT_REF
|
CommentTypeCommentRef
|
||||||
// Reference from a pull request
|
// Reference from a pull request
|
||||||
COMMENT_TYPE_PULL_REF
|
CommentTypePullRef
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// CommentTag defines comment tag type
|
||||||
type CommentTag int
|
type CommentTag int
|
||||||
|
|
||||||
|
// Enumerate all the comment tag types
|
||||||
const (
|
const (
|
||||||
COMMENT_TAG_NONE CommentTag = iota
|
CommentTagNone CommentTag = iota
|
||||||
COMMENT_TAG_POSTER
|
CommentTagPoster
|
||||||
COMMENT_TAG_WRITER
|
CommentTagWriter
|
||||||
COMMENT_TAG_OWNER
|
CommentTagOwner
|
||||||
)
|
)
|
||||||
|
|
||||||
// Comment represents a comment in commit and issue page.
|
// Comment represents a comment in commit and issue page.
|
||||||
@@ -72,15 +75,19 @@ type Comment struct {
|
|||||||
ShowTag CommentTag `xorm:"-"`
|
ShowTag CommentTag `xorm:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BeforeInsert will be invoked by XORM before inserting a record
|
||||||
|
// representing this object.
|
||||||
func (c *Comment) BeforeInsert() {
|
func (c *Comment) BeforeInsert() {
|
||||||
c.CreatedUnix = time.Now().Unix()
|
c.CreatedUnix = time.Now().Unix()
|
||||||
c.UpdatedUnix = c.CreatedUnix
|
c.UpdatedUnix = c.CreatedUnix
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BeforeUpdate is invoked from XORM before updating this object.
|
||||||
func (c *Comment) BeforeUpdate() {
|
func (c *Comment) BeforeUpdate() {
|
||||||
c.UpdatedUnix = time.Now().Unix()
|
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) {
|
func (c *Comment) AfterSet(colName string, _ xorm.Cell) {
|
||||||
var err error
|
var err error
|
||||||
switch colName {
|
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() {
|
func (c *Comment) AfterDelete() {
|
||||||
_, err := DeleteAttachmentsByComment(c.ID, true)
|
_, err := DeleteAttachmentsByComment(c.ID, true)
|
||||||
|
|
||||||
@@ -115,13 +123,55 @@ func (c *Comment) AfterDelete() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HTMLURL formats a URL-string to the issue-comment
|
||||||
|
func (c *Comment) HTMLURL() string {
|
||||||
|
issue, err := GetIssueByID(c.IssueID)
|
||||||
|
if err != nil { // Silently dropping errors :unamused:
|
||||||
|
log.Error(4, "GetIssueByID(%d): %v", c.IssueID, err)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s#issuecomment-%d", issue.HTMLURL(), c.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IssueURL formats a URL-string to the issue
|
||||||
|
func (c *Comment) IssueURL() string {
|
||||||
|
issue, err := GetIssueByID(c.IssueID)
|
||||||
|
if err != nil { // Silently dropping errors :unamused:
|
||||||
|
log.Error(4, "GetIssueByID(%d): %v", c.IssueID, err)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if issue.IsPull {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return issue.HTMLURL()
|
||||||
|
}
|
||||||
|
|
||||||
|
// PRURL formats a URL-string to the pull-request
|
||||||
|
func (c *Comment) PRURL() string {
|
||||||
|
issue, err := GetIssueByID(c.IssueID)
|
||||||
|
if err != nil { // Silently dropping errors :unamused:
|
||||||
|
log.Error(4, "GetIssueByID(%d): %v", c.IssueID, err)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if !issue.IsPull {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return issue.HTMLURL()
|
||||||
|
}
|
||||||
|
|
||||||
|
// APIFormat converts a Comment to the api.Comment format
|
||||||
func (c *Comment) APIFormat() *api.Comment {
|
func (c *Comment) APIFormat() *api.Comment {
|
||||||
return &api.Comment{
|
return &api.Comment{
|
||||||
ID: c.ID,
|
ID: c.ID,
|
||||||
Poster: c.Poster.APIFormat(),
|
Poster: c.Poster.APIFormat(),
|
||||||
Body: c.Content,
|
HTMLURL: c.HTMLURL(),
|
||||||
Created: c.Created,
|
IssueURL: c.IssueURL(),
|
||||||
Updated: c.Updated,
|
PRURL: c.PRURL(),
|
||||||
|
Body: c.Content,
|
||||||
|
Created: c.Created,
|
||||||
|
Updated: c.Updated,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,21 +187,21 @@ func (c *Comment) EventTag() string {
|
|||||||
|
|
||||||
// MailParticipants sends new comment emails to repository watchers
|
// MailParticipants sends new comment emails to repository watchers
|
||||||
// and mentioned people.
|
// and mentioned people.
|
||||||
func (cmt *Comment) MailParticipants(opType ActionType, issue *Issue) (err error) {
|
func (c *Comment) MailParticipants(e Engine, opType ActionType, issue *Issue) (err error) {
|
||||||
mentions := markdown.FindAllMentions(cmt.Content)
|
mentions := markdown.FindAllMentions(c.Content)
|
||||||
if err = UpdateIssueMentions(cmt.IssueID, mentions); err != nil {
|
if err = UpdateIssueMentions(e, c.IssueID, mentions); err != nil {
|
||||||
return fmt.Errorf("UpdateIssueMentions [%d]: %v", cmt.IssueID, err)
|
return fmt.Errorf("UpdateIssueMentions [%d]: %v", c.IssueID, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
switch opType {
|
switch opType {
|
||||||
case ACTION_COMMENT_ISSUE:
|
case ActionCommentIssue:
|
||||||
issue.Content = cmt.Content
|
issue.Content = c.Content
|
||||||
case ACTION_CLOSE_ISSUE:
|
case ActionCloseIssue:
|
||||||
issue.Content = fmt.Sprintf("Closed #%d", issue.Index)
|
issue.Content = fmt.Sprintf("Closed #%d", issue.Index)
|
||||||
case ACTION_REOPEN_ISSUE:
|
case ActionReopenIssue:
|
||||||
issue.Content = fmt.Sprintf("Reopened #%d", issue.Index)
|
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)
|
log.Error(4, "mailIssueCommentToParticipants: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,8 +237,8 @@ func createComment(e *xorm.Session, opts *CreateCommentOptions) (_ *Comment, err
|
|||||||
|
|
||||||
// Check comment type.
|
// Check comment type.
|
||||||
switch opts.Type {
|
switch opts.Type {
|
||||||
case COMMENT_TYPE_COMMENT:
|
case CommentTypeComment:
|
||||||
act.OpType = ACTION_COMMENT_ISSUE
|
act.OpType = ActionCommentIssue
|
||||||
|
|
||||||
if _, err = e.Exec("UPDATE `issue` SET num_comments=num_comments+1 WHERE id=?", opts.Issue.ID); err != nil {
|
if _, err = e.Exec("UPDATE `issue` SET num_comments=num_comments+1 WHERE id=?", opts.Issue.ID); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -216,10 +266,10 @@ func createComment(e *xorm.Session, opts *CreateCommentOptions) (_ *Comment, err
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case COMMENT_TYPE_REOPEN:
|
case CommentTypeReopen:
|
||||||
act.OpType = ACTION_REOPEN_ISSUE
|
act.OpType = ActionReopenIssue
|
||||||
if opts.Issue.IsPull {
|
if opts.Issue.IsPull {
|
||||||
act.OpType = ACTION_REOPEN_PULL_REQUEST
|
act.OpType = ActionReopenPullRequest
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.Issue.IsPull {
|
if opts.Issue.IsPull {
|
||||||
@@ -231,10 +281,10 @@ func createComment(e *xorm.Session, opts *CreateCommentOptions) (_ *Comment, err
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
case COMMENT_TYPE_CLOSE:
|
case CommentTypeClose:
|
||||||
act.OpType = ACTION_CLOSE_ISSUE
|
act.OpType = ActionCloseIssue
|
||||||
if opts.Issue.IsPull {
|
if opts.Issue.IsPull {
|
||||||
act.OpType = ACTION_CLOSE_PULL_REQUEST
|
act.OpType = ActionClosePullRequest
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.Issue.IsPull {
|
if opts.Issue.IsPull {
|
||||||
@@ -253,16 +303,18 @@ func createComment(e *xorm.Session, opts *CreateCommentOptions) (_ *Comment, err
|
|||||||
if err = notifyWatchers(e, act); err != nil {
|
if err = notifyWatchers(e, act); err != nil {
|
||||||
log.Error(4, "notifyWatchers: %v", err)
|
log.Error(4, "notifyWatchers: %v", err)
|
||||||
}
|
}
|
||||||
comment.MailParticipants(act.OpType, opts.Issue)
|
if err = comment.MailParticipants(e, act.OpType, opts.Issue); err != nil {
|
||||||
|
log.Error(4, "MailParticipants: %v", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return comment, nil
|
return comment, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func createStatusComment(e *xorm.Session, doer *User, repo *Repository, issue *Issue) (*Comment, error) {
|
func createStatusComment(e *xorm.Session, doer *User, repo *Repository, issue *Issue) (*Comment, error) {
|
||||||
cmtType := COMMENT_TYPE_CLOSE
|
cmtType := CommentTypeClose
|
||||||
if !issue.IsClosed {
|
if !issue.IsClosed {
|
||||||
cmtType = COMMENT_TYPE_REOPEN
|
cmtType = CommentTypeReopen
|
||||||
}
|
}
|
||||||
return createComment(e, &CreateCommentOptions{
|
return createComment(e, &CreateCommentOptions{
|
||||||
Type: cmtType,
|
Type: cmtType,
|
||||||
@@ -272,6 +324,7 @@ func createStatusComment(e *xorm.Session, doer *User, repo *Repository, issue *I
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateCommentOptions defines options for creating comment
|
||||||
type CreateCommentOptions struct {
|
type CreateCommentOptions struct {
|
||||||
Type CommentType
|
Type CommentType
|
||||||
Doer *User
|
Doer *User
|
||||||
@@ -304,7 +357,7 @@ func CreateComment(opts *CreateCommentOptions) (comment *Comment, err error) {
|
|||||||
// CreateIssueComment creates a plain issue comment.
|
// CreateIssueComment creates a plain issue comment.
|
||||||
func CreateIssueComment(doer *User, repo *Repository, issue *Issue, content string, attachments []string) (*Comment, error) {
|
func CreateIssueComment(doer *User, repo *Repository, issue *Issue, content string, attachments []string) (*Comment, error) {
|
||||||
return CreateComment(&CreateCommentOptions{
|
return CreateComment(&CreateCommentOptions{
|
||||||
Type: COMMENT_TYPE_COMMENT,
|
Type: CommentTypeComment,
|
||||||
Doer: doer,
|
Doer: doer,
|
||||||
Repo: repo,
|
Repo: repo,
|
||||||
Issue: issue,
|
Issue: issue,
|
||||||
@@ -321,7 +374,7 @@ func CreateRefComment(doer *User, repo *Repository, issue *Issue, content, commi
|
|||||||
|
|
||||||
// Check if same reference from same commit has already existed.
|
// Check if same reference from same commit has already existed.
|
||||||
has, err := x.Get(&Comment{
|
has, err := x.Get(&Comment{
|
||||||
Type: COMMENT_TYPE_COMMIT_REF,
|
Type: CommentTypeCommitRef,
|
||||||
IssueID: issue.ID,
|
IssueID: issue.ID,
|
||||||
CommitSHA: commitSHA,
|
CommitSHA: commitSHA,
|
||||||
})
|
})
|
||||||
@@ -332,7 +385,7 @@ func CreateRefComment(doer *User, repo *Repository, issue *Issue, content, commi
|
|||||||
}
|
}
|
||||||
|
|
||||||
_, err = CreateComment(&CreateCommentOptions{
|
_, err = CreateComment(&CreateCommentOptions{
|
||||||
Type: COMMENT_TYPE_COMMIT_REF,
|
Type: CommentTypeCommitRef,
|
||||||
Doer: doer,
|
Doer: doer,
|
||||||
Repo: repo,
|
Repo: repo,
|
||||||
Issue: issue,
|
Issue: issue,
|
||||||
@@ -356,7 +409,18 @@ func GetCommentByID(id int64) (*Comment, error) {
|
|||||||
|
|
||||||
func getCommentsByIssueIDSince(e Engine, issueID, since int64) ([]*Comment, error) {
|
func getCommentsByIssueIDSince(e Engine, issueID, since int64) ([]*Comment, error) {
|
||||||
comments := make([]*Comment, 0, 10)
|
comments := make([]*Comment, 0, 10)
|
||||||
sess := e.Where("issue_id = ?", issueID).Asc("created_unix")
|
sess := e.
|
||||||
|
Where("issue_id = ?", issueID).
|
||||||
|
Asc("created_unix")
|
||||||
|
if since > 0 {
|
||||||
|
sess.And("updated_unix >= ?", since)
|
||||||
|
}
|
||||||
|
return comments, sess.Find(&comments)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCommentsByRepoIDSince(e Engine, repoID, since int64) ([]*Comment, error) {
|
||||||
|
comments := make([]*Comment, 0, 10)
|
||||||
|
sess := e.Where("issue.repo_id = ?", repoID).Join("INNER", "issue", "issue.id = comment.issue_id", repoID).Asc("created_unix")
|
||||||
if since > 0 {
|
if since > 0 {
|
||||||
sess.And("updated_unix >= ?", since)
|
sess.And("updated_unix >= ?", since)
|
||||||
}
|
}
|
||||||
@@ -372,11 +436,16 @@ func GetCommentsByIssueID(issueID int64) ([]*Comment, error) {
|
|||||||
return getCommentsByIssueID(x, issueID)
|
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) {
|
func GetCommentsByIssueIDSince(issueID, since int64) ([]*Comment, error) {
|
||||||
return getCommentsByIssueIDSince(x, issueID, since)
|
return getCommentsByIssueIDSince(x, issueID, since)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetCommentsByRepoIDSince returns a list of comments for all issues in a repo since a given time point.
|
||||||
|
func GetCommentsByRepoIDSince(repoID, since int64) ([]*Comment, error) {
|
||||||
|
return getCommentsByRepoIDSince(x, repoID, since)
|
||||||
|
}
|
||||||
|
|
||||||
// UpdateComment updates information of comment.
|
// UpdateComment updates information of comment.
|
||||||
func UpdateComment(c *Comment) error {
|
func UpdateComment(c *Comment) error {
|
||||||
_, err := x.Id(c.ID).AllCols().Update(c)
|
_, err := x.Id(c.ID).AllCols().Update(c)
|
||||||
@@ -403,7 +472,7 @@ func DeleteCommentByID(id int64) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if comment.Type == COMMENT_TYPE_COMMENT {
|
if comment.Type == CommentTypeComment {
|
||||||
if _, err = sess.Exec("UPDATE `issue` SET num_comments = num_comments - 1 WHERE id = ?", comment.IssueID); err != nil {
|
if _, err = sess.Exec("UPDATE `issue` SET num_comments = num_comments - 1 WHERE id = ?", comment.IssueID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,9 +13,7 @@ import (
|
|||||||
|
|
||||||
"github.com/go-xorm/xorm"
|
"github.com/go-xorm/xorm"
|
||||||
|
|
||||||
api "github.com/gogits/go-gogs-client"
|
api "code.gitea.io/sdk/gitea"
|
||||||
|
|
||||||
"github.com/gogits/gogs/modules/base"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var labelColorPattern = regexp.MustCompile("#([a-fA-F0-9]{6})")
|
var labelColorPattern = regexp.MustCompile("#([a-fA-F0-9]{6})")
|
||||||
@@ -64,11 +62,12 @@ type Label struct {
|
|||||||
IsChecked bool `xorm:"-"`
|
IsChecked bool `xorm:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// APIFormat converts a Label to the api.Label format
|
||||||
func (label *Label) APIFormat() *api.Label {
|
func (label *Label) APIFormat() *api.Label {
|
||||||
return &api.Label{
|
return &api.Label{
|
||||||
ID: label.ID,
|
ID: label.ID,
|
||||||
Name: label.Name,
|
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
|
// ForegroundColor calculates the text color for labels based
|
||||||
// on their background color.
|
// on their background color.
|
||||||
func (l *Label) ForegroundColor() template.CSS {
|
func (label *Label) ForegroundColor() template.CSS {
|
||||||
if strings.HasPrefix(l.Color, "#") {
|
if strings.HasPrefix(label.Color, "#") {
|
||||||
if color, err := strconv.ParseUint(l.Color[1:], 16, 64); err == nil {
|
if color, err := strconv.ParseUint(label.Color[1:], 16, 64); err == nil {
|
||||||
r := float32(0xFF & (color >> 16))
|
r := float32(0xFF & (color >> 16))
|
||||||
g := float32(0xFF & (color >> 8))
|
g := float32(0xFF & (color >> 8))
|
||||||
b := float32(0xFF & color)
|
b := float32(0xFF & color)
|
||||||
@@ -103,6 +102,27 @@ func NewLabels(labels ...*Label) error {
|
|||||||
return err
|
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.
|
// getLabelInRepoByID returns a label by ID in given repository.
|
||||||
// If pass repoID as 0, then ORM will ignore limitation of repository
|
// If pass repoID as 0, then ORM will ignore limitation of repository
|
||||||
// and can return arbitrary label with any valid ID.
|
// and can return arbitrary label with any valid ID.
|
||||||
@@ -129,6 +149,11 @@ func GetLabelByID(id int64) (*Label, error) {
|
|||||||
return getLabelInRepoByID(x, 0, id)
|
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.
|
// GetLabelInRepoByID returns a label by ID in given repository.
|
||||||
func GetLabelInRepoByID(repoID, labelID int64) (*Label, error) {
|
func GetLabelInRepoByID(repoID, labelID int64) (*Label, error) {
|
||||||
return getLabelInRepoByID(x, repoID, labelID)
|
return getLabelInRepoByID(x, repoID, labelID)
|
||||||
@@ -138,13 +163,20 @@ func GetLabelInRepoByID(repoID, labelID int64) (*Label, error) {
|
|||||||
// it silently ignores label IDs that are not belong to the repository.
|
// it silently ignores label IDs that are not belong to the repository.
|
||||||
func GetLabelsInRepoByIDs(repoID int64, labelIDs []int64) ([]*Label, error) {
|
func GetLabelsInRepoByIDs(repoID int64, labelIDs []int64) ([]*Label, error) {
|
||||||
labels := make([]*Label, 0, len(labelIDs))
|
labels := make([]*Label, 0, len(labelIDs))
|
||||||
return labels, x.Where("repo_id = ?", repoID).In("id", base.Int64sToStrings(labelIDs)).Asc("name").Find(&labels)
|
return labels, x.
|
||||||
|
Where("repo_id = ?", repoID).
|
||||||
|
In("id", labelIDs).
|
||||||
|
Asc("name").
|
||||||
|
Find(&labels)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetLabelsByRepoID returns all labels that belong to given repository by ID.
|
// GetLabelsByRepoID returns all labels that belong to given repository by ID.
|
||||||
func GetLabelsByRepoID(repoID int64) ([]*Label, error) {
|
func GetLabelsByRepoID(repoID int64) ([]*Label, error) {
|
||||||
labels := make([]*Label, 0, 10)
|
labels := make([]*Label, 0, 10)
|
||||||
return labels, x.Where("repo_id = ?", repoID).Asc("name").Find(&labels)
|
return labels, x.
|
||||||
|
Where("repo_id = ?", repoID).
|
||||||
|
Asc("name").
|
||||||
|
Find(&labels)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getLabelsByIssueID(e Engine, issueID int64) ([]*Label, error) {
|
func getLabelsByIssueID(e Engine, issueID int64) ([]*Label, error) {
|
||||||
@@ -161,7 +193,11 @@ func getLabelsByIssueID(e Engine, issueID int64) ([]*Label, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
labels := make([]*Label, 0, len(labelIDs))
|
labels := make([]*Label, 0, len(labelIDs))
|
||||||
return labels, e.Where("id > 0").In("id", base.Int64sToStrings(labelIDs)).Asc("name").Find(&labels)
|
return labels, e.
|
||||||
|
Where("id > 0").
|
||||||
|
In("id", labelIDs).
|
||||||
|
Asc("name").
|
||||||
|
Find(&labels)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetLabelsByIssueID returns all labels that belong to given issue by ID.
|
// GetLabelsByIssueID returns all labels that belong to given issue by ID.
|
||||||
@@ -197,7 +233,9 @@ func DeleteLabel(repoID, labelID int64) error {
|
|||||||
|
|
||||||
if _, err = sess.Id(labelID).Delete(new(Label)); err != nil {
|
if _, err = sess.Id(labelID).Delete(new(Label)); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if _, err = sess.Where("label_id = ?", labelID).Delete(new(IssueLabel)); err != nil {
|
} else if _, err = sess.
|
||||||
|
Where("label_id = ?", labelID).
|
||||||
|
Delete(new(IssueLabel)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -293,7 +331,10 @@ func NewIssueLabels(issue *Issue, labels []*Label) (err error) {
|
|||||||
|
|
||||||
func getIssueLabels(e Engine, issueID int64) ([]*IssueLabel, error) {
|
func getIssueLabels(e Engine, issueID int64) ([]*IssueLabel, error) {
|
||||||
issueLabels := make([]*IssueLabel, 0, 10)
|
issueLabels := make([]*IssueLabel, 0, 10)
|
||||||
return issueLabels, e.Where("issue_id=?", issueID).Asc("label_id").Find(&issueLabels)
|
return issueLabels, e.
|
||||||
|
Where("issue_id=?", issueID).
|
||||||
|
Asc("label_id").
|
||||||
|
Find(&issueLabels)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetIssueLabels returns all issue-label relations of given issue by ID.
|
// GetIssueLabels returns all issue-label relations of given issue by ID.
|
||||||
|
|||||||
@@ -9,12 +9,12 @@ import (
|
|||||||
|
|
||||||
"github.com/Unknwon/com"
|
"github.com/Unknwon/com"
|
||||||
|
|
||||||
"github.com/gogits/gogs/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"github.com/gogits/gogs/modules/markdown"
|
"code.gitea.io/gitea/modules/markdown"
|
||||||
"github.com/gogits/gogs/modules/setting"
|
"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)
|
return fmt.Sprintf("[%s] %s (#%d)", issue.Repo.Name, issue.Title, issue.Index)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,7 +69,7 @@ func mailIssueCommentToParticipants(issue *Issue, doer *User, mentions []string)
|
|||||||
// and mentioned people.
|
// and mentioned people.
|
||||||
func (issue *Issue) MailParticipants() (err error) {
|
func (issue *Issue) MailParticipants() (err error) {
|
||||||
mentions := markdown.FindAllMentions(issue.Content)
|
mentions := markdown.FindAllMentions(issue.Content)
|
||||||
if err = UpdateIssueMentions(issue.ID, mentions); err != nil {
|
if err = UpdateIssueMentions(x, issue.ID, mentions); err != nil {
|
||||||
return fmt.Errorf("UpdateIssueMentions [%d]: %v", issue.ID, err)
|
return fmt.Errorf("UpdateIssueMentions [%d]: %v", issue.ID, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,34 +19,37 @@ import (
|
|||||||
"github.com/go-xorm/core"
|
"github.com/go-xorm/core"
|
||||||
"github.com/go-xorm/xorm"
|
"github.com/go-xorm/xorm"
|
||||||
|
|
||||||
"github.com/gogits/gogs/modules/auth/ldap"
|
"code.gitea.io/gitea/modules/auth/ldap"
|
||||||
"github.com/gogits/gogs/modules/auth/pam"
|
"code.gitea.io/gitea/modules/auth/pam"
|
||||||
"github.com/gogits/gogs/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// LoginType represents an login type.
|
||||||
type LoginType int
|
type LoginType int
|
||||||
|
|
||||||
// Note: new type must append to the end of list to maintain compatibility.
|
// Note: new type must append to the end of list to maintain compatibility.
|
||||||
const (
|
const (
|
||||||
LOGIN_NOTYPE LoginType = iota
|
LoginNoType LoginType = iota
|
||||||
LOGIN_PLAIN // 1
|
LoginPlain // 1
|
||||||
LOGIN_LDAP // 2
|
LoginLDAP // 2
|
||||||
LOGIN_SMTP // 3
|
LoginSMTP // 3
|
||||||
LOGIN_PAM // 4
|
LoginPAM // 4
|
||||||
LOGIN_DLDAP // 5
|
LoginDLDAP // 5
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// LoginNames contains the name of LoginType values.
|
||||||
var LoginNames = map[LoginType]string{
|
var LoginNames = map[LoginType]string{
|
||||||
LOGIN_LDAP: "LDAP (via BindDN)",
|
LoginLDAP: "LDAP (via BindDN)",
|
||||||
LOGIN_DLDAP: "LDAP (simple auth)", // Via direct bind
|
LoginDLDAP: "LDAP (simple auth)", // Via direct bind
|
||||||
LOGIN_SMTP: "SMTP",
|
LoginSMTP: "SMTP",
|
||||||
LOGIN_PAM: "PAM",
|
LoginPAM: "PAM",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SecurityProtocolNames contains the name of SecurityProtocol values.
|
||||||
var SecurityProtocolNames = map[ldap.SecurityProtocol]string{
|
var SecurityProtocolNames = map[ldap.SecurityProtocol]string{
|
||||||
ldap.SECURITY_PROTOCOL_UNENCRYPTED: "Unencrypted",
|
ldap.SecurityProtocolUnencrypted: "Unencrypted",
|
||||||
ldap.SECURITY_PROTOCOL_LDAPS: "LDAPS",
|
ldap.SecurityProtocolLDAPS: "LDAPS",
|
||||||
ldap.SECURITY_PROTOCOL_START_TLS: "StartTLS",
|
ldap.SecurityProtocolStartTLS: "StartTLS",
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure structs implemented interface.
|
// Ensure structs implemented interface.
|
||||||
@@ -56,22 +59,28 @@ var (
|
|||||||
_ core.Conversion = &PAMConfig{}
|
_ core.Conversion = &PAMConfig{}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// LDAPConfig holds configuration for LDAP login source.
|
||||||
type LDAPConfig struct {
|
type LDAPConfig struct {
|
||||||
*ldap.Source
|
*ldap.Source
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FromDB fills up a LDAPConfig from serialized format.
|
||||||
func (cfg *LDAPConfig) FromDB(bs []byte) error {
|
func (cfg *LDAPConfig) FromDB(bs []byte) error {
|
||||||
return json.Unmarshal(bs, &cfg)
|
return json.Unmarshal(bs, &cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ToDB exports a LDAPConfig to a serialized format.
|
||||||
func (cfg *LDAPConfig) ToDB() ([]byte, error) {
|
func (cfg *LDAPConfig) ToDB() ([]byte, error) {
|
||||||
return json.Marshal(cfg)
|
return json.Marshal(cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SecurityProtocolName returns the name of configured security
|
||||||
|
// protocol.
|
||||||
func (cfg *LDAPConfig) SecurityProtocolName() string {
|
func (cfg *LDAPConfig) SecurityProtocolName() string {
|
||||||
return SecurityProtocolNames[cfg.SecurityProtocol]
|
return SecurityProtocolNames[cfg.SecurityProtocol]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SMTPConfig holds configuration for the SMTP login source.
|
||||||
type SMTPConfig struct {
|
type SMTPConfig struct {
|
||||||
Auth string
|
Auth string
|
||||||
Host string
|
Host string
|
||||||
@@ -81,22 +90,27 @@ type SMTPConfig struct {
|
|||||||
SkipVerify bool
|
SkipVerify bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FromDB fills up an SMTPConfig from serialized format.
|
||||||
func (cfg *SMTPConfig) FromDB(bs []byte) error {
|
func (cfg *SMTPConfig) FromDB(bs []byte) error {
|
||||||
return json.Unmarshal(bs, cfg)
|
return json.Unmarshal(bs, cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ToDB exports an SMTPConfig to a serialized format.
|
||||||
func (cfg *SMTPConfig) ToDB() ([]byte, error) {
|
func (cfg *SMTPConfig) ToDB() ([]byte, error) {
|
||||||
return json.Marshal(cfg)
|
return json.Marshal(cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PAMConfig holds configuration for the PAM login source.
|
||||||
type PAMConfig struct {
|
type PAMConfig struct {
|
||||||
ServiceName string // pam service (e.g. system-auth)
|
ServiceName string // pam service (e.g. system-auth)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FromDB fills up a PAMConfig from serialized format.
|
||||||
func (cfg *PAMConfig) FromDB(bs []byte) error {
|
func (cfg *PAMConfig) FromDB(bs []byte) error {
|
||||||
return json.Unmarshal(bs, &cfg)
|
return json.Unmarshal(bs, &cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ToDB exports a PAMConfig to a serialized format.
|
||||||
func (cfg *PAMConfig) ToDB() ([]byte, error) {
|
func (cfg *PAMConfig) ToDB() ([]byte, error) {
|
||||||
return json.Marshal(cfg)
|
return json.Marshal(cfg)
|
||||||
}
|
}
|
||||||
@@ -115,13 +129,15 @@ type LoginSource struct {
|
|||||||
UpdatedUnix int64
|
UpdatedUnix int64
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *LoginSource) BeforeInsert() {
|
// BeforeInsert is invoked from XORM before inserting an object of this type.
|
||||||
s.CreatedUnix = time.Now().Unix()
|
func (source *LoginSource) BeforeInsert() {
|
||||||
s.UpdatedUnix = s.CreatedUnix
|
source.CreatedUnix = time.Now().Unix()
|
||||||
|
source.UpdatedUnix = source.CreatedUnix
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *LoginSource) BeforeUpdate() {
|
// BeforeUpdate is invoked from XORM before updating this object.
|
||||||
s.UpdatedUnix = time.Now().Unix()
|
func (source *LoginSource) BeforeUpdate() {
|
||||||
|
source.UpdatedUnix = time.Now().Unix()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cell2Int64 converts a xorm.Cell type to int64,
|
// Cell2Int64 converts a xorm.Cell type to int64,
|
||||||
@@ -135,15 +151,16 @@ func Cell2Int64(val xorm.Cell) int64 {
|
|||||||
return (*val).(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) {
|
func (source *LoginSource) BeforeSet(colName string, val xorm.Cell) {
|
||||||
switch colName {
|
switch colName {
|
||||||
case "type":
|
case "type":
|
||||||
switch LoginType(Cell2Int64(val)) {
|
switch LoginType(Cell2Int64(val)) {
|
||||||
case LOGIN_LDAP, LOGIN_DLDAP:
|
case LoginLDAP, LoginDLDAP:
|
||||||
source.Cfg = new(LDAPConfig)
|
source.Cfg = new(LDAPConfig)
|
||||||
case LOGIN_SMTP:
|
case LoginSMTP:
|
||||||
source.Cfg = new(SMTPConfig)
|
source.Cfg = new(SMTPConfig)
|
||||||
case LOGIN_PAM:
|
case LoginPAM:
|
||||||
source.Cfg = new(PAMConfig)
|
source.Cfg = new(PAMConfig)
|
||||||
default:
|
default:
|
||||||
panic("unrecognized login source type: " + com.ToStr(*val))
|
panic("unrecognized login source type: " + com.ToStr(*val))
|
||||||
@@ -151,74 +168,90 @@ 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 {
|
switch colName {
|
||||||
case "created_unix":
|
case "created_unix":
|
||||||
s.Created = time.Unix(s.CreatedUnix, 0).Local()
|
source.Created = time.Unix(source.CreatedUnix, 0).Local()
|
||||||
case "updated_unix":
|
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 {
|
func (source *LoginSource) TypeName() string {
|
||||||
return LoginNames[source.Type]
|
return LoginNames[source.Type]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsLDAP returns true of this source is of the LDAP type.
|
||||||
func (source *LoginSource) IsLDAP() bool {
|
func (source *LoginSource) IsLDAP() bool {
|
||||||
return source.Type == LOGIN_LDAP
|
return source.Type == LoginLDAP
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsDLDAP returns true of this source is of the DLDAP type.
|
||||||
func (source *LoginSource) IsDLDAP() bool {
|
func (source *LoginSource) IsDLDAP() bool {
|
||||||
return source.Type == LOGIN_DLDAP
|
return source.Type == LoginDLDAP
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsSMTP returns true of this source is of the SMTP type.
|
||||||
func (source *LoginSource) IsSMTP() bool {
|
func (source *LoginSource) IsSMTP() bool {
|
||||||
return source.Type == LOGIN_SMTP
|
return source.Type == LoginSMTP
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsPAM returns true of this source is of the PAM type.
|
||||||
func (source *LoginSource) IsPAM() bool {
|
func (source *LoginSource) IsPAM() bool {
|
||||||
return source.Type == LOGIN_PAM
|
return source.Type == LoginPAM
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HasTLS returns true of this source supports TLS.
|
||||||
func (source *LoginSource) HasTLS() bool {
|
func (source *LoginSource) HasTLS() bool {
|
||||||
return ((source.IsLDAP() || source.IsDLDAP()) &&
|
return ((source.IsLDAP() || source.IsDLDAP()) &&
|
||||||
source.LDAP().SecurityProtocol > ldap.SECURITY_PROTOCOL_UNENCRYPTED) ||
|
source.LDAP().SecurityProtocol > ldap.SecurityProtocolUnencrypted) ||
|
||||||
source.IsSMTP()
|
source.IsSMTP()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UseTLS returns true of this source is configured to use TLS.
|
||||||
func (source *LoginSource) UseTLS() bool {
|
func (source *LoginSource) UseTLS() bool {
|
||||||
switch source.Type {
|
switch source.Type {
|
||||||
case LOGIN_LDAP, LOGIN_DLDAP:
|
case LoginLDAP, LoginDLDAP:
|
||||||
return source.LDAP().SecurityProtocol != ldap.SECURITY_PROTOCOL_UNENCRYPTED
|
return source.LDAP().SecurityProtocol != ldap.SecurityProtocolUnencrypted
|
||||||
case LOGIN_SMTP:
|
case LoginSMTP:
|
||||||
return source.SMTP().TLS
|
return source.SMTP().TLS
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SkipVerify returns true if this source is configured to skip SSL
|
||||||
|
// verification.
|
||||||
func (source *LoginSource) SkipVerify() bool {
|
func (source *LoginSource) SkipVerify() bool {
|
||||||
switch source.Type {
|
switch source.Type {
|
||||||
case LOGIN_LDAP, LOGIN_DLDAP:
|
case LoginLDAP, LoginDLDAP:
|
||||||
return source.LDAP().SkipVerify
|
return source.LDAP().SkipVerify
|
||||||
case LOGIN_SMTP:
|
case LoginSMTP:
|
||||||
return source.SMTP().SkipVerify
|
return source.SMTP().SkipVerify
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LDAP returns LDAPConfig for this source, if of LDAP type.
|
||||||
func (source *LoginSource) LDAP() *LDAPConfig {
|
func (source *LoginSource) LDAP() *LDAPConfig {
|
||||||
return source.Cfg.(*LDAPConfig)
|
return source.Cfg.(*LDAPConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SMTP returns SMTPConfig for this source, if of SMTP type.
|
||||||
func (source *LoginSource) SMTP() *SMTPConfig {
|
func (source *LoginSource) SMTP() *SMTPConfig {
|
||||||
return source.Cfg.(*SMTPConfig)
|
return source.Cfg.(*SMTPConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PAM returns PAMConfig for this source, if of PAM type.
|
||||||
func (source *LoginSource) PAM() *PAMConfig {
|
func (source *LoginSource) PAM() *PAMConfig {
|
||||||
return source.Cfg.(*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 {
|
func CreateLoginSource(source *LoginSource) error {
|
||||||
has, err := x.Get(&LoginSource{Name: source.Name})
|
has, err := x.Get(&LoginSource{Name: source.Name})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -231,6 +264,7 @@ func CreateLoginSource(source *LoginSource) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LoginSources returns a slice of all login sources found in DB.
|
||||||
func LoginSources() ([]*LoginSource, error) {
|
func LoginSources() ([]*LoginSource, error) {
|
||||||
auths := make([]*LoginSource, 0, 5)
|
auths := make([]*LoginSource, 0, 5)
|
||||||
return auths, x.Find(&auths)
|
return auths, x.Find(&auths)
|
||||||
@@ -248,11 +282,13 @@ func GetLoginSourceByID(id int64) (*LoginSource, error) {
|
|||||||
return source, nil
|
return source, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpdateSource updates a LoginSource record in DB.
|
||||||
func UpdateSource(source *LoginSource) error {
|
func UpdateSource(source *LoginSource) error {
|
||||||
_, err := x.Id(source.ID).AllCols().Update(source)
|
_, err := x.Id(source.ID).AllCols().Update(source)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeleteSource deletes a LoginSource record in DB.
|
||||||
func DeleteSource(source *LoginSource) error {
|
func DeleteSource(source *LoginSource) error {
|
||||||
count, err := x.Count(&User{LoginSource: source.ID})
|
count, err := x.Count(&User{LoginSource: source.ID})
|
||||||
if err != nil {
|
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,
|
// LoginViaLDAP queries if login/password is valid against the LDAP directory pool,
|
||||||
// and create a local user if success when enabled.
|
// and create a local user if success when enabled.
|
||||||
func LoginViaLDAP(user *User, login, passowrd string, source *LoginSource, autoRegister bool) (*User, error) {
|
func LoginViaLDAP(user *User, login, password string, source *LoginSource, autoRegister bool) (*User, error) {
|
||||||
username, fn, sn, mail, isAdmin, succeed := source.Cfg.(*LDAPConfig).SearchEntry(login, passowrd, source.Type == LOGIN_DLDAP)
|
username, fn, sn, mail, isAdmin, succeed := source.Cfg.(*LDAPConfig).SearchEntry(login, password, source.Type == LoginDLDAP)
|
||||||
if !succeed {
|
if !succeed {
|
||||||
// User not in LDAP, do nothing
|
// User not in LDAP, do nothing
|
||||||
return nil, ErrUserNotExist{0, login}
|
return nil, ErrUserNotExist{0, login, 0}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !autoRegister {
|
if !autoRegister {
|
||||||
@@ -357,13 +393,16 @@ func (auth *smtpLoginAuth) Next(fromServer []byte, more bool) ([]byte, error) {
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SMTP authentication type names.
|
||||||
const (
|
const (
|
||||||
SMTP_PLAIN = "PLAIN"
|
SMTPPlain = "PLAIN"
|
||||||
SMTP_LOGIN = "LOGIN"
|
SMTPLogin = "LOGIN"
|
||||||
)
|
)
|
||||||
|
|
||||||
var SMTPAuths = []string{SMTP_PLAIN, SMTP_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 {
|
func SMTPAuth(a smtp.Auth, cfg *SMTPConfig) error {
|
||||||
c, err := smtp.Dial(fmt.Sprintf("%s:%d", cfg.Host, cfg.Port))
|
c, err := smtp.Dial(fmt.Sprintf("%s:%d", cfg.Host, cfg.Port))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -404,16 +443,16 @@ func LoginViaSMTP(user *User, login, password string, sourceID int64, cfg *SMTPC
|
|||||||
if len(cfg.AllowedDomains) > 0 {
|
if len(cfg.AllowedDomains) > 0 {
|
||||||
idx := strings.Index(login, "@")
|
idx := strings.Index(login, "@")
|
||||||
if idx == -1 {
|
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:]) {
|
} else if !com.IsSliceContainsStr(strings.Split(cfg.AllowedDomains, ","), login[idx+1:]) {
|
||||||
return nil, ErrUserNotExist{0, login}
|
return nil, ErrUserNotExist{0, login, 0}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var auth smtp.Auth
|
var auth smtp.Auth
|
||||||
if cfg.Auth == SMTP_PLAIN {
|
if cfg.Auth == SMTPPlain {
|
||||||
auth = smtp.PlainAuth("", login, password, cfg.Host)
|
auth = smtp.PlainAuth("", login, password, cfg.Host)
|
||||||
} else if cfg.Auth == SMTP_LOGIN {
|
} else if cfg.Auth == SMTPLogin {
|
||||||
auth = &smtpLoginAuth{login, password}
|
auth = &smtpLoginAuth{login, password}
|
||||||
} else {
|
} else {
|
||||||
return nil, errors.New("Unsupported SMTP auth type")
|
return nil, errors.New("Unsupported SMTP auth type")
|
||||||
@@ -425,7 +464,7 @@ func LoginViaSMTP(user *User, login, password string, sourceID int64, cfg *SMTPC
|
|||||||
tperr, ok := err.(*textproto.Error)
|
tperr, ok := err.(*textproto.Error)
|
||||||
if (ok && tperr.Code == 535) ||
|
if (ok && tperr.Code == 535) ||
|
||||||
strings.Contains(err.Error(), "Username and Password not accepted") {
|
strings.Contains(err.Error(), "Username and Password not accepted") {
|
||||||
return nil, ErrUserNotExist{0, login}
|
return nil, ErrUserNotExist{0, login, 0}
|
||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -445,7 +484,7 @@ func LoginViaSMTP(user *User, login, password string, sourceID int64, cfg *SMTPC
|
|||||||
Name: strings.ToLower(username),
|
Name: strings.ToLower(username),
|
||||||
Email: login,
|
Email: login,
|
||||||
Passwd: password,
|
Passwd: password,
|
||||||
LoginType: LOGIN_SMTP,
|
LoginType: LoginSMTP,
|
||||||
LoginSource: sourceID,
|
LoginSource: sourceID,
|
||||||
LoginName: login,
|
LoginName: login,
|
||||||
IsActive: true,
|
IsActive: true,
|
||||||
@@ -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,
|
// LoginViaPAM queries if login/password is valid against the PAM,
|
||||||
// and create a local user if success when enabled.
|
// and create a local user if success when enabled.
|
||||||
func LoginViaPAM(user *User, login, password string, sourceID int64, cfg *PAMConfig, autoRegister bool) (*User, error) {
|
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") {
|
if strings.Contains(err.Error(), "Authentication failure") {
|
||||||
return nil, ErrUserNotExist{0, login}
|
return nil, ErrUserNotExist{0, login, 0}
|
||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -479,7 +518,7 @@ func LoginViaPAM(user *User, login, password string, sourceID int64, cfg *PAMCon
|
|||||||
Name: login,
|
Name: login,
|
||||||
Email: login,
|
Email: login,
|
||||||
Passwd: password,
|
Passwd: password,
|
||||||
LoginType: LOGIN_PAM,
|
LoginType: LoginPAM,
|
||||||
LoginSource: sourceID,
|
LoginSource: sourceID,
|
||||||
LoginName: login,
|
LoginName: login,
|
||||||
IsActive: true,
|
IsActive: true,
|
||||||
@@ -487,17 +526,18 @@ func LoginViaPAM(user *User, login, password string, sourceID int64, cfg *PAMCon
|
|||||||
return user, CreateUser(user)
|
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) {
|
func ExternalUserLogin(user *User, login, password string, source *LoginSource, autoRegister bool) (*User, error) {
|
||||||
if !source.IsActived {
|
if !source.IsActived {
|
||||||
return nil, ErrLoginSourceNotActived
|
return nil, ErrLoginSourceNotActived
|
||||||
}
|
}
|
||||||
|
|
||||||
switch source.Type {
|
switch source.Type {
|
||||||
case LOGIN_LDAP, LOGIN_DLDAP:
|
case LoginLDAP, LoginDLDAP:
|
||||||
return LoginViaLDAP(user, login, password, source, autoRegister)
|
return LoginViaLDAP(user, login, password, source, autoRegister)
|
||||||
case LOGIN_SMTP:
|
case LoginSMTP:
|
||||||
return LoginViaSMTP(user, login, password, source.ID, source.Cfg.(*SMTPConfig), autoRegister)
|
return LoginViaSMTP(user, login, password, source.ID, source.Cfg.(*SMTPConfig), autoRegister)
|
||||||
case LOGIN_PAM:
|
case LoginPAM:
|
||||||
return LoginViaPAM(user, login, password, source.ID, source.Cfg.(*PAMConfig), autoRegister)
|
return LoginViaPAM(user, login, password, source.ID, source.Cfg.(*PAMConfig), autoRegister)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -505,12 +545,12 @@ func ExternalUserLogin(user *User, login, password string, source *LoginSource,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// UserSignIn validates user name and password.
|
// UserSignIn validates user name and password.
|
||||||
func UserSignIn(username, passowrd string) (*User, error) {
|
func UserSignIn(username, password string) (*User, error) {
|
||||||
var user *User
|
var user *User
|
||||||
if strings.Contains(username, "@") {
|
if strings.Contains(username, "@") {
|
||||||
user = &User{Email: strings.ToLower(username)}
|
user = &User{Email: strings.ToLower(strings.TrimSpace(username))}
|
||||||
} else {
|
} else {
|
||||||
user = &User{LowerName: strings.ToLower(username)}
|
user = &User{LowerName: strings.ToLower(strings.TrimSpace(username))}
|
||||||
}
|
}
|
||||||
|
|
||||||
hasUser, err := x.Get(user)
|
hasUser, err := x.Get(user)
|
||||||
@@ -520,12 +560,12 @@ func UserSignIn(username, passowrd string) (*User, error) {
|
|||||||
|
|
||||||
if hasUser {
|
if hasUser {
|
||||||
switch user.LoginType {
|
switch user.LoginType {
|
||||||
case LOGIN_NOTYPE, LOGIN_PLAIN:
|
case LoginNoType, LoginPlain:
|
||||||
if user.ValidatePassword(passowrd) {
|
if user.ValidatePassword(password) {
|
||||||
return user, nil
|
return user, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, ErrUserNotExist{user.ID, user.Name}
|
return nil, ErrUserNotExist{user.ID, user.Name, 0}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
var source LoginSource
|
var source LoginSource
|
||||||
@@ -536,7 +576,7 @@ func UserSignIn(username, passowrd string) (*User, error) {
|
|||||||
return nil, ErrLoginSourceNotExist{user.LoginSource}
|
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 {
|
for _, source := range sources {
|
||||||
authUser, err := ExternalUserLogin(nil, username, passowrd, source, true)
|
authUser, err := ExternalUserLogin(nil, username, password, source, true)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return authUser, 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)
|
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}
|
||||||
}
|
}
|
||||||
|
|||||||
120
models/mail.go
120
models/mail.go
@@ -5,58 +5,45 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"html/template"
|
"html/template"
|
||||||
"path"
|
"path"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/base"
|
||||||
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
"code.gitea.io/gitea/modules/mailer"
|
||||||
|
"code.gitea.io/gitea/modules/markdown"
|
||||||
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"gopkg.in/gomail.v2"
|
"gopkg.in/gomail.v2"
|
||||||
"gopkg.in/macaron.v1"
|
"gopkg.in/macaron.v1"
|
||||||
|
|
||||||
"github.com/gogits/gogs/modules/base"
|
|
||||||
"github.com/gogits/gogs/modules/log"
|
|
||||||
"github.com/gogits/gogs/modules/mailer"
|
|
||||||
"github.com/gogits/gogs/modules/markdown"
|
|
||||||
"github.com/gogits/gogs/modules/setting"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
MAIL_AUTH_ACTIVATE base.TplName = "auth/activate"
|
mailAuthActivate base.TplName = "auth/activate"
|
||||||
MAIL_AUTH_ACTIVATE_EMAIL base.TplName = "auth/activate_email"
|
mailAuthActivateEmail base.TplName = "auth/activate_email"
|
||||||
MAIL_AUTH_RESET_PASSWORD base.TplName = "auth/reset_passwd"
|
mailAuthResetPassword base.TplName = "auth/reset_passwd"
|
||||||
MAIL_AUTH_REGISTER_NOTIFY base.TplName = "auth/register_notify"
|
mailAuthRegisterNotify base.TplName = "auth/register_notify"
|
||||||
|
|
||||||
MAIL_ISSUE_COMMENT base.TplName = "issue/comment"
|
mailIssueComment base.TplName = "issue/comment"
|
||||||
MAIL_ISSUE_MENTION base.TplName = "issue/mention"
|
mailIssueMention base.TplName = "issue/mention"
|
||||||
|
|
||||||
MAIL_NOTIFY_COLLABORATOR base.TplName = "notify/collaborator"
|
mailNotifyCollaborator base.TplName = "notify/collaborator"
|
||||||
)
|
)
|
||||||
|
|
||||||
type MailRender interface {
|
var templates *template.Template
|
||||||
HTMLString(string, interface{}, ...macaron.HTMLOptions) (string, error)
|
|
||||||
}
|
// InitMailRender initializes the macaron mail renderer
|
||||||
|
func InitMailRender(tmpls *template.Template) {
|
||||||
var mailRender MailRender
|
templates = tmpls
|
||||||
|
|
||||||
func InitMailRender(dir, appendDir string, funcMap []template.FuncMap) {
|
|
||||||
opt := &macaron.RenderOptions{
|
|
||||||
Directory: dir,
|
|
||||||
AppendDirectories: []string{appendDir},
|
|
||||||
Funcs: funcMap,
|
|
||||||
Extensions: []string{".tmpl", ".html"},
|
|
||||||
}
|
|
||||||
ts := macaron.NewTemplateSet()
|
|
||||||
ts.Set(macaron.DEFAULT_TPL_SET_NAME, opt)
|
|
||||||
|
|
||||||
mailRender = &macaron.TplRender{
|
|
||||||
TemplateSet: ts,
|
|
||||||
Opt: opt,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SendTestMail sends a test mail
|
||||||
func SendTestMail(email string) error {
|
func SendTestMail(email string) error {
|
||||||
return gomail.Send(&mailer.Sender{}, mailer.NewMessage([]string{email}, "Gogs Test Email!", "Gogs Test Email!").Message)
|
return gomail.Send(&mailer.Sender{}, mailer.NewMessage([]string{email}, "Gitea Test Email!", "Gitea Test Email!").Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SendUserMail sends a mail to the user
|
||||||
func SendUserMail(c *macaron.Context, u *User, tpl base.TplName, code, subject, info string) {
|
func SendUserMail(c *macaron.Context, u *User, tpl base.TplName, code, subject, info string) {
|
||||||
data := map[string]interface{}{
|
data := map[string]interface{}{
|
||||||
"Username": u.DisplayName(),
|
"Username": u.DisplayName(),
|
||||||
@@ -64,27 +51,31 @@ func SendUserMail(c *macaron.Context, u *User, tpl base.TplName, code, subject,
|
|||||||
"ResetPwdCodeLives": setting.Service.ResetPwdCodeLives / 60,
|
"ResetPwdCodeLives": setting.Service.ResetPwdCodeLives / 60,
|
||||||
"Code": code,
|
"Code": code,
|
||||||
}
|
}
|
||||||
body, err := mailRender.HTMLString(string(tpl), data)
|
|
||||||
if err != nil {
|
var content bytes.Buffer
|
||||||
log.Error(3, "HTMLString: %v", err)
|
|
||||||
|
if err := templates.ExecuteTemplate(&content, string(tpl), data); err != nil {
|
||||||
|
log.Error(3, "Template: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
msg := mailer.NewMessage([]string{u.Email}, subject, body)
|
msg := mailer.NewMessage([]string{u.Email}, subject, content.String())
|
||||||
msg.Info = fmt.Sprintf("UID: %d, %s", u.ID, info)
|
msg.Info = fmt.Sprintf("UID: %d, %s", u.ID, info)
|
||||||
|
|
||||||
mailer.SendAsync(msg)
|
mailer.SendAsync(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SendActivateAccountMail sends an activation mail to the user
|
||||||
func SendActivateAccountMail(c *macaron.Context, u *User) {
|
func SendActivateAccountMail(c *macaron.Context, u *User) {
|
||||||
SendUserMail(c, u, MAIL_AUTH_ACTIVATE, 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) {
|
func SendResetPasswordMail(c *macaron.Context, u *User) {
|
||||||
SendUserMail(c, u, MAIL_AUTH_RESET_PASSWORD, 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) {
|
func SendActivateEmailMail(c *macaron.Context, u *User, email *EmailAddress) {
|
||||||
data := map[string]interface{}{
|
data := map[string]interface{}{
|
||||||
"Username": u.DisplayName(),
|
"Username": u.DisplayName(),
|
||||||
@@ -92,13 +83,15 @@ func SendActivateEmailMail(c *macaron.Context, u *User, email *EmailAddress) {
|
|||||||
"Code": u.GenerateEmailActivateCode(email.Email),
|
"Code": u.GenerateEmailActivateCode(email.Email),
|
||||||
"Email": email.Email,
|
"Email": email.Email,
|
||||||
}
|
}
|
||||||
body, err := mailRender.HTMLString(string(MAIL_AUTH_ACTIVATE_EMAIL), data)
|
|
||||||
if err != nil {
|
var content bytes.Buffer
|
||||||
log.Error(3, "HTMLString: %v", err)
|
|
||||||
|
if err := templates.ExecuteTemplate(&content, string(mailAuthActivateEmail), data); err != nil {
|
||||||
|
log.Error(3, "Template: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
msg := mailer.NewMessage([]string{email.Email}, c.Tr("mail.activate_email"), body)
|
msg := mailer.NewMessage([]string{email.Email}, c.Tr("mail.activate_email"), content.String())
|
||||||
msg.Info = fmt.Sprintf("UID: %d, activate email", u.ID)
|
msg.Info = fmt.Sprintf("UID: %d, activate email", u.ID)
|
||||||
|
|
||||||
mailer.SendAsync(msg)
|
mailer.SendAsync(msg)
|
||||||
@@ -109,13 +102,15 @@ func SendRegisterNotifyMail(c *macaron.Context, u *User) {
|
|||||||
data := map[string]interface{}{
|
data := map[string]interface{}{
|
||||||
"Username": u.DisplayName(),
|
"Username": u.DisplayName(),
|
||||||
}
|
}
|
||||||
body, err := mailRender.HTMLString(string(MAIL_AUTH_REGISTER_NOTIFY), data)
|
|
||||||
if err != nil {
|
var content bytes.Buffer
|
||||||
log.Error(3, "HTMLString: %v", err)
|
|
||||||
|
if err := templates.ExecuteTemplate(&content, string(mailAuthRegisterNotify), data); err != nil {
|
||||||
|
log.Error(3, "Template: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
msg := mailer.NewMessage([]string{u.Email}, c.Tr("mail.register_notify"), body)
|
msg := mailer.NewMessage([]string{u.Email}, c.Tr("mail.register_notify"), content.String())
|
||||||
msg.Info = fmt.Sprintf("UID: %d, registration notify", u.ID)
|
msg.Info = fmt.Sprintf("UID: %d, registration notify", u.ID)
|
||||||
|
|
||||||
mailer.SendAsync(msg)
|
mailer.SendAsync(msg)
|
||||||
@@ -131,13 +126,15 @@ func SendCollaboratorMail(u, doer *User, repo *Repository) {
|
|||||||
"RepoName": repoName,
|
"RepoName": repoName,
|
||||||
"Link": repo.HTMLURL(),
|
"Link": repo.HTMLURL(),
|
||||||
}
|
}
|
||||||
body, err := mailRender.HTMLString(string(MAIL_NOTIFY_COLLABORATOR), data)
|
|
||||||
if err != nil {
|
var content bytes.Buffer
|
||||||
log.Error(3, "HTMLString: %v", err)
|
|
||||||
|
if err := templates.ExecuteTemplate(&content, string(mailNotifyCollaborator), data); err != nil {
|
||||||
|
log.Error(3, "Template: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
msg := mailer.NewMessage([]string{u.Email}, subject, body)
|
msg := mailer.NewMessage([]string{u.Email}, subject, content.String())
|
||||||
msg.Info = fmt.Sprintf("UID: %d, add collaborator", u.ID)
|
msg.Info = fmt.Sprintf("UID: %d, add collaborator", u.ID)
|
||||||
|
|
||||||
mailer.SendAsync(msg)
|
mailer.SendAsync(msg)
|
||||||
@@ -152,15 +149,18 @@ func composeTplData(subject, body, link string) map[string]interface{} {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func composeIssueMessage(issue *Issue, doer *User, tplName base.TplName, tos []string, info string) *mailer.Message {
|
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()))
|
body := string(markdown.RenderSpecialLink([]byte(issue.Content), issue.Repo.HTMLURL(), issue.Repo.ComposeMetas()))
|
||||||
data := composeTplData(subject, body, issue.HTMLURL())
|
data := composeTplData(subject, body, issue.HTMLURL())
|
||||||
data["Doer"] = doer
|
data["Doer"] = doer
|
||||||
content, err := mailRender.HTMLString(string(tplName), data)
|
|
||||||
if err != nil {
|
var content bytes.Buffer
|
||||||
log.Error(3, "HTMLString (%s): %v", tplName, err)
|
|
||||||
|
if err := templates.ExecuteTemplate(&content, string(tplName), data); err != nil {
|
||||||
|
log.Error(3, "Template: %v", err)
|
||||||
}
|
}
|
||||||
msg := mailer.NewMessageFrom(tos, fmt.Sprintf(`"%s" <%s>`, doer.DisplayName(), setting.MailService.User), subject, content)
|
|
||||||
|
msg := mailer.NewMessageFrom(tos, fmt.Sprintf(`"%s" <%s>`, doer.DisplayName(), setting.MailService.FromEmail), subject, content.String())
|
||||||
msg.Info = fmt.Sprintf("Subject: %s, %s", subject, info)
|
msg.Info = fmt.Sprintf("Subject: %s, %s", subject, info)
|
||||||
return msg
|
return msg
|
||||||
}
|
}
|
||||||
@@ -171,7 +171,7 @@ func SendIssueCommentMail(issue *Issue, doer *User, tos []string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
mailer.SendAsync(composeIssueMessage(issue, doer, MAIL_ISSUE_COMMENT, tos, "issue comment"))
|
mailer.SendAsync(composeIssueMessage(issue, doer, mailIssueComment, tos, "issue comment"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendIssueMentionMail composes and sends issue mention emails to target receivers.
|
// SendIssueMentionMail composes and sends issue mention emails to target receivers.
|
||||||
@@ -179,5 +179,5 @@ func SendIssueMentionMail(issue *Issue, doer *User, tos []string) {
|
|||||||
if len(tos) == 0 {
|
if len(tos) == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
mailer.SendAsync(composeIssueMessage(issue, doer, MAIL_ISSUE_MENTION, tos, "issue mention"))
|
mailer.SendAsync(composeIssueMessage(issue, doer, mailIssueMention, tos, "issue mention"))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,13 +20,14 @@ import (
|
|||||||
gouuid "github.com/satori/go.uuid"
|
gouuid "github.com/satori/go.uuid"
|
||||||
"gopkg.in/ini.v1"
|
"gopkg.in/ini.v1"
|
||||||
|
|
||||||
"github.com/gogits/gogs/modules/base"
|
"code.gitea.io/gitea/modules/base"
|
||||||
"github.com/gogits/gogs/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"github.com/gogits/gogs/modules/setting"
|
"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 {
|
type Migration interface {
|
||||||
Description() string
|
Description() string
|
||||||
Migrate(*xorm.Engine) error
|
Migrate(*xorm.Engine) error
|
||||||
@@ -37,19 +38,22 @@ type migration struct {
|
|||||||
migrate func(*xorm.Engine) error
|
migrate func(*xorm.Engine) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewMigration creates a new migration
|
||||||
func NewMigration(desc string, fn func(*xorm.Engine) error) Migration {
|
func NewMigration(desc string, fn func(*xorm.Engine) error) Migration {
|
||||||
return &migration{desc, fn}
|
return &migration{desc, fn}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Description returns the migration's description
|
||||||
func (m *migration) Description() string {
|
func (m *migration) Description() string {
|
||||||
return m.description
|
return m.description
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Migrate executes the migration
|
||||||
func (m *migration) Migrate(x *xorm.Engine) error {
|
func (m *migration) Migrate(x *xorm.Engine) error {
|
||||||
return m.migrate(x)
|
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 {
|
type Version struct {
|
||||||
ID int64 `xorm:"pk autoincr"`
|
ID int64 `xorm:"pk autoincr"`
|
||||||
Version int64
|
Version int64
|
||||||
@@ -57,11 +61,11 @@ type Version struct {
|
|||||||
|
|
||||||
// This is a sequence of migrations. Add new migrations to the bottom of the list.
|
// 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
|
// 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{
|
var migrations = []Migration{
|
||||||
// v0 -> v4: before 0.6.0 -> 0.7.33
|
// v0 -> v4: before 0.6.0 -> 0.7.33
|
||||||
NewMigration("fix locale file load panic", fixLocaleFileLoadPanic), // V4 -> V5:v0.6.0
|
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("generate issue-label from issue", issueToIssueLabel), // V6 -> V7:v0.6.4
|
||||||
NewMigration("refactor attachment table", attachmentRefactor), // V7 -> V8:v0.6.4
|
NewMigration("refactor attachment table", attachmentRefactor), // V7 -> V8:v0.6.4
|
||||||
NewMigration("rename pull request fields", renamePullRequestFields), // V8 -> V9:v0.6.16
|
NewMigration("rename pull request fields", renamePullRequestFields), // V8 -> V9:v0.6.16
|
||||||
@@ -72,6 +76,8 @@ var migrations = []Migration{
|
|||||||
|
|
||||||
// v13 -> v14:v0.9.87
|
// v13 -> v14:v0.9.87
|
||||||
NewMigration("set comment updated with created", setCommentUpdatedWithCreated),
|
NewMigration("set comment updated with created", setCommentUpdatedWithCreated),
|
||||||
|
|
||||||
|
NewMigration("create user column diff view style", createUserColumnDiffViewStyle),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Migrate database to current version
|
// Migrate database to current version
|
||||||
@@ -87,7 +93,7 @@ func Migrate(x *xorm.Engine) error {
|
|||||||
} else if !has {
|
} else if !has {
|
||||||
// If the version record does not exist we think
|
// If the version record does not exist we think
|
||||||
// it is a fresh installation and we can skip all migrations.
|
// 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 {
|
if _, err = x.InsertOne(currentVersion); err != nil {
|
||||||
return fmt.Errorf("insert: %v", err)
|
return fmt.Errorf("insert: %v", err)
|
||||||
@@ -95,19 +101,19 @@ func Migrate(x *xorm.Engine) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
v := currentVersion.Version
|
v := currentVersion.Version
|
||||||
if _MIN_DB_VER > v {
|
if minDBVersion > v {
|
||||||
log.Fatal(4, `Gogs no longer supports auto-migration from your previously installed version.
|
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.`)
|
Please try to upgrade to a lower version (>= v0.6.0) first, then upgrade to current version.`)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if int(v-_MIN_DB_VER) > len(migrations) {
|
if int(v-minDBVersion) > len(migrations) {
|
||||||
// User downgraded Gogs.
|
// User downgraded Gogs.
|
||||||
currentVersion.Version = int64(len(migrations) + _MIN_DB_VER)
|
currentVersion.Version = int64(len(migrations) + minDBVersion)
|
||||||
_, err = x.Id(1).Update(currentVersion)
|
_, err = x.Id(1).Update(currentVersion)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for i, m := range migrations[v-_MIN_DB_VER:] {
|
for i, m := range migrations[v-minDBVersion:] {
|
||||||
log.Info("Migration: %s", m.Description())
|
log.Info("Migration: %s", m.Description())
|
||||||
if err = m.Migrate(x); err != nil {
|
if err = m.Migrate(x); err != nil {
|
||||||
return fmt.Errorf("do migrate: %v", err)
|
return fmt.Errorf("do migrate: %v", err)
|
||||||
@@ -142,7 +148,7 @@ func fixLocaleFileLoadPanic(_ *xorm.Engine) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func trimCommitActionAppUrlPrefix(x *xorm.Engine) error {
|
func trimCommitActionAppURLPrefix(x *xorm.Engine) error {
|
||||||
type PushCommit struct {
|
type PushCommit struct {
|
||||||
Sha1 string
|
Sha1 string
|
||||||
Message string
|
Message string
|
||||||
@@ -153,7 +159,7 @@ func trimCommitActionAppUrlPrefix(x *xorm.Engine) error {
|
|||||||
type PushCommits struct {
|
type PushCommits struct {
|
||||||
Len int
|
Len int
|
||||||
Commits []*PushCommit
|
Commits []*PushCommit
|
||||||
CompareUrl string
|
CompareURL string `json:"CompareUrl"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Action struct {
|
type Action struct {
|
||||||
@@ -184,11 +190,11 @@ func trimCommitActionAppUrlPrefix(x *xorm.Engine) error {
|
|||||||
return fmt.Errorf("unmarshal action content[%d]: %v", actID, err)
|
return fmt.Errorf("unmarshal action content[%d]: %v", actID, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
infos := strings.Split(pushCommits.CompareUrl, "/")
|
infos := strings.Split(pushCommits.CompareURL, "/")
|
||||||
if len(infos) <= 4 {
|
if len(infos) <= 4 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
pushCommits.CompareUrl = strings.Join(infos[len(infos)-4:], "/")
|
pushCommits.CompareURL = strings.Join(infos[len(infos)-4:], "/")
|
||||||
|
|
||||||
p, err := json.Marshal(pushCommits)
|
p, err := json.Marshal(pushCommits)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -451,8 +457,12 @@ func generateOrgRandsAndSalt(x *xorm.Engine) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, org := range orgs {
|
for _, org := range orgs {
|
||||||
org.Rands = base.GetRandomString(10)
|
if org.Rands, err = base.GetRandomString(10); err != nil {
|
||||||
org.Salt = base.GetRandomString(10)
|
return err
|
||||||
|
}
|
||||||
|
if org.Salt, err = base.GetRandomString(10); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if _, err = sess.Id(org.ID).Update(org); err != nil {
|
if _, err = sess.Id(org.ID).Update(org); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -461,27 +471,34 @@ func generateOrgRandsAndSalt(x *xorm.Engine) (err error) {
|
|||||||
return sess.Commit()
|
return sess.Commit()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TAction defines the struct for migrating table action
|
||||||
type TAction struct {
|
type TAction struct {
|
||||||
ID int64 `xorm:"pk autoincr"`
|
ID int64 `xorm:"pk autoincr"`
|
||||||
CreatedUnix int64
|
CreatedUnix int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TableName will be invoked by XORM to customrize the table name
|
||||||
func (t *TAction) TableName() string { return "action" }
|
func (t *TAction) TableName() string { return "action" }
|
||||||
|
|
||||||
|
// TNotice defines the struct for migrating table notice
|
||||||
type TNotice struct {
|
type TNotice struct {
|
||||||
ID int64 `xorm:"pk autoincr"`
|
ID int64 `xorm:"pk autoincr"`
|
||||||
CreatedUnix int64
|
CreatedUnix int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TableName will be invoked by XORM to customrize the table name
|
||||||
func (t *TNotice) TableName() string { return "notice" }
|
func (t *TNotice) TableName() string { return "notice" }
|
||||||
|
|
||||||
|
// TComment defines the struct for migrating table comment
|
||||||
type TComment struct {
|
type TComment struct {
|
||||||
ID int64 `xorm:"pk autoincr"`
|
ID int64 `xorm:"pk autoincr"`
|
||||||
CreatedUnix int64
|
CreatedUnix int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TableName will be invoked by XORM to customrize the table name
|
||||||
func (t *TComment) TableName() string { return "comment" }
|
func (t *TComment) TableName() string { return "comment" }
|
||||||
|
|
||||||
|
// TIssue defines the struct for migrating table issue
|
||||||
type TIssue struct {
|
type TIssue struct {
|
||||||
ID int64 `xorm:"pk autoincr"`
|
ID int64 `xorm:"pk autoincr"`
|
||||||
DeadlineUnix int64
|
DeadlineUnix int64
|
||||||
@@ -489,99 +506,124 @@ type TIssue struct {
|
|||||||
UpdatedUnix int64
|
UpdatedUnix int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TableName will be invoked by XORM to customrize the table name
|
||||||
func (t *TIssue) TableName() string { return "issue" }
|
func (t *TIssue) TableName() string { return "issue" }
|
||||||
|
|
||||||
|
// TMilestone defines the struct for migrating table milestone
|
||||||
type TMilestone struct {
|
type TMilestone struct {
|
||||||
ID int64 `xorm:"pk autoincr"`
|
ID int64 `xorm:"pk autoincr"`
|
||||||
DeadlineUnix int64
|
DeadlineUnix int64
|
||||||
ClosedDateUnix int64
|
ClosedDateUnix int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TableName will be invoked by XORM to customrize the table name
|
||||||
func (t *TMilestone) TableName() string { return "milestone" }
|
func (t *TMilestone) TableName() string { return "milestone" }
|
||||||
|
|
||||||
|
// TAttachment defines the struct for migrating table attachment
|
||||||
type TAttachment struct {
|
type TAttachment struct {
|
||||||
ID int64 `xorm:"pk autoincr"`
|
ID int64 `xorm:"pk autoincr"`
|
||||||
CreatedUnix int64
|
CreatedUnix int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TableName will be invoked by XORM to customrize the table name
|
||||||
func (t *TAttachment) TableName() string { return "attachment" }
|
func (t *TAttachment) TableName() string { return "attachment" }
|
||||||
|
|
||||||
|
// TLoginSource defines the struct for migrating table login_source
|
||||||
type TLoginSource struct {
|
type TLoginSource struct {
|
||||||
ID int64 `xorm:"pk autoincr"`
|
ID int64 `xorm:"pk autoincr"`
|
||||||
CreatedUnix int64
|
CreatedUnix int64
|
||||||
UpdatedUnix int64
|
UpdatedUnix int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TableName will be invoked by XORM to customrize the table name
|
||||||
func (t *TLoginSource) TableName() string { return "login_source" }
|
func (t *TLoginSource) TableName() string { return "login_source" }
|
||||||
|
|
||||||
|
// TPull defines the struct for migrating table pull_request
|
||||||
type TPull struct {
|
type TPull struct {
|
||||||
ID int64 `xorm:"pk autoincr"`
|
ID int64 `xorm:"pk autoincr"`
|
||||||
MergedUnix int64
|
MergedUnix int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TableName will be invoked by XORM to customrize the table name
|
||||||
func (t *TPull) TableName() string { return "pull_request" }
|
func (t *TPull) TableName() string { return "pull_request" }
|
||||||
|
|
||||||
|
// TRelease defines the struct for migrating table release
|
||||||
type TRelease struct {
|
type TRelease struct {
|
||||||
ID int64 `xorm:"pk autoincr"`
|
ID int64 `xorm:"pk autoincr"`
|
||||||
CreatedUnix int64
|
CreatedUnix int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TableName will be invoked by XORM to customrize the table name
|
||||||
func (t *TRelease) TableName() string { return "release" }
|
func (t *TRelease) TableName() string { return "release" }
|
||||||
|
|
||||||
|
// TRepo defines the struct for migrating table repository
|
||||||
type TRepo struct {
|
type TRepo struct {
|
||||||
ID int64 `xorm:"pk autoincr"`
|
ID int64 `xorm:"pk autoincr"`
|
||||||
CreatedUnix int64
|
CreatedUnix int64
|
||||||
UpdatedUnix int64
|
UpdatedUnix int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TableName will be invoked by XORM to customrize the table name
|
||||||
func (t *TRepo) TableName() string { return "repository" }
|
func (t *TRepo) TableName() string { return "repository" }
|
||||||
|
|
||||||
|
// TMirror defines the struct for migrating table mirror
|
||||||
type TMirror struct {
|
type TMirror struct {
|
||||||
ID int64 `xorm:"pk autoincr"`
|
ID int64 `xorm:"pk autoincr"`
|
||||||
UpdatedUnix int64
|
UpdatedUnix int64
|
||||||
NextUpdateUnix int64
|
NextUpdateUnix int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TableName will be invoked by XORM to customrize the table name
|
||||||
func (t *TMirror) TableName() string { return "mirror" }
|
func (t *TMirror) TableName() string { return "mirror" }
|
||||||
|
|
||||||
|
// TPublicKey defines the struct for migrating table public_key
|
||||||
type TPublicKey struct {
|
type TPublicKey struct {
|
||||||
ID int64 `xorm:"pk autoincr"`
|
ID int64 `xorm:"pk autoincr"`
|
||||||
CreatedUnix int64
|
CreatedUnix int64
|
||||||
UpdatedUnix int64
|
UpdatedUnix int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TableName will be invoked by XORM to customrize the table name
|
||||||
func (t *TPublicKey) TableName() string { return "public_key" }
|
func (t *TPublicKey) TableName() string { return "public_key" }
|
||||||
|
|
||||||
|
// TDeployKey defines the struct for migrating table deploy_key
|
||||||
type TDeployKey struct {
|
type TDeployKey struct {
|
||||||
ID int64 `xorm:"pk autoincr"`
|
ID int64 `xorm:"pk autoincr"`
|
||||||
CreatedUnix int64
|
CreatedUnix int64
|
||||||
UpdatedUnix int64
|
UpdatedUnix int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TableName will be invoked by XORM to customrize the table name
|
||||||
func (t *TDeployKey) TableName() string { return "deploy_key" }
|
func (t *TDeployKey) TableName() string { return "deploy_key" }
|
||||||
|
|
||||||
|
// TAccessToken defines the struct for migrating table access_token
|
||||||
type TAccessToken struct {
|
type TAccessToken struct {
|
||||||
ID int64 `xorm:"pk autoincr"`
|
ID int64 `xorm:"pk autoincr"`
|
||||||
CreatedUnix int64
|
CreatedUnix int64
|
||||||
UpdatedUnix int64
|
UpdatedUnix int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TableName will be invoked by XORM to customrize the table name
|
||||||
func (t *TAccessToken) TableName() string { return "access_token" }
|
func (t *TAccessToken) TableName() string { return "access_token" }
|
||||||
|
|
||||||
|
// TUser defines the struct for migrating table user
|
||||||
type TUser struct {
|
type TUser struct {
|
||||||
ID int64 `xorm:"pk autoincr"`
|
ID int64 `xorm:"pk autoincr"`
|
||||||
CreatedUnix int64
|
CreatedUnix int64
|
||||||
UpdatedUnix int64
|
UpdatedUnix int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TableName will be invoked by XORM to customrize the table name
|
||||||
func (t *TUser) TableName() string { return "user" }
|
func (t *TUser) TableName() string { return "user" }
|
||||||
|
|
||||||
|
// TWebhook defines the struct for migrating table webhook
|
||||||
type TWebhook struct {
|
type TWebhook struct {
|
||||||
ID int64 `xorm:"pk autoincr"`
|
ID int64 `xorm:"pk autoincr"`
|
||||||
CreatedUnix int64
|
CreatedUnix int64
|
||||||
UpdatedUnix int64
|
UpdatedUnix int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TableName will be invoked by XORM to customrize the table name
|
||||||
func (t *TWebhook) TableName() string { return "webhook" }
|
func (t *TWebhook) TableName() string { return "webhook" }
|
||||||
|
|
||||||
func convertDateToUnix(x *xorm.Engine) (err error) {
|
func convertDateToUnix(x *xorm.Engine) (err error) {
|
||||||
@@ -628,7 +670,7 @@ func convertDateToUnix(x *xorm.Engine) (err error) {
|
|||||||
offset := 0
|
offset := 0
|
||||||
for {
|
for {
|
||||||
beans := make([]*Bean, 0, 100)
|
beans := make([]*Bean, 0, 100)
|
||||||
if err = x.Sql(fmt.Sprintf("SELECT * FROM `%s` ORDER BY id ASC LIMIT 100 OFFSET %d",
|
if err = x.SQL(fmt.Sprintf("SELECT * FROM `%s` ORDER BY id ASC LIMIT 100 OFFSET %d",
|
||||||
table.name, offset)).Find(&beans); err != nil {
|
table.name, offset)).Find(&beans); err != nil {
|
||||||
return fmt.Errorf("select beans [table: %s, offset: %d]: %v", table.name, offset, err)
|
return fmt.Errorf("select beans [table: %s, offset: %d]: %v", table.name, offset, err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,3 +22,17 @@ func setCommentUpdatedWithCreated(x *xorm.Engine) (err error) {
|
|||||||
}
|
}
|
||||||
return nil
|
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,13 +13,16 @@ import (
|
|||||||
"path"
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
// Needed for the MySQL driver
|
||||||
_ "github.com/go-sql-driver/mysql"
|
_ "github.com/go-sql-driver/mysql"
|
||||||
"github.com/go-xorm/core"
|
"github.com/go-xorm/core"
|
||||||
"github.com/go-xorm/xorm"
|
"github.com/go-xorm/xorm"
|
||||||
|
|
||||||
|
// Needed for the Postgresql driver
|
||||||
_ "github.com/lib/pq"
|
_ "github.com/lib/pq"
|
||||||
|
|
||||||
"github.com/gogits/gogs/models/migrations"
|
"code.gitea.io/gitea/models/migrations"
|
||||||
"github.com/gogits/gogs/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Engine represents a xorm engine or session.
|
// Engine represents a xorm engine or session.
|
||||||
@@ -33,7 +36,7 @@ type Engine interface {
|
|||||||
Insert(...interface{}) (int64, error)
|
Insert(...interface{}) (int64, error)
|
||||||
InsertOne(interface{}) (int64, error)
|
InsertOne(interface{}) (int64, error)
|
||||||
Iterate(interface{}, xorm.IterFunc) error
|
Iterate(interface{}, xorm.IterFunc) error
|
||||||
Sql(string, ...interface{}) *xorm.Session
|
SQL(interface{}, ...interface{}) *xorm.Session
|
||||||
Where(interface{}, ...interface{}) *xorm.Session
|
Where(interface{}, ...interface{}) *xorm.Session
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,16 +48,22 @@ func sessionRelease(sess *xorm.Session) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
x *xorm.Engine
|
x *xorm.Engine
|
||||||
tables []interface{}
|
tables []interface{}
|
||||||
|
|
||||||
|
// HasEngine specifies if we have a xorm.Engine
|
||||||
HasEngine bool
|
HasEngine bool
|
||||||
|
|
||||||
|
// DbCfg holds the database settings
|
||||||
DbCfg struct {
|
DbCfg struct {
|
||||||
Type, Host, Name, User, Passwd, Path, SSLMode string
|
Type, Host, Name, User, Passwd, Path, SSLMode string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EnableSQLite3 use SQLite3
|
||||||
EnableSQLite3 bool
|
EnableSQLite3 bool
|
||||||
EnableTiDB bool
|
|
||||||
|
// EnableTiDB enable TiDB
|
||||||
|
EnableTiDB bool
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -69,12 +78,13 @@ func init() {
|
|||||||
new(Team), new(OrgUser), new(TeamUser), new(TeamRepo),
|
new(Team), new(OrgUser), new(TeamUser), new(TeamRepo),
|
||||||
new(Notice), new(EmailAddress))
|
new(Notice), new(EmailAddress))
|
||||||
|
|
||||||
gonicNames := []string{"SSL"}
|
gonicNames := []string{"SSL", "UID"}
|
||||||
for _, name := range gonicNames {
|
for _, name := range gonicNames {
|
||||||
core.LintGonicMapper[name] = true
|
core.LintGonicMapper[name] = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LoadConfigs loads the database settings
|
||||||
func LoadConfigs() {
|
func LoadConfigs() {
|
||||||
sec := setting.Cfg.Section("database")
|
sec := setting.Cfg.Section("database")
|
||||||
DbCfg.Type = sec.Key("DB_TYPE").String()
|
DbCfg.Type = sec.Key("DB_TYPE").String()
|
||||||
@@ -95,7 +105,7 @@ func LoadConfigs() {
|
|||||||
DbCfg.Passwd = sec.Key("PASSWD").String()
|
DbCfg.Passwd = sec.Key("PASSWD").String()
|
||||||
}
|
}
|
||||||
DbCfg.SSLMode = sec.Key("SSL_MODE").String()
|
DbCfg.SSLMode = sec.Key("SSL_MODE").String()
|
||||||
DbCfg.Path = sec.Key("PATH").MustString("data/gogs.db")
|
DbCfg.Path = sec.Key("PATH").MustString("data/gitea.db")
|
||||||
}
|
}
|
||||||
|
|
||||||
// parsePostgreSQLHostPort parses given input in various forms defined in
|
// parsePostgreSQLHostPort parses given input in various forms defined in
|
||||||
@@ -115,7 +125,7 @@ func parsePostgreSQLHostPort(info string) (string, string) {
|
|||||||
|
|
||||||
func getEngine() (*xorm.Engine, error) {
|
func getEngine() (*xorm.Engine, error) {
|
||||||
connStr := ""
|
connStr := ""
|
||||||
var Param string = "?"
|
var Param = "?"
|
||||||
if strings.Contains(DbCfg.Name, Param) {
|
if strings.Contains(DbCfg.Name, Param) {
|
||||||
Param = "&"
|
Param = "&"
|
||||||
}
|
}
|
||||||
@@ -139,7 +149,7 @@ func getEngine() (*xorm.Engine, error) {
|
|||||||
}
|
}
|
||||||
case "sqlite3":
|
case "sqlite3":
|
||||||
if !EnableSQLite3 {
|
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 {
|
if err := os.MkdirAll(path.Dir(DbCfg.Path), os.ModePerm); err != nil {
|
||||||
return nil, fmt.Errorf("Fail to create directories: %v", err)
|
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"
|
connStr = "file:" + DbCfg.Path + "?cache=shared&mode=rwc"
|
||||||
case "tidb":
|
case "tidb":
|
||||||
if !EnableTiDB {
|
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 {
|
if err := os.MkdirAll(path.Dir(DbCfg.Path), os.ModePerm); err != nil {
|
||||||
return nil, fmt.Errorf("Fail to create directories: %v", err)
|
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)
|
return xorm.NewEngine(DbCfg.Type, connStr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewTestEngine sets a new test xorm.Engine
|
||||||
func NewTestEngine(x *xorm.Engine) (err error) {
|
func NewTestEngine(x *xorm.Engine) (err error) {
|
||||||
x, err = getEngine()
|
x, err = getEngine()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -169,6 +180,7 @@ func NewTestEngine(x *xorm.Engine) (err error) {
|
|||||||
return x.StoreEngine("InnoDB").Sync2(tables...)
|
return x.StoreEngine("InnoDB").Sync2(tables...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetEngine sets the xorm.Engine
|
||||||
func SetEngine() (err error) {
|
func SetEngine() (err error) {
|
||||||
x, err = getEngine()
|
x, err = getEngine()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -180,7 +192,10 @@ func SetEngine() (err error) {
|
|||||||
// WARNING: for serv command, MUST remove the output to os.stdout,
|
// WARNING: for serv command, MUST remove the output to os.stdout,
|
||||||
// so use log file to instead print to stdout.
|
// so use log file to instead print to stdout.
|
||||||
logPath := path.Join(setting.LogRootPath, "xorm.log")
|
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)
|
f, err := os.Create(logPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -191,22 +206,28 @@ func SetEngine() (err error) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewEngine initializes a new xorm.Engine
|
||||||
func NewEngine() (err error) {
|
func NewEngine() (err error) {
|
||||||
if err = SetEngine(); err != nil {
|
if err = SetEngine(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err = x.Ping(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if err = migrations.Migrate(x); err != nil {
|
if err = migrations.Migrate(x); err != nil {
|
||||||
return fmt.Errorf("migrate: %v", err)
|
return fmt.Errorf("migrate: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = x.StoreEngine("InnoDB").Sync2(tables...); err != nil {
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Statistic contains the database statistics
|
||||||
type Statistic struct {
|
type Statistic struct {
|
||||||
Counter struct {
|
Counter struct {
|
||||||
User, Org, PublicKey,
|
User, Org, PublicKey,
|
||||||
@@ -218,6 +239,7 @@ type Statistic struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetStatistic returns the database statistics
|
||||||
func GetStatistic() (stats Statistic) {
|
func GetStatistic() (stats Statistic) {
|
||||||
stats.Counter.User = CountUsers()
|
stats.Counter.User = CountUsers()
|
||||||
stats.Counter.Org = CountOrganizations()
|
stats.Counter.Org = CountOrganizations()
|
||||||
@@ -244,6 +266,7 @@ func GetStatistic() (stats Statistic) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ping tests if database is alive
|
||||||
func Ping() error {
|
func Ping() error {
|
||||||
return x.Ping()
|
return x.Ping()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// +build tidb go1.4.2
|
// +build tidb
|
||||||
|
|
||||||
// Copyright 2015 The Gogs Authors. All rights reserved.
|
// Copyright 2015 The Gogs Authors. All rights reserved.
|
||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
|
|||||||
213
models/org.go
213
models/org.go
@@ -10,14 +10,14 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/go-xorm/builder"
|
||||||
"github.com/go-xorm/xorm"
|
"github.com/go-xorm/xorm"
|
||||||
|
|
||||||
"github.com/gogits/gogs/modules/base"
|
|
||||||
"github.com/gogits/gogs/modules/log"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
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")
|
ErrTeamNotExist = errors.New("Team does not exist")
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -41,7 +41,7 @@ func (org *User) GetTeam(name string) (*Team, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (org *User) getOwnerTeam(e Engine) (*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.
|
// GetOwnerTeam returns owner team of organization.
|
||||||
@@ -50,7 +50,10 @@ func (org *User) GetOwnerTeam() (*Team, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (org *User) getTeams(e Engine) error {
|
func (org *User) getTeams(e Engine) error {
|
||||||
return e.Where("org_id=?", org.ID).Find(&org.Teams)
|
return e.
|
||||||
|
Where("org_id=?", org.ID).
|
||||||
|
OrderBy("CASE WHEN name LIKE '" + ownerTeamName + "' THEN '' ELSE name END").
|
||||||
|
Find(&org.Teams)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTeams returns all teams that belong to organization.
|
// GetTeams returns all teams that belong to organization.
|
||||||
@@ -65,14 +68,12 @@ func (org *User) GetMembers() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
org.Members = make([]*User, len(ous))
|
var ids = make([]int64, len(ous))
|
||||||
for i, ou := range ous {
|
for i, ou := range ous {
|
||||||
org.Members[i], err = GetUserByID(ou.Uid)
|
ids[i] = ou.UID
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return nil
|
org.Members, err = GetUsersByIDs(ids)
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddMember adds new member to organization.
|
// AddMember adds new member to organization.
|
||||||
@@ -108,8 +109,12 @@ func CreateOrganization(org, owner *User) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
org.LowerName = strings.ToLower(org.Name)
|
org.LowerName = strings.ToLower(org.Name)
|
||||||
org.Rands = GetUserSalt()
|
if org.Rands, err = GetUserSalt(); err != nil {
|
||||||
org.Salt = GetUserSalt()
|
return err
|
||||||
|
}
|
||||||
|
if org.Salt, err = GetUserSalt(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
org.UseCustomAvatar = true
|
org.UseCustomAvatar = true
|
||||||
org.MaxRepoCreation = -1
|
org.MaxRepoCreation = -1
|
||||||
org.NumTeams = 1
|
org.NumTeams = 1
|
||||||
@@ -128,7 +133,7 @@ func CreateOrganization(org, owner *User) (err error) {
|
|||||||
|
|
||||||
// Add initial creator to organization and owner team.
|
// Add initial creator to organization and owner team.
|
||||||
if _, err = sess.Insert(&OrgUser{
|
if _, err = sess.Insert(&OrgUser{
|
||||||
Uid: owner.ID,
|
UID: owner.ID,
|
||||||
OrgID: org.ID,
|
OrgID: org.ID,
|
||||||
IsOwner: true,
|
IsOwner: true,
|
||||||
NumTeams: 1,
|
NumTeams: 1,
|
||||||
@@ -139,9 +144,9 @@ func CreateOrganization(org, owner *User) (err error) {
|
|||||||
// Create default owner team.
|
// Create default owner team.
|
||||||
t := &Team{
|
t := &Team{
|
||||||
OrgID: org.ID,
|
OrgID: org.ID,
|
||||||
LowerName: strings.ToLower(OWNER_TEAM),
|
LowerName: strings.ToLower(ownerTeamName),
|
||||||
Name: OWNER_TEAM,
|
Name: ownerTeamName,
|
||||||
Authorize: ACCESS_MODE_OWNER,
|
Authorize: AccessModeOwner,
|
||||||
NumMembers: 1,
|
NumMembers: 1,
|
||||||
}
|
}
|
||||||
if _, err = sess.Insert(t); err != nil {
|
if _, err = sess.Insert(t); err != nil {
|
||||||
@@ -149,7 +154,7 @@ func CreateOrganization(org, owner *User) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if _, err = sess.Insert(&TeamUser{
|
if _, err = sess.Insert(&TeamUser{
|
||||||
Uid: owner.ID,
|
UID: owner.ID,
|
||||||
OrgID: org.ID,
|
OrgID: org.ID,
|
||||||
TeamID: t.ID,
|
TeamID: t.ID,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
@@ -170,7 +175,7 @@ func GetOrgByName(name string) (*User, error) {
|
|||||||
}
|
}
|
||||||
u := &User{
|
u := &User{
|
||||||
LowerName: strings.ToLower(name),
|
LowerName: strings.ToLower(name),
|
||||||
Type: USER_TYPE_ORGANIZATION,
|
Type: UserTypeOrganization,
|
||||||
}
|
}
|
||||||
has, err := x.Get(u)
|
has, err := x.Get(u)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -183,24 +188,27 @@ func GetOrgByName(name string) (*User, error) {
|
|||||||
|
|
||||||
// CountOrganizations returns number of organizations.
|
// CountOrganizations returns number of organizations.
|
||||||
func CountOrganizations() int64 {
|
func CountOrganizations() int64 {
|
||||||
count, _ := x.Where("type=1").Count(new(User))
|
count, _ := x.
|
||||||
|
Where("type=1").
|
||||||
|
Count(new(User))
|
||||||
return count
|
return count
|
||||||
}
|
}
|
||||||
|
|
||||||
// Organizations returns number of organizations in given page.
|
// Organizations returns number of organizations in given page.
|
||||||
func Organizations(page, pageSize int) ([]*User, error) {
|
func Organizations(page, pageSize int) ([]*User, error) {
|
||||||
orgs := make([]*User, 0, pageSize)
|
orgs := make([]*User, 0, pageSize)
|
||||||
return orgs, x.Limit(pageSize, (page-1)*pageSize).Where("type=1").Asc("id").Find(&orgs)
|
return orgs, x.
|
||||||
|
Limit(pageSize, (page-1)*pageSize).
|
||||||
|
Where("type=1").
|
||||||
|
Asc("name").
|
||||||
|
Find(&orgs)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteOrganization completely and permanently deletes everything of organization.
|
// DeleteOrganization completely and permanently deletes everything of organization.
|
||||||
func DeleteOrganization(org *User) (err error) {
|
func DeleteOrganization(org *User) (err error) {
|
||||||
if err := DeleteUser(org); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
sess := x.NewSession()
|
sess := x.NewSession()
|
||||||
defer sessionRelease(sess)
|
defer sess.Close()
|
||||||
|
|
||||||
if err = sess.Begin(); err != nil {
|
if err = sess.Begin(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -217,7 +225,11 @@ func DeleteOrganization(org *User) (err error) {
|
|||||||
return fmt.Errorf("deleteUser: %v", err)
|
return fmt.Errorf("deleteUser: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return sess.Commit()
|
if err = sess.Commit(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return RewriteAllPublicKeys()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ________ ____ ___
|
// ________ ____ ___
|
||||||
@@ -230,7 +242,7 @@ func DeleteOrganization(org *User) (err error) {
|
|||||||
// OrgUser represents an organization-user relation.
|
// OrgUser represents an organization-user relation.
|
||||||
type OrgUser struct {
|
type OrgUser struct {
|
||||||
ID int64 `xorm:"pk autoincr"`
|
ID int64 `xorm:"pk autoincr"`
|
||||||
Uid int64 `xorm:"INDEX UNIQUE(s)"`
|
UID int64 `xorm:"INDEX UNIQUE(s)"`
|
||||||
OrgID int64 `xorm:"INDEX UNIQUE(s)"`
|
OrgID int64 `xorm:"INDEX UNIQUE(s)"`
|
||||||
IsPublic bool
|
IsPublic bool
|
||||||
IsOwner bool
|
IsOwner bool
|
||||||
@@ -238,20 +250,31 @@ type OrgUser struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// IsOrganizationOwner returns true if given user is in the owner team.
|
// 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).Get(new(OrgUser))
|
has, _ := x.
|
||||||
|
Where("is_owner=?", true).
|
||||||
|
And("uid=?", uid).
|
||||||
|
And("org_id=?", orgID).
|
||||||
|
Get(new(OrgUser))
|
||||||
return has
|
return has
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsOrganizationMember returns true if given user is member of organization.
|
// 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).Get(new(OrgUser))
|
has, _ := x.
|
||||||
|
Where("uid=?", uid).
|
||||||
|
And("org_id=?", orgID).
|
||||||
|
Get(new(OrgUser))
|
||||||
return has
|
return has
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsPublicMembership returns true if given user public his/her membership.
|
// 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("is_public=?", true).Get(new(OrgUser))
|
has, _ := x.
|
||||||
|
Where("uid=?", uid).
|
||||||
|
And("org_id=?", orgID).
|
||||||
|
And("is_public=?", true).
|
||||||
|
Get(new(OrgUser))
|
||||||
return has
|
return has
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -260,8 +283,11 @@ func getOrgsByUserID(sess *xorm.Session, userID int64, showAll bool) ([]*User, e
|
|||||||
if !showAll {
|
if !showAll {
|
||||||
sess.And("`org_user`.is_public=?", true)
|
sess.And("`org_user`.is_public=?", true)
|
||||||
}
|
}
|
||||||
return orgs, sess.And("`org_user`.uid=?", userID).
|
return orgs, sess.
|
||||||
Join("INNER", "`org_user`", "`org_user`.org_id=`user`.id").Find(&orgs)
|
And("`org_user`.uid=?", userID).
|
||||||
|
Join("INNER", "`org_user`", "`org_user`.org_id=`user`.id").
|
||||||
|
Asc("`user`.name").
|
||||||
|
Find(&orgs)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetOrgsByUserID returns a list of organizations that the given user ID
|
// GetOrgsByUserID returns a list of organizations that the given user ID
|
||||||
@@ -278,8 +304,12 @@ func GetOrgsByUserIDDesc(userID int64, desc string, showAll bool) ([]*User, erro
|
|||||||
|
|
||||||
func getOwnedOrgsByUserID(sess *xorm.Session, userID int64) ([]*User, error) {
|
func getOwnedOrgsByUserID(sess *xorm.Session, userID int64) ([]*User, error) {
|
||||||
orgs := make([]*User, 0, 10)
|
orgs := make([]*User, 0, 10)
|
||||||
return orgs, sess.Where("`org_user`.uid=?", userID).And("`org_user`.is_owner=?", true).
|
return orgs, sess.
|
||||||
Join("INNER", "`org_user`", "`org_user`.org_id=`user`.id").Find(&orgs)
|
Where("`org_user`.uid=?", userID).
|
||||||
|
And("`org_user`.is_owner=?", true).
|
||||||
|
Join("INNER", "`org_user`", "`org_user`.org_id=`user`.id").
|
||||||
|
Asc("`user`.name").
|
||||||
|
Find(&orgs)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetOwnedOrgsByUserID returns a list of organizations are owned by given user ID.
|
// GetOwnedOrgsByUserID returns a list of organizations are owned by given user ID.
|
||||||
@@ -288,7 +318,7 @@ func GetOwnedOrgsByUserID(userID int64) ([]*User, error) {
|
|||||||
return getOwnedOrgsByUserID(sess, userID)
|
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.
|
// given user ID, ordered descending by the given condition.
|
||||||
func GetOwnedOrgsByUserIDDesc(userID int64, desc string) ([]*User, error) {
|
func GetOwnedOrgsByUserIDDesc(userID int64, desc string) ([]*User, error) {
|
||||||
sess := x.NewSession()
|
sess := x.NewSession()
|
||||||
@@ -298,26 +328,35 @@ func GetOwnedOrgsByUserIDDesc(userID int64, desc string) ([]*User, error) {
|
|||||||
// GetOrgUsersByUserID returns all organization-user relations by user ID.
|
// GetOrgUsersByUserID returns all organization-user relations by user ID.
|
||||||
func GetOrgUsersByUserID(uid int64, all bool) ([]*OrgUser, error) {
|
func GetOrgUsersByUserID(uid int64, all bool) ([]*OrgUser, error) {
|
||||||
ous := make([]*OrgUser, 0, 10)
|
ous := make([]*OrgUser, 0, 10)
|
||||||
sess := x.Where("uid=?", uid)
|
sess := x.
|
||||||
|
Join("LEFT", "user", "`org_user`.org_id=`user`.id").
|
||||||
|
Where("`org_user`.uid=?", uid)
|
||||||
if !all {
|
if !all {
|
||||||
// Only show public organizations
|
// Only show public organizations
|
||||||
sess.And("is_public=?", true)
|
sess.And("is_public=?", true)
|
||||||
}
|
}
|
||||||
err := sess.Find(&ous)
|
err := sess.
|
||||||
|
Asc("`user`.name").
|
||||||
|
Find(&ous)
|
||||||
return ous, err
|
return ous, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetOrgUsersByOrgID returns all organization-user relations by organization ID.
|
// GetOrgUsersByOrgID returns all organization-user relations by organization ID.
|
||||||
func GetOrgUsersByOrgID(orgID int64) ([]*OrgUser, error) {
|
func GetOrgUsersByOrgID(orgID int64) ([]*OrgUser, error) {
|
||||||
ous := make([]*OrgUser, 0, 10)
|
ous := make([]*OrgUser, 0, 10)
|
||||||
err := x.Where("org_id=?", orgID).Find(&ous)
|
err := x.
|
||||||
|
Where("org_id=?", orgID).
|
||||||
|
Find(&ous)
|
||||||
return ous, err
|
return ous, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChangeOrgUserStatus changes public or private membership status.
|
// ChangeOrgUserStatus changes public or private membership status.
|
||||||
func ChangeOrgUserStatus(orgID, uid int64, public bool) error {
|
func ChangeOrgUserStatus(orgID, uid int64, public bool) error {
|
||||||
ou := new(OrgUser)
|
ou := new(OrgUser)
|
||||||
has, err := x.Where("uid=?", uid).And("org_id=?", orgID).Get(ou)
|
has, err := x.
|
||||||
|
Where("uid=?", uid).
|
||||||
|
And("org_id=?", orgID).
|
||||||
|
Get(ou)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
} else if !has {
|
} else if !has {
|
||||||
@@ -342,7 +381,7 @@ func AddOrgUser(orgID, uid int64) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ou := &OrgUser{
|
ou := &OrgUser{
|
||||||
Uid: uid,
|
UID: uid,
|
||||||
OrgID: orgID,
|
OrgID: orgID,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -361,7 +400,10 @@ func AddOrgUser(orgID, uid int64) error {
|
|||||||
func RemoveOrgUser(orgID, userID int64) error {
|
func RemoveOrgUser(orgID, userID int64) error {
|
||||||
ou := new(OrgUser)
|
ou := new(OrgUser)
|
||||||
|
|
||||||
has, err := x.Where("uid=?", userID).And("org_id=?", orgID).Get(ou)
|
has, err := x.
|
||||||
|
Where("uid=?", userID).
|
||||||
|
And("org_id=?", orgID).
|
||||||
|
Get(ou)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("get org-user: %v", err)
|
return fmt.Errorf("get org-user: %v", err)
|
||||||
} else if !has {
|
} else if !has {
|
||||||
@@ -416,7 +458,10 @@ func RemoveOrgUser(orgID, userID int64) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(repoIDs) > 0 {
|
if len(repoIDs) > 0 {
|
||||||
if _, err = sess.Where("user_id = ?", user.ID).In("repo_id", repoIDs).Delete(new(Access)); err != nil {
|
if _, err = sess.
|
||||||
|
Where("user_id = ?", user.ID).
|
||||||
|
In("repo_id", repoIDs).
|
||||||
|
Delete(new(Access)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -450,13 +495,17 @@ func RemoveOrgRepo(orgID, repoID int64) error {
|
|||||||
|
|
||||||
func (org *User) getUserTeams(e Engine, userID int64, cols ...string) ([]*Team, error) {
|
func (org *User) getUserTeams(e Engine, userID int64, cols ...string) ([]*Team, error) {
|
||||||
teams := make([]*Team, 0, org.NumTeams)
|
teams := make([]*Team, 0, org.NumTeams)
|
||||||
return teams, e.Where("team_user.org_id = ?", org.ID).
|
return teams, e.
|
||||||
And("team_user.uid = ?", userID).
|
Where("`team_user`.org_id = ?", org.ID).
|
||||||
Join("INNER", "team_user", "team_user.team_id = team.id").
|
Join("INNER", "team_user", "`team_user`.team_id = team.id").
|
||||||
Cols(cols...).Find(&teams)
|
Join("INNER", "user", "`user`.id=team_user.uid").
|
||||||
|
And("`team_user`.uid = ?", userID).
|
||||||
|
Asc("`user`.name").
|
||||||
|
Cols(cols...).
|
||||||
|
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) {
|
func (org *User) GetUserTeamIDs(userID int64) ([]int64, error) {
|
||||||
teams, err := org.getUserTeams(x, userID, "team.id")
|
teams, err := org.getUserTeams(x, userID, "team.id")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -470,7 +519,7 @@ func (org *User) GetUserTeamIDs(userID int64) ([]int64, error) {
|
|||||||
return teamIDs, nil
|
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.
|
// and that the user has joined.
|
||||||
func (org *User) GetUserTeams(userID int64) ([]*Team, error) {
|
func (org *User) GetUserTeams(userID int64) ([]*Team, error) {
|
||||||
return org.getUserTeams(x, userID)
|
return org.getUserTeams(x, userID)
|
||||||
@@ -493,35 +542,32 @@ func (org *User) GetUserRepositories(userID int64, page, pageSize int) ([]*Repos
|
|||||||
page = 1
|
page = 1
|
||||||
}
|
}
|
||||||
repos := make([]*Repository, 0, pageSize)
|
repos := make([]*Repository, 0, pageSize)
|
||||||
// FIXME: use XORM chain operations instead of raw SQL.
|
if err := x.
|
||||||
if err = x.Sql(fmt.Sprintf(`SELECT repository.* FROM repository
|
Select("`repository`.*").
|
||||||
INNER JOIN team_repo
|
Join("INNER", "team_repo", "`team_repo`.repo_id=`repository`.id").
|
||||||
ON team_repo.repo_id = repository.id
|
Where("(`repository`.owner_id=? AND `repository`.is_private=?)", org.ID, false).
|
||||||
WHERE (repository.owner_id = ? AND repository.is_private = ?) OR team_repo.team_id IN (%s)
|
Or(builder.In("team_repo.team_id", teamIDs)).
|
||||||
GROUP BY repository.id
|
GroupBy("`repository`.id").
|
||||||
ORDER BY updated_unix DESC
|
OrderBy("updated_unix DESC").
|
||||||
LIMIT %d OFFSET %d`,
|
Limit(pageSize, (page-1)*pageSize).
|
||||||
strings.Join(base.Int64sToStrings(teamIDs), ","), pageSize, (page-1)*pageSize),
|
Find(&repos); err != nil {
|
||||||
org.ID, false).Find(&repos); err != nil {
|
|
||||||
return nil, 0, fmt.Errorf("get repositories: %v", err)
|
return nil, 0, fmt.Errorf("get repositories: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
results, err := x.Query(fmt.Sprintf(`SELECT repository.id FROM repository
|
repoCount, err := x.
|
||||||
INNER JOIN team_repo
|
Join("INNER", "team_repo", "`team_repo`.repo_id=`repository`.id").
|
||||||
ON team_repo.repo_id = repository.id
|
Where("(`repository`.owner_id=? AND `repository`.is_private=?)", org.ID, false).
|
||||||
WHERE (repository.owner_id = ? AND repository.is_private = ?) OR team_repo.team_id IN (%s)
|
Or(builder.In("team_repo.team_id", teamIDs)).
|
||||||
GROUP BY repository.id
|
GroupBy("`repository`.id").
|
||||||
ORDER BY updated_unix DESC`,
|
Count(&Repository{})
|
||||||
strings.Join(base.Int64sToStrings(teamIDs), ",")),
|
|
||||||
org.ID, false)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(4, "count user repositories in organization: %v", err)
|
return nil, 0, fmt.Errorf("count user repositories in organization: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return repos, int64(len(results)), nil
|
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.
|
// that the user with the given userID has access to.
|
||||||
func (org *User) GetUserMirrorRepositories(userID int64) ([]*Repository, error) {
|
func (org *User) GetUserMirrorRepositories(userID int64) ([]*Repository, error) {
|
||||||
teamIDs, err := org.GetUserTeamIDs(userID)
|
teamIDs, err := org.GetUserTeamIDs(userID)
|
||||||
@@ -533,15 +579,12 @@ func (org *User) GetUserMirrorRepositories(userID int64) ([]*Repository, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
repos := make([]*Repository, 0, 10)
|
repos := make([]*Repository, 0, 10)
|
||||||
if err = x.Sql(fmt.Sprintf(`SELECT repository.* FROM repository
|
return repos, x.
|
||||||
INNER JOIN team_repo
|
Select("`repository`.*").
|
||||||
ON team_repo.repo_id = repository.id AND repository.is_mirror = ?
|
Join("INNER", "team_repo", "`team_repo`.repo_id=`repository`.id AND `repository`.is_mirror=?", true).
|
||||||
WHERE (repository.owner_id = ? AND repository.is_private = ?) OR team_repo.team_id IN (%s)
|
Where("(`repository`.owner_id=? AND `repository`.is_private=?)", org.ID, false).
|
||||||
GROUP BY repository.id
|
Or(builder.In("team_repo.team_id", teamIDs)).
|
||||||
ORDER BY updated_unix DESC`,
|
GroupBy("`repository`.id").
|
||||||
strings.Join(base.Int64sToStrings(teamIDs), ",")),
|
OrderBy("updated_unix DESC").
|
||||||
true, org.ID, false).Find(&repos); err != nil {
|
Find(&repos)
|
||||||
return nil, fmt.Errorf("get repositories: %v", err)
|
|
||||||
}
|
|
||||||
return repos, nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
const OWNER_TEAM = "Owners"
|
const ownerTeamName = "Owners"
|
||||||
|
|
||||||
// Team represents a organization team.
|
// Team represents a organization team.
|
||||||
type Team struct {
|
type Team struct {
|
||||||
@@ -28,17 +28,19 @@ type Team struct {
|
|||||||
|
|
||||||
// IsOwnerTeam returns true if team is owner team.
|
// IsOwnerTeam returns true if team is owner team.
|
||||||
func (t *Team) IsOwnerTeam() bool {
|
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.
|
// IsMember returns true if given user is a member of team.
|
||||||
func (t *Team) IsMember(uid int64) bool {
|
func (t *Team) IsMember(userID int64) bool {
|
||||||
return IsTeamMember(t.OrgID, t.ID, uid)
|
return IsTeamMember(t.OrgID, t.ID, userID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Team) getRepositories(e Engine) (err error) {
|
func (t *Team) getRepositories(e Engine) (err error) {
|
||||||
teamRepos := make([]*TeamRepo, 0, t.NumRepos)
|
teamRepos := make([]*TeamRepo, 0, t.NumRepos)
|
||||||
if err = x.Where("team_id=?", t.ID).Find(&teamRepos); err != nil {
|
if err = x.
|
||||||
|
Where("team_id=?", t.ID).
|
||||||
|
Find(&teamRepos); err != nil {
|
||||||
return fmt.Errorf("get team-repos: %v", err)
|
return fmt.Errorf("get team-repos: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,13 +72,13 @@ func (t *Team) GetMembers() (err error) {
|
|||||||
|
|
||||||
// AddMember adds new membership of the team to the organization,
|
// AddMember adds new membership of the team to the organization,
|
||||||
// the user will have membership to the organization automatically when needed.
|
// the user will have membership to the organization automatically when needed.
|
||||||
func (t *Team) AddMember(uid int64) error {
|
func (t *Team) AddMember(userID int64) error {
|
||||||
return AddTeamMember(t.OrgID, t.ID, uid)
|
return AddTeamMember(t.OrgID, t.ID, userID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveMember removes member from team of organization.
|
// RemoveMember removes member from team of organization.
|
||||||
func (t *Team) RemoveMember(uid int64) error {
|
func (t *Team) RemoveMember(userID int64) error {
|
||||||
return RemoveTeamMember(t.OrgID, t.ID, uid)
|
return RemoveTeamMember(t.OrgID, t.ID, userID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Team) hasRepository(e Engine, repoID int64) bool {
|
func (t *Team) hasRepository(e Engine, repoID int64) bool {
|
||||||
@@ -155,7 +157,7 @@ func (t *Team) removeRepository(e Engine, repo *Repository, recalculate bool) (e
|
|||||||
return fmt.Errorf("get team members: %v", err)
|
return fmt.Errorf("get team members: %v", err)
|
||||||
}
|
}
|
||||||
for _, u := range t.Members {
|
for _, u := range t.Members {
|
||||||
has, err := hasAccess(e, u, repo, ACCESS_MODE_READ)
|
has, err := hasAccess(e, u, repo, AccessModeRead)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
} else if has {
|
} else if has {
|
||||||
@@ -194,13 +196,30 @@ func (t *Team) RemoveRepository(repoID int64) error {
|
|||||||
return sess.Commit()
|
return sess.Commit()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsUsableTeamName tests if a name could be as team name
|
||||||
|
func IsUsableTeamName(name string) (err error) {
|
||||||
|
var reservedTeamNames = []string{"new"}
|
||||||
|
|
||||||
|
for i := range reservedTeamNames {
|
||||||
|
if name == reservedTeamNames[i] {
|
||||||
|
return ErrNameReserved{name}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// NewTeam creates a record of new team.
|
// NewTeam creates a record of new team.
|
||||||
// It's caller's responsibility to assign organization ID.
|
// It's caller's responsibility to assign organization ID.
|
||||||
func NewTeam(t *Team) error {
|
func NewTeam(t *Team) (err error) {
|
||||||
if len(t.Name) == 0 {
|
if len(t.Name) == 0 {
|
||||||
return errors.New("empty team name")
|
return errors.New("empty team name")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err = IsUsableTeamName(t.Name); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
has, err := x.Id(t.OrgID).Get(new(User))
|
has, err := x.Id(t.OrgID).Get(new(User))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -209,7 +228,10 @@ func NewTeam(t *Team) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
t.LowerName = strings.ToLower(t.Name)
|
t.LowerName = strings.ToLower(t.Name)
|
||||||
has, err = x.Where("org_id=?", t.OrgID).And("lower_name=?", t.LowerName).Get(new(Team))
|
has, err = x.
|
||||||
|
Where("org_id=?", t.OrgID).
|
||||||
|
And("lower_name=?", t.LowerName).
|
||||||
|
Get(new(Team))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
} else if has {
|
} else if has {
|
||||||
@@ -235,9 +257,9 @@ func NewTeam(t *Team) error {
|
|||||||
return sess.Commit()
|
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{
|
t := &Team{
|
||||||
OrgID: orgId,
|
OrgID: orgID,
|
||||||
LowerName: strings.ToLower(name),
|
LowerName: strings.ToLower(name),
|
||||||
}
|
}
|
||||||
has, err := e.Get(t)
|
has, err := e.Get(t)
|
||||||
@@ -250,13 +272,13 @@ func getTeam(e Engine, orgId int64, name string) (*Team, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetTeam returns team by given team name and organization.
|
// GetTeam returns team by given team name and organization.
|
||||||
func GetTeam(orgId int64, name string) (*Team, error) {
|
func GetTeam(orgID int64, name string) (*Team, error) {
|
||||||
return getTeam(x, orgId, name)
|
return getTeam(x, orgID, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getTeamByID(e Engine, teamId int64) (*Team, error) {
|
func getTeamByID(e Engine, teamID int64) (*Team, error) {
|
||||||
t := new(Team)
|
t := new(Team)
|
||||||
has, err := e.Id(teamId).Get(t)
|
has, err := e.Id(teamID).Get(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if !has {
|
} else if !has {
|
||||||
@@ -266,8 +288,8 @@ func getTeamByID(e Engine, teamId int64) (*Team, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetTeamByID returns team by given ID.
|
// GetTeamByID returns team by given ID.
|
||||||
func GetTeamByID(teamId int64) (*Team, error) {
|
func GetTeamByID(teamID int64) (*Team, error) {
|
||||||
return getTeamByID(x, teamId)
|
return getTeamByID(x, teamID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateTeam updates information of team.
|
// UpdateTeam updates information of team.
|
||||||
@@ -287,7 +309,11 @@ func UpdateTeam(t *Team, authChanged bool) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
t.LowerName = strings.ToLower(t.Name)
|
t.LowerName = strings.ToLower(t.Name)
|
||||||
has, err := x.Where("org_id=?", t.OrgID).And("lower_name=?", t.LowerName).And("id!=?", t.ID).Get(new(Team))
|
has, err := x.
|
||||||
|
Where("org_id=?", t.OrgID).
|
||||||
|
And("lower_name=?", t.LowerName).
|
||||||
|
And("id!=?", t.ID).
|
||||||
|
Get(new(Team))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
} else if has {
|
} else if has {
|
||||||
@@ -341,7 +367,10 @@ func DeleteTeam(t *Team) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Delete team-user.
|
// Delete team-user.
|
||||||
if _, err = sess.Where("org_id=?", org.ID).Where("team_id=?", t.ID).Delete(new(TeamUser)); err != nil {
|
if _, err = sess.
|
||||||
|
Where("org_id=?", org.ID).
|
||||||
|
Where("team_id=?", t.ID).
|
||||||
|
Delete(new(TeamUser)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -369,29 +398,35 @@ type TeamUser struct {
|
|||||||
ID int64 `xorm:"pk autoincr"`
|
ID int64 `xorm:"pk autoincr"`
|
||||||
OrgID int64 `xorm:"INDEX"`
|
OrgID int64 `xorm:"INDEX"`
|
||||||
TeamID int64 `xorm:"UNIQUE(s)"`
|
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).Get(new(TeamUser))
|
has, _ := e.
|
||||||
|
Where("org_id=?", orgID).
|
||||||
|
And("team_id=?", teamID).
|
||||||
|
And("uid=?", userID).
|
||||||
|
Get(new(TeamUser))
|
||||||
return has
|
return has
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsTeamMember returns true if given user is a member of team.
|
// IsTeamMember returns true if given user is a member of team.
|
||||||
func IsTeamMember(orgID, teamID, uid int64) bool {
|
func IsTeamMember(orgID, teamID, userID int64) bool {
|
||||||
return isTeamMember(x, orgID, teamID, uid)
|
return isTeamMember(x, orgID, teamID, userID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getTeamMembers(e Engine, teamID int64) (_ []*User, err error) {
|
func getTeamMembers(e Engine, teamID int64) (_ []*User, err error) {
|
||||||
teamUsers := make([]*TeamUser, 0, 10)
|
teamUsers := make([]*TeamUser, 0, 10)
|
||||||
if err = e.Where("team_id=?", teamID).Find(&teamUsers); err != nil {
|
if err = e.
|
||||||
|
Where("team_id=?", teamID).
|
||||||
|
Find(&teamUsers); err != nil {
|
||||||
return nil, fmt.Errorf("get team-users: %v", err)
|
return nil, fmt.Errorf("get team-users: %v", err)
|
||||||
}
|
}
|
||||||
members := make([]*User, 0, len(teamUsers))
|
members := make([]*User, 0, len(teamUsers))
|
||||||
for i := range teamUsers {
|
for i := range teamUsers {
|
||||||
member := new(User)
|
member := new(User)
|
||||||
if _, err = e.Id(teamUsers[i].Uid).Get(member); err != nil {
|
if _, err = e.Id(teamUsers[i].UID).Get(member); err != nil {
|
||||||
return nil, fmt.Errorf("get user '%d': %v", teamUsers[i].Uid, err)
|
return nil, fmt.Errorf("get user '%d': %v", teamUsers[i].UID, err)
|
||||||
}
|
}
|
||||||
members = append(members, member)
|
members = append(members, member)
|
||||||
}
|
}
|
||||||
@@ -403,9 +438,12 @@ func GetTeamMembers(teamID int64) ([]*User, error) {
|
|||||||
return getTeamMembers(x, teamID)
|
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)
|
tus := make([]*TeamUser, 0, 5)
|
||||||
if err := e.Where("uid=?", uid).And("org_id=?", orgId).Find(&tus); err != nil {
|
if err := e.
|
||||||
|
Where("uid=?", userID).
|
||||||
|
And("org_id=?", orgID).
|
||||||
|
Find(&tus); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -424,18 +462,18 @@ func getUserTeams(e Engine, orgId, uid int64) ([]*Team, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetUserTeams returns all teams that user belongs to in given organization.
|
// GetUserTeams returns all teams that user belongs to in given organization.
|
||||||
func GetUserTeams(orgId, uid int64) ([]*Team, error) {
|
func GetUserTeams(orgID, userID int64) ([]*Team, error) {
|
||||||
return getUserTeams(x, orgId, uid)
|
return getUserTeams(x, orgID, userID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddTeamMember adds new membership of given team to given organization,
|
// AddTeamMember adds new membership of given team to given organization,
|
||||||
// the user will have membership to given organization automatically when needed.
|
// the user will have membership to given organization automatically when needed.
|
||||||
func AddTeamMember(orgID, teamID, uid int64) error {
|
func AddTeamMember(orgID, teamID, userID int64) error {
|
||||||
if IsTeamMember(orgID, teamID, uid) {
|
if IsTeamMember(orgID, teamID, userID) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := AddOrgUser(orgID, uid); err != nil {
|
if err := AddOrgUser(orgID, userID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -457,7 +495,7 @@ func AddTeamMember(orgID, teamID, uid int64) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tu := &TeamUser{
|
tu := &TeamUser{
|
||||||
Uid: uid,
|
UID: userID,
|
||||||
OrgID: orgID,
|
OrgID: orgID,
|
||||||
TeamID: teamID,
|
TeamID: teamID,
|
||||||
}
|
}
|
||||||
@@ -476,7 +514,10 @@ func AddTeamMember(orgID, teamID, uid int64) error {
|
|||||||
|
|
||||||
// We make sure it exists before.
|
// We make sure it exists before.
|
||||||
ou := new(OrgUser)
|
ou := new(OrgUser)
|
||||||
if _, err = sess.Where("uid = ?", uid).And("org_id = ?", orgID).Get(ou); err != nil {
|
if _, err = sess.
|
||||||
|
Where("uid = ?", userID).
|
||||||
|
And("org_id = ?", orgID).
|
||||||
|
Get(ou); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
ou.NumTeams++
|
ou.NumTeams++
|
||||||
@@ -490,8 +531,8 @@ func AddTeamMember(orgID, teamID, uid int64) error {
|
|||||||
return sess.Commit()
|
return sess.Commit()
|
||||||
}
|
}
|
||||||
|
|
||||||
func removeTeamMember(e Engine, orgID, teamID, uid int64) error {
|
func removeTeamMember(e Engine, orgID, teamID, userID int64) error {
|
||||||
if !isTeamMember(e, orgID, teamID, uid) {
|
if !isTeamMember(e, orgID, teamID, userID) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -503,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.
|
// Check if the user to delete is the last member in owner team.
|
||||||
if t.IsOwnerTeam() && t.NumMembers == 1 {
|
if t.IsOwnerTeam() && t.NumMembers == 1 {
|
||||||
return ErrLastOrgOwner{UID: uid}
|
return ErrLastOrgOwner{UID: userID}
|
||||||
}
|
}
|
||||||
|
|
||||||
t.NumMembers--
|
t.NumMembers--
|
||||||
@@ -519,13 +560,16 @@ func removeTeamMember(e Engine, orgID, teamID, uid int64) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tu := &TeamUser{
|
tu := &TeamUser{
|
||||||
Uid: uid,
|
UID: userID,
|
||||||
OrgID: orgID,
|
OrgID: orgID,
|
||||||
TeamID: teamID,
|
TeamID: teamID,
|
||||||
}
|
}
|
||||||
if _, err := e.Delete(tu); err != nil {
|
if _, err := e.Delete(tu); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if _, err = e.Id(t.ID).AllCols().Update(t); err != nil {
|
} else if _, err = e.
|
||||||
|
Id(t.ID).
|
||||||
|
AllCols().
|
||||||
|
Update(t); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -538,7 +582,10 @@ func removeTeamMember(e Engine, orgID, teamID, uid int64) error {
|
|||||||
|
|
||||||
// This must exist.
|
// This must exist.
|
||||||
ou := new(OrgUser)
|
ou := new(OrgUser)
|
||||||
_, err = e.Where("uid = ?", uid).And("org_id = ?", org.ID).Get(ou)
|
_, err = e.
|
||||||
|
Where("uid = ?", userID).
|
||||||
|
And("org_id = ?", org.ID).
|
||||||
|
Get(ou)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -546,20 +593,23 @@ func removeTeamMember(e Engine, orgID, teamID, uid int64) error {
|
|||||||
if t.IsOwnerTeam() {
|
if t.IsOwnerTeam() {
|
||||||
ou.IsOwner = false
|
ou.IsOwner = false
|
||||||
}
|
}
|
||||||
if _, err = e.Id(ou.ID).AllCols().Update(ou); err != nil {
|
if _, err = e.
|
||||||
|
Id(ou.ID).
|
||||||
|
AllCols().
|
||||||
|
Update(ou); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveTeamMember removes member from given team of given organization.
|
// 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()
|
sess := x.NewSession()
|
||||||
defer sessionRelease(sess)
|
defer sessionRelease(sess)
|
||||||
if err := sess.Begin(); err != nil {
|
if err := sess.Begin(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := removeTeamMember(sess, orgID, teamID, uid); err != nil {
|
if err := removeTeamMember(sess, orgID, teamID, userID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return sess.Commit()
|
return sess.Commit()
|
||||||
@@ -581,7 +631,11 @@ type TeamRepo struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func hasTeamRepo(e Engine, orgID, teamID, repoID int64) bool {
|
func hasTeamRepo(e Engine, orgID, teamID, repoID int64) bool {
|
||||||
has, _ := e.Where("org_id=?", orgID).And("team_id=?", teamID).And("repo_id=?", repoID).Get(new(TeamRepo))
|
has, _ := e.
|
||||||
|
Where("org_id=?", orgID).
|
||||||
|
And("team_id=?", teamID).
|
||||||
|
And("repo_id=?", repoID).
|
||||||
|
Get(new(TeamRepo))
|
||||||
return has
|
return has
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
280
models/pull.go
280
models/pull.go
@@ -11,33 +11,35 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"code.gitea.io/git"
|
||||||
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
"code.gitea.io/gitea/modules/process"
|
||||||
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
"code.gitea.io/gitea/modules/sync"
|
||||||
|
api "code.gitea.io/sdk/gitea"
|
||||||
"github.com/Unknwon/com"
|
"github.com/Unknwon/com"
|
||||||
"github.com/go-xorm/xorm"
|
"github.com/go-xorm/xorm"
|
||||||
|
|
||||||
"github.com/gogits/git-module"
|
|
||||||
api "github.com/gogits/go-gogs-client"
|
|
||||||
|
|
||||||
"github.com/gogits/gogs/modules/log"
|
|
||||||
"github.com/gogits/gogs/modules/process"
|
|
||||||
"github.com/gogits/gogs/modules/setting"
|
|
||||||
"github.com/gogits/gogs/modules/sync"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var PullRequestQueue = sync.NewUniqueQueue(setting.Repository.PullRequestQueueLength)
|
var pullRequestQueue = sync.NewUniqueQueue(setting.Repository.PullRequestQueueLength)
|
||||||
|
|
||||||
|
// PullRequestType defines pull request type
|
||||||
type PullRequestType int
|
type PullRequestType int
|
||||||
|
|
||||||
|
// Enumerate all the pull request types
|
||||||
const (
|
const (
|
||||||
PULL_REQUEST_GOGS PullRequestType = iota
|
PullRequestGitea PullRequestType = iota
|
||||||
PLLL_ERQUEST_GIT
|
PullRequestGit
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// PullRequestStatus defines pull request status
|
||||||
type PullRequestStatus int
|
type PullRequestStatus int
|
||||||
|
|
||||||
|
// Enumerate all the pull request status
|
||||||
const (
|
const (
|
||||||
PULL_REQUEST_STATUS_CONFLICT PullRequestStatus = iota
|
PullRequestStatusConflict PullRequestStatus = iota
|
||||||
PULL_REQUEST_STATUS_CHECKING
|
PullRequestStatusChecking
|
||||||
PULL_REQUEST_STATUS_MERGEABLE
|
PullRequestStatusMergeable
|
||||||
)
|
)
|
||||||
|
|
||||||
// PullRequest represents relation between pull request and repositories.
|
// PullRequest represents relation between pull request and repositories.
|
||||||
@@ -67,10 +69,12 @@ type PullRequest struct {
|
|||||||
MergedUnix int64
|
MergedUnix int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BeforeUpdate is invoked from XORM before updating an object of this type.
|
||||||
func (pr *PullRequest) BeforeUpdate() {
|
func (pr *PullRequest) BeforeUpdate() {
|
||||||
pr.MergedUnix = pr.Merged.Unix()
|
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.
|
// Note: don't try to get Issue because will end up recursive querying.
|
||||||
func (pr *PullRequest) AfterSet(colName string, _ xorm.Cell) {
|
func (pr *PullRequest) AfterSet(colName string, _ xorm.Cell) {
|
||||||
switch colName {
|
switch colName {
|
||||||
@@ -98,10 +102,12 @@ func (pr *PullRequest) loadAttributes(e Engine) (err error) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LoadAttributes loads pull request attributes from database
|
||||||
func (pr *PullRequest) LoadAttributes() error {
|
func (pr *PullRequest) LoadAttributes() error {
|
||||||
return pr.loadAttributes(x)
|
return pr.loadAttributes(x)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LoadIssue loads issue information from database
|
||||||
func (pr *PullRequest) LoadIssue() (err error) {
|
func (pr *PullRequest) LoadIssue() (err error) {
|
||||||
if pr.Issue != nil {
|
if pr.Issue != nil {
|
||||||
return nil
|
return nil
|
||||||
@@ -111,11 +117,58 @@ func (pr *PullRequest) LoadIssue() (err error) {
|
|||||||
return err
|
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
|
// Required - Issue
|
||||||
// Optional - Merger
|
// Optional - Merger
|
||||||
func (pr *PullRequest) APIFormat() *api.PullRequest {
|
func (pr *PullRequest) APIFormat() *api.PullRequest {
|
||||||
|
var (
|
||||||
|
baseBranch *Branch
|
||||||
|
headBranch *Branch
|
||||||
|
baseCommit *git.Commit
|
||||||
|
headCommit *git.Commit
|
||||||
|
err error
|
||||||
|
)
|
||||||
apiIssue := pr.Issue.APIFormat()
|
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(AccessModeNone),
|
||||||
|
}
|
||||||
|
apiHeadBranchInfo := &api.PRBranchInfo{
|
||||||
|
Name: pr.HeadBranch,
|
||||||
|
Ref: pr.HeadBranch,
|
||||||
|
Sha: headCommit.ID.String(),
|
||||||
|
RepoID: pr.HeadRepoID,
|
||||||
|
Repository: pr.HeadRepo.APIFormat(AccessModeNone),
|
||||||
|
}
|
||||||
apiPullRequest := &api.PullRequest{
|
apiPullRequest := &api.PullRequest{
|
||||||
ID: pr.ID,
|
ID: pr.ID,
|
||||||
Index: pr.Index,
|
Index: pr.Index,
|
||||||
@@ -128,12 +181,17 @@ func (pr *PullRequest) APIFormat() *api.PullRequest {
|
|||||||
State: apiIssue.State,
|
State: apiIssue.State,
|
||||||
Comments: apiIssue.Comments,
|
Comments: apiIssue.Comments,
|
||||||
HTMLURL: pr.Issue.HTMLURL(),
|
HTMLURL: pr.Issue.HTMLURL(),
|
||||||
|
DiffURL: pr.Issue.DiffURL(),
|
||||||
|
PatchURL: pr.Issue.PatchURL(),
|
||||||
HasMerged: pr.HasMerged,
|
HasMerged: pr.HasMerged,
|
||||||
|
Base: apiBaseBranchInfo,
|
||||||
|
Head: apiHeadBranchInfo,
|
||||||
|
MergeBase: pr.MergeBase,
|
||||||
}
|
}
|
||||||
|
|
||||||
if pr.Status != PULL_REQUEST_STATUS_CHECKING {
|
if pr.Status != PullRequestStatusChecking {
|
||||||
mergeable := pr.Status != PULL_REQUEST_STATUS_CONFLICT
|
mergeable := pr.Status != PullRequestStatusConflict
|
||||||
apiPullRequest.Mergeable = &mergeable
|
apiPullRequest.Mergeable = mergeable
|
||||||
}
|
}
|
||||||
if pr.HasMerged {
|
if pr.HasMerged {
|
||||||
apiPullRequest.Merged = &pr.Merged
|
apiPullRequest.Merged = &pr.Merged
|
||||||
@@ -152,10 +210,12 @@ func (pr *PullRequest) getHeadRepo(e Engine) (err error) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetHeadRepo loads the head repository
|
||||||
func (pr *PullRequest) GetHeadRepo() error {
|
func (pr *PullRequest) GetHeadRepo() error {
|
||||||
return pr.getHeadRepo(x)
|
return pr.getHeadRepo(x)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetBaseRepo loads the target repository
|
||||||
func (pr *PullRequest) GetBaseRepo() (err error) {
|
func (pr *PullRequest) GetBaseRepo() (err error) {
|
||||||
if pr.BaseRepo != nil {
|
if pr.BaseRepo != nil {
|
||||||
return nil
|
return nil
|
||||||
@@ -170,12 +230,12 @@ func (pr *PullRequest) GetBaseRepo() (err error) {
|
|||||||
|
|
||||||
// IsChecking returns true if this pull request is still checking conflict.
|
// IsChecking returns true if this pull request is still checking conflict.
|
||||||
func (pr *PullRequest) IsChecking() bool {
|
func (pr *PullRequest) IsChecking() bool {
|
||||||
return pr.Status == PULL_REQUEST_STATUS_CHECKING
|
return pr.Status == PullRequestStatusChecking
|
||||||
}
|
}
|
||||||
|
|
||||||
// CanAutoMerge returns true if this pull request can be merged automatically.
|
// CanAutoMerge returns true if this pull request can be merged automatically.
|
||||||
func (pr *PullRequest) CanAutoMerge() bool {
|
func (pr *PullRequest) CanAutoMerge() bool {
|
||||||
return pr.Status == PULL_REQUEST_STATUS_MERGEABLE
|
return pr.Status == PullRequestStatusMergeable
|
||||||
}
|
}
|
||||||
|
|
||||||
// Merge merges pull request to base repository.
|
// Merge merges pull request to base repository.
|
||||||
@@ -210,7 +270,11 @@ func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository) (err error
|
|||||||
|
|
||||||
// Clone base repo.
|
// Clone base repo.
|
||||||
tmpBasePath := path.Join(setting.AppDataPath, "tmp/repos", com.ToStr(time.Now().Nanosecond())+".git")
|
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))
|
defer os.RemoveAll(path.Dir(tmpBasePath))
|
||||||
|
|
||||||
var stderr string
|
var stderr string
|
||||||
@@ -287,11 +351,11 @@ func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository) (err error
|
|||||||
log.Error(4, "LoadAttributes: %v", err)
|
log.Error(4, "LoadAttributes: %v", err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if err = PrepareWebhooks(pr.Issue.Repo, HOOK_EVENT_PULL_REQUEST, &api.PullRequestPayload{
|
if err = PrepareWebhooks(pr.Issue.Repo, HookEventPullRequest, &api.PullRequestPayload{
|
||||||
Action: api.HOOK_ISSUE_CLOSED,
|
Action: api.HookIssueClosed,
|
||||||
Index: pr.Index,
|
Index: pr.Index,
|
||||||
PullRequest: pr.APIFormat(),
|
PullRequest: pr.APIFormat(),
|
||||||
Repository: pr.Issue.Repo.APIFormat(nil),
|
Repository: pr.Issue.Repo.APIFormat(AccessModeNone),
|
||||||
Sender: doer.APIFormat(),
|
Sender: doer.APIFormat(),
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
log.Error(4, "PrepareWebhooks: %v", err)
|
log.Error(4, "PrepareWebhooks: %v", err)
|
||||||
@@ -316,22 +380,22 @@ func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository) (err error
|
|||||||
l.PushFront(mergeCommit)
|
l.PushFront(mergeCommit)
|
||||||
|
|
||||||
p := &api.PushPayload{
|
p := &api.PushPayload{
|
||||||
Ref: git.BRANCH_PREFIX + pr.BaseBranch,
|
Ref: git.BranchPrefix + pr.BaseBranch,
|
||||||
Before: pr.MergeBase,
|
Before: pr.MergeBase,
|
||||||
After: pr.MergedCommitID,
|
After: pr.MergedCommitID,
|
||||||
CompareURL: setting.AppUrl + pr.BaseRepo.ComposeCompareURL(pr.MergeBase, pr.MergedCommitID),
|
CompareURL: setting.AppURL + pr.BaseRepo.ComposeCompareURL(pr.MergeBase, pr.MergedCommitID),
|
||||||
Commits: ListToPushCommits(l).ToApiPayloadCommits(pr.BaseRepo.HTMLURL()),
|
Commits: ListToPushCommits(l).ToAPIPayloadCommits(pr.BaseRepo.HTMLURL()),
|
||||||
Repo: pr.BaseRepo.APIFormat(nil),
|
Repo: pr.BaseRepo.APIFormat(AccessModeNone),
|
||||||
Pusher: pr.HeadRepo.MustOwner().APIFormat(),
|
Pusher: pr.HeadRepo.MustOwner().APIFormat(),
|
||||||
Sender: doer.APIFormat(),
|
Sender: doer.APIFormat(),
|
||||||
}
|
}
|
||||||
if err = PrepareWebhooks(pr.BaseRepo, HOOK_EVENT_PUSH, p); err != nil {
|
if err = PrepareWebhooks(pr.BaseRepo, HookEventPush, p); err != nil {
|
||||||
return fmt.Errorf("PrepareWebhooks: %v", err)
|
return fmt.Errorf("PrepareWebhooks: %v", err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// patchConflicts is a list of conflit description from Git.
|
// patchConflicts is a list of conflict description from Git.
|
||||||
var patchConflicts = []string{
|
var patchConflicts = []string{
|
||||||
"patch does not apply",
|
"patch does not apply",
|
||||||
"already exists in working directory",
|
"already exists in working directory",
|
||||||
@@ -339,8 +403,7 @@ var patchConflicts = []string{
|
|||||||
"error:",
|
"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) {
|
func (pr *PullRequest) testPatch() (err error) {
|
||||||
if pr.BaseRepo == nil {
|
if pr.BaseRepo == nil {
|
||||||
pr.BaseRepo, err = GetRepositoryByID(pr.BaseRepoID)
|
pr.BaseRepo, err = GetRepositoryByID(pr.BaseRepoID)
|
||||||
@@ -365,20 +428,23 @@ func (pr *PullRequest) testPatch() (err error) {
|
|||||||
|
|
||||||
log.Trace("PullRequest[%d].testPatch (patchPath): %s", pr.ID, patchPath)
|
log.Trace("PullRequest[%d].testPatch (patchPath): %s", pr.ID, patchPath)
|
||||||
|
|
||||||
|
// Delete old temp local copy before we create a new temp local copy
|
||||||
|
RemoveAllWithNotice("Deleting old local copy", pr.BaseRepo.LocalCopyPath())
|
||||||
|
|
||||||
if err := pr.BaseRepo.UpdateLocalCopyBranch(pr.BaseBranch); err != nil {
|
if err := pr.BaseRepo.UpdateLocalCopyBranch(pr.BaseBranch); err != nil {
|
||||||
return fmt.Errorf("UpdateLocalCopy: %v", err)
|
return fmt.Errorf("UpdateLocalCopy: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
pr.Status = PULL_REQUEST_STATUS_CHECKING
|
pr.Status = PullRequestStatusChecking
|
||||||
_, stderr, err := process.ExecDir(-1, pr.BaseRepo.LocalCopyPath(),
|
_, stderr, err := process.ExecDir(-1, pr.BaseRepo.LocalCopyPath(),
|
||||||
fmt.Sprintf("testPatch (git apply --check): %d", pr.BaseRepo.ID),
|
fmt.Sprintf("testPatch (git apply --check): %d", pr.BaseRepo.ID),
|
||||||
"git", "apply", "--check", patchPath)
|
"git", "apply", "--check", patchPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
for i := range patchConflicts {
|
for i := range patchConflicts {
|
||||||
if strings.Contains(stderr, patchConflicts[i]) {
|
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)
|
fmt.Println(stderr)
|
||||||
pr.Status = PULL_REQUEST_STATUS_CONFLICT
|
pr.Status = PullRequestStatusConflict
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -416,8 +482,8 @@ func NewPullRequest(repo *Repository, pull *Issue, labelIDs []int64, uuids []str
|
|||||||
return fmt.Errorf("testPatch: %v", err)
|
return fmt.Errorf("testPatch: %v", err)
|
||||||
}
|
}
|
||||||
// No conflict appears after test means mergeable.
|
// No conflict appears after test means mergeable.
|
||||||
if pr.Status == PULL_REQUEST_STATUS_CHECKING {
|
if pr.Status == PullRequestStatusChecking {
|
||||||
pr.Status = PULL_REQUEST_STATUS_MERGEABLE
|
pr.Status = PullRequestStatusMergeable
|
||||||
}
|
}
|
||||||
|
|
||||||
pr.IssueID = pull.ID
|
pr.IssueID = pull.ID
|
||||||
@@ -432,7 +498,7 @@ func NewPullRequest(repo *Repository, pull *Issue, labelIDs []int64, uuids []str
|
|||||||
if err = NotifyWatchers(&Action{
|
if err = NotifyWatchers(&Action{
|
||||||
ActUserID: pull.Poster.ID,
|
ActUserID: pull.Poster.ID,
|
||||||
ActUserName: pull.Poster.Name,
|
ActUserName: pull.Poster.Name,
|
||||||
OpType: ACTION_CREATE_PULL_REQUEST,
|
OpType: ActionCreatePullRequest,
|
||||||
Content: fmt.Sprintf("%d|%s", pull.Index, pull.Title),
|
Content: fmt.Sprintf("%d|%s", pull.Index, pull.Title),
|
||||||
RepoID: repo.ID,
|
RepoID: repo.ID,
|
||||||
RepoUserName: repo.Owner.Name,
|
RepoUserName: repo.Owner.Name,
|
||||||
@@ -446,11 +512,11 @@ func NewPullRequest(repo *Repository, pull *Issue, labelIDs []int64, uuids []str
|
|||||||
|
|
||||||
pr.Issue = pull
|
pr.Issue = pull
|
||||||
pull.PullRequest = pr
|
pull.PullRequest = pr
|
||||||
if err = PrepareWebhooks(repo, HOOK_EVENT_PULL_REQUEST, &api.PullRequestPayload{
|
if err = PrepareWebhooks(repo, HookEventPullRequest, &api.PullRequestPayload{
|
||||||
Action: api.HOOK_ISSUE_OPENED,
|
Action: api.HookIssueOpened,
|
||||||
Index: pull.Index,
|
Index: pull.Index,
|
||||||
PullRequest: pr.APIFormat(),
|
PullRequest: pr.APIFormat(),
|
||||||
Repository: repo.APIFormat(nil),
|
Repository: repo.APIFormat(AccessModeNone),
|
||||||
Sender: pull.Poster.APIFormat(),
|
Sender: pull.Poster.APIFormat(),
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
log.Error(4, "PrepareWebhooks: %v", err)
|
log.Error(4, "PrepareWebhooks: %v", err)
|
||||||
@@ -460,13 +526,55 @@ func NewPullRequest(repo *Repository, pull *Issue, labelIDs []int64, uuids []str
|
|||||||
return nil
|
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
|
// GetUnmergedPullRequest returnss a pull request that is open and has not been merged
|
||||||
// by given head/base and repo/branch.
|
// by given head/base and repo/branch.
|
||||||
func GetUnmergedPullRequest(headRepoID, baseRepoID int64, headBranch, baseBranch string) (*PullRequest, error) {
|
func GetUnmergedPullRequest(headRepoID, baseRepoID int64, headBranch, baseBranch string) (*PullRequest, error) {
|
||||||
pr := new(PullRequest)
|
pr := new(PullRequest)
|
||||||
has, err := x.Where("head_repo_id=? AND head_branch=? AND base_repo_id=? AND base_branch=? AND has_merged=? AND issue.is_closed=?",
|
has, err := x.
|
||||||
headRepoID, headBranch, baseRepoID, baseBranch, false, false).
|
Where("head_repo_id=? AND head_branch=? AND base_repo_id=? AND base_branch=? AND has_merged=? AND issue.is_closed=?",
|
||||||
Join("INNER", "issue", "issue.id=pull_request.issue_id").Get(pr)
|
headRepoID, headBranch, baseRepoID, baseBranch, false, false).
|
||||||
|
Join("INNER", "issue", "issue.id=pull_request.issue_id").
|
||||||
|
Get(pr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if !has {
|
} else if !has {
|
||||||
@@ -480,18 +588,46 @@ func GetUnmergedPullRequest(headRepoID, baseRepoID int64, headBranch, baseBranch
|
|||||||
// by given head information (repo and branch).
|
// by given head information (repo and branch).
|
||||||
func GetUnmergedPullRequestsByHeadInfo(repoID int64, branch string) ([]*PullRequest, error) {
|
func GetUnmergedPullRequestsByHeadInfo(repoID int64, branch string) ([]*PullRequest, error) {
|
||||||
prs := make([]*PullRequest, 0, 2)
|
prs := make([]*PullRequest, 0, 2)
|
||||||
return prs, x.Where("head_repo_id = ? AND head_branch = ? AND has_merged = ? AND issue.is_closed = ?",
|
return prs, x.
|
||||||
repoID, branch, false, false).
|
Where("head_repo_id = ? AND head_branch = ? AND has_merged = ? AND issue.is_closed = ?",
|
||||||
Join("INNER", "issue", "issue.id = pull_request.issue_id").Find(&prs)
|
repoID, branch, false, false).
|
||||||
|
Join("INNER", "issue", "issue.id = pull_request.issue_id").
|
||||||
|
Find(&prs)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetUnmergedPullRequestsByBaseInfo returnss all pull requests that are open and has not been merged
|
// GetUnmergedPullRequestsByBaseInfo returnss all pull requests that are open and has not been merged
|
||||||
// by given base information (repo and branch).
|
// by given base information (repo and branch).
|
||||||
func GetUnmergedPullRequestsByBaseInfo(repoID int64, branch string) ([]*PullRequest, error) {
|
func GetUnmergedPullRequestsByBaseInfo(repoID int64, branch string) ([]*PullRequest, error) {
|
||||||
prs := make([]*PullRequest, 0, 2)
|
prs := make([]*PullRequest, 0, 2)
|
||||||
return prs, x.Where("base_repo_id=? AND base_branch=? AND has_merged=? AND issue.is_closed=?",
|
return prs, x.
|
||||||
repoID, branch, false, false).
|
Where("base_repo_id=? AND base_branch=? AND has_merged=? AND issue.is_closed=?",
|
||||||
Join("INNER", "issue", "issue.id=pull_request.issue_id").Find(&prs)
|
repoID, branch, false, false).
|
||||||
|
Join("INNER", "issue", "issue.id=pull_request.issue_id").
|
||||||
|
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) {
|
func getPullRequestByID(e Engine, id int64) (*PullRequest, error) {
|
||||||
@@ -534,7 +670,7 @@ func (pr *PullRequest) Update() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update updates specific fields of pull request.
|
// UpdateCols updates specific fields of pull request.
|
||||||
func (pr *PullRequest) UpdateCols(cols ...string) error {
|
func (pr *PullRequest) UpdateCols(cols ...string) error {
|
||||||
_, err := x.Id(pr.ID).Cols(cols...).Update(pr)
|
_, err := x.Id(pr.ID).Cols(cols...).Update(pr)
|
||||||
return err
|
return err
|
||||||
@@ -608,7 +744,9 @@ func (pr *PullRequest) PushToBaseRepo() (err error) {
|
|||||||
headFile := fmt.Sprintf("refs/pull/%d/head", pr.Index)
|
headFile := fmt.Sprintf("refs/pull/%d/head", pr.Index)
|
||||||
|
|
||||||
// Remove head in case there is a conflict.
|
// 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 {
|
if err = git.Push(headRepoPath, tmpRemoteName, fmt.Sprintf("%s:%s", pr.HeadBranch, headFile)); err != nil {
|
||||||
return fmt.Errorf("Push: %v", err)
|
return fmt.Errorf("Push: %v", err)
|
||||||
@@ -619,14 +757,15 @@ func (pr *PullRequest) PushToBaseRepo() (err error) {
|
|||||||
|
|
||||||
// AddToTaskQueue adds itself to pull request test task queue.
|
// AddToTaskQueue adds itself to pull request test task queue.
|
||||||
func (pr *PullRequest) AddToTaskQueue() {
|
func (pr *PullRequest) AddToTaskQueue() {
|
||||||
go PullRequestQueue.AddFunc(pr.ID, func() {
|
go pullRequestQueue.AddFunc(pr.ID, func() {
|
||||||
pr.Status = PULL_REQUEST_STATUS_CHECKING
|
pr.Status = PullRequestStatusChecking
|
||||||
if err := pr.UpdateCols("status"); err != nil {
|
if err := pr.UpdateCols("status"); err != nil {
|
||||||
log.Error(5, "AddToTaskQueue.UpdateCols[%d].(add to queue): %v", pr.ID, err)
|
log.Error(5, "AddToTaskQueue.UpdateCols[%d].(add to queue): %v", pr.ID, err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PullRequestList defines a list of pull requests
|
||||||
type PullRequestList []*PullRequest
|
type PullRequestList []*PullRequest
|
||||||
|
|
||||||
func (prs PullRequestList) loadAttributes(e Engine) error {
|
func (prs PullRequestList) loadAttributes(e Engine) error {
|
||||||
@@ -640,7 +779,10 @@ func (prs PullRequestList) loadAttributes(e Engine) error {
|
|||||||
issueIDs = append(issueIDs, prs[i].IssueID)
|
issueIDs = append(issueIDs, prs[i].IssueID)
|
||||||
}
|
}
|
||||||
issues := make([]*Issue, 0, len(issueIDs))
|
issues := make([]*Issue, 0, len(issueIDs))
|
||||||
if err := e.Where("id > 0").In("id", issueIDs).Find(&issues); err != nil {
|
if err := e.
|
||||||
|
Where("id > 0").
|
||||||
|
In("id", issueIDs).
|
||||||
|
Find(&issues); err != nil {
|
||||||
return fmt.Errorf("find issues: %v", err)
|
return fmt.Errorf("find issues: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -654,6 +796,7 @@ func (prs PullRequestList) loadAttributes(e Engine) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LoadAttributes load all the prs attributes
|
||||||
func (prs PullRequestList) LoadAttributes() error {
|
func (prs PullRequestList) LoadAttributes() error {
|
||||||
return prs.loadAttributes(x)
|
return prs.loadAttributes(x)
|
||||||
}
|
}
|
||||||
@@ -695,11 +838,11 @@ func AddTestPullRequestTask(doer *User, repoID int64, branch string, isSync bool
|
|||||||
log.Error(4, "LoadAttributes: %v", err)
|
log.Error(4, "LoadAttributes: %v", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if err = PrepareWebhooks(pr.Issue.Repo, HOOK_EVENT_PULL_REQUEST, &api.PullRequestPayload{
|
if err = PrepareWebhooks(pr.Issue.Repo, HookEventPullRequest, &api.PullRequestPayload{
|
||||||
Action: api.HOOK_ISSUE_SYNCHRONIZED,
|
Action: api.HookIssueSynchronized,
|
||||||
Index: pr.Issue.Index,
|
Index: pr.Issue.Index,
|
||||||
PullRequest: pr.Issue.PullRequest.APIFormat(),
|
PullRequest: pr.Issue.PullRequest.APIFormat(),
|
||||||
Repository: pr.Issue.Repo.APIFormat(nil),
|
Repository: pr.Issue.Repo.APIFormat(AccessModeNone),
|
||||||
Sender: doer.APIFormat(),
|
Sender: doer.APIFormat(),
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
log.Error(4, "PrepareWebhooks [pull_id: %v]: %v", pr.ID, err)
|
log.Error(4, "PrepareWebhooks [pull_id: %v]: %v", pr.ID, err)
|
||||||
@@ -723,11 +866,15 @@ func AddTestPullRequestTask(doer *User, repoID int64, branch string, isSync bool
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ChangeUsernameInPullRequests changes the name of head_user_name
|
||||||
func ChangeUsernameInPullRequests(oldUserName, newUserName string) error {
|
func ChangeUsernameInPullRequests(oldUserName, newUserName string) error {
|
||||||
pr := PullRequest{
|
pr := PullRequest{
|
||||||
HeadUserName: strings.ToLower(newUserName),
|
HeadUserName: strings.ToLower(newUserName),
|
||||||
}
|
}
|
||||||
_, err := x.Cols("head_user_name").Where("head_user_name = ?", strings.ToLower(oldUserName)).Update(pr)
|
_, err := x.
|
||||||
|
Cols("head_user_name").
|
||||||
|
Where("head_user_name = ?", strings.ToLower(oldUserName)).
|
||||||
|
Update(pr)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -735,12 +882,12 @@ func ChangeUsernameInPullRequests(oldUserName, newUserName string) error {
|
|||||||
// and set to be either conflict or mergeable.
|
// and set to be either conflict or mergeable.
|
||||||
func (pr *PullRequest) checkAndUpdateStatus() {
|
func (pr *PullRequest) checkAndUpdateStatus() {
|
||||||
// Status is not changed to conflict means mergeable.
|
// Status is not changed to conflict means mergeable.
|
||||||
if pr.Status == PULL_REQUEST_STATUS_CHECKING {
|
if pr.Status == PullRequestStatusChecking {
|
||||||
pr.Status = PULL_REQUEST_STATUS_MERGEABLE
|
pr.Status = PullRequestStatusMergeable
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure there is no waiting test to process before levaing the checking status.
|
// 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 {
|
if err := pr.UpdateCols("status"); err != nil {
|
||||||
log.Error(4, "Update[%d]: %v", pr.ID, err)
|
log.Error(4, "Update[%d]: %v", pr.ID, err)
|
||||||
}
|
}
|
||||||
@@ -752,7 +899,7 @@ func (pr *PullRequest) checkAndUpdateStatus() {
|
|||||||
func TestPullRequests() {
|
func TestPullRequests() {
|
||||||
prs := make([]*PullRequest, 0, 10)
|
prs := make([]*PullRequest, 0, 10)
|
||||||
x.Iterate(PullRequest{
|
x.Iterate(PullRequest{
|
||||||
Status: PULL_REQUEST_STATUS_CHECKING,
|
Status: PullRequestStatusChecking,
|
||||||
},
|
},
|
||||||
func(idx int, bean interface{}) error {
|
func(idx int, bean interface{}) error {
|
||||||
pr := bean.(*PullRequest)
|
pr := bean.(*PullRequest)
|
||||||
@@ -776,13 +923,13 @@ func TestPullRequests() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Start listening on new test requests.
|
// Start listening on new test requests.
|
||||||
for prID := range PullRequestQueue.Queue() {
|
for prID := range pullRequestQueue.Queue() {
|
||||||
log.Trace("TestPullRequests[%v]: processing test task", prID)
|
log.Trace("TestPullRequests[%v]: processing test task", prID)
|
||||||
PullRequestQueue.Remove(prID)
|
pullRequestQueue.Remove(prID)
|
||||||
|
|
||||||
pr, err := GetPullRequestByID(com.StrTo(prID).MustInt64())
|
pr, err := GetPullRequestByID(com.StrTo(prID).MustInt64())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(4, "GetPullRequestByID[%d]: %v", prID, err)
|
log.Error(4, "GetPullRequestByID[%s]: %v", prID, err)
|
||||||
continue
|
continue
|
||||||
} else if err = pr.testPatch(); err != nil {
|
} else if err = pr.testPatch(); err != nil {
|
||||||
log.Error(4, "testPatch[%d]: %v", pr.ID, err)
|
log.Error(4, "testPatch[%d]: %v", pr.ID, err)
|
||||||
@@ -793,6 +940,7 @@ func TestPullRequests() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// InitTestPullRequests runs the task to test all the checking status pull requests
|
||||||
func InitTestPullRequests() {
|
func InitTestPullRequests() {
|
||||||
go TestPullRequests()
|
go TestPullRequests()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,9 +12,9 @@ import (
|
|||||||
|
|
||||||
"github.com/go-xorm/xorm"
|
"github.com/go-xorm/xorm"
|
||||||
|
|
||||||
"github.com/gogits/git-module"
|
"code.gitea.io/git"
|
||||||
|
|
||||||
"github.com/gogits/gogs/modules/process"
|
"code.gitea.io/gitea/modules/process"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Release represents a release of repository.
|
// Release represents a release of repository.
|
||||||
@@ -38,12 +38,14 @@ type Release struct {
|
|||||||
CreatedUnix int64
|
CreatedUnix int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BeforeInsert is invoked from XORM before inserting an object of this type.
|
||||||
func (r *Release) BeforeInsert() {
|
func (r *Release) BeforeInsert() {
|
||||||
if r.CreatedUnix == 0 {
|
if r.CreatedUnix == 0 {
|
||||||
r.CreatedUnix = time.Now().Unix()
|
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) {
|
func (r *Release) AfterSet(colName string, _ xorm.Cell) {
|
||||||
switch colName {
|
switch colName {
|
||||||
case "created_unix":
|
case "created_unix":
|
||||||
@@ -127,7 +129,9 @@ func GetRelease(repoID int64, tagName string) (*Release, error) {
|
|||||||
// GetReleaseByID returns release with given ID.
|
// GetReleaseByID returns release with given ID.
|
||||||
func GetReleaseByID(id int64) (*Release, error) {
|
func GetReleaseByID(id int64) (*Release, error) {
|
||||||
rel := new(Release)
|
rel := new(Release)
|
||||||
has, err := x.Id(id).Get(rel)
|
has, err := x.
|
||||||
|
Id(id).
|
||||||
|
Get(rel)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if !has {
|
} else if !has {
|
||||||
@@ -138,20 +142,26 @@ func GetReleaseByID(id int64) (*Release, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetReleasesByRepoID returns a list of releases of repository.
|
// GetReleasesByRepoID returns a list of releases of repository.
|
||||||
func GetReleasesByRepoID(repoID int64) (rels []*Release, err error) {
|
func GetReleasesByRepoID(repoID int64, page, pageSize int) (rels []*Release, err error) {
|
||||||
err = x.Desc("created_unix").Find(&rels, Release{RepoID: repoID})
|
if page <= 0 {
|
||||||
|
page = 1
|
||||||
|
}
|
||||||
|
err = x.
|
||||||
|
Desc("created_unix").
|
||||||
|
Limit(pageSize, (page-1)*pageSize).
|
||||||
|
Find(&rels, Release{RepoID: repoID})
|
||||||
return rels, err
|
return rels, err
|
||||||
}
|
}
|
||||||
|
|
||||||
type ReleaseSorter struct {
|
type releaseSorter struct {
|
||||||
rels []*Release
|
rels []*Release
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rs *ReleaseSorter) Len() int {
|
func (rs *releaseSorter) Len() int {
|
||||||
return len(rs.rels)
|
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
|
diffNum := rs.rels[i].NumCommits - rs.rels[j].NumCommits
|
||||||
if diffNum != 0 {
|
if diffNum != 0 {
|
||||||
return diffNum > 0
|
return diffNum > 0
|
||||||
@@ -159,13 +169,13 @@ func (rs *ReleaseSorter) Less(i, j int) bool {
|
|||||||
return rs.rels[i].Created.After(rs.rels[j].Created)
|
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]
|
rs.rels[i], rs.rels[j] = rs.rels[j], rs.rels[i]
|
||||||
}
|
}
|
||||||
|
|
||||||
// SortReleases sorts releases by number of commits and created time.
|
// SortReleases sorts releases by number of commits and created time.
|
||||||
func SortReleases(rels []*Release) {
|
func SortReleases(rels []*Release) {
|
||||||
sorter := &ReleaseSorter{rels: rels}
|
sorter := &releaseSorter{rels: rels}
|
||||||
sort.Sort(sorter)
|
sort.Sort(sorter)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,7 +189,7 @@ func UpdateRelease(gitRepo *git.Repository, rel *Release) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// DeleteReleaseByID deletes a release and corresponding Git tag by given ID.
|
// DeleteReleaseByID deletes a release and corresponding Git tag by given ID.
|
||||||
func DeleteReleaseByID(id int64) error {
|
func DeleteReleaseByID(id int64, u *User, delTag bool) error {
|
||||||
rel, err := GetReleaseByID(id)
|
rel, err := GetReleaseByID(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("GetReleaseByID: %v", err)
|
return fmt.Errorf("GetReleaseByID: %v", err)
|
||||||
@@ -190,11 +200,20 @@ func DeleteReleaseByID(id int64) error {
|
|||||||
return fmt.Errorf("GetRepositoryByID: %v", err)
|
return fmt.Errorf("GetRepositoryByID: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, stderr, err := process.ExecDir(-1, repo.RepoPath(),
|
has, err := HasAccess(u, repo, AccessModeWrite)
|
||||||
fmt.Sprintf("DeleteReleaseByID (git tag -d): %d", rel.ID),
|
if err != nil {
|
||||||
"git", "tag", "-d", rel.TagName)
|
return fmt.Errorf("HasAccess: %v", err)
|
||||||
if err != nil && !strings.Contains(stderr, "not found") {
|
} else if !has {
|
||||||
return fmt.Errorf("git tag -d: %v - %s", err, stderr)
|
return fmt.Errorf("DeleteReleaseByID: permission denied")
|
||||||
|
}
|
||||||
|
|
||||||
|
if delTag {
|
||||||
|
_, stderr, err := process.ExecDir(-1, repo.RepoPath(),
|
||||||
|
fmt.Sprintf("DeleteReleaseByID (git tag -d): %d", rel.ID),
|
||||||
|
"git", "tag", "-d", rel.TagName)
|
||||||
|
if err != nil && !strings.Contains(stderr, "not found") {
|
||||||
|
return fmt.Errorf("git tag -d: %v - %s", err, stderr)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err = x.Id(rel.ID).Delete(new(Release)); err != nil {
|
if _, err = x.Id(rel.ID).Delete(new(Release)); err != nil {
|
||||||
|
|||||||
376
models/repo.go
376
models/repo.go
@@ -19,54 +19,72 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"code.gitea.io/git"
|
||||||
|
"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"
|
||||||
|
api "code.gitea.io/sdk/gitea"
|
||||||
"github.com/Unknwon/cae/zip"
|
"github.com/Unknwon/cae/zip"
|
||||||
"github.com/Unknwon/com"
|
"github.com/Unknwon/com"
|
||||||
"github.com/go-xorm/xorm"
|
"github.com/go-xorm/xorm"
|
||||||
"github.com/mcuadros/go-version"
|
version "github.com/mcuadros/go-version"
|
||||||
"gopkg.in/ini.v1"
|
ini "gopkg.in/ini.v1"
|
||||||
|
|
||||||
git "github.com/gogits/git-module"
|
|
||||||
api "github.com/gogits/go-gogs-client"
|
|
||||||
|
|
||||||
"github.com/gogits/gogs/modules/bindata"
|
|
||||||
"github.com/gogits/gogs/modules/log"
|
|
||||||
"github.com/gogits/gogs/modules/markdown"
|
|
||||||
"github.com/gogits/gogs/modules/process"
|
|
||||||
"github.com/gogits/gogs/modules/setting"
|
|
||||||
"github.com/gogits/gogs/modules/sync"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
_TPL_UPDATE_HOOK = "#!/usr/bin/env %s\n%s update $1 $2 $3 --config='%s'\n"
|
tplUpdateHook = "#!/usr/bin/env %s\n%s update $1 $2 $3 --config='%s'\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
var repoWorkingPool = sync.NewExclusivePool()
|
var repoWorkingPool = sync.NewExclusivePool()
|
||||||
|
|
||||||
var (
|
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")
|
ErrRepoFileNotLoaded = errors.New("Repository file not loaded")
|
||||||
ErrMirrorNotExist = errors.New("Mirror does not exist")
|
|
||||||
ErrInvalidReference = errors.New("Invalid reference specified")
|
// ErrMirrorNotExist mirror does not exist error
|
||||||
ErrNameEmpty = errors.New("Name is empty")
|
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 (
|
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
|
ItemsPerPage = 40
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// LoadRepoConfig loads the repository config
|
||||||
func LoadRepoConfig() {
|
func LoadRepoConfig() {
|
||||||
// Load .gitignore and license files and readme templates.
|
// Load .gitignore and license files and readme templates.
|
||||||
types := []string{"gitignore", "license", "readme", "label"}
|
types := []string{"gitignore", "license", "readme", "label"}
|
||||||
typeFiles := make([][]string, 4)
|
typeFiles := make([][]string, 4)
|
||||||
for i, t := range types {
|
for i, t := range types {
|
||||||
files, err := bindata.AssetDir("conf/" + t)
|
files, err := options.Dir(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(4, "Fail to get %s files: %v", t, err)
|
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) {
|
if com.IsDir(customPath) {
|
||||||
customFiles, err := com.StatDir(customPath)
|
customFiles, err := com.StatDir(customPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -106,6 +124,7 @@ func LoadRepoConfig() {
|
|||||||
Licenses = sortedLicenses
|
Licenses = sortedLicenses
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewRepoContext creates a new repository context
|
||||||
func NewRepoContext() {
|
func NewRepoContext() {
|
||||||
zip.Verbose = false
|
zip.Verbose = false
|
||||||
|
|
||||||
@@ -186,6 +205,7 @@ type Repository struct {
|
|||||||
ExternalWikiURL string
|
ExternalWikiURL string
|
||||||
EnableIssues bool `xorm:"NOT NULL DEFAULT true"`
|
EnableIssues bool `xorm:"NOT NULL DEFAULT true"`
|
||||||
EnableExternalTracker bool
|
EnableExternalTracker bool
|
||||||
|
ExternalTrackerURL string
|
||||||
ExternalTrackerFormat string
|
ExternalTrackerFormat string
|
||||||
ExternalTrackerStyle string
|
ExternalTrackerStyle string
|
||||||
ExternalMetas map[string]string `xorm:"-"`
|
ExternalMetas map[string]string `xorm:"-"`
|
||||||
@@ -201,15 +221,18 @@ type Repository struct {
|
|||||||
UpdatedUnix int64
|
UpdatedUnix int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BeforeInsert is invoked from XORM before inserting an object of this type.
|
||||||
func (repo *Repository) BeforeInsert() {
|
func (repo *Repository) BeforeInsert() {
|
||||||
repo.CreatedUnix = time.Now().Unix()
|
repo.CreatedUnix = time.Now().Unix()
|
||||||
repo.UpdatedUnix = repo.CreatedUnix
|
repo.UpdatedUnix = repo.CreatedUnix
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BeforeUpdate is invoked from XORM before updating this object.
|
||||||
func (repo *Repository) BeforeUpdate() {
|
func (repo *Repository) BeforeUpdate() {
|
||||||
repo.UpdatedUnix = time.Now().Unix()
|
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) {
|
func (repo *Repository) AfterSet(colName string, _ xorm.Cell) {
|
||||||
switch colName {
|
switch colName {
|
||||||
case "default_branch":
|
case "default_branch":
|
||||||
@@ -225,7 +248,7 @@ func (repo *Repository) AfterSet(colName string, _ xorm.Cell) {
|
|||||||
repo.NumOpenMilestones = repo.NumMilestones - repo.NumClosedMilestones
|
repo.NumOpenMilestones = repo.NumMilestones - repo.NumClosedMilestones
|
||||||
case "external_tracker_style":
|
case "external_tracker_style":
|
||||||
if len(repo.ExternalTrackerStyle) == 0 {
|
if len(repo.ExternalTrackerStyle) == 0 {
|
||||||
repo.ExternalTrackerStyle = markdown.ISSUE_NAME_STYLE_NUMERIC
|
repo.ExternalTrackerStyle = markdown.IssueNameStyleNumeric
|
||||||
}
|
}
|
||||||
case "created_unix":
|
case "created_unix":
|
||||||
repo.Created = time.Unix(repo.CreatedUnix, 0).Local()
|
repo.Created = time.Unix(repo.CreatedUnix, 0).Local()
|
||||||
@@ -242,17 +265,24 @@ func (repo *Repository) MustOwner() *User {
|
|||||||
return repo.mustOwner(x)
|
return repo.mustOwner(x)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FullName returns the repository full name
|
||||||
func (repo *Repository) FullName() string {
|
func (repo *Repository) FullName() string {
|
||||||
return repo.MustOwner().Name + "/" + repo.Name
|
return repo.MustOwner().Name + "/" + repo.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HTMLURL returns the repository HTML URL
|
||||||
func (repo *Repository) HTMLURL() string {
|
func (repo *Repository) HTMLURL() string {
|
||||||
return setting.AppUrl + repo.FullName()
|
return setting.AppURL + repo.FullName()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Arguments that are allowed to be nil: permission
|
// APIFormat converts a Repository to api.Repository
|
||||||
func (repo *Repository) APIFormat(permission *api.Permission) *api.Repository {
|
func (repo *Repository) APIFormat(mode AccessMode) *api.Repository {
|
||||||
cloneLink := repo.CloneLink()
|
cloneLink := repo.CloneLink()
|
||||||
|
permission := &api.Permission{
|
||||||
|
Admin: mode >= AccessModeAdmin,
|
||||||
|
Push: mode >= AccessModeWrite,
|
||||||
|
Pull: mode >= AccessModeRead,
|
||||||
|
}
|
||||||
return &api.Repository{
|
return &api.Repository{
|
||||||
ID: repo.ID,
|
ID: repo.ID,
|
||||||
Owner: repo.Owner.APIFormat(),
|
Owner: repo.Owner.APIFormat(),
|
||||||
@@ -285,6 +315,7 @@ func (repo *Repository) getOwner(e Engine) (err error) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetOwner returns the repository owner
|
||||||
func (repo *Repository) GetOwner() error {
|
func (repo *Repository) GetOwner() error {
|
||||||
return repo.getOwner(x)
|
return repo.getOwner(x)
|
||||||
}
|
}
|
||||||
@@ -311,10 +342,10 @@ func (repo *Repository) ComposeMetas() map[string]string {
|
|||||||
"repo": repo.Name,
|
"repo": repo.Name,
|
||||||
}
|
}
|
||||||
switch repo.ExternalTrackerStyle {
|
switch repo.ExternalTrackerStyle {
|
||||||
case markdown.ISSUE_NAME_STYLE_ALPHANUMERIC:
|
case markdown.IssueNameStyleAlphanumeric:
|
||||||
repo.ExternalMetas["style"] = markdown.ISSUE_NAME_STYLE_ALPHANUMERIC
|
repo.ExternalMetas["style"] = markdown.IssueNameStyleAlphanumeric
|
||||||
default:
|
default:
|
||||||
repo.ExternalMetas["style"] = markdown.ISSUE_NAME_STYLE_NUMERIC
|
repo.ExternalMetas["style"] = markdown.IssueNameStyleNumeric
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -335,7 +366,9 @@ func (repo *Repository) getAssignees(e Engine) (_ []*User, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
accesses := make([]*Access, 0, 10)
|
accesses := make([]*Access, 0, 10)
|
||||||
if err = e.Where("repo_id = ? AND mode >= ?", repo.ID, ACCESS_MODE_WRITE).Find(&accesses); err != nil {
|
if err = e.
|
||||||
|
Where("repo_id = ? AND mode >= ?", repo.ID, AccessModeWrite).
|
||||||
|
Find(&accesses); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -380,11 +413,13 @@ func (repo *Repository) IssueStats(uid int64, filterMode int, isPull bool) (int6
|
|||||||
return GetRepoIssueStats(repo.ID, uid, filterMode, isPull)
|
return GetRepoIssueStats(repo.ID, uid, filterMode, isPull)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetMirror sets the repository mirror, returns an error upon failure
|
||||||
func (repo *Repository) GetMirror() (err error) {
|
func (repo *Repository) GetMirror() (err error) {
|
||||||
repo.Mirror, err = GetMirrorByRepoID(repo.ID)
|
repo.Mirror, err = GetMirrorByRepoID(repo.ID)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetBaseRepo returns the base repository
|
||||||
func (repo *Repository) GetBaseRepo() (err error) {
|
func (repo *Repository) GetBaseRepo() (err error) {
|
||||||
if !repo.IsFork {
|
if !repo.IsFork {
|
||||||
return nil
|
return nil
|
||||||
@@ -398,31 +433,38 @@ func (repo *Repository) repoPath(e Engine) string {
|
|||||||
return RepoPath(repo.mustOwner(e).Name, repo.Name)
|
return RepoPath(repo.mustOwner(e).Name, repo.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RepoPath returns the repository path
|
||||||
func (repo *Repository) RepoPath() string {
|
func (repo *Repository) RepoPath() string {
|
||||||
return repo.repoPath(x)
|
return repo.repoPath(x)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GitConfigPath returns the repository git config path
|
||||||
func (repo *Repository) GitConfigPath() string {
|
func (repo *Repository) GitConfigPath() string {
|
||||||
return filepath.Join(repo.RepoPath(), "config")
|
return filepath.Join(repo.RepoPath(), "config")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RelLink returns the repository relative link
|
||||||
func (repo *Repository) RelLink() string {
|
func (repo *Repository) RelLink() string {
|
||||||
return "/" + repo.FullName()
|
return "/" + repo.FullName()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Link returns the repository link
|
||||||
func (repo *Repository) Link() string {
|
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 {
|
func (repo *Repository) ComposeCompareURL(oldCommitID, newCommitID string) string {
|
||||||
return fmt.Sprintf("%s/%s/compare/%s...%s", repo.MustOwner().Name, repo.Name, oldCommitID, newCommitID)
|
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 {
|
func (repo *Repository) HasAccess(u *User) bool {
|
||||||
has, _ := HasAccess(u, repo, ACCESS_MODE_READ)
|
has, _ := HasAccess(u, repo, AccessModeRead)
|
||||||
return has
|
return has
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsOwnedBy returns true when user owns this repository
|
||||||
func (repo *Repository) IsOwnedBy(userID int64) bool {
|
func (repo *Repository) IsOwnedBy(userID int64) bool {
|
||||||
return repo.OwnerID == userID
|
return repo.OwnerID == userID
|
||||||
}
|
}
|
||||||
@@ -437,7 +479,7 @@ func (repo *Repository) CanEnablePulls() bool {
|
|||||||
return !repo.IsMirror
|
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 {
|
func (repo *Repository) AllowsPulls() bool {
|
||||||
return repo.CanEnablePulls() && repo.EnablePulls
|
return repo.CanEnablePulls() && repo.EnablePulls
|
||||||
}
|
}
|
||||||
@@ -447,6 +489,7 @@ func (repo *Repository) CanEnableEditor() bool {
|
|||||||
return !repo.IsMirror
|
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
|
// FIXME: should have a mutex to prevent producing same index for two issues that are created
|
||||||
// closely enough.
|
// closely enough.
|
||||||
func (repo *Repository) NextIssueIndex() int64 {
|
func (repo *Repository) NextIssueIndex() int64 {
|
||||||
@@ -454,22 +497,23 @@ func (repo *Repository) NextIssueIndex() int64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
DescPattern = regexp.MustCompile(`https?://\S+`)
|
descPattern = regexp.MustCompile(`https?://\S+`)
|
||||||
)
|
)
|
||||||
|
|
||||||
// DescriptionHtml does special handles to description and return HTML string.
|
// DescriptionHTML does special handles to description and return HTML string.
|
||||||
func (repo *Repository) DescriptionHtml() template.HTML {
|
func (repo *Repository) DescriptionHTML() template.HTML {
|
||||||
sanitize := func(s string) string {
|
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 {
|
func (repo *Repository) LocalCopyPath() string {
|
||||||
return path.Join(setting.AppDataPath, "tmp/local-rpeo", com.ToStr(repo.ID))
|
return path.Join(setting.AppDataPath, "tmp/local-repo", 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.
|
// 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
|
// 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.
|
// operations are operating against target branch when caller has confidence for no race condition.
|
||||||
@@ -518,8 +562,12 @@ func (repo *Repository) SavePatch(index int64, patch []byte) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("PatchPath: %v", err)
|
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 {
|
if err = ioutil.WriteFile(patchPath, patch, 0644); err != nil {
|
||||||
return fmt.Errorf("WriteFile: %v", err)
|
return fmt.Errorf("WriteFile: %v", err)
|
||||||
}
|
}
|
||||||
@@ -549,7 +597,7 @@ type CloneLink struct {
|
|||||||
|
|
||||||
// ComposeHTTPSCloneURL returns HTTPS clone URL based on given owner and repository name.
|
// ComposeHTTPSCloneURL returns HTTPS clone URL based on given owner and repository name.
|
||||||
func ComposeHTTPSCloneURL(owner, repo string) string {
|
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 {
|
func (repo *Repository) cloneLink(isWiki bool) *CloneLink {
|
||||||
@@ -574,6 +622,7 @@ func (repo *Repository) CloneLink() (cl *CloneLink) {
|
|||||||
return repo.cloneLink(false)
|
return repo.cloneLink(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MigrateRepoOptions contains the repository migrate options
|
||||||
type MigrateRepoOptions struct {
|
type MigrateRepoOptions struct {
|
||||||
Name string
|
Name string
|
||||||
Description string
|
Description string
|
||||||
@@ -628,7 +677,10 @@ func MigrateRepository(u *User, opts MigrateRepoOptions) (*Repository, error) {
|
|||||||
|
|
||||||
migrateTimeout := time.Duration(setting.Git.Timeout.Migrate) * time.Second
|
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{
|
if err = git.Clone(opts.RemoteAddr, repoPath, git.CloneRepoOptions{
|
||||||
Mirror: true,
|
Mirror: true,
|
||||||
Quiet: true,
|
Quiet: true,
|
||||||
@@ -639,13 +691,20 @@ func MigrateRepository(u *User, opts MigrateRepoOptions) (*Repository, error) {
|
|||||||
|
|
||||||
wikiRemotePath := wikiRemoteURL(opts.RemoteAddr)
|
wikiRemotePath := wikiRemoteURL(opts.RemoteAddr)
|
||||||
if len(wikiRemotePath) > 0 {
|
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{
|
if err = git.Clone(wikiRemotePath, wikiPath, git.CloneRepoOptions{
|
||||||
Mirror: true,
|
Mirror: true,
|
||||||
Quiet: true,
|
Quiet: true,
|
||||||
Timeout: migrateTimeout,
|
Timeout: migrateTimeout,
|
||||||
|
Branch: "master",
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
log.Info("Clone wiki: %v", err)
|
log.Warn("Clone wiki: %v", err)
|
||||||
|
if err := os.RemoveAll(wikiPath); err != nil {
|
||||||
|
return repo, fmt.Errorf("Fail to remove %s: %v", wikiPath, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -707,10 +766,10 @@ func cleanUpMigrateGitConfig(configPath string) error {
|
|||||||
|
|
||||||
func createUpdateHook(repoPath string) error {
|
func createUpdateHook(repoPath string) error {
|
||||||
return git.SetUpdateHook(repoPath,
|
return git.SetUpdateHook(repoPath,
|
||||||
fmt.Sprintf(_TPL_UPDATE_HOOK, setting.ScriptType, "\""+setting.AppPath+"\"", setting.CustomConf))
|
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) {
|
func CleanUpMigrateInfo(repo *Repository) (*Repository, error) {
|
||||||
repoPath := repo.RepoPath()
|
repoPath := repo.RepoPath()
|
||||||
if err := createUpdateHook(repoPath); err != nil {
|
if err := createUpdateHook(repoPath); err != nil {
|
||||||
@@ -758,6 +817,7 @@ func initRepoCommit(tmpPath string, sig *git.Signature) (err error) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateRepoOptions contains the create repository options
|
||||||
type CreateRepoOptions struct {
|
type CreateRepoOptions struct {
|
||||||
Name string
|
Name string
|
||||||
Description string
|
Description string
|
||||||
@@ -770,14 +830,27 @@ type CreateRepoOptions struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getRepoInitFile(tp, name string) ([]byte, error) {
|
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.
|
// Use custom file when available.
|
||||||
customPath := path.Join(setting.CustomPath, relPath)
|
customPath := path.Join(setting.CustomPath, relPath)
|
||||||
if com.IsFile(customPath) {
|
if com.IsFile(customPath) {
|
||||||
return ioutil.ReadFile(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)
|
||||||
|
case "label":
|
||||||
|
return options.Labels(cleanedName)
|
||||||
|
default:
|
||||||
|
return []byte{}, fmt.Errorf("Invalid init file type")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func prepareRepoCommit(repo *Repository, tmpDir, repoPath string, opts CreateRepoOptions) error {
|
func prepareRepoCommit(repo *Repository, tmpDir, repoPath string, opts CreateRepoOptions) error {
|
||||||
@@ -860,7 +933,11 @@ func initRepository(e Engine, repoPath string, u *User, repo *Repository, opts C
|
|||||||
|
|
||||||
// Initialize repository according to user's choice.
|
// Initialize repository according to user's choice.
|
||||||
if opts.AutoInit {
|
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)
|
defer os.RemoveAll(tmpDir)
|
||||||
|
|
||||||
if err = prepareRepoCommit(repo, tmpDir, repoPath, opts); err != nil {
|
if err = prepareRepoCommit(repo, tmpDir, repoPath, opts); err != nil {
|
||||||
@@ -896,6 +973,7 @@ var (
|
|||||||
reservedRepoPatterns = []string{"*.git", "*.wiki"}
|
reservedRepoPatterns = []string{"*.git", "*.wiki"}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// IsUsableRepoName returns true when repository is usable
|
||||||
func IsUsableRepoName(name string) error {
|
func IsUsableRepoName(name string) error {
|
||||||
return isUsableName(reservedRepoNames, reservedRepoPatterns, name)
|
return isUsableName(reservedRepoNames, reservedRepoPatterns, name)
|
||||||
}
|
}
|
||||||
@@ -1029,6 +1107,7 @@ func CountUserRepositories(userID int64, private bool) int64 {
|
|||||||
return countRepositories(userID, private)
|
return countRepositories(userID, private)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Repositories returns all repositories
|
||||||
func Repositories(page, pageSize int) (_ []*Repository, err error) {
|
func Repositories(page, pageSize int) (_ []*Repository, err error) {
|
||||||
repos := make([]*Repository, 0, pageSize)
|
repos := make([]*Repository, 0, pageSize)
|
||||||
return repos, x.Limit(pageSize, (page-1)*pageSize).Asc("id").Find(&repos)
|
return repos, x.Limit(pageSize, (page-1)*pageSize).Asc("id").Find(&repos)
|
||||||
@@ -1154,7 +1233,12 @@ func TransferOwnership(doer *User, newOwnerName string, repo *Repository) error
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Rename remote repository to new path and delete local copy.
|
// 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 {
|
if err = os.Rename(RepoPath(owner.Name, repo.Name), RepoPath(newOwner.Name, repo.Name)); err != nil {
|
||||||
return fmt.Errorf("rename repository directory: %v", err)
|
return fmt.Errorf("rename repository directory: %v", err)
|
||||||
}
|
}
|
||||||
@@ -1210,7 +1294,9 @@ func ChangeRepositoryName(u *User, oldRepoName, newRepoName string) (err error)
|
|||||||
|
|
||||||
func getRepositoriesByForkID(e Engine, forkID int64) ([]*Repository, error) {
|
func getRepositoriesByForkID(e Engine, forkID int64) ([]*Repository, error) {
|
||||||
repos := make([]*Repository, 0, 10)
|
repos := make([]*Repository, 0, 10)
|
||||||
return repos, e.Where("fork_id=?", forkID).Find(&repos)
|
return repos, e.
|
||||||
|
Where("fork_id=?", forkID).
|
||||||
|
Find(&repos)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetRepositoriesByForkID returns all repositories with given fork ID.
|
// GetRepositoriesByForkID returns all repositories with given fork ID.
|
||||||
@@ -1272,6 +1358,7 @@ func updateRepository(e Engine, repo *Repository, visibilityChanged bool) (err e
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpdateRepository updates a repository
|
||||||
func UpdateRepository(repo *Repository, visibilityChanged bool) (err error) {
|
func UpdateRepository(repo *Repository, visibilityChanged bool) (err error) {
|
||||||
sess := x.NewSession()
|
sess := x.NewSession()
|
||||||
defer sessionRelease(sess)
|
defer sessionRelease(sess)
|
||||||
@@ -1342,7 +1429,9 @@ func DeleteRepository(uid, repoID int64) error {
|
|||||||
// Delete comments and attachments.
|
// Delete comments and attachments.
|
||||||
issues := make([]*Issue, 0, 25)
|
issues := make([]*Issue, 0, 25)
|
||||||
attachmentPaths := make([]string, 0, len(issues))
|
attachmentPaths := make([]string, 0, len(issues))
|
||||||
if err = sess.Where("repo_id=?", repoID).Find(&issues); err != nil {
|
if err = sess.
|
||||||
|
Where("repo_id=?", repoID).
|
||||||
|
Find(&issues); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for i := range issues {
|
for i := range issues {
|
||||||
@@ -1351,7 +1440,9 @@ func DeleteRepository(uid, repoID int64) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
attachments := make([]*Attachment, 0, 5)
|
attachments := make([]*Attachment, 0, 5)
|
||||||
if err = sess.Where("issue_id=?", issues[i].ID).Find(&attachments); err != nil {
|
if err = sess.
|
||||||
|
Where("issue_id=?", issues[i].ID).
|
||||||
|
Find(&attachments); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for j := range attachments {
|
for j := range attachments {
|
||||||
@@ -1451,7 +1542,9 @@ func GetRepositoryByID(id int64) (*Repository, error) {
|
|||||||
|
|
||||||
// GetUserRepositories returns a list of repositories of given user.
|
// GetUserRepositories returns a list of repositories of given user.
|
||||||
func GetUserRepositories(userID int64, private bool, page, pageSize int) ([]*Repository, error) {
|
func GetUserRepositories(userID int64, private bool, page, pageSize int) ([]*Repository, error) {
|
||||||
sess := x.Where("owner_id = ?", userID).Desc("updated_unix")
|
sess := x.
|
||||||
|
Where("owner_id = ?", userID).
|
||||||
|
Desc("updated_unix")
|
||||||
if !private {
|
if !private {
|
||||||
sess.And("is_private=?", false)
|
sess.And("is_private=?", false)
|
||||||
}
|
}
|
||||||
@@ -1465,16 +1558,23 @@ func GetUserRepositories(userID int64, private bool, page, pageSize int) ([]*Rep
|
|||||||
return repos, sess.Find(&repos)
|
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) {
|
func GetUserMirrorRepositories(userID int64) ([]*Repository, error) {
|
||||||
repos := make([]*Repository, 0, 10)
|
repos := make([]*Repository, 0, 10)
|
||||||
return repos, x.Where("owner_id = ?", userID).And("is_mirror = ?", true).Find(&repos)
|
return repos, x.
|
||||||
|
Where("owner_id = ?", userID).
|
||||||
|
And("is_mirror = ?", true).
|
||||||
|
Find(&repos)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetRecentUpdatedRepositories returns the list of repositories that are recently updated.
|
// GetRecentUpdatedRepositories returns the list of repositories that are recently updated.
|
||||||
func GetRecentUpdatedRepositories(page, pageSize int) (repos []*Repository, err error) {
|
func GetRecentUpdatedRepositories(page, pageSize int) (repos []*Repository, err error) {
|
||||||
return repos, x.Limit(pageSize, (page-1)*pageSize).
|
return repos, x.
|
||||||
Where("is_private=?", false).Limit(pageSize).Desc("updated_unix").Find(&repos)
|
Limit(pageSize, (page-1)*pageSize).
|
||||||
|
Where("is_private=?", false).
|
||||||
|
Limit(pageSize).
|
||||||
|
Desc("updated_unix").
|
||||||
|
Find(&repos)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getRepositoryCount(e Engine, u *User) (int64, error) {
|
func getRepositoryCount(e Engine, u *User) (int64, error) {
|
||||||
@@ -1486,6 +1586,7 @@ func GetRepositoryCount(u *User) (int64, error) {
|
|||||||
return getRepositoryCount(x, u)
|
return getRepositoryCount(x, u)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SearchRepoOptions holds the search options
|
||||||
type SearchRepoOptions struct {
|
type SearchRepoOptions struct {
|
||||||
Keyword string
|
Keyword string
|
||||||
OwnerID int64
|
OwnerID int64
|
||||||
@@ -1533,23 +1634,27 @@ func SearchRepositoryByName(opts *SearchRepoOptions) (repos []*Repository, _ int
|
|||||||
|
|
||||||
// DeleteRepositoryArchives deletes all repositories' archives.
|
// DeleteRepositoryArchives deletes all repositories' archives.
|
||||||
func DeleteRepositoryArchives() error {
|
func DeleteRepositoryArchives() error {
|
||||||
return x.Where("id > 0").Iterate(new(Repository),
|
return x.
|
||||||
func(idx int, bean interface{}) error {
|
Where("id > 0").
|
||||||
repo := bean.(*Repository)
|
Iterate(new(Repository),
|
||||||
return os.RemoveAll(filepath.Join(repo.RepoPath(), "archives"))
|
func(idx int, bean interface{}) error {
|
||||||
})
|
repo := bean.(*Repository)
|
||||||
|
return os.RemoveAll(filepath.Join(repo.RepoPath(), "archives"))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func gatherMissingRepoRecords() ([]*Repository, error) {
|
func gatherMissingRepoRecords() ([]*Repository, error) {
|
||||||
repos := make([]*Repository, 0, 10)
|
repos := make([]*Repository, 0, 10)
|
||||||
if err := x.Where("id > 0").Iterate(new(Repository),
|
if err := x.
|
||||||
func(idx int, bean interface{}) error {
|
Where("id > 0").
|
||||||
repo := bean.(*Repository)
|
Iterate(new(Repository),
|
||||||
if !com.IsDir(repo.RepoPath()) {
|
func(idx int, bean interface{}) error {
|
||||||
repos = append(repos, repo)
|
repo := bean.(*Repository)
|
||||||
}
|
if !com.IsDir(repo.RepoPath()) {
|
||||||
return nil
|
repos = append(repos, repo)
|
||||||
}); err != nil {
|
}
|
||||||
|
return nil
|
||||||
|
}); err != nil {
|
||||||
if err2 := CreateRepositoryNotice(fmt.Sprintf("gatherMissingRepoRecords: %v", err)); err2 != nil {
|
if err2 := CreateRepositoryNotice(fmt.Sprintf("gatherMissingRepoRecords: %v", err)); err2 != nil {
|
||||||
return nil, fmt.Errorf("CreateRepositoryNotice: %v", err)
|
return nil, fmt.Errorf("CreateRepositoryNotice: %v", err)
|
||||||
}
|
}
|
||||||
@@ -1603,66 +1708,73 @@ func ReinitMissingRepositories() error {
|
|||||||
|
|
||||||
// RewriteRepositoryUpdateHook rewrites all repositories' update hook.
|
// RewriteRepositoryUpdateHook rewrites all repositories' update hook.
|
||||||
func RewriteRepositoryUpdateHook() error {
|
func RewriteRepositoryUpdateHook() error {
|
||||||
return x.Where("id > 0").Iterate(new(Repository),
|
return x.
|
||||||
func(idx int, bean interface{}) error {
|
Where("id > 0").
|
||||||
repo := bean.(*Repository)
|
Iterate(new(Repository),
|
||||||
return createUpdateHook(repo.RepoPath())
|
func(idx int, bean interface{}) error {
|
||||||
})
|
repo := bean.(*Repository)
|
||||||
|
return createUpdateHook(repo.RepoPath())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prevent duplicate running tasks.
|
// Prevent duplicate running tasks.
|
||||||
var taskStatusTable = sync.NewStatusTable()
|
var taskStatusTable = sync.NewStatusTable()
|
||||||
|
|
||||||
const (
|
const (
|
||||||
_MIRROR_UPDATE = "mirror_update"
|
mirrorUpdate = "mirror_update"
|
||||||
_GIT_FSCK = "git_fsck"
|
gitFsck = "git_fsck"
|
||||||
_CHECK_REPOs = "check_repos"
|
checkRepos = "check_repos"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GitFsck calls 'git fsck' to check repository health.
|
// GitFsck calls 'git fsck' to check repository health.
|
||||||
func GitFsck() {
|
func GitFsck() {
|
||||||
if taskStatusTable.IsRunning(_GIT_FSCK) {
|
if taskStatusTable.IsRunning(gitFsck) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
taskStatusTable.Start(_GIT_FSCK)
|
taskStatusTable.Start(gitFsck)
|
||||||
defer taskStatusTable.Stop(_GIT_FSCK)
|
defer taskStatusTable.Stop(gitFsck)
|
||||||
|
|
||||||
log.Trace("Doing: GitFsck")
|
log.Trace("Doing: GitFsck")
|
||||||
|
|
||||||
if err := x.Where("id>0").Iterate(new(Repository),
|
if err := x.
|
||||||
func(idx int, bean interface{}) error {
|
Where("id>0").
|
||||||
repo := bean.(*Repository)
|
Iterate(new(Repository),
|
||||||
repoPath := repo.RepoPath()
|
func(idx int, bean interface{}) error {
|
||||||
if err := git.Fsck(repoPath, setting.Cron.RepoHealthCheck.Timeout, setting.Cron.RepoHealthCheck.Args...); err != nil {
|
repo := bean.(*Repository)
|
||||||
desc := fmt.Sprintf("Fail to health check repository (%s): %v", repoPath, err)
|
repoPath := repo.RepoPath()
|
||||||
log.Warn(desc)
|
if err := git.Fsck(repoPath, setting.Cron.RepoHealthCheck.Timeout, setting.Cron.RepoHealthCheck.Args...); err != nil {
|
||||||
if err = CreateRepositoryNotice(desc); err != nil {
|
desc := fmt.Sprintf("Fail to health check repository (%s): %v", repoPath, err)
|
||||||
log.Error(4, "CreateRepositoryNotice: %v", err)
|
log.Warn(desc)
|
||||||
|
if err = CreateRepositoryNotice(desc); err != nil {
|
||||||
|
log.Error(4, "CreateRepositoryNotice: %v", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
return nil
|
||||||
return nil
|
}); err != nil {
|
||||||
}); err != nil {
|
|
||||||
log.Error(4, "GitFsck: %v", err)
|
log.Error(4, "GitFsck: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GitGcRepos calls 'git gc' to remove unnecessary files and optimize the local repository
|
||||||
func GitGcRepos() error {
|
func GitGcRepos() error {
|
||||||
args := append([]string{"gc"}, setting.Git.GCArgs...)
|
args := append([]string{"gc"}, setting.Git.GCArgs...)
|
||||||
return x.Where("id > 0").Iterate(new(Repository),
|
return x.
|
||||||
func(idx int, bean interface{}) error {
|
Where("id > 0").
|
||||||
repo := bean.(*Repository)
|
Iterate(new(Repository),
|
||||||
if err := repo.GetOwner(); err != nil {
|
func(idx int, bean interface{}) error {
|
||||||
return err
|
repo := bean.(*Repository)
|
||||||
}
|
if err := repo.GetOwner(); err != nil {
|
||||||
_, stderr, err := process.ExecDir(
|
return err
|
||||||
time.Duration(setting.Git.Timeout.GC)*time.Second,
|
}
|
||||||
RepoPath(repo.Owner.Name, repo.Name), "Repository garbage collection",
|
_, stderr, err := process.ExecDir(
|
||||||
"git", args...)
|
time.Duration(setting.Git.Timeout.GC)*time.Second,
|
||||||
if err != nil {
|
RepoPath(repo.Owner.Name, repo.Name), "Repository garbage collection",
|
||||||
return fmt.Errorf("%v: %v", err, stderr)
|
"git", args...)
|
||||||
}
|
if err != nil {
|
||||||
return nil
|
return fmt.Errorf("%v: %v", err, stderr)
|
||||||
})
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
type repoChecker struct {
|
type repoChecker struct {
|
||||||
@@ -1686,12 +1798,13 @@ func repoStatsCheck(checker *repoChecker) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CheckRepoStats checks the repository stats
|
||||||
func CheckRepoStats() {
|
func CheckRepoStats() {
|
||||||
if taskStatusTable.IsRunning(_CHECK_REPOs) {
|
if taskStatusTable.IsRunning(checkRepos) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
taskStatusTable.Start(_CHECK_REPOs)
|
taskStatusTable.Start(checkRepos)
|
||||||
defer taskStatusTable.Stop(_CHECK_REPOs)
|
defer taskStatusTable.Stop(checkRepos)
|
||||||
|
|
||||||
log.Trace("Doing: CheckRepoStats")
|
log.Trace("Doing: CheckRepoStats")
|
||||||
|
|
||||||
@@ -1780,6 +1893,7 @@ func CheckRepoStats() {
|
|||||||
// ***** END: Repository.NumForks *****
|
// ***** END: Repository.NumForks *****
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RepositoryList contains a list of repositories
|
||||||
type RepositoryList []*Repository
|
type RepositoryList []*Repository
|
||||||
|
|
||||||
func (repos RepositoryList) loadAttributes(e Engine) error {
|
func (repos RepositoryList) loadAttributes(e Engine) error {
|
||||||
@@ -1797,7 +1911,10 @@ func (repos RepositoryList) loadAttributes(e Engine) error {
|
|||||||
userIDs = append(userIDs, userID)
|
userIDs = append(userIDs, userID)
|
||||||
}
|
}
|
||||||
users := make([]*User, 0, len(userIDs))
|
users := make([]*User, 0, len(userIDs))
|
||||||
if err := e.Where("id > 0").In("id", userIDs).Find(&users); err != nil {
|
if err := e.
|
||||||
|
Where("id > 0").
|
||||||
|
In("id", userIDs).
|
||||||
|
Find(&users); err != nil {
|
||||||
return fmt.Errorf("find users: %v", err)
|
return fmt.Errorf("find users: %v", err)
|
||||||
}
|
}
|
||||||
for i := range users {
|
for i := range users {
|
||||||
@@ -1809,10 +1926,12 @@ func (repos RepositoryList) loadAttributes(e Engine) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LoadAttributes loads the attributes for the given RepositoryList
|
||||||
func (repos RepositoryList) LoadAttributes() error {
|
func (repos RepositoryList) LoadAttributes() error {
|
||||||
return repos.loadAttributes(x)
|
return repos.loadAttributes(x)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MirrorRepositoryList contains the mirror repositories
|
||||||
type MirrorRepositoryList []*Repository
|
type MirrorRepositoryList []*Repository
|
||||||
|
|
||||||
func (repos MirrorRepositoryList) loadAttributes(e Engine) error {
|
func (repos MirrorRepositoryList) loadAttributes(e Engine) error {
|
||||||
@@ -1830,7 +1949,10 @@ func (repos MirrorRepositoryList) loadAttributes(e Engine) error {
|
|||||||
repoIDs = append(repoIDs, repos[i].ID)
|
repoIDs = append(repoIDs, repos[i].ID)
|
||||||
}
|
}
|
||||||
mirrors := make([]*Mirror, 0, len(repoIDs))
|
mirrors := make([]*Mirror, 0, len(repoIDs))
|
||||||
if err := e.Where("id > 0").In("repo_id", repoIDs).Find(&mirrors); err != nil {
|
if err := e.
|
||||||
|
Where("id > 0").
|
||||||
|
In("repo_id", repoIDs).
|
||||||
|
Find(&mirrors); err != nil {
|
||||||
return fmt.Errorf("find mirrors: %v", err)
|
return fmt.Errorf("find mirrors: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1844,6 +1966,7 @@ func (repos MirrorRepositoryList) loadAttributes(e Engine) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LoadAttributes loads the attributes for the given MirrorRepositoryList
|
||||||
func (repos MirrorRepositoryList) LoadAttributes() error {
|
func (repos MirrorRepositoryList) LoadAttributes() error {
|
||||||
return repos.loadAttributes(x)
|
return repos.loadAttributes(x)
|
||||||
}
|
}
|
||||||
@@ -1893,7 +2016,7 @@ func watchRepo(e Engine, userID, repoID int64, watch bool) (err error) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Watch or unwatch repository.
|
// WatchRepo watch or unwatch repository.
|
||||||
func WatchRepo(userID, repoID int64, watch bool) (err error) {
|
func WatchRepo(userID, repoID int64, watch bool) (err error) {
|
||||||
return watchRepo(x, userID, repoID, watch)
|
return watchRepo(x, userID, repoID, watch)
|
||||||
}
|
}
|
||||||
@@ -1908,10 +2031,12 @@ func GetWatchers(repoID int64) ([]*Watch, error) {
|
|||||||
return getWatchers(x, repoID)
|
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) {
|
func (repo *Repository) GetWatchers(page int) ([]*User, error) {
|
||||||
users := make([]*User, 0, ItemsPerPage)
|
users := make([]*User, 0, ItemsPerPage)
|
||||||
sess := x.Limit(ItemsPerPage, (page-1)*ItemsPerPage).Where("watch.repo_id=?", repo.ID)
|
sess := x.
|
||||||
|
Limit(ItemsPerPage, (page-1)*ItemsPerPage).
|
||||||
|
Where("watch.repo_id=?", repo.ID)
|
||||||
if setting.UsePostgreSQL {
|
if setting.UsePostgreSQL {
|
||||||
sess = sess.Join("LEFT", "watch", `"user".id=watch.user_id`)
|
sess = sess.Join("LEFT", "watch", `"user".id=watch.user_id`)
|
||||||
} else {
|
} else {
|
||||||
@@ -1959,13 +2084,14 @@ func NotifyWatchers(act *Action) error {
|
|||||||
// /_______ /|__| (____ /__|
|
// /_______ /|__| (____ /__|
|
||||||
// \/ \/
|
// \/ \/
|
||||||
|
|
||||||
|
// Star contains the star information
|
||||||
type Star struct {
|
type Star struct {
|
||||||
ID int64 `xorm:"pk autoincr"`
|
ID int64 `xorm:"pk autoincr"`
|
||||||
UID int64 `xorm:"UNIQUE(s)"`
|
UID int64 `xorm:"UNIQUE(s)"`
|
||||||
RepoID 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) {
|
func StarRepo(userID, repoID int64, star bool) (err error) {
|
||||||
if star {
|
if star {
|
||||||
if IsStaring(userID, repoID) {
|
if IsStaring(userID, repoID) {
|
||||||
@@ -1997,9 +2123,12 @@ func IsStaring(userID, repoID int64) bool {
|
|||||||
return has
|
return has
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetStargazers returns the users who gave stars to this repository
|
||||||
func (repo *Repository) GetStargazers(page int) ([]*User, error) {
|
func (repo *Repository) GetStargazers(page int) ([]*User, error) {
|
||||||
users := make([]*User, 0, ItemsPerPage)
|
users := make([]*User, 0, ItemsPerPage)
|
||||||
sess := x.Limit(ItemsPerPage, (page-1)*ItemsPerPage).Where("star.repo_id=?", repo.ID)
|
sess := x.
|
||||||
|
Limit(ItemsPerPage, (page-1)*ItemsPerPage).
|
||||||
|
Where("star.repo_id=?", repo.ID)
|
||||||
if setting.UsePostgreSQL {
|
if setting.UsePostgreSQL {
|
||||||
sess = sess.Join("LEFT", "star", `"user".id=star.uid`)
|
sess = sess.Join("LEFT", "star", `"user".id=star.uid`)
|
||||||
} else {
|
} else {
|
||||||
@@ -2018,10 +2147,13 @@ func (repo *Repository) GetStargazers(page int) ([]*User, error) {
|
|||||||
// HasForkedRepo checks if given user has already forked a repository with given ID.
|
// HasForkedRepo checks if given user has already forked a repository with given ID.
|
||||||
func HasForkedRepo(ownerID, repoID int64) (*Repository, bool) {
|
func HasForkedRepo(ownerID, repoID int64) (*Repository, bool) {
|
||||||
repo := new(Repository)
|
repo := new(Repository)
|
||||||
has, _ := x.Where("owner_id=? AND fork_id=?", ownerID, repoID).Get(repo)
|
has, _ := x.
|
||||||
|
Where("owner_id=? AND fork_id=?", ownerID, repoID).
|
||||||
|
Get(repo)
|
||||||
return repo, has
|
return repo, has
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ForkRepository forks a repository
|
||||||
func ForkRepository(u *User, oldRepo *Repository, name, desc string) (_ *Repository, err error) {
|
func ForkRepository(u *User, oldRepo *Repository, name, desc string) (_ *Repository, err error) {
|
||||||
repo := &Repository{
|
repo := &Repository{
|
||||||
OwnerID: u.ID,
|
OwnerID: u.ID,
|
||||||
@@ -2061,7 +2193,7 @@ func ForkRepository(u *User, oldRepo *Repository, name, desc string) (_ *Reposit
|
|||||||
repoPath, fmt.Sprintf("ForkRepository(git update-server-info): %s", repoPath),
|
repoPath, fmt.Sprintf("ForkRepository(git update-server-info): %s", repoPath),
|
||||||
"git", "update-server-info")
|
"git", "update-server-info")
|
||||||
if err != nil {
|
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 {
|
if err = createUpdateHook(repoPath); err != nil {
|
||||||
@@ -2071,6 +2203,7 @@ func ForkRepository(u *User, oldRepo *Repository, name, desc string) (_ *Reposit
|
|||||||
return repo, sess.Commit()
|
return repo, sess.Commit()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetForks returns all the forks of the repository
|
||||||
func (repo *Repository) GetForks() ([]*Repository, error) {
|
func (repo *Repository) GetForks() ([]*Repository, error) {
|
||||||
forks := make([]*Repository, 0, repo.NumForks)
|
forks := make([]*Repository, 0, repo.NumForks)
|
||||||
return forks, x.Find(&forks, &Repository{ForkID: repo.ID})
|
return forks, x.Find(&forks, &Repository{ForkID: repo.ID})
|
||||||
@@ -2084,6 +2217,7 @@ func (repo *Repository) GetForks() ([]*Repository, error) {
|
|||||||
// \/ \/ \/ \/ \/
|
// \/ \/ \/ \/ \/
|
||||||
//
|
//
|
||||||
|
|
||||||
|
// CreateNewBranch creates a new repository branch
|
||||||
func (repo *Repository) CreateNewBranch(doer *User, oldBranchName, branchName string) (err error) {
|
func (repo *Repository) CreateNewBranch(doer *User, oldBranchName, branchName string) (err error) {
|
||||||
repoWorkingPool.CheckIn(com.ToStr(repo.ID))
|
repoWorkingPool.CheckIn(com.ToStr(repo.ID))
|
||||||
defer repoWorkingPool.CheckOut(com.ToStr(repo.ID))
|
defer repoWorkingPool.CheckOut(com.ToStr(repo.ID))
|
||||||
|
|||||||
@@ -5,14 +5,16 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/gogits/git-module"
|
"code.gitea.io/git"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Branch holds the branch information
|
||||||
type Branch struct {
|
type Branch struct {
|
||||||
Path string
|
Path string
|
||||||
Name string
|
Name string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetBranchesByPath returns a branch by it's path
|
||||||
func GetBranchesByPath(path string) ([]*Branch, error) {
|
func GetBranchesByPath(path string) ([]*Branch, error) {
|
||||||
gitRepo, err := git.OpenRepository(path)
|
gitRepo, err := git.OpenRepository(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -34,24 +36,27 @@ func GetBranchesByPath(path string) ([]*Branch, error) {
|
|||||||
return branches, nil
|
return branches, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (repo *Repository) GetBranch(br string) (*Branch, error) {
|
// GetBranch returns a branch by it's name
|
||||||
if !git.IsBranchExist(repo.RepoPath(), br) {
|
func (repo *Repository) GetBranch(branch string) (*Branch, error) {
|
||||||
return nil, &ErrBranchNotExist{br}
|
if !git.IsBranchExist(repo.RepoPath(), branch) {
|
||||||
|
return nil, &ErrBranchNotExist{branch}
|
||||||
}
|
}
|
||||||
return &Branch{
|
return &Branch{
|
||||||
Path: repo.RepoPath(),
|
Path: repo.RepoPath(),
|
||||||
Name: br,
|
Name: branch,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetBranches returns all the branches of a repository
|
||||||
func (repo *Repository) GetBranches() ([]*Branch, error) {
|
func (repo *Repository) GetBranches() ([]*Branch, error) {
|
||||||
return GetBranchesByPath(repo.RepoPath())
|
return GetBranchesByPath(repo.RepoPath())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (br *Branch) GetCommit() (*git.Commit, error) {
|
// GetCommit returns all the commits of a branch
|
||||||
gitRepo, err := git.OpenRepository(br.Path)
|
func (branch *Branch) GetCommit() (*git.Commit, error) {
|
||||||
|
gitRepo, err := git.OpenRepository(branch.Path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return gitRepo.GetBranchCommit(br.Name)
|
return gitRepo.GetBranchCommit(branch.Name)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,13 +16,14 @@ type Collaboration struct {
|
|||||||
Mode AccessMode `xorm:"DEFAULT 2 NOT NULL"`
|
Mode AccessMode `xorm:"DEFAULT 2 NOT NULL"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ModeI18nKey returns the collaboration mode I18n Key
|
||||||
func (c *Collaboration) ModeI18nKey() string {
|
func (c *Collaboration) ModeI18nKey() string {
|
||||||
switch c.Mode {
|
switch c.Mode {
|
||||||
case ACCESS_MODE_READ:
|
case AccessModeRead:
|
||||||
return "repo.settings.collaboration.read"
|
return "repo.settings.collaboration.read"
|
||||||
case ACCESS_MODE_WRITE:
|
case AccessModeWrite:
|
||||||
return "repo.settings.collaboration.write"
|
return "repo.settings.collaboration.write"
|
||||||
case ACCESS_MODE_ADMIN:
|
case AccessModeAdmin:
|
||||||
return "repo.settings.collaboration.admin"
|
return "repo.settings.collaboration.admin"
|
||||||
default:
|
default:
|
||||||
return "repo.settings.collaboration.undefined"
|
return "repo.settings.collaboration.undefined"
|
||||||
@@ -42,7 +43,7 @@ func (repo *Repository) AddCollaborator(u *User) error {
|
|||||||
} else if has {
|
} else if has {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
collaboration.Mode = ACCESS_MODE_WRITE
|
collaboration.Mode = AccessModeWrite
|
||||||
|
|
||||||
sess := x.NewSession()
|
sess := x.NewSession()
|
||||||
defer sessionRelease(sess)
|
defer sessionRelease(sess)
|
||||||
@@ -67,7 +68,7 @@ func (repo *Repository) AddCollaborator(u *User) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (repo *Repository) getCollaborations(e Engine) ([]*Collaboration, 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})
|
return collaborations, e.Find(&collaborations, &Collaboration{RepoID: repo.ID})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,7 +106,7 @@ func (repo *Repository) GetCollaborators() ([]*Collaborator, error) {
|
|||||||
// ChangeCollaborationAccessMode sets new access mode for the collaboration.
|
// ChangeCollaborationAccessMode sets new access mode for the collaboration.
|
||||||
func (repo *Repository) ChangeCollaborationAccessMode(uid int64, mode AccessMode) error {
|
func (repo *Repository) ChangeCollaborationAccessMode(uid int64, mode AccessMode) error {
|
||||||
// Discard invalid input
|
// Discard invalid input
|
||||||
if mode <= ACCESS_MODE_NONE || mode > ACCESS_MODE_OWNER {
|
if mode <= AccessModeNone || mode > AccessModeOwner {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,7 +132,10 @@ func (repo *Repository) ChangeCollaborationAccessMode(uid int64, mode AccessMode
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err = sess.Id(collaboration.ID).AllCols().Update(collaboration); err != nil {
|
if _, err = sess.
|
||||||
|
Id(collaboration.ID).
|
||||||
|
AllCols().
|
||||||
|
Update(collaboration); err != nil {
|
||||||
return fmt.Errorf("update collaboration: %v", err)
|
return fmt.Errorf("update collaboration: %v", err)
|
||||||
} else if _, err = sess.Exec("UPDATE access SET mode = ? WHERE user_id = ? AND repo_id = ?", mode, uid, repo.ID); err != nil {
|
} else if _, err = sess.Exec("UPDATE access SET mode = ? WHERE user_id = ? AND repo_id = ?", mode, uid, repo.ID); err != nil {
|
||||||
return fmt.Errorf("update access table: %v", err)
|
return fmt.Errorf("update access table: %v", err)
|
||||||
|
|||||||
@@ -18,11 +18,11 @@ import (
|
|||||||
"github.com/Unknwon/com"
|
"github.com/Unknwon/com"
|
||||||
gouuid "github.com/satori/go.uuid"
|
gouuid "github.com/satori/go.uuid"
|
||||||
|
|
||||||
git "github.com/gogits/git-module"
|
"code.gitea.io/git"
|
||||||
|
|
||||||
"github.com/gogits/gogs/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"github.com/gogits/gogs/modules/process"
|
"code.gitea.io/gitea/modules/process"
|
||||||
"github.com/gogits/gogs/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ___________ .___.__ __ ___________.__.__
|
// ___________ .___.__ __ ___________.__.__
|
||||||
@@ -50,6 +50,7 @@ func discardLocalRepoBranchChanges(localPath, branch string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DiscardLocalRepoBranchChanges discards the local repository branch changes
|
||||||
func (repo *Repository) DiscardLocalRepoBranchChanges(branch string) error {
|
func (repo *Repository) DiscardLocalRepoBranchChanges(branch string) error {
|
||||||
return discardLocalRepoBranchChanges(repo.LocalCopyPath(), branch)
|
return discardLocalRepoBranchChanges(repo.LocalCopyPath(), branch)
|
||||||
}
|
}
|
||||||
@@ -66,10 +67,12 @@ func checkoutNewBranch(repoPath, localPath, oldBranch, newBranch string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CheckoutNewBranch checks out a new branch
|
||||||
func (repo *Repository) CheckoutNewBranch(oldBranch, newBranch string) error {
|
func (repo *Repository) CheckoutNewBranch(oldBranch, newBranch string) error {
|
||||||
return checkoutNewBranch(repo.RepoPath(), repo.LocalCopyPath(), oldBranch, newBranch)
|
return checkoutNewBranch(repo.RepoPath(), repo.LocalCopyPath(), oldBranch, newBranch)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpdateRepoFileOptions holds the repository file update options
|
||||||
type UpdateRepoFileOptions struct {
|
type UpdateRepoFileOptions struct {
|
||||||
LastCommitID string
|
LastCommitID string
|
||||||
OldBranch string
|
OldBranch string
|
||||||
@@ -101,7 +104,11 @@ func (repo *Repository) UpdateRepoFile(doer *User, opts UpdateRepoFileOptions) (
|
|||||||
localPath := repo.LocalCopyPath()
|
localPath := repo.LocalCopyPath()
|
||||||
oldFilePath := path.Join(localPath, opts.OldTreeName)
|
oldFilePath := path.Join(localPath, opts.OldTreeName)
|
||||||
filePath := path.Join(localPath, opts.NewTreeName)
|
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 it's meant to be a new file, make sure it doesn't exist.
|
||||||
if opts.IsNewFile {
|
if opts.IsNewFile {
|
||||||
@@ -151,13 +158,13 @@ func (repo *Repository) UpdateRepoFile(doer *User, opts UpdateRepoFileOptions) (
|
|||||||
}
|
}
|
||||||
oldCommitID := opts.LastCommitID
|
oldCommitID := opts.LastCommitID
|
||||||
if opts.NewBranch != opts.OldBranch {
|
if opts.NewBranch != opts.OldBranch {
|
||||||
oldCommitID = git.EMPTY_SHA
|
oldCommitID = git.EmptySHA
|
||||||
}
|
}
|
||||||
if err := CommitRepoAction(CommitRepoActionOptions{
|
if err := CommitRepoAction(CommitRepoActionOptions{
|
||||||
PusherName: doer.Name,
|
PusherName: doer.Name,
|
||||||
RepoOwnerID: repo.MustOwner().ID,
|
RepoOwnerID: repo.MustOwner().ID,
|
||||||
RepoName: repo.Name,
|
RepoName: repo.Name,
|
||||||
RefFullName: git.BRANCH_PREFIX + opts.NewBranch,
|
RefFullName: git.BranchPrefix + opts.NewBranch,
|
||||||
OldCommitID: oldCommitID,
|
OldCommitID: oldCommitID,
|
||||||
NewCommitID: commit.ID.String(),
|
NewCommitID: commit.ID.String(),
|
||||||
Commits: pushCommits,
|
Commits: pushCommits,
|
||||||
@@ -182,7 +189,12 @@ func (repo *Repository) GetDiffPreview(branch, treePath, content string) (diff *
|
|||||||
|
|
||||||
localPath := repo.LocalCopyPath()
|
localPath := repo.LocalCopyPath()
|
||||||
filePath := path.Join(localPath, treePath)
|
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 {
|
if err = ioutil.WriteFile(filePath, []byte(content), 0666); err != nil {
|
||||||
return nil, fmt.Errorf("WriteFile: %v", err)
|
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 {
|
type DeleteRepoFileOptions struct {
|
||||||
LastCommitID string
|
LastCommitID string
|
||||||
OldBranch string
|
OldBranch string
|
||||||
@@ -231,6 +244,7 @@ type DeleteRepoFileOptions struct {
|
|||||||
Message string
|
Message string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeleteRepoFile deletes a repository file
|
||||||
func (repo *Repository) DeleteRepoFile(doer *User, opts DeleteRepoFileOptions) (err error) {
|
func (repo *Repository) DeleteRepoFile(doer *User, opts DeleteRepoFileOptions) (err error) {
|
||||||
repoWorkingPool.CheckIn(com.ToStr(repo.ID))
|
repoWorkingPool.CheckIn(com.ToStr(repo.ID))
|
||||||
defer repoWorkingPool.CheckOut(com.ToStr(repo.ID))
|
defer repoWorkingPool.CheckOut(com.ToStr(repo.ID))
|
||||||
@@ -283,7 +297,7 @@ func (repo *Repository) DeleteRepoFile(doer *User, opts DeleteRepoFileOptions) (
|
|||||||
PusherName: doer.Name,
|
PusherName: doer.Name,
|
||||||
RepoOwnerID: repo.MustOwner().ID,
|
RepoOwnerID: repo.MustOwner().ID,
|
||||||
RepoName: repo.Name,
|
RepoName: repo.Name,
|
||||||
RefFullName: git.BRANCH_PREFIX + opts.NewBranch,
|
RefFullName: git.BranchPrefix + opts.NewBranch,
|
||||||
OldCommitID: opts.LastCommitID,
|
OldCommitID: opts.LastCommitID,
|
||||||
NewCommitID: commit.ID.String(),
|
NewCommitID: commit.ID.String(),
|
||||||
Commits: pushCommits,
|
Commits: pushCommits,
|
||||||
@@ -351,6 +365,7 @@ func NewUpload(name string, buf []byte, file multipart.File) (_ *Upload, err err
|
|||||||
return upload, nil
|
return upload, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetUploadByUUID returns the Upload by UUID
|
||||||
func GetUploadByUUID(uuid string) (*Upload, error) {
|
func GetUploadByUUID(uuid string) (*Upload, error) {
|
||||||
upload := &Upload{UUID: uuid}
|
upload := &Upload{UUID: uuid}
|
||||||
has, err := x.Get(upload)
|
has, err := x.Get(upload)
|
||||||
@@ -362,6 +377,7 @@ func GetUploadByUUID(uuid string) (*Upload, error) {
|
|||||||
return upload, nil
|
return upload, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetUploadsByUUIDs returns multiple uploads by UUIDS
|
||||||
func GetUploadsByUUIDs(uuids []string) ([]*Upload, error) {
|
func GetUploadsByUUIDs(uuids []string) ([]*Upload, error) {
|
||||||
if len(uuids) == 0 {
|
if len(uuids) == 0 {
|
||||||
return []*Upload{}, nil
|
return []*Upload{}, nil
|
||||||
@@ -372,6 +388,7 @@ func GetUploadsByUUIDs(uuids []string) ([]*Upload, error) {
|
|||||||
return uploads, x.In("uuid", uuids).Find(&uploads)
|
return uploads, x.In("uuid", uuids).Find(&uploads)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeleteUploads deletes multiple uploads
|
||||||
func DeleteUploads(uploads ...*Upload) (err error) {
|
func DeleteUploads(uploads ...*Upload) (err error) {
|
||||||
if len(uploads) == 0 {
|
if len(uploads) == 0 {
|
||||||
return nil
|
return nil
|
||||||
@@ -387,7 +404,9 @@ func DeleteUploads(uploads ...*Upload) (err error) {
|
|||||||
for i := 0; i < len(uploads); i++ {
|
for i := 0; i < len(uploads); i++ {
|
||||||
ids[i] = uploads[i].ID
|
ids[i] = uploads[i].ID
|
||||||
}
|
}
|
||||||
if _, err = sess.In("id", ids).Delete(new(Upload)); err != nil {
|
if _, err = sess.
|
||||||
|
In("id", ids).
|
||||||
|
Delete(new(Upload)); err != nil {
|
||||||
return fmt.Errorf("delete uploads: %v", err)
|
return fmt.Errorf("delete uploads: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -405,10 +424,12 @@ func DeleteUploads(uploads ...*Upload) (err error) {
|
|||||||
return sess.Commit()
|
return sess.Commit()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeleteUpload delete a upload
|
||||||
func DeleteUpload(u *Upload) error {
|
func DeleteUpload(u *Upload) error {
|
||||||
return DeleteUploads(u)
|
return DeleteUploads(u)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeleteUploadByUUID deletes a upload by UUID
|
||||||
func DeleteUploadByUUID(uuid string) error {
|
func DeleteUploadByUUID(uuid string) error {
|
||||||
upload, err := GetUploadByUUID(uuid)
|
upload, err := GetUploadByUUID(uuid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -425,6 +446,7 @@ func DeleteUploadByUUID(uuid string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UploadRepoFileOptions contains the uploaded repository file options
|
||||||
type UploadRepoFileOptions struct {
|
type UploadRepoFileOptions struct {
|
||||||
LastCommitID string
|
LastCommitID string
|
||||||
OldBranch string
|
OldBranch string
|
||||||
@@ -434,6 +456,7 @@ type UploadRepoFileOptions struct {
|
|||||||
Files []string // In UUID format.
|
Files []string // In UUID format.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UploadRepoFiles uploads files to a repository
|
||||||
func (repo *Repository) UploadRepoFiles(doer *User, opts UploadRepoFileOptions) (err error) {
|
func (repo *Repository) UploadRepoFiles(doer *User, opts UploadRepoFileOptions) (err error) {
|
||||||
if len(opts.Files) == 0 {
|
if len(opts.Files) == 0 {
|
||||||
return nil
|
return nil
|
||||||
@@ -461,7 +484,10 @@ func (repo *Repository) UploadRepoFiles(doer *User, opts UploadRepoFileOptions)
|
|||||||
|
|
||||||
localPath := repo.LocalCopyPath()
|
localPath := repo.LocalCopyPath()
|
||||||
dirPath := path.Join(localPath, opts.TreePath)
|
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.
|
// Copy uploaded files into repository.
|
||||||
for _, upload := range uploads {
|
for _, upload := range uploads {
|
||||||
@@ -507,7 +533,7 @@ func (repo *Repository) UploadRepoFiles(doer *User, opts UploadRepoFileOptions)
|
|||||||
PusherName: doer.Name,
|
PusherName: doer.Name,
|
||||||
RepoOwnerID: repo.MustOwner().ID,
|
RepoOwnerID: repo.MustOwner().ID,
|
||||||
RepoName: repo.Name,
|
RepoName: repo.Name,
|
||||||
RefFullName: git.BRANCH_PREFIX + opts.NewBranch,
|
RefFullName: git.BranchPrefix + opts.NewBranch,
|
||||||
OldCommitID: opts.LastCommitID,
|
OldCommitID: opts.LastCommitID,
|
||||||
NewCommitID: commit.ID.String(),
|
NewCommitID: commit.ID.String(),
|
||||||
Commits: pushCommits,
|
Commits: pushCommits,
|
||||||
|
|||||||
@@ -13,12 +13,13 @@ import (
|
|||||||
"github.com/go-xorm/xorm"
|
"github.com/go-xorm/xorm"
|
||||||
"gopkg.in/ini.v1"
|
"gopkg.in/ini.v1"
|
||||||
|
|
||||||
"github.com/gogits/gogs/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"github.com/gogits/gogs/modules/process"
|
"code.gitea.io/gitea/modules/process"
|
||||||
"github.com/gogits/gogs/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"github.com/gogits/gogs/modules/sync"
|
"code.gitea.io/gitea/modules/sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// MirrorQueue holds an UniqueQueue object of the mirror
|
||||||
var MirrorQueue = sync.NewUniqueQueue(setting.Repository.MirrorQueueLength)
|
var MirrorQueue = sync.NewUniqueQueue(setting.Repository.MirrorQueueLength)
|
||||||
|
|
||||||
// Mirror represents mirror information of a repository.
|
// Mirror represents mirror information of a repository.
|
||||||
@@ -37,16 +38,19 @@ type Mirror struct {
|
|||||||
address string `xorm:"-"`
|
address string `xorm:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BeforeInsert will be invoked by XORM before inserting a record
|
||||||
func (m *Mirror) BeforeInsert() {
|
func (m *Mirror) BeforeInsert() {
|
||||||
m.UpdatedUnix = time.Now().Unix()
|
m.UpdatedUnix = time.Now().Unix()
|
||||||
m.NextUpdateUnix = m.NextUpdate.Unix()
|
m.NextUpdateUnix = m.NextUpdate.Unix()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BeforeUpdate is invoked from XORM before updating this object.
|
||||||
func (m *Mirror) BeforeUpdate() {
|
func (m *Mirror) BeforeUpdate() {
|
||||||
m.UpdatedUnix = time.Now().Unix()
|
m.UpdatedUnix = time.Now().Unix()
|
||||||
m.NextUpdateUnix = m.NextUpdate.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) {
|
func (m *Mirror) AfterSet(colName string, _ xorm.Cell) {
|
||||||
var err error
|
var err error
|
||||||
switch colName {
|
switch colName {
|
||||||
@@ -180,10 +184,12 @@ func updateMirror(e Engine, m *Mirror) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpdateMirror updates the mirror
|
||||||
func UpdateMirror(m *Mirror) error {
|
func UpdateMirror(m *Mirror) error {
|
||||||
return updateMirror(x, m)
|
return updateMirror(x, m)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeleteMirrorByRepoID deletes a mirror by repoID
|
||||||
func DeleteMirrorByRepoID(repoID int64) error {
|
func DeleteMirrorByRepoID(repoID int64) error {
|
||||||
_, err := x.Delete(&Mirror{RepoID: repoID})
|
_, err := x.Delete(&Mirror{RepoID: repoID})
|
||||||
return err
|
return err
|
||||||
@@ -191,24 +197,26 @@ func DeleteMirrorByRepoID(repoID int64) error {
|
|||||||
|
|
||||||
// MirrorUpdate checks and updates mirror repositories.
|
// MirrorUpdate checks and updates mirror repositories.
|
||||||
func MirrorUpdate() {
|
func MirrorUpdate() {
|
||||||
if taskStatusTable.IsRunning(_MIRROR_UPDATE) {
|
if taskStatusTable.IsRunning(mirrorUpdate) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
taskStatusTable.Start(_MIRROR_UPDATE)
|
taskStatusTable.Start(mirrorUpdate)
|
||||||
defer taskStatusTable.Stop(_MIRROR_UPDATE)
|
defer taskStatusTable.Stop(mirrorUpdate)
|
||||||
|
|
||||||
log.Trace("Doing: MirrorUpdate")
|
log.Trace("Doing: MirrorUpdate")
|
||||||
|
|
||||||
if err := x.Where("next_update_unix<=?", time.Now().Unix()).Iterate(new(Mirror), func(idx int, bean interface{}) error {
|
if err := x.
|
||||||
m := bean.(*Mirror)
|
Where("next_update_unix<=?", time.Now().Unix()).
|
||||||
if m.Repo == nil {
|
Iterate(new(Mirror), func(idx int, bean interface{}) error {
|
||||||
log.Error(4, "Disconnected mirror repository found: %d", m.ID)
|
m := bean.(*Mirror)
|
||||||
return nil
|
if m.Repo == nil {
|
||||||
}
|
log.Error(4, "Disconnected mirror repository found: %d", m.ID)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
MirrorQueue.Add(m.RepoID)
|
MirrorQueue.Add(m.RepoID)
|
||||||
return nil
|
return nil
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
log.Error(4, "MirrorUpdate: %v", err)
|
log.Error(4, "MirrorUpdate: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -223,7 +231,7 @@ func SyncMirrors() {
|
|||||||
|
|
||||||
m, err := GetMirrorByRepoID(com.StrTo(repoID).MustInt64())
|
m, err := GetMirrorByRepoID(com.StrTo(repoID).MustInt64())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(4, "GetMirrorByRepoID [%d]: %v", repoID, err)
|
log.Error(4, "GetMirrorByRepoID [%s]: %v", repoID, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -233,12 +241,13 @@ func SyncMirrors() {
|
|||||||
|
|
||||||
m.ScheduleNextUpdate()
|
m.ScheduleNextUpdate()
|
||||||
if err = UpdateMirror(m); err != nil {
|
if err = UpdateMirror(m); err != nil {
|
||||||
log.Error(4, "UpdateMirror [%d]: %v", repoID, err)
|
log.Error(4, "UpdateMirror [%s]: %v", repoID, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// InitSyncMirrors initializes a go routine to sync the mirros
|
||||||
func InitSyncMirrors() {
|
func InitSyncMirrors() {
|
||||||
go SyncMirrors()
|
go SyncMirrors()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
package models_test
|
package models_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
. "github.com/gogits/gogs/models"
|
|
||||||
. "github.com/smartystreets/goconvey/convey"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/gogits/gogs/modules/markdown"
|
. "code.gitea.io/gitea/models"
|
||||||
|
"code.gitea.io/gitea/modules/markdown"
|
||||||
|
. "github.com/smartystreets/goconvey/convey"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestRepo(t *testing.T) {
|
func TestRepo(t *testing.T) {
|
||||||
@@ -24,7 +24,7 @@ func TestRepo(t *testing.T) {
|
|||||||
Convey("It should be nil even if other settings are present", func() {
|
Convey("It should be nil even if other settings are present", func() {
|
||||||
repo.EnableExternalTracker = false
|
repo.EnableExternalTracker = false
|
||||||
repo.ExternalTrackerFormat = "http://someurl.com/{user}/{repo}/{issue}"
|
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))
|
So(repo.ComposeMetas(), ShouldEqual, map[string]string(nil))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -33,17 +33,17 @@ func TestRepo(t *testing.T) {
|
|||||||
repo.EnableExternalTracker = true
|
repo.EnableExternalTracker = true
|
||||||
Convey("It should default to numeric issue style", func() {
|
Convey("It should default to numeric issue style", func() {
|
||||||
metas := repo.ComposeMetas()
|
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() {
|
Convey("It should pass through numeric issue style setting", func() {
|
||||||
repo.ExternalTrackerStyle = markdown.ISSUE_NAME_STYLE_NUMERIC
|
repo.ExternalTrackerStyle = markdown.IssueNameStyleNumeric
|
||||||
metas := repo.ComposeMetas()
|
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() {
|
Convey("It should pass through alphanumeric issue style setting", func() {
|
||||||
repo.ExternalTrackerStyle = markdown.ISSUE_NAME_STYLE_ALPHANUMERIC
|
repo.ExternalTrackerStyle = markdown.IssueNameStyleAlphanumeric
|
||||||
metas := repo.ComposeMetas()
|
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() {
|
Convey("It should contain the user name", func() {
|
||||||
metas := repo.ComposeMetas()
|
metas := repo.ComposeMetas()
|
||||||
|
|||||||
@@ -22,23 +22,25 @@ import (
|
|||||||
"github.com/go-xorm/xorm"
|
"github.com/go-xorm/xorm"
|
||||||
"golang.org/x/crypto/ssh"
|
"golang.org/x/crypto/ssh"
|
||||||
|
|
||||||
"github.com/gogits/gogs/modules/base"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"github.com/gogits/gogs/modules/log"
|
"code.gitea.io/gitea/modules/process"
|
||||||
"github.com/gogits/gogs/modules/process"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"github.com/gogits/gogs/modules/setting"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
_TPL_PUBLICK_KEY = `command="%s serv key-%d --config='%s'",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty %s` + "\n"
|
tplPublicKey = `command="%s serv key-%d --config='%s'",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty %s` + "\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
var sshOpLocker sync.Mutex
|
var sshOpLocker sync.Mutex
|
||||||
|
|
||||||
|
// KeyType specifies the key type
|
||||||
type KeyType int
|
type KeyType int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
KEY_TYPE_USER = iota + 1
|
// KeyTypeUser specifies the user key
|
||||||
KEY_TYPE_DEPLOY
|
KeyTypeUser = iota + 1
|
||||||
|
// KeyTypeDeploy specifies the deploy key
|
||||||
|
KeyTypeDeploy
|
||||||
)
|
)
|
||||||
|
|
||||||
// PublicKey represents a user or deploy SSH public key.
|
// PublicKey represents a user or deploy SSH public key.
|
||||||
@@ -59,33 +61,36 @@ type PublicKey struct {
|
|||||||
HasUsed bool `xorm:"-"`
|
HasUsed bool `xorm:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *PublicKey) BeforeInsert() {
|
// BeforeInsert will be invoked by XORM before inserting a record
|
||||||
k.CreatedUnix = time.Now().Unix()
|
func (key *PublicKey) BeforeInsert() {
|
||||||
|
key.CreatedUnix = time.Now().Unix()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *PublicKey) BeforeUpdate() {
|
// BeforeUpdate is invoked from XORM before updating this object.
|
||||||
k.UpdatedUnix = time.Now().Unix()
|
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 {
|
switch colName {
|
||||||
case "created_unix":
|
case "created_unix":
|
||||||
k.Created = time.Unix(k.CreatedUnix, 0).Local()
|
key.Created = time.Unix(key.CreatedUnix, 0).Local()
|
||||||
case "updated_unix":
|
case "updated_unix":
|
||||||
k.Updated = time.Unix(k.UpdatedUnix, 0).Local()
|
key.Updated = time.Unix(key.UpdatedUnix, 0).Local()
|
||||||
k.HasUsed = k.Updated.After(k.Created)
|
key.HasUsed = key.Updated.After(key.Created)
|
||||||
k.HasRecentActivity = k.Updated.Add(7 * 24 * time.Hour).After(time.Now())
|
key.HasRecentActivity = key.Updated.Add(7 * 24 * time.Hour).After(time.Now())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// OmitEmail returns content of public key without email address.
|
// OmitEmail returns content of public key without email address.
|
||||||
func (k *PublicKey) OmitEmail() string {
|
func (key *PublicKey) OmitEmail() string {
|
||||||
return strings.Join(strings.Split(k.Content, " ")[:2], " ")
|
return strings.Join(strings.Split(key.Content, " ")[:2], " ")
|
||||||
}
|
}
|
||||||
|
|
||||||
// AuthorizedString returns formatted public key string for authorized_keys file.
|
// AuthorizedString returns formatted public key string for authorized_keys file.
|
||||||
func (key *PublicKey) AuthorizedString() string {
|
func (key *PublicKey) AuthorizedString() string {
|
||||||
return fmt.Sprintf(_TPL_PUBLICK_KEY, setting.AppPath, key.ID, setting.CustomConf, key.Content)
|
return fmt.Sprintf(tplPublicKey, setting.AppPath, key.ID, setting.CustomConf, key.Content)
|
||||||
}
|
}
|
||||||
|
|
||||||
func extractTypeFromBase64Key(key string) (string, error) {
|
func extractTypeFromBase64Key(key string) (string, error) {
|
||||||
@@ -106,6 +111,8 @@ func extractTypeFromBase64Key(key string) (string, error) {
|
|||||||
func parseKeyString(content string) (string, error) {
|
func parseKeyString(content string) (string, error) {
|
||||||
// Transform all legal line endings to a single "\n".
|
// Transform all legal line endings to a single "\n".
|
||||||
content = strings.NewReplacer("\r\n", "\n", "\r", "\n").Replace(content)
|
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")
|
lines := strings.Split(content, "\n")
|
||||||
|
|
||||||
var keyType, keyContent, keyComment string
|
var keyType, keyContent, keyComment string
|
||||||
@@ -352,7 +359,7 @@ func appendAuthorizedKeysToFile(keys ...*PublicKey) error {
|
|||||||
func checkKeyContent(content string) error {
|
func checkKeyContent(content string) error {
|
||||||
has, err := x.Get(&PublicKey{
|
has, err := x.Get(&PublicKey{
|
||||||
Content: content,
|
Content: content,
|
||||||
Type: KEY_TYPE_USER,
|
Type: KeyTypeUser,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -366,7 +373,12 @@ func addKey(e Engine, key *PublicKey) (err error) {
|
|||||||
// Calculate fingerprint.
|
// Calculate fingerprint.
|
||||||
tmpPath := strings.Replace(path.Join(os.TempDir(), fmt.Sprintf("%d", time.Now().Nanosecond()),
|
tmpPath := strings.Replace(path.Join(os.TempDir(), fmt.Sprintf("%d", time.Now().Nanosecond()),
|
||||||
"id_rsa.pub"), "\\", "/", -1)
|
"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 {
|
if err = ioutil.WriteFile(tmpPath, []byte(key.Content), 0644); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -398,7 +410,9 @@ func AddPublicKey(ownerID int64, name, content string) (*PublicKey, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Key name of same user cannot be duplicated.
|
// Key name of same user cannot be duplicated.
|
||||||
has, err := x.Where("owner_id = ? AND name = ?", ownerID, name).Get(new(PublicKey))
|
has, err := x.
|
||||||
|
Where("owner_id = ? AND name = ?", ownerID, name).
|
||||||
|
Get(new(PublicKey))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if has {
|
} else if has {
|
||||||
@@ -415,8 +429,8 @@ func AddPublicKey(ownerID int64, name, content string) (*PublicKey, error) {
|
|||||||
OwnerID: ownerID,
|
OwnerID: ownerID,
|
||||||
Name: name,
|
Name: name,
|
||||||
Content: content,
|
Content: content,
|
||||||
Mode: ACCESS_MODE_WRITE,
|
Mode: AccessModeWrite,
|
||||||
Type: KEY_TYPE_USER,
|
Type: KeyTypeUser,
|
||||||
}
|
}
|
||||||
if err = addKey(sess, key); err != nil {
|
if err = addKey(sess, key); err != nil {
|
||||||
return nil, fmt.Errorf("addKey: %v", err)
|
return nil, fmt.Errorf("addKey: %v", err)
|
||||||
@@ -428,7 +442,9 @@ func AddPublicKey(ownerID int64, name, content string) (*PublicKey, error) {
|
|||||||
// GetPublicKeyByID returns public key by given ID.
|
// GetPublicKeyByID returns public key by given ID.
|
||||||
func GetPublicKeyByID(keyID int64) (*PublicKey, error) {
|
func GetPublicKeyByID(keyID int64) (*PublicKey, error) {
|
||||||
key := new(PublicKey)
|
key := new(PublicKey)
|
||||||
has, err := x.Id(keyID).Get(key)
|
has, err := x.
|
||||||
|
Id(keyID).
|
||||||
|
Get(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if !has {
|
} else if !has {
|
||||||
@@ -441,7 +457,9 @@ func GetPublicKeyByID(keyID int64) (*PublicKey, error) {
|
|||||||
// and returns public key found.
|
// and returns public key found.
|
||||||
func SearchPublicKeyByContent(content string) (*PublicKey, error) {
|
func SearchPublicKeyByContent(content string) (*PublicKey, error) {
|
||||||
key := new(PublicKey)
|
key := new(PublicKey)
|
||||||
has, err := x.Where("content like ?", content+"%").Get(key)
|
has, err := x.
|
||||||
|
Where("content like ?", content+"%").
|
||||||
|
Get(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if !has {
|
} else if !has {
|
||||||
@@ -453,7 +471,9 @@ func SearchPublicKeyByContent(content string) (*PublicKey, error) {
|
|||||||
// ListPublicKeys returns a list of public keys belongs to given user.
|
// ListPublicKeys returns a list of public keys belongs to given user.
|
||||||
func ListPublicKeys(uid int64) ([]*PublicKey, error) {
|
func ListPublicKeys(uid int64) ([]*PublicKey, error) {
|
||||||
keys := make([]*PublicKey, 0, 5)
|
keys := make([]*PublicKey, 0, 5)
|
||||||
return keys, x.Where("owner_id = ?", uid).Find(&keys)
|
return keys, x.
|
||||||
|
Where("owner_id = ?", uid).
|
||||||
|
Find(&keys)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdatePublicKey updates given public key.
|
// UpdatePublicKey updates given public key.
|
||||||
@@ -468,7 +488,7 @@ func deletePublicKeys(e *xorm.Session, keyIDs ...int64) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := e.In("id", strings.Join(base.Int64sToStrings(keyIDs), ",")).Delete(new(PublicKey))
|
_, err := e.In("id", keyIDs).Delete(new(PublicKey))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -564,45 +584,52 @@ type DeployKey struct {
|
|||||||
HasUsed bool `xorm:"-"`
|
HasUsed bool `xorm:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *DeployKey) BeforeInsert() {
|
// BeforeInsert will be invoked by XORM before inserting a record
|
||||||
k.CreatedUnix = time.Now().Unix()
|
func (key *DeployKey) BeforeInsert() {
|
||||||
|
key.CreatedUnix = time.Now().Unix()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *DeployKey) BeforeUpdate() {
|
// BeforeUpdate is invoked from XORM before updating this object.
|
||||||
k.UpdatedUnix = time.Now().Unix()
|
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 {
|
switch colName {
|
||||||
case "created_unix":
|
case "created_unix":
|
||||||
k.Created = time.Unix(k.CreatedUnix, 0).Local()
|
key.Created = time.Unix(key.CreatedUnix, 0).Local()
|
||||||
case "updated_unix":
|
case "updated_unix":
|
||||||
k.Updated = time.Unix(k.UpdatedUnix, 0).Local()
|
key.Updated = time.Unix(key.UpdatedUnix, 0).Local()
|
||||||
k.HasUsed = k.Updated.After(k.Created)
|
key.HasUsed = key.Updated.After(key.Created)
|
||||||
k.HasRecentActivity = k.Updated.Add(7 * 24 * time.Hour).After(time.Now())
|
key.HasRecentActivity = key.Updated.Add(7 * 24 * time.Hour).After(time.Now())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetContent gets associated public key content.
|
// GetContent gets associated public key content.
|
||||||
func (k *DeployKey) GetContent() error {
|
func (key *DeployKey) GetContent() error {
|
||||||
pkey, err := GetPublicKeyByID(k.KeyID)
|
pkey, err := GetPublicKeyByID(key.KeyID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
k.Content = pkey.Content
|
key.Content = pkey.Content
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkDeployKey(e Engine, keyID, repoID int64, name string) error {
|
func checkDeployKey(e Engine, keyID, repoID int64, name string) error {
|
||||||
// Note: We want error detail, not just true or false here.
|
// Note: We want error detail, not just true or false here.
|
||||||
has, err := e.Where("key_id = ? AND repo_id = ?", keyID, repoID).Get(new(DeployKey))
|
has, err := e.
|
||||||
|
Where("key_id = ? AND repo_id = ?", keyID, repoID).
|
||||||
|
Get(new(DeployKey))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
} else if has {
|
} else if has {
|
||||||
return ErrDeployKeyAlreadyExist{keyID, repoID}
|
return ErrDeployKeyAlreadyExist{keyID, repoID}
|
||||||
}
|
}
|
||||||
|
|
||||||
has, err = e.Where("repo_id = ? AND name = ?", repoID, name).Get(new(DeployKey))
|
has, err = e.
|
||||||
|
Where("repo_id = ? AND name = ?", repoID, name).
|
||||||
|
Get(new(DeployKey))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
} else if has {
|
} else if has {
|
||||||
@@ -630,7 +657,9 @@ func addDeployKey(e *xorm.Session, keyID, repoID int64, name, fingerprint string
|
|||||||
|
|
||||||
// HasDeployKey returns true if public key is a deploy key of given repository.
|
// HasDeployKey returns true if public key is a deploy key of given repository.
|
||||||
func HasDeployKey(keyID, repoID int64) bool {
|
func HasDeployKey(keyID, repoID int64) bool {
|
||||||
has, _ := x.Where("key_id = ? AND repo_id = ?", keyID, repoID).Get(new(DeployKey))
|
has, _ := x.
|
||||||
|
Where("key_id = ? AND repo_id = ?", keyID, repoID).
|
||||||
|
Get(new(DeployKey))
|
||||||
return has
|
return has
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -642,8 +671,8 @@ func AddDeployKey(repoID int64, name, content string) (*DeployKey, error) {
|
|||||||
|
|
||||||
pkey := &PublicKey{
|
pkey := &PublicKey{
|
||||||
Content: content,
|
Content: content,
|
||||||
Mode: ACCESS_MODE_READ,
|
Mode: AccessModeRead,
|
||||||
Type: KEY_TYPE_DEPLOY,
|
Type: KeyTypeDeploy,
|
||||||
}
|
}
|
||||||
has, err := x.Get(pkey)
|
has, err := x.Get(pkey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -720,7 +749,7 @@ func DeleteDeployKey(doer *User, id int64) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("GetRepositoryByID: %v", err)
|
return fmt.Errorf("GetRepositoryByID: %v", err)
|
||||||
}
|
}
|
||||||
yes, err := HasAccess(doer, repo, ACCESS_MODE_ADMIN)
|
yes, err := HasAccess(doer, repo, AccessModeAdmin)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("HasAccess: %v", err)
|
return fmt.Errorf("HasAccess: %v", err)
|
||||||
} else if !yes {
|
} else if !yes {
|
||||||
@@ -739,7 +768,9 @@ func DeleteDeployKey(doer *User, id int64) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if this is the last reference to same key content.
|
// Check if this is the last reference to same key content.
|
||||||
has, err := sess.Where("key_id = ?", key.KeyID).Get(new(DeployKey))
|
has, err := sess.
|
||||||
|
Where("key_id = ?", key.KeyID).
|
||||||
|
Get(new(DeployKey))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
} else if !has {
|
} else if !has {
|
||||||
@@ -754,5 +785,7 @@ func DeleteDeployKey(doer *User, id int64) error {
|
|||||||
// ListDeployKeys returns all deploy keys by given repository ID.
|
// ListDeployKeys returns all deploy keys by given repository ID.
|
||||||
func ListDeployKeys(repoID int64) ([]*DeployKey, error) {
|
func ListDeployKeys(repoID int64) ([]*DeployKey, error) {
|
||||||
keys := make([]*DeployKey, 0, 5)
|
keys := make([]*DeployKey, 0, 5)
|
||||||
return keys, x.Where("repo_id = ?", repoID).Find(&keys)
|
return keys, x.
|
||||||
|
Where("repo_id = ?", repoID).
|
||||||
|
Find(&keys)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import (
|
|||||||
|
|
||||||
. "github.com/smartystreets/goconvey/convey"
|
. "github.com/smartystreets/goconvey/convey"
|
||||||
|
|
||||||
"github.com/gogits/gogs/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import (
|
|||||||
"github.com/go-xorm/xorm"
|
"github.com/go-xorm/xorm"
|
||||||
gouuid "github.com/satori/go.uuid"
|
gouuid "github.com/satori/go.uuid"
|
||||||
|
|
||||||
"github.com/gogits/gogs/modules/base"
|
"code.gitea.io/gitea/modules/base"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AccessToken represents a personal access token.
|
// AccessToken represents a personal access token.
|
||||||
@@ -28,14 +28,17 @@ type AccessToken struct {
|
|||||||
HasUsed bool `xorm:"-"`
|
HasUsed bool `xorm:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BeforeInsert will be invoked by XORM before inserting a record representing this object.
|
||||||
func (t *AccessToken) BeforeInsert() {
|
func (t *AccessToken) BeforeInsert() {
|
||||||
t.CreatedUnix = time.Now().Unix()
|
t.CreatedUnix = time.Now().Unix()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BeforeUpdate is invoked from XORM before updating this object.
|
||||||
func (t *AccessToken) BeforeUpdate() {
|
func (t *AccessToken) BeforeUpdate() {
|
||||||
t.UpdatedUnix = time.Now().Unix()
|
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) {
|
func (t *AccessToken) AfterSet(colName string, _ xorm.Cell) {
|
||||||
switch colName {
|
switch colName {
|
||||||
case "created_unix":
|
case "created_unix":
|
||||||
@@ -72,7 +75,10 @@ func GetAccessTokenBySHA(sha string) (*AccessToken, error) {
|
|||||||
// ListAccessTokens returns a list of access tokens belongs to given user.
|
// ListAccessTokens returns a list of access tokens belongs to given user.
|
||||||
func ListAccessTokens(uid int64) ([]*AccessToken, error) {
|
func ListAccessTokens(uid int64) ([]*AccessToken, error) {
|
||||||
tokens := make([]*AccessToken, 0, 5)
|
tokens := make([]*AccessToken, 0, 5)
|
||||||
return tokens, x.Where("uid=?", uid).Desc("id").Find(&tokens)
|
return tokens, x.
|
||||||
|
Where("uid=?", uid).
|
||||||
|
Desc("id").
|
||||||
|
Find(&tokens)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateAccessToken updates information of access token.
|
// UpdateAccessToken updates information of access token.
|
||||||
@@ -82,7 +88,14 @@ func UpdateAccessToken(t *AccessToken) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// DeleteAccessTokenByID deletes access token by given ID.
|
// DeleteAccessTokenByID deletes access token by given ID.
|
||||||
func DeleteAccessTokenByID(id int64) error {
|
func DeleteAccessTokenByID(id, userID int64) error {
|
||||||
_, err := x.Id(id).Delete(new(AccessToken))
|
cnt, err := x.Id(id).Delete(&AccessToken{
|
||||||
return err
|
UID: userID,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
} else if cnt != 1 {
|
||||||
|
return ErrAccessTokenNotExist{}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,11 +10,12 @@ import (
|
|||||||
"os/exec"
|
"os/exec"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
git "github.com/gogits/git-module"
|
"code.gitea.io/git"
|
||||||
|
|
||||||
"github.com/gogits/gogs/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// UpdateTask defines an UpdateTask
|
||||||
type UpdateTask struct {
|
type UpdateTask struct {
|
||||||
ID int64 `xorm:"pk autoincr"`
|
ID int64 `xorm:"pk autoincr"`
|
||||||
UUID string `xorm:"index"`
|
UUID string `xorm:"index"`
|
||||||
@@ -23,6 +24,7 @@ type UpdateTask struct {
|
|||||||
NewCommitID string
|
NewCommitID string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AddUpdateTask adds an UpdateTask
|
||||||
func AddUpdateTask(task *UpdateTask) error {
|
func AddUpdateTask(task *UpdateTask) error {
|
||||||
_, err := x.Insert(task)
|
_, err := x.Insert(task)
|
||||||
return err
|
return err
|
||||||
@@ -42,6 +44,7 @@ func GetUpdateTaskByUUID(uuid string) (*UpdateTask, error) {
|
|||||||
return task, nil
|
return task, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeleteUpdateTaskByUUID deletes an UpdateTask from the database
|
||||||
func DeleteUpdateTaskByUUID(uuid string) error {
|
func DeleteUpdateTaskByUUID(uuid string) error {
|
||||||
_, err := x.Delete(&UpdateTask{UUID: uuid})
|
_, err := x.Delete(&UpdateTask{UUID: uuid})
|
||||||
return err
|
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 {
|
func ListToPushCommits(l *list.List) *PushCommits {
|
||||||
commits := make([]*PushCommit, 0)
|
var commits []*PushCommit
|
||||||
var actEmail string
|
var actEmail string
|
||||||
for e := l.Front(); e != nil; e = e.Next() {
|
for e := l.Front(); e != nil; e = e.Next() {
|
||||||
commit := e.Value.(*git.Commit)
|
commit := e.Value.(*git.Commit)
|
||||||
@@ -73,6 +77,7 @@ func ListToPushCommits(l *list.List) *PushCommits {
|
|||||||
return &PushCommits{l.Len(), commits, "", nil}
|
return &PushCommits{l.Len(), commits, "", nil}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PushUpdateOptions defines the push update options
|
||||||
type PushUpdateOptions struct {
|
type PushUpdateOptions struct {
|
||||||
PusherID int64
|
PusherID int64
|
||||||
PusherName string
|
PusherName string
|
||||||
@@ -86,10 +91,10 @@ type PushUpdateOptions struct {
|
|||||||
// PushUpdate must be called for any push actions in order to
|
// PushUpdate must be called for any push actions in order to
|
||||||
// generates necessary push action history feeds.
|
// generates necessary push action history feeds.
|
||||||
func PushUpdate(opts PushUpdateOptions) (err error) {
|
func PushUpdate(opts PushUpdateOptions) (err error) {
|
||||||
isNewRef := opts.OldCommitID == git.EMPTY_SHA
|
isNewRef := opts.OldCommitID == git.EmptySHA
|
||||||
isDelRef := opts.NewCommitID == git.EMPTY_SHA
|
isDelRef := opts.NewCommitID == git.EmptySHA
|
||||||
if isNewRef && isDelRef {
|
if isNewRef && isDelRef {
|
||||||
return fmt.Errorf("Old and new revisions are both %s", git.EMPTY_SHA)
|
return fmt.Errorf("Old and new revisions are both %s", git.EmptySHA)
|
||||||
}
|
}
|
||||||
|
|
||||||
repoPath := RepoPath(opts.RepoUserName, opts.RepoName)
|
repoPath := RepoPath(opts.RepoUserName, opts.RepoName)
|
||||||
@@ -101,7 +106,7 @@ func PushUpdate(opts PushUpdateOptions) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if isDelRef {
|
if isDelRef {
|
||||||
log.GitLogger.Info("Reference '%s' has been deleted from '%s/%s' by %d",
|
log.GitLogger.Info("Reference '%s' has been deleted from '%s/%s' by %s",
|
||||||
opts.RefFullName, opts.RepoUserName, opts.RepoName, opts.PusherName)
|
opts.RefFullName, opts.RepoUserName, opts.RepoName, opts.PusherName)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -122,7 +127,7 @@ func PushUpdate(opts PushUpdateOptions) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Push tags.
|
// Push tags.
|
||||||
if strings.HasPrefix(opts.RefFullName, git.TAG_PREFIX) {
|
if strings.HasPrefix(opts.RefFullName, git.TagPrefix) {
|
||||||
if err := CommitRepoAction(CommitRepoActionOptions{
|
if err := CommitRepoAction(CommitRepoActionOptions{
|
||||||
PusherName: opts.PusherName,
|
PusherName: opts.PusherName,
|
||||||
RepoOwnerID: owner.ID,
|
RepoOwnerID: owner.ID,
|
||||||
|
|||||||
290
models/user.go
290
models/user.go
@@ -8,10 +8,12 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"container/list"
|
"container/list"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
|
"crypto/subtle"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"image"
|
"image"
|
||||||
|
// Needed for jpeg support
|
||||||
_ "image/jpeg"
|
_ "image/jpeg"
|
||||||
"image/png"
|
"image/png"
|
||||||
"os"
|
"os"
|
||||||
@@ -23,31 +25,47 @@ import (
|
|||||||
"github.com/Unknwon/com"
|
"github.com/Unknwon/com"
|
||||||
"github.com/go-xorm/xorm"
|
"github.com/go-xorm/xorm"
|
||||||
"github.com/nfnt/resize"
|
"github.com/nfnt/resize"
|
||||||
|
"golang.org/x/crypto/pbkdf2"
|
||||||
|
|
||||||
"github.com/gogits/git-module"
|
"code.gitea.io/git"
|
||||||
api "github.com/gogits/go-gogs-client"
|
api "code.gitea.io/sdk/gitea"
|
||||||
|
|
||||||
"github.com/gogits/gogs/modules/avatar"
|
"code.gitea.io/gitea/modules/avatar"
|
||||||
"github.com/gogits/gogs/modules/base"
|
"code.gitea.io/gitea/modules/base"
|
||||||
"github.com/gogits/gogs/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"github.com/gogits/gogs/modules/markdown"
|
"code.gitea.io/gitea/modules/markdown"
|
||||||
"github.com/gogits/gogs/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// UserType defines the user type
|
||||||
type UserType int
|
type UserType int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
USER_TYPE_INDIVIDUAL UserType = iota // Historic reason to make it starts at 0.
|
// UserTypeIndividual defines an individual user
|
||||||
USER_TYPE_ORGANIZATION
|
UserTypeIndividual UserType = iota // Historic reason to make it starts at 0.
|
||||||
|
|
||||||
|
// UserTypeOrganization defines an organization
|
||||||
|
UserTypeOrganization
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrUserNotKeyOwner = errors.New("User does not the owner of public key")
|
// ErrUserNotKeyOwner user does not own this key error
|
||||||
ErrEmailNotExist = errors.New("E-mail does not exist")
|
ErrUserNotKeyOwner = errors.New("User does not own this public key")
|
||||||
ErrEmailNotActivated = errors.New("E-mail address has not been activated")
|
|
||||||
ErrUserNameIllegal = errors.New("User name contains illegal characters")
|
// 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")
|
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.
|
// User represents the object of individual and member of organization.
|
||||||
@@ -71,14 +89,16 @@ type User struct {
|
|||||||
Rands string `xorm:"VARCHAR(10)"`
|
Rands string `xorm:"VARCHAR(10)"`
|
||||||
Salt string `xorm:"VARCHAR(10)"`
|
Salt string `xorm:"VARCHAR(10)"`
|
||||||
|
|
||||||
Created time.Time `xorm:"-"`
|
Created time.Time `xorm:"-"`
|
||||||
CreatedUnix int64
|
CreatedUnix int64
|
||||||
Updated time.Time `xorm:"-"`
|
Updated time.Time `xorm:"-"`
|
||||||
UpdatedUnix int64
|
UpdatedUnix int64
|
||||||
|
LastLogin time.Time `xorm:"-"`
|
||||||
|
LastLoginUnix int64
|
||||||
|
|
||||||
// Remember visibility choice for convenience, true for private
|
// Remember visibility choice for convenience, true for private
|
||||||
LastRepoVisibility bool
|
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"`
|
MaxRepoCreation int `xorm:"NOT NULL DEFAULT -1"`
|
||||||
|
|
||||||
// Permissions
|
// Permissions
|
||||||
@@ -105,13 +125,18 @@ type User struct {
|
|||||||
NumMembers int
|
NumMembers int
|
||||||
Teams []*Team `xorm:"-"`
|
Teams []*Team `xorm:"-"`
|
||||||
Members []*User `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() {
|
func (u *User) BeforeInsert() {
|
||||||
u.CreatedUnix = time.Now().Unix()
|
u.CreatedUnix = time.Now().Unix()
|
||||||
u.UpdatedUnix = u.CreatedUnix
|
u.UpdatedUnix = u.CreatedUnix
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BeforeUpdate is invoked from XORM before updating this object.
|
||||||
func (u *User) BeforeUpdate() {
|
func (u *User) BeforeUpdate() {
|
||||||
if u.MaxRepoCreation < -1 {
|
if u.MaxRepoCreation < -1 {
|
||||||
u.MaxRepoCreation = -1
|
u.MaxRepoCreation = -1
|
||||||
@@ -119,6 +144,18 @@ func (u *User) BeforeUpdate() {
|
|||||||
u.UpdatedUnix = time.Now().Unix()
|
u.UpdatedUnix = time.Now().Unix()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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) {
|
func (u *User) AfterSet(colName string, _ xorm.Cell) {
|
||||||
switch colName {
|
switch colName {
|
||||||
case "full_name":
|
case "full_name":
|
||||||
@@ -127,22 +164,25 @@ func (u *User) AfterSet(colName string, _ xorm.Cell) {
|
|||||||
u.Created = time.Unix(u.CreatedUnix, 0).Local()
|
u.Created = time.Unix(u.CreatedUnix, 0).Local()
|
||||||
case "updated_unix":
|
case "updated_unix":
|
||||||
u.Updated = time.Unix(u.UpdatedUnix, 0).Local()
|
u.Updated = time.Unix(u.UpdatedUnix, 0).Local()
|
||||||
|
case "last_login_unix":
|
||||||
|
u.LastLogin = time.Unix(u.LastLoginUnix, 0).Local()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// APIFormat converts a User to api.User
|
||||||
func (u *User) APIFormat() *api.User {
|
func (u *User) APIFormat() *api.User {
|
||||||
return &api.User{
|
return &api.User{
|
||||||
ID: u.ID,
|
ID: u.ID,
|
||||||
UserName: u.Name,
|
UserName: u.Name,
|
||||||
FullName: u.FullName,
|
FullName: u.FullName,
|
||||||
Email: u.Email,
|
Email: u.Email,
|
||||||
AvatarUrl: u.AvatarLink(),
|
AvatarURL: u.AvatarLink(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns true if user login type is LOGIN_PLAIN.
|
// IsLocal returns true if user login type is LoginPlain.
|
||||||
func (u *User) IsLocal() bool {
|
func (u *User) IsLocal() bool {
|
||||||
return u.LoginType <= LOGIN_PLAIN
|
return u.LoginType <= LoginPlain
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasForkedRepo checks if user has already forked a repository with given ID.
|
// HasForkedRepo checks if user has already forked a repository with given ID.
|
||||||
@@ -151,6 +191,7 @@ func (u *User) HasForkedRepo(repoID int64) bool {
|
|||||||
return has
|
return has
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RepoCreationNum returns the number of repositories created by the user
|
||||||
func (u *User) RepoCreationNum() int {
|
func (u *User) RepoCreationNum() int {
|
||||||
if u.MaxRepoCreation <= -1 {
|
if u.MaxRepoCreation <= -1 {
|
||||||
return setting.Repository.MaxCreationLimit
|
return setting.Repository.MaxCreationLimit
|
||||||
@@ -158,6 +199,7 @@ func (u *User) RepoCreationNum() int {
|
|||||||
return u.MaxRepoCreation
|
return u.MaxRepoCreation
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CanCreateRepo returns if user login can create a repository
|
||||||
func (u *User) CanCreateRepo() bool {
|
func (u *User) CanCreateRepo() bool {
|
||||||
if u.MaxRepoCreation <= -1 {
|
if u.MaxRepoCreation <= -1 {
|
||||||
if setting.Repository.MaxCreationLimit <= -1 {
|
if setting.Repository.MaxCreationLimit <= -1 {
|
||||||
@@ -181,14 +223,14 @@ func (u *User) CanImportLocal() bool {
|
|||||||
// DashboardLink returns the user dashboard page link.
|
// DashboardLink returns the user dashboard page link.
|
||||||
func (u *User) DashboardLink() string {
|
func (u *User) DashboardLink() string {
|
||||||
if u.IsOrganization() {
|
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.
|
// HomeLink returns the user or organization home page link.
|
||||||
func (u *User) HomeLink() string {
|
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.
|
// GenerateEmailActivateCode generates an activate code based on user information and given e-mail.
|
||||||
@@ -244,17 +286,17 @@ func (u *User) GenerateRandomAvatar() error {
|
|||||||
// which includes app sub-url as prefix. However, it is possible
|
// which includes app sub-url as prefix. However, it is possible
|
||||||
// to return full URL if user enables Gravatar-like service.
|
// to return full URL if user enables Gravatar-like service.
|
||||||
func (u *User) RelAvatarLink() string {
|
func (u *User) RelAvatarLink() string {
|
||||||
defaultImgUrl := setting.AppSubUrl + "/img/avatar_default.png"
|
defaultImgURL := setting.AppSubURL + "/img/avatar_default.png"
|
||||||
if u.ID == -1 {
|
if u.ID == -1 {
|
||||||
return defaultImgUrl
|
return defaultImgURL
|
||||||
}
|
}
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case u.UseCustomAvatar:
|
case u.UseCustomAvatar:
|
||||||
if !com.IsExist(u.CustomAvatarPath()) {
|
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:
|
case setting.DisableGravatar, setting.OfflineMode:
|
||||||
if !com.IsExist(u.CustomAvatarPath()) {
|
if !com.IsExist(u.CustomAvatarPath()) {
|
||||||
if err := u.GenerateRandomAvatar(); err != nil {
|
if err := u.GenerateRandomAvatar(); err != nil {
|
||||||
@@ -262,7 +304,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)
|
return base.AvatarLink(u.AvatarEmail)
|
||||||
}
|
}
|
||||||
@@ -271,15 +313,17 @@ func (u *User) RelAvatarLink() string {
|
|||||||
func (u *User) AvatarLink() string {
|
func (u *User) AvatarLink() string {
|
||||||
link := u.RelAvatarLink()
|
link := u.RelAvatarLink()
|
||||||
if link[0] == '/' && link[1] != '/' {
|
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
|
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) {
|
func (u *User) GetFollowers(page int) ([]*User, error) {
|
||||||
users := make([]*User, 0, ItemsPerPage)
|
users := make([]*User, 0, ItemsPerPage)
|
||||||
sess := x.Limit(ItemsPerPage, (page-1)*ItemsPerPage).Where("follow.follow_id=?", u.ID)
|
sess := x.
|
||||||
|
Limit(ItemsPerPage, (page-1)*ItemsPerPage).
|
||||||
|
Where("follow.follow_id=?", u.ID)
|
||||||
if setting.UsePostgreSQL {
|
if setting.UsePostgreSQL {
|
||||||
sess = sess.Join("LEFT", "follow", `"user".id=follow.user_id`)
|
sess = sess.Join("LEFT", "follow", `"user".id=follow.user_id`)
|
||||||
} else {
|
} else {
|
||||||
@@ -288,6 +332,7 @@ func (u *User) GetFollowers(page int) ([]*User, error) {
|
|||||||
return users, sess.Find(&users)
|
return users, sess.Find(&users)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsFollowing returns true if user is following followID.
|
||||||
func (u *User) IsFollowing(followID int64) bool {
|
func (u *User) IsFollowing(followID int64) bool {
|
||||||
return IsFollowing(u.ID, followID)
|
return IsFollowing(u.ID, followID)
|
||||||
}
|
}
|
||||||
@@ -295,7 +340,9 @@ func (u *User) IsFollowing(followID int64) bool {
|
|||||||
// GetFollowing returns range of user's following.
|
// GetFollowing returns range of user's following.
|
||||||
func (u *User) GetFollowing(page int) ([]*User, error) {
|
func (u *User) GetFollowing(page int) ([]*User, error) {
|
||||||
users := make([]*User, 0, ItemsPerPage)
|
users := make([]*User, 0, ItemsPerPage)
|
||||||
sess := x.Limit(ItemsPerPage, (page-1)*ItemsPerPage).Where("follow.user_id=?", u.ID)
|
sess := x.
|
||||||
|
Limit(ItemsPerPage, (page-1)*ItemsPerPage).
|
||||||
|
Where("follow.user_id=?", u.ID)
|
||||||
if setting.UsePostgreSQL {
|
if setting.UsePostgreSQL {
|
||||||
sess = sess.Join("LEFT", "follow", `"user".id=follow.follow_id`)
|
sess = sess.Join("LEFT", "follow", `"user".id=follow.follow_id`)
|
||||||
} else {
|
} else {
|
||||||
@@ -315,7 +362,7 @@ func (u *User) NewGitSig() *git.Signature {
|
|||||||
|
|
||||||
// EncodePasswd encodes password to safe format.
|
// EncodePasswd encodes password to safe format.
|
||||||
func (u *User) EncodePasswd() {
|
func (u *User) EncodePasswd() {
|
||||||
newPasswd := base.PBKDF2([]byte(u.Passwd), []byte(u.Salt), 10000, 50, sha256.New)
|
newPasswd := pbkdf2.Key([]byte(u.Passwd), []byte(u.Salt), 10000, 50, sha256.New)
|
||||||
u.Passwd = fmt.Sprintf("%x", newPasswd)
|
u.Passwd = fmt.Sprintf("%x", newPasswd)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -323,7 +370,7 @@ func (u *User) EncodePasswd() {
|
|||||||
func (u *User) ValidatePassword(passwd string) bool {
|
func (u *User) ValidatePassword(passwd string) bool {
|
||||||
newUser := &User{Passwd: passwd, Salt: u.Salt}
|
newUser := &User{Passwd: passwd, Salt: u.Salt}
|
||||||
newUser.EncodePasswd()
|
newUser.EncodePasswd()
|
||||||
return u.Passwd == newUser.Passwd
|
return subtle.ConstantTimeCompare([]byte(u.Passwd), []byte(newUser.Passwd)) == 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// UploadAvatar saves custom avatar for user.
|
// UploadAvatar saves custom avatar for user.
|
||||||
@@ -334,7 +381,7 @@ func (u *User) UploadAvatar(data []byte) error {
|
|||||||
return fmt.Errorf("Decode: %v", err)
|
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()
|
sess := x.NewSession()
|
||||||
defer sessionRelease(sess)
|
defer sessionRelease(sess)
|
||||||
@@ -347,7 +394,10 @@ func (u *User) UploadAvatar(data []byte) error {
|
|||||||
return fmt.Errorf("updateUser: %v", err)
|
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())
|
fw, err := os.Create(u.CustomAvatarPath())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Create: %v", err)
|
return fmt.Errorf("Create: %v", err)
|
||||||
@@ -364,7 +414,10 @@ func (u *User) UploadAvatar(data []byte) error {
|
|||||||
// DeleteAvatar deletes the user's custom avatar.
|
// DeleteAvatar deletes the user's custom avatar.
|
||||||
func (u *User) DeleteAvatar() error {
|
func (u *User) DeleteAvatar() error {
|
||||||
log.Trace("DeleteAvatar[%d]: %s", u.ID, u.CustomAvatarPath())
|
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
|
u.UseCustomAvatar = false
|
||||||
if err := UpdateUser(u); err != nil {
|
if err := UpdateUser(u); err != nil {
|
||||||
@@ -375,7 +428,7 @@ func (u *User) DeleteAvatar() error {
|
|||||||
|
|
||||||
// IsAdminOfRepo returns true if user has admin or higher access of repository.
|
// IsAdminOfRepo returns true if user has admin or higher access of repository.
|
||||||
func (u *User) IsAdminOfRepo(repo *Repository) bool {
|
func (u *User) IsAdminOfRepo(repo *Repository) bool {
|
||||||
has, err := HasAccess(u, repo, ACCESS_MODE_ADMIN)
|
has, err := HasAccess(u, repo, AccessModeAdmin)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(3, "HasAccess: %v", err)
|
log.Error(3, "HasAccess: %v", err)
|
||||||
}
|
}
|
||||||
@@ -384,7 +437,7 @@ func (u *User) IsAdminOfRepo(repo *Repository) bool {
|
|||||||
|
|
||||||
// IsWriterOfRepo returns true if user has write access to given repository.
|
// IsWriterOfRepo returns true if user has write access to given repository.
|
||||||
func (u *User) IsWriterOfRepo(repo *Repository) bool {
|
func (u *User) IsWriterOfRepo(repo *Repository) bool {
|
||||||
has, err := HasAccess(u, repo, ACCESS_MODE_WRITE)
|
has, err := HasAccess(u, repo, AccessModeWrite)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(3, "HasAccess: %v", err)
|
log.Error(3, "HasAccess: %v", err)
|
||||||
}
|
}
|
||||||
@@ -393,21 +446,23 @@ func (u *User) IsWriterOfRepo(repo *Repository) bool {
|
|||||||
|
|
||||||
// IsOrganization returns true if user is actually a organization.
|
// IsOrganization returns true if user is actually a organization.
|
||||||
func (u *User) IsOrganization() bool {
|
func (u *User) IsOrganization() bool {
|
||||||
return u.Type == USER_TYPE_ORGANIZATION
|
return u.Type == UserTypeOrganization
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsUserOrgOwner returns true if user is in the owner team of given organization.
|
// IsUserOrgOwner returns true if user is in the owner team of given organization.
|
||||||
func (u *User) IsUserOrgOwner(orgId int64) bool {
|
func (u *User) IsUserOrgOwner(orgID int64) bool {
|
||||||
return IsOrganizationOwner(orgId, u.ID)
|
return IsOrganizationOwner(orgID, u.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsPublicMember returns true if user public his/her membership in give organization.
|
// IsPublicMember returns true if user public his/her membership in give organization.
|
||||||
func (u *User) IsPublicMember(orgId int64) bool {
|
func (u *User) IsPublicMember(orgID int64) bool {
|
||||||
return IsPublicMembership(orgId, u.ID)
|
return IsPublicMembership(orgID, u.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *User) getOrganizationCount(e Engine) (int64, error) {
|
func (u *User) getOrganizationCount(e Engine) (int64, error) {
|
||||||
return e.Where("uid=?", u.ID).Count(new(OrgUser))
|
return e.
|
||||||
|
Where("uid=?", u.ID).
|
||||||
|
Count(new(OrgUser))
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetOrganizationCount returns count of membership of organization of user.
|
// GetOrganizationCount returns count of membership of organization of user.
|
||||||
@@ -421,7 +476,7 @@ func (u *User) GetRepositories(page, pageSize int) (err error) {
|
|||||||
return err
|
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) {
|
func (u *User) GetMirrorRepositories() ([]*Repository, error) {
|
||||||
return GetUserMirrorRepositories(u.ID)
|
return GetUserMirrorRepositories(u.ID)
|
||||||
}
|
}
|
||||||
@@ -458,6 +513,7 @@ func (u *User) DisplayName() string {
|
|||||||
return u.Name
|
return u.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ShortName ellipses username to length
|
||||||
func (u *User) ShortName(length int) string {
|
func (u *User) ShortName(length int) string {
|
||||||
return base.EllipsisString(u.Name, length)
|
return base.EllipsisString(u.Name, length)
|
||||||
}
|
}
|
||||||
@@ -470,11 +526,13 @@ func IsUserExist(uid int64, name string) (bool, error) {
|
|||||||
if len(name) == 0 {
|
if len(name) == 0 {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
return x.Where("id!=?", uid).Get(&User{LowerName: strings.ToLower(name)})
|
return x.
|
||||||
|
Where("id!=?", uid).
|
||||||
|
Get(&User{LowerName: strings.ToLower(name)})
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetUserSalt returns a ramdom user salt token.
|
// GetUserSalt returns a ramdom user salt token.
|
||||||
func GetUserSalt() string {
|
func GetUserSalt() (string, error) {
|
||||||
return base.GetRandomString(10)
|
return base.GetRandomString(10)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -488,12 +546,12 @@ func NewGhostUser() *User {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
reversedUsernames = []string{"debug", "raw", "install", "api", "avatar", "user", "org", "help", "stars", "issues", "pulls", "commits", "repo", "template", "admin", "new", ".", ".."}
|
reservedUsernames = []string{"assets", "css", "img", "js", "less", "plugins", "debug", "raw", "install", "api", "avatar", "user", "org", "help", "stars", "issues", "pulls", "commits", "repo", "template", "admin", "new", ".", ".."}
|
||||||
reversedUserPatterns = []string{"*.keys"}
|
reservedUserPatterns = []string{"*.keys"}
|
||||||
)
|
)
|
||||||
|
|
||||||
// isUsableName checks if name is reserved or pattern of name is not allowed
|
// 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 '*'.
|
// Names are exact match, patterns can be prefix or suffix match with placeholder '*'.
|
||||||
func isUsableName(names, patterns []string, name string) error {
|
func isUsableName(names, patterns []string, name string) error {
|
||||||
name = strings.TrimSpace(strings.ToLower(name))
|
name = strings.TrimSpace(strings.ToLower(name))
|
||||||
@@ -517,8 +575,9 @@ func isUsableName(names, patterns []string, name string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsUsableUsername returns an error when a username is reserved
|
||||||
func IsUsableUsername(name string) error {
|
func IsUsableUsername(name string) error {
|
||||||
return isUsableName(reversedUsernames, reversedUserPatterns, name)
|
return isUsableName(reservedUsernames, reservedUserPatterns, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateUser creates record of a new user.
|
// CreateUser creates record of a new user.
|
||||||
@@ -545,8 +604,12 @@ func CreateUser(u *User) (err error) {
|
|||||||
u.LowerName = strings.ToLower(u.Name)
|
u.LowerName = strings.ToLower(u.Name)
|
||||||
u.AvatarEmail = u.Email
|
u.AvatarEmail = u.Email
|
||||||
u.Avatar = base.HashEmail(u.AvatarEmail)
|
u.Avatar = base.HashEmail(u.AvatarEmail)
|
||||||
u.Rands = GetUserSalt()
|
if u.Rands, err = GetUserSalt(); err != nil {
|
||||||
u.Salt = GetUserSalt()
|
return err
|
||||||
|
}
|
||||||
|
if u.Salt, err = GetUserSalt(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
u.EncodePasswd()
|
u.EncodePasswd()
|
||||||
u.MaxRepoCreation = -1
|
u.MaxRepoCreation = -1
|
||||||
|
|
||||||
@@ -566,7 +629,9 @@ func CreateUser(u *User) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func countUsers(e Engine) int64 {
|
func countUsers(e Engine) int64 {
|
||||||
count, _ := e.Where("type=0").Count(new(User))
|
count, _ := e.
|
||||||
|
Where("type=0").
|
||||||
|
Count(new(User))
|
||||||
return count
|
return count
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -578,7 +643,11 @@ func CountUsers() int64 {
|
|||||||
// Users returns number of users in given page.
|
// Users returns number of users in given page.
|
||||||
func Users(page, pageSize int) ([]*User, error) {
|
func Users(page, pageSize int) ([]*User, error) {
|
||||||
users := make([]*User, 0, pageSize)
|
users := make([]*User, 0, pageSize)
|
||||||
return users, x.Limit(pageSize, (page-1)*pageSize).Where("type=0").Asc("id").Find(&users)
|
return users, x.
|
||||||
|
Limit(pageSize, (page-1)*pageSize).
|
||||||
|
Where("type=0").
|
||||||
|
Asc("name").
|
||||||
|
Find(&users)
|
||||||
}
|
}
|
||||||
|
|
||||||
// get user by erify code
|
// get user by erify code
|
||||||
@@ -599,7 +668,7 @@ func getVerifyUser(code string) (user *User) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// verify active code when active account
|
// VerifyUserActiveCode verifies active code when active account
|
||||||
func VerifyUserActiveCode(code string) (user *User) {
|
func VerifyUserActiveCode(code string) (user *User) {
|
||||||
minutes := setting.Service.ActiveCodeLives
|
minutes := setting.Service.ActiveCodeLives
|
||||||
|
|
||||||
@@ -615,7 +684,7 @@ func VerifyUserActiveCode(code string) (user *User) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// verify active code when active account
|
// VerifyActiveEmailCode verifies active email code when active account
|
||||||
func VerifyActiveEmailCode(code, email string) *EmailAddress {
|
func VerifyActiveEmailCode(code, email string) *EmailAddress {
|
||||||
minutes := setting.Service.ActiveCodeLives
|
minutes := setting.Service.ActiveCodeLives
|
||||||
|
|
||||||
@@ -652,11 +721,13 @@ func ChangeUserName(u *User, newUserName string) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Delete all local copies of repository wiki that user owns.
|
// Delete all local copies of repository wiki that user owns.
|
||||||
if err = x.Where("owner_id=?", u.ID).Iterate(new(Repository), func(idx int, bean interface{}) error {
|
if err = x.
|
||||||
repo := bean.(*Repository)
|
Where("owner_id=?", u.ID).
|
||||||
RemoveAllWithNotice("Delete repository wiki local copy", repo.LocalWikiPath())
|
Iterate(new(Repository), func(idx int, bean interface{}) error {
|
||||||
return nil
|
repo := bean.(*Repository)
|
||||||
}); err != nil {
|
RemoveAllWithNotice("Delete repository wiki local copy", repo.LocalWikiPath())
|
||||||
|
return nil
|
||||||
|
}); err != nil {
|
||||||
return fmt.Errorf("Delete repository wiki local copy: %v", err)
|
return fmt.Errorf("Delete repository wiki local copy: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -667,7 +738,11 @@ func updateUser(e Engine, u *User) error {
|
|||||||
// Organization does not need email
|
// Organization does not need email
|
||||||
if !u.IsOrganization() {
|
if !u.IsOrganization() {
|
||||||
u.Email = strings.ToLower(u.Email)
|
u.Email = strings.ToLower(u.Email)
|
||||||
has, err := e.Where("id!=?", u.ID).And("type=?", u.Type).And("email=?", u.Email).Get(new(User))
|
has, err := e.
|
||||||
|
Where("id!=?", u.ID).
|
||||||
|
And("type=?", u.Type).
|
||||||
|
And("email=?", u.Email).
|
||||||
|
Get(new(User))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
} else if has {
|
} else if has {
|
||||||
@@ -803,9 +878,18 @@ func deleteUser(e *xorm.Session, u *User) error {
|
|||||||
// FIXME: system notice
|
// FIXME: system notice
|
||||||
// Note: There are something just cannot be roll back,
|
// Note: There are something just cannot be roll back,
|
||||||
// so just keep error logs of those operations.
|
// so just keep error logs of those operations.
|
||||||
|
path := UserPath(u.Name)
|
||||||
|
|
||||||
os.RemoveAll(UserPath(u.Name))
|
if err := os.RemoveAll(path); err != nil {
|
||||||
os.Remove(u.CustomAvatarPath())
|
return fmt.Errorf("Fail to RemoveAll %s: %v", path, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
avatarPath := u.CustomAvatarPath()
|
||||||
|
if com.IsExist(avatarPath) {
|
||||||
|
if err := os.Remove(avatarPath); err != nil {
|
||||||
|
return fmt.Errorf("Fail to remove %s: %v", avatarPath, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -834,7 +918,9 @@ func DeleteUser(u *User) (err error) {
|
|||||||
// DeleteInactivateUsers deletes all inactivate users and email addresses.
|
// DeleteInactivateUsers deletes all inactivate users and email addresses.
|
||||||
func DeleteInactivateUsers() (err error) {
|
func DeleteInactivateUsers() (err error) {
|
||||||
users := make([]*User, 0, 10)
|
users := make([]*User, 0, 10)
|
||||||
if err = x.Where("is_active = ?", false).Find(&users); err != nil {
|
if err = x.
|
||||||
|
Where("is_active = ?", false).
|
||||||
|
Find(&users); err != nil {
|
||||||
return fmt.Errorf("get all inactive users: %v", err)
|
return fmt.Errorf("get all inactive users: %v", err)
|
||||||
}
|
}
|
||||||
// FIXME: should only update authorized_keys file once after all deletions.
|
// FIXME: should only update authorized_keys file once after all deletions.
|
||||||
@@ -848,7 +934,9 @@ func DeleteInactivateUsers() (err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = x.Where("is_activated = ?", false).Delete(new(EmailAddress))
|
_, err = x.
|
||||||
|
Where("is_activated = ?", false).
|
||||||
|
Delete(new(EmailAddress))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -857,15 +945,19 @@ func UserPath(userName string) string {
|
|||||||
return filepath.Join(setting.RepoRootPath, strings.ToLower(userName))
|
return filepath.Join(setting.RepoRootPath, strings.ToLower(userName))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetUserByKeyID get user information by user's public key id
|
||||||
func GetUserByKeyID(keyID int64) (*User, error) {
|
func GetUserByKeyID(keyID int64) (*User, error) {
|
||||||
user := new(User)
|
var user User
|
||||||
has, err := x.Sql("SELECT a.* FROM `user` AS a, public_key AS b WHERE a.id = b.owner_id AND b.id=?", keyID).Get(user)
|
has, err := x.Join("INNER", "public_key", "`public_key`.owner_id = `user`.id").
|
||||||
|
Where("`public_key`.id=?", keyID).
|
||||||
|
Get(&user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if !has {
|
|
||||||
return nil, ErrUserNotKeyOwner
|
|
||||||
}
|
}
|
||||||
return user, nil
|
if !has {
|
||||||
|
return nil, ErrUserNotExist{0, "", keyID}
|
||||||
|
}
|
||||||
|
return &user, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getUserByID(e Engine, id int64) (*User, error) {
|
func getUserByID(e Engine, id int64) (*User, error) {
|
||||||
@@ -874,7 +966,7 @@ func getUserByID(e Engine, id int64) (*User, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if !has {
|
} else if !has {
|
||||||
return nil, ErrUserNotExist{id, ""}
|
return nil, ErrUserNotExist{id, "", 0}
|
||||||
}
|
}
|
||||||
return u, nil
|
return u, nil
|
||||||
}
|
}
|
||||||
@@ -886,11 +978,11 @@ func GetUserByID(id int64) (*User, error) {
|
|||||||
|
|
||||||
// GetAssigneeByID returns the user with write access of repository by given ID.
|
// GetAssigneeByID returns the user with write access of repository by given ID.
|
||||||
func GetAssigneeByID(repo *Repository, userID int64) (*User, error) {
|
func GetAssigneeByID(repo *Repository, userID int64) (*User, error) {
|
||||||
has, err := HasAccess(&User{ID: userID}, repo, ACCESS_MODE_WRITE)
|
has, err := HasAccess(&User{ID: userID}, repo, AccessModeWrite)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if !has {
|
} else if !has {
|
||||||
return nil, ErrUserNotExist{userID, ""}
|
return nil, ErrUserNotExist{userID, "", 0}
|
||||||
}
|
}
|
||||||
return GetUserByID(userID)
|
return GetUserByID(userID)
|
||||||
}
|
}
|
||||||
@@ -898,14 +990,14 @@ func GetAssigneeByID(repo *Repository, userID int64) (*User, error) {
|
|||||||
// GetUserByName returns user by given name.
|
// GetUserByName returns user by given name.
|
||||||
func GetUserByName(name string) (*User, error) {
|
func GetUserByName(name string) (*User, error) {
|
||||||
if len(name) == 0 {
|
if len(name) == 0 {
|
||||||
return nil, ErrUserNotExist{0, name}
|
return nil, ErrUserNotExist{0, name, 0}
|
||||||
}
|
}
|
||||||
u := &User{LowerName: strings.ToLower(name)}
|
u := &User{LowerName: strings.ToLower(name)}
|
||||||
has, err := x.Get(u)
|
has, err := x.Get(u)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if !has {
|
} else if !has {
|
||||||
return nil, ErrUserNotExist{0, name}
|
return nil, ErrUserNotExist{0, name, 0}
|
||||||
}
|
}
|
||||||
return u, nil
|
return u, nil
|
||||||
}
|
}
|
||||||
@@ -923,6 +1015,16 @@ func GetUserEmailsByNames(names []string) []string {
|
|||||||
return mails
|
return mails
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetUsersByIDs returns all resolved users from a list of Ids.
|
||||||
|
func GetUsersByIDs(ids []int64) ([]*User, error) {
|
||||||
|
ous := make([]*User, 0, len(ids))
|
||||||
|
err := x.
|
||||||
|
In("id", ids).
|
||||||
|
Asc("name").
|
||||||
|
Find(&ous)
|
||||||
|
return ous, err
|
||||||
|
}
|
||||||
|
|
||||||
// GetUserIDsByNames returns a slice of ids corresponds to names.
|
// GetUserIDsByNames returns a slice of ids corresponds to names.
|
||||||
func GetUserIDsByNames(names []string) []int64 {
|
func GetUserIDsByNames(names []string) []int64 {
|
||||||
ids := make([]int64, 0, len(names))
|
ids := make([]int64, 0, len(names))
|
||||||
@@ -981,7 +1083,7 @@ func ValidateCommitsWithEmails(oldCommits *list.List) *list.List {
|
|||||||
// GetUserByEmail returns the user object by given e-mail if exists.
|
// GetUserByEmail returns the user object by given e-mail if exists.
|
||||||
func GetUserByEmail(email string) (*User, error) {
|
func GetUserByEmail(email string) (*User, error) {
|
||||||
if len(email) == 0 {
|
if len(email) == 0 {
|
||||||
return nil, ErrUserNotExist{0, "email"}
|
return nil, ErrUserNotExist{0, email, 0}
|
||||||
}
|
}
|
||||||
|
|
||||||
email = strings.ToLower(email)
|
email = strings.ToLower(email)
|
||||||
@@ -1005,9 +1107,10 @@ func GetUserByEmail(email string) (*User, error) {
|
|||||||
return GetUserByID(emailAddress.UID)
|
return GetUserByID(emailAddress.UID)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, ErrUserNotExist{0, email}
|
return nil, ErrUserNotExist{0, email, 0}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SearchUserOptions contains the options for searching
|
||||||
type SearchUserOptions struct {
|
type SearchUserOptions struct {
|
||||||
Keyword string
|
Keyword string
|
||||||
Type UserType
|
Type UserType
|
||||||
@@ -1034,7 +1137,8 @@ func SearchUserByName(opts *SearchUserOptions) (users []*User, _ int64, _ error)
|
|||||||
searchQuery := "%" + opts.Keyword + "%"
|
searchQuery := "%" + opts.Keyword + "%"
|
||||||
users = make([]*User, 0, opts.PageSize)
|
users = make([]*User, 0, opts.PageSize)
|
||||||
// Append conditions
|
// Append conditions
|
||||||
sess := x.Where("LOWER(lower_name) LIKE ?", searchQuery).
|
sess := x.
|
||||||
|
Where("LOWER(lower_name) LIKE ?", searchQuery).
|
||||||
Or("LOWER(full_name) LIKE ?", searchQuery).
|
Or("LOWER(full_name) LIKE ?", searchQuery).
|
||||||
And("type = ?", opts.Type)
|
And("type = ?", opts.Type)
|
||||||
|
|
||||||
@@ -1048,7 +1152,9 @@ func SearchUserByName(opts *SearchUserOptions) (users []*User, _ int64, _ error)
|
|||||||
if len(opts.OrderBy) > 0 {
|
if len(opts.OrderBy) > 0 {
|
||||||
sess.OrderBy(opts.OrderBy)
|
sess.OrderBy(opts.OrderBy)
|
||||||
}
|
}
|
||||||
return users, count, sess.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize).Find(&users)
|
return users, count, sess.
|
||||||
|
Limit(opts.PageSize, (opts.Page-1)*opts.PageSize).
|
||||||
|
Find(&users)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ___________ .__ .__
|
// ___________ .__ .__
|
||||||
@@ -1065,6 +1171,7 @@ type Follow struct {
|
|||||||
FollowID int64 `xorm:"UNIQUE(follow)"`
|
FollowID int64 `xorm:"UNIQUE(follow)"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsFollowing returns true if user is following followID.
|
||||||
func IsFollowing(userID, followID int64) bool {
|
func IsFollowing(userID, followID int64) bool {
|
||||||
has, _ := x.Get(&Follow{UserID: userID, FollowID: followID})
|
has, _ := x.Get(&Follow{UserID: userID, FollowID: followID})
|
||||||
return has
|
return has
|
||||||
@@ -1121,3 +1228,18 @@ func UnfollowUser(userID, followID int64) (err error) {
|
|||||||
}
|
}
|
||||||
return sess.Commit()
|
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
|
||||||
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user